diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index fedf55d1..00000000 --- a/.dockerignore +++ /dev/null @@ -1,33 +0,0 @@ -admin/admin -checker/checker -dashboard/dashboard -evdist/evdist -generator/generator -receiver/receiver -repochecker/repochecker -frontend/fic/build -frontend/fic/node_modules -qa/ui/build -qa/ui/node_modules -fickit-backend-initrd.img -fickit-backend-kernel -fickit-backend-squashfs.img -fickit-backend-state -fickit-frontend-initrd.img -fickit-frontend-kernel -fickit-frontend-squashfs.img -fickit-frontend-state -fickit-prepare-initrd.img -fickit-prepare-kernel -fickit-update-initrd.img -fickit-update-kernel -DASHBOARD -FILES -PKI -REMOTE -repochecker/*.so -SETTINGS -SETTINGSDIST -submissions -TEAMS -vendor \ No newline at end of file diff --git a/.drone-manifest-fic-admin.yml b/.drone-manifest-fic-admin.yml deleted file mode 100644 index fc1154f7..00000000 --- a/.drone-manifest-fic-admin.yml +++ /dev/null @@ -1,22 +0,0 @@ -image: nemunaire/fic-admin:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} -{{#if build.tags}} -tags: -{{#each build.tags}} - - {{this}} -{{/each}} -{{/if}} -manifests: - - image: nemunaire/fic-admin:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 - platform: - architecture: amd64 - os: linux - - image: nemunaire/fic-admin:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 - platform: - architecture: arm64 - os: linux - variant: v8 - - image: nemunaire/fic-admin:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm - platform: - architecture: arm - os: linux - variant: v7 diff --git a/.drone-manifest-fic-checker.yml b/.drone-manifest-fic-checker.yml deleted file mode 100644 index 13721117..00000000 --- a/.drone-manifest-fic-checker.yml +++ /dev/null @@ -1,22 +0,0 @@ -image: nemunaire/fic-checker:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} -{{#if build.tags}} -tags: -{{#each build.tags}} - - {{this}} -{{/each}} -{{/if}} -manifests: - - image: nemunaire/fic-checker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 - platform: - architecture: amd64 - os: linux - - image: nemunaire/fic-checker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 - platform: - architecture: arm64 - os: linux - variant: v8 - - image: nemunaire/fic-checker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm - platform: - architecture: arm - os: linux - variant: v7 diff --git a/.drone-manifest-fic-dashboard.yml b/.drone-manifest-fic-dashboard.yml deleted file mode 100644 index 4f74d234..00000000 --- a/.drone-manifest-fic-dashboard.yml +++ /dev/null @@ -1,22 +0,0 @@ -image: nemunaire/fic-dashboard:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} -{{#if build.tags}} -tags: -{{#each build.tags}} - - {{this}} -{{/each}} -{{/if}} -manifests: - - image: nemunaire/fic-dashboard:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 - platform: - architecture: amd64 - os: linux - - image: nemunaire/fic-dashboard:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 - platform: - architecture: arm64 - os: linux - variant: v8 - - image: nemunaire/fic-dashboard:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm - platform: - architecture: arm - os: linux - variant: v7 diff --git a/.drone-manifest-fic-evdist.yml b/.drone-manifest-fic-evdist.yml deleted file mode 100644 index 70b1e5c5..00000000 --- a/.drone-manifest-fic-evdist.yml +++ /dev/null @@ -1,22 +0,0 @@ -image: nemunaire/fic-evdist:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} -{{#if build.tags}} -tags: -{{#each build.tags}} - - {{this}} -{{/each}} -{{/if}} -manifests: - - image: nemunaire/fic-evdist:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 - platform: - architecture: amd64 - os: linux - - image: nemunaire/fic-evdist:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 - platform: - architecture: arm64 - os: linux - variant: v8 - - image: nemunaire/fic-evdist:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm - platform: - architecture: arm - os: linux - variant: v7 diff --git a/.drone-manifest-fic-frontend-ui.yml b/.drone-manifest-fic-frontend-ui.yml deleted file mode 100644 index 0fc4a3a6..00000000 --- a/.drone-manifest-fic-frontend-ui.yml +++ /dev/null @@ -1,22 +0,0 @@ -image: nemunaire/fic-frontend-ui:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} -{{#if build.tags}} -tags: -{{#each build.tags}} - - {{this}} -{{/each}} -{{/if}} -manifests: - - image: nemunaire/fic-frontend-ui:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 - platform: - architecture: amd64 - os: linux - - image: nemunaire/fic-frontend-ui:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 - platform: - architecture: arm64 - os: linux - variant: v8 - - image: nemunaire/fic-frontend-ui:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm - platform: - architecture: arm - os: linux - variant: v7 diff --git a/.drone-manifest-fic-generator.yml b/.drone-manifest-fic-generator.yml deleted file mode 100644 index a2b2443f..00000000 --- a/.drone-manifest-fic-generator.yml +++ /dev/null @@ -1,22 +0,0 @@ -image: nemunaire/fic-generator:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} -{{#if build.tags}} -tags: -{{#each build.tags}} - - {{this}} -{{/each}} -{{/if}} -manifests: - - image: nemunaire/fic-generator:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 - platform: - architecture: amd64 - os: linux - - image: nemunaire/fic-generator:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 - platform: - architecture: arm64 - os: linux - variant: v8 - - image: nemunaire/fic-generator:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm - platform: - architecture: arm - os: linux - variant: v7 diff --git a/.drone-manifest-fic-get-remote-files.yml b/.drone-manifest-fic-get-remote-files.yml deleted file mode 100644 index 0f64b8c1..00000000 --- a/.drone-manifest-fic-get-remote-files.yml +++ /dev/null @@ -1,22 +0,0 @@ -image: nemunaire/fic-get-remote-files:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} -{{#if build.tags}} -tags: -{{#each build.tags}} - - {{this}} -{{/each}} -{{/if}} -manifests: - - image: nemunaire/fic-get-remote-files:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 - platform: - architecture: amd64 - os: linux - - image: nemunaire/fic-get-remote-files:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 - platform: - architecture: arm64 - os: linux - variant: v8 - - image: nemunaire/fic-get-remote-files:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm - platform: - architecture: arm - os: linux - variant: v7 diff --git a/.drone-manifest-fic-nginx.yml b/.drone-manifest-fic-nginx.yml deleted file mode 100644 index cbd2dcce..00000000 --- a/.drone-manifest-fic-nginx.yml +++ /dev/null @@ -1,22 +0,0 @@ -image: nemunaire/fic-nginx:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} -{{#if build.tags}} -tags: -{{#each build.tags}} - - {{this}} -{{/each}} -{{/if}} -manifests: - - image: nemunaire/fic-nginx:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 - platform: - architecture: amd64 - os: linux - - image: nemunaire/fic-nginx:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 - platform: - architecture: arm64 - os: linux - variant: v8 - - image: nemunaire/fic-nginx:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm - platform: - architecture: arm - os: linux - variant: v7 diff --git a/.drone-manifest-fic-qa.yml b/.drone-manifest-fic-qa.yml deleted file mode 100644 index 5a158970..00000000 --- a/.drone-manifest-fic-qa.yml +++ /dev/null @@ -1,22 +0,0 @@ -image: nemunaire/fic-qa:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} -{{#if build.tags}} -tags: -{{#each build.tags}} - - {{this}} -{{/each}} -{{/if}} -manifests: - - image: nemunaire/fic-qa:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 - platform: - architecture: amd64 - os: linux - - image: nemunaire/fic-qa:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 - platform: - architecture: arm64 - os: linux - variant: v8 - - image: nemunaire/fic-qa:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm - platform: - architecture: arm - os: linux - variant: v7 diff --git a/.drone-manifest-fic-receiver.yml b/.drone-manifest-fic-receiver.yml deleted file mode 100644 index eda2fe25..00000000 --- a/.drone-manifest-fic-receiver.yml +++ /dev/null @@ -1,22 +0,0 @@ -image: nemunaire/fic-receiver:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} -{{#if build.tags}} -tags: -{{#each build.tags}} - - {{this}} -{{/each}} -{{/if}} -manifests: - - image: nemunaire/fic-receiver:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 - platform: - architecture: amd64 - os: linux - - image: nemunaire/fic-receiver:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 - platform: - architecture: arm64 - os: linux - variant: v8 - - image: nemunaire/fic-receiver:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm - platform: - architecture: arm - os: linux - variant: v7 diff --git a/.drone-manifest-fic-repochecker.yml b/.drone-manifest-fic-repochecker.yml deleted file mode 100644 index 9b239931..00000000 --- a/.drone-manifest-fic-repochecker.yml +++ /dev/null @@ -1,22 +0,0 @@ -image: nemunaire/fic-repochecker:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} -{{#if build.tags}} -tags: -{{#each build.tags}} - - {{this}} -{{/each}} -{{/if}} -manifests: - - image: nemunaire/fic-repochecker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 - platform: - architecture: amd64 - os: linux - - image: nemunaire/fic-repochecker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 - platform: - architecture: arm64 - os: linux - variant: v8 - - image: nemunaire/fic-repochecker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm - platform: - architecture: arm - os: linux - variant: v7 diff --git a/.drone-manifest-fickit-deploy.yml b/.drone-manifest-fickit-deploy.yml deleted file mode 100644 index bfa37052..00000000 --- a/.drone-manifest-fickit-deploy.yml +++ /dev/null @@ -1,22 +0,0 @@ -image: nemunaire/fickit-deploy:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} -{{#if build.tags}} -tags: -{{#each build.tags}} - - {{this}} -{{/each}} -{{/if}} -manifests: - - image: nemunaire/fickit-deploy:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 - platform: - architecture: amd64 - os: linux - - image: nemunaire/fickit-deploy:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 - platform: - architecture: arm64 - os: linux - variant: v8 - - image: nemunaire/fickit-deploy:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm - platform: - architecture: arm - os: linux - variant: v7 diff --git a/.drone.yml b/.drone.yml deleted file mode 100644 index 3e0e2cbc..00000000 --- a/.drone.yml +++ /dev/null @@ -1,816 +0,0 @@ ---- -kind: pipeline -type: docker -name: build-amd64 - -platform: - os: linux - arch: amd64 - -workspace: - base: /go - path: src/srs.epita.fr/fic-server - -steps: - - name: get deps - image: golang:alpine - commands: - - apk --no-cache add git - - go get -v -d ./... - - mkdir deploy - - - name: build qa ui - image: node:23-alpine - commands: - - cd qa/ui - - npm install --network-timeout=100000 - - npm run build - - tar chjf ../../deploy/htdocs-qa.tar.bz2 build - - - name: vet and tests - image: golang:alpine - commands: - - apk --no-cache add build-base - - go vet -buildvcs=false -tags gitgo ./... - - go vet -buildvcs=false ./... - - go test ./... - - - name: build admin - image: golang:alpine - commands: - - go build -buildvcs=false -tags gitgo -o deploy/admin-gitgo-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/admin - - go build -buildvcs=false -o deploy/admin-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/admin - - tar chjf deploy/htdocs-admin.tar.bz2 htdocs-admin - environment: - CGO_ENABLED: 0 - when: - branch: - exclude: - - master - - - name: build checker - image: golang:alpine - commands: - - go build -buildvcs=false -o deploy/checker-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/checker - environment: - CGO_ENABLED: 0 - when: - branch: - exclude: - - master - - - name: build evdist - image: golang:alpine - commands: - - go build -buildvcs=false -o deploy/evdist-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/evdist - environment: - CGO_ENABLED: 0 - when: - branch: - exclude: - - master - - - name: build generator - image: golang:alpine - commands: - - go build -buildvcs=false -o deploy/generator-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/generator - environment: - CGO_ENABLED: 0 - when: - branch: - exclude: - - master - - - name: build receiver - image: golang:alpine - commands: - - go build -buildvcs=false -o deploy/receiver-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/receiver - environment: - CGO_ENABLED: 0 - when: - branch: - exclude: - - master - - - name: build frontend fic ui - image: node:23-alpine - commands: - - cd frontend/fic - - npm install --network-timeout=100000 - - npm run build - - tar chjf ../../deploy/htdocs-frontend-fic.tar.bz2 build - when: - branch: - exclude: - - master - - - name: build dashboard - image: golang:alpine - commands: - - go build -buildvcs=false -o deploy/dashboard-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/dashboard - - tar chjf deploy/htdocs-dashboard.tar.bz2 htdocs-dashboard - environment: - CGO_ENABLED: 0 - when: - branch: - exclude: - - master - - - name: build repochecker - image: golang:alpine - commands: - - apk --no-cache add build-base - - go build -buildvcs=false --tags checkupdate -o deploy/repochecker-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker - - go build -buildvcs=false -buildmode=plugin -o deploy/repochecker-epita-rules-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.so srs.epita.fr/fic-server/repochecker/epita - - go build -buildvcs=false -buildmode=plugin -o deploy/repochecker-file-inspector-rules-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.so srs.epita.fr/fic-server/repochecker/file-inspector - - go build -buildvcs=false -buildmode=plugin -o deploy/repochecker-grammalecte-rules-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.so srs.epita.fr/fic-server/repochecker/grammalecte - - go build -buildvcs=false -buildmode=plugin -o deploy/repochecker-pcap-inspector-rules-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.so srs.epita.fr/fic-server/repochecker/pcap-inspector - - go build -buildvcs=false -buildmode=plugin -o deploy/repochecker-videos-rules-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}.so srs.epita.fr/fic-server/repochecker/videos - - grep "const version" repochecker/update.go | sed -r 's/^.*=\s*(\S.*)$/\1/' > deploy/repochecker.version - when: - branch: - exclude: - - master - - - name: build qa - image: golang:alpine - commands: - - go build -buildvcs=false -o deploy/qa-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/qa - environment: - CGO_ENABLED: 0 - when: - branch: - exclude: - - master - - - name: docker admin - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-admin - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-admin - when: - branch: - - master - - - name: docker checker - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-checker - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-checker - when: - branch: - - master - - - name: docker evdist - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-evdist - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-evdist - when: - branch: - - master - - - name: docker generator - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-generator - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-generator - when: - branch: - - master - - - name: docker receiver - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-receiver - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-receiver - when: - branch: - - master - - - name: docker frontend nginx - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-nginx - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-nginx - when: - branch: - - master - - - name: docker frontend ui - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-frontend-ui - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-frontend-ui - when: - branch: - - master - - - name: docker dashboard - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-dashboard - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-dashboard - when: - branch: - - master - - - name: docker qa - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-qa - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-qa - when: - branch: - - master - - - name: docker repochecker - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-repochecker - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-repochecker - when: - branch: - - master - - - name: docker remote-scores-sync-zqds - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-remote-scores-sync-zqds - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-remote-scores-sync-zqds - when: - branch: - - master - - - name: docker remote-challenge-sync-airbus - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-remote-challenge-sync-airbus - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-remote-challenge-sync-airbus - when: - branch: - - master - - - name: docker fic-get-remote-files - failure: ignore - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-get-remote-files - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-get-remote-files - when: - branch: - - master - - - name: docker fickit-deploy - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fickit-deploy - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-deploy - when: - branch: - - master - -trigger: - event: - - cron - - push - - tag - ---- -kind: pipeline -type: docker -name: build-arm64 - -platform: - os: linux - arch: arm64 - -workspace: - base: /go - path: src/srs.epita.fr/fic-server - -steps: - - name: get deps - image: golang:alpine - commands: - - apk --no-cache add git - - go get -d ./... - - mkdir deploy - - - name: build admin - image: golang:alpine - commands: - - apk --no-cache add build-base - - go build -buildvcs=false -o deploy/admin-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/admin - environment: - CGO_ENABLED: 0 - when: - branch: - exclude: - - master - - - name: build checker - image: golang:alpine - commands: - - go build -buildvcs=false -o deploy/checker-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/checker - environment: - CGO_ENABLED: 0 - when: - branch: - exclude: - - master - - - name: build evdist - image: golang:alpine - commands: - - go build -buildvcs=false -o deploy/evdist-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/evdist - environment: - CGO_ENABLED: 0 - when: - branch: - exclude: - - master - - - name: build generator - image: golang:alpine - commands: - - go build -buildvcs=false -o deploy/generator-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/generator - environment: - CGO_ENABLED: 0 - when: - branch: - exclude: - - master - - - name: build receiver - image: golang:alpine - commands: - - go build -buildvcs=false -o deploy/receiver-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/receiver - environment: - CGO_ENABLED: 0 - when: - branch: - exclude: - - master - - - name: build frontend fic ui - image: node:23-alpine - commands: - - cd frontend/fic - - npm install --network-timeout=100000 - - npm run build - when: - branch: - exclude: - - master - - - name: build dashboard - image: golang:alpine - commands: - - go build -buildvcs=false -o deploy/dashboard-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/dashboard - environment: - CGO_ENABLED: 0 - when: - branch: - exclude: - - master - - - name: build repochecker - image: golang:alpine - commands: - - apk --no-cache add build-base - - go build -buildvcs=false --tags checkupdate -o deploy/repochecker-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker - environment: - CGO_ENABLED: 0 - when: - branch: - exclude: - - master - - - name: build repochecker for macOS - image: golang:alpine - commands: - - apk --no-cache add build-base - - go build -buildvcs=false --tags checkupdate -o deploy/repochecker-darwin-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker - environment: - CGO_ENABLED: 0 - GOOS: darwin - GOARCH: arm64 - when: - branch: - exclude: - - master - - - name: build qa ui - image: node:23-alpine - commands: - - cd qa/ui - - npm install --network-timeout=100000 - - npm run build - - tar chjf ../../deploy/htdocs-qa.tar.bz2 build - when: - branch: - exclude: - - master - - - name: build qa - image: golang:alpine - commands: - - go build -buildvcs=false -o deploy/qa-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/qa - environment: - CGO_ENABLED: 0 - when: - branch: - exclude: - - master - - - name: docker admin - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-admin - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-admin - when: - branch: - - master - - - name: docker checker - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-checker - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-checker - when: - branch: - - master - - - name: docker evdist - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-evdist - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-evdist - when: - branch: - - master - - - name: docker generator - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-generator - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-generator - when: - branch: - - master - - - name: docker fic-get-remote-files - failure: ignore - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-get-remote-files - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-get-remote-files - when: - branch: - - master - - - name: docker receiver - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-receiver - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-receiver - when: - branch: - - master - - - name: docker frontend nginx - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-nginx - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-nginx - when: - branch: - - master - - - name: docker dashboard - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-dashboard - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-dashboard - when: - branch: - - master - - - name: docker qa - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-qa - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-qa - when: - branch: - - master - - - name: docker repochecker - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-repochecker - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-repochecker - when: - branch: - - master - -trigger: - event: - - cron - - push - - tag - ---- -kind: pipeline -name: docker-manifest -steps: - - name: publish admin - image: plugins/manifest - settings: - auto_tag: true - ignore_missing: true - spec: .drone-manifest-fic-admin.yml - username: - from_secret: docker_username - password: - from_secret: docker_password - - - name: publish checker - image: plugins/manifest - settings: - auto_tag: true - ignore_missing: true - spec: .drone-manifest-fic-checker.yml - username: - from_secret: docker_username - password: - from_secret: docker_password - - - name: publish evdist - image: plugins/manifest - settings: - auto_tag: true - ignore_missing: true - spec: .drone-manifest-fic-evdist.yml - username: - from_secret: docker_username - password: - from_secret: docker_password - - - name: publish generator - image: plugins/manifest - settings: - auto_tag: true - ignore_missing: true - spec: .drone-manifest-fic-generator.yml - username: - from_secret: docker_username - password: - from_secret: docker_password - - - name: publish receiver - image: plugins/manifest - settings: - auto_tag: true - ignore_missing: true - spec: .drone-manifest-fic-receiver.yml - username: - from_secret: docker_username - password: - from_secret: docker_password - - - name: publish frontend nginx - image: plugins/manifest - settings: - auto_tag: true - ignore_missing: true - spec: .drone-manifest-fic-nginx.yml - username: - from_secret: docker_username - password: - from_secret: docker_password - - - name: publish frontend ui - image: plugins/manifest - settings: - auto_tag: true - ignore_missing: true - spec: .drone-manifest-fic-frontend-ui.yml - username: - from_secret: docker_username - password: - from_secret: docker_password - - - name: publish dashboard - image: plugins/manifest - settings: - auto_tag: true - ignore_missing: true - spec: .drone-manifest-fic-dashboard.yml - username: - from_secret: docker_username - password: - from_secret: docker_password - - - name: publish repochecker - image: plugins/manifest - settings: - auto_tag: true - ignore_missing: true - spec: .drone-manifest-fic-repochecker.yml - username: - from_secret: docker_username - password: - from_secret: docker_password - - - name: publish qa - image: plugins/manifest - settings: - auto_tag: true - ignore_missing: true - spec: .drone-manifest-fic-qa.yml - username: - from_secret: docker_username - password: - from_secret: docker_password - - - name: docker fic-get-remote-files - failure: ignore - image: plugins/docker - settings: - username: - from_secret: docker_username - password: - from_secret: docker_password - repo: nemunaire/fic-get-remote-files - auto_tag: true - auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - dockerfile: Dockerfile-get-remote-files - when: - branch: - - master - - - name: publish fickit-deploy - image: plugins/manifest - settings: - auto_tag: true - ignore_missing: true - spec: .drone-manifest-fickit-deploy.yml - username: - from_secret: docker_username - password: - from_secret: docker_password - -trigger: - event: - - push - - tag - -depends_on: -- build-amd64 -- build-arm64 diff --git a/.gitignore b/.gitignore index 4327ccd0..b1d213ad 100644 --- a/.gitignore +++ b/.gitignore @@ -1,43 +1,11 @@ -vendor/ -DASHBOARD/ -FILES/ -PKI/ -REMOTE/ -SETTINGS/ -SETTINGSDIST/ -TEAMS/ -submissions/ -admin/sync/README.html -fickit-boot-cmdline -fickit-boot-initrd.img -fickit-boot-kernel -fickit-backend-cmdline -fickit-backend-initrd.img -fickit-backend-squashfs.img -fickit-backend-kernel -fickit-backend-state -fickit-frontend-cmdline -fickit-frontend-initrd.img -fickit-frontend-squashfs.img -fickit-frontend-kernel -fickit-frontend-state -fickit-prepare-bios.img -fickit-prepare-cmdline -fickit-prepare-initrd.img -fickit-prepare-kernel -fickit-prepare-state -fickit-update-cmdline -fickit-update-initrd.img -fickit-update-kernel -fickit-update-squashfs.img -result -started - -# Standalone binaries -admin/get-remote-files/get-remote-files -fic-admin -fic-backend -fic-dashboard -fic-frontend -fic-qa -fic-repochecker +*.swp +*~ +*# +onyx/cache/*.cache.php +onyx/cache/templates/*/*.php +onyx/log/* +onyx/config/root.xml +onyx/db/*.profile.php +onyx/tpl/*/*.html +submission/* +misc/openssl.cnf diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 0e57016f..00000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,122 +0,0 @@ ---- - -stages: - - deps - - build - - fickit - - sast - - qa - - image - - container_scanning - -cache: - paths: - - .go/pkg/mod/ - - qa/ui/node_modules/ - - frontend/ui/node_modules/ - -include: - - '.gitlab-ci/build.yml' - - '.gitlab-ci/image.yml' - - template: SAST.gitlab-ci.yml - - template: Security/Dependency-Scanning.gitlab-ci.yml - - template: Security/Secret-Detection.gitlab-ci.yml - - template: Security/Container-Scanning.gitlab-ci.yml - -.scanners-matrix: - parallel: - matrix: - - IMAGE_NAME: [checker, admin, evdist, frontend-ui, nginx, dashboard, repochecker, qa, receiver, generator, remote-challenge-sync-airbus] - -container_scanning: - stage: container_scanning - extends: - - .scanners-matrix - variables: - DOCKER_SERVICE: localhost - DOCKERFILE_PATH: Dockerfile-${IMAGE_NAME} - CI_APPLICATION_REPOSITORY: ${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}/${IMAGE_NAME} - CI_APPLICATION_TAG: latest - GIT_STRATEGY: fetch - before_script: - - 'echo "Scanning: ${IMAGE_NAME}"' - rules: - - if: '$CI_COMMIT_BRANCH == "master"' - -sast: - stage: sast - interruptible: true - needs: [] - before_script: - - rm -rf .go/ - -secret_detection: - stage: sast - interruptible: true - needs: [] - -dependency_scanning: - stage: qa - interruptible: true - needs: [] - -get-deps: - stage: deps - image: golang:1-alpine - before_script: - - export GOPATH="$CI_PROJECT_DIR/.go" - - mkdir -p .go - script: - - apk --no-cache add git - - go get -v -d ./... - -vet: - stage: sast - needs: ["build-qa-ui"] - dependencies: - - build-qa-ui - image: golang:1-alpine - before_script: - - export GOPATH="$CI_PROJECT_DIR/.go" - - mkdir -p .go - script: - - apk --no-cache add build-base - - go vet -v -buildvcs=false -tags gitgo ./... - - go vet -v -buildvcs=false ./... - -fickit: - stage: fickit - interruptible: true - needs: ["build-admin","build-checker","build-dashboard","build-evdist","build-generator","build-qa","build-receiver","build-repochecker"] - image: nemunaire/linuxkit - tags: ['docker'] - before_script: - - mkdir -p ~/.docker - - echo "{\"auths\":{\"${CI_REGISTRY}\":{\"username\":\"${CI_REGISTRY_USER}\",\"password\":\"${CI_REGISTRY_PASSWORD}\"}}}" > ~/.docker/config.json - script: - - dockerd & sleep 5 - - - linuxkit pkg push -force -org "${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}" fickit-pkg/boot/ - - linuxkit pkg push -force -org "${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}" fickit-pkg/kexec/ - - linuxkit pkg push -force -org "${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}" fickit-pkg/mariadb-client/ - - linuxkit pkg push -force -org "${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}" fickit-pkg/mdadm/ - - linuxkit pkg push -force -org "${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}" fickit-pkg/rsync/ - - linuxkit pkg push -force -org "${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}" fickit-pkg/syslinux/ - - linuxkit pkg push -force -org "${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}" fickit-pkg/unbound/ - - - sed -i "s@nemunaire/fic-@${CI_REGISTRY_IMAGE}/master/@;s@nemunaire/@${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}/@" fickit-backend.yml fickit-boot.yml fickit-frontend.yml fickit-prepare.yml fickit-update.yml - - - linuxkit build -format kernel+squashfs fickit-backend.yml - - linuxkit build -format kernel+squashfs fickit-frontend.yml - - linuxkit build -format kernel+initrd fickit-boot.yml - - linuxkit build -format kernel+initrd fickit-prepare.yml - - linuxkit build -format kernel+initrd fickit-update.yml - artifacts: - expire_in: 8 hours - paths: - - fickit-backend-squashfs.img - - fickit-frontend-squashfs.img - - fickit-boot-kernel - - fickit-boot-initrd.img - - fickit-prepare-initrd.img - - fickit-update-initrd.img diff --git a/.gitlab-ci/build.yml b/.gitlab-ci/build.yml deleted file mode 100644 index 621d8bbb..00000000 --- a/.gitlab-ci/build.yml +++ /dev/null @@ -1,93 +0,0 @@ ---- - -.build: - stage: build - image: golang:1-alpine - before_script: - - export GOPATH="$CI_PROJECT_DIR/.go" - - mkdir -p .go - variables: - CGO_ENABLED: 0 - -build-qa-ui: - stage: build - image: node:21-alpine - before_script: - script: - - cd qa/ui - - npm install --network-timeout=100000 - - npm run build - artifacts: - paths: - - qa/ui/build/ - when: on_success - -build-checker: - extends: - - .build - script: - - go build -v -buildvcs=false -o deploy/checker srs.epita.fr/fic-server/checker - -build-generator: - extends: - - .build - script: - - go build -v -buildvcs=false -o deploy/generator srs.epita.fr/fic-server/generator - -build-receiver: - extends: - - .build - script: - - go build -v -buildvcs=false -o deploy/receiver srs.epita.fr/fic-server/receiver - -build-admin: - extends: - - .build - script: - - go build -v -buildvcs=false -tags gitgo -o deploy/admin-gitgo srs.epita.fr/fic-server/admin - - go build -v -buildvcs=false -o deploy/admin srs.epita.fr/fic-server/admin - -build-evdist: - extends: - - .build - script: - - go build -v -buildvcs=false -o deploy/evdist srs.epita.fr/fic-server/evdist - -build-frontend-ui: - stage: build - image: node:21-alpine - before_script: - script: - - cd frontend/fic - - npm install --network-timeout=100000 - - npm run build - -build-dashboard: - extends: - - .build - script: - - go build -v -buildvcs=false -o deploy/dashboard srs.epita.fr/fic-server/dashboard - -build-repochecker: - extends: - - .build - variables: - CGO_ENABLED: 1 - script: - - apk --no-cache add build-base - - go build -buildvcs=false --tags checkupdate -v -o deploy/repochecker srs.epita.fr/fic-server/repochecker - - go build -buildvcs=false -buildmode=plugin -v -o deploy/repochecker-epita-rules.so srs.epita.fr/fic-server/repochecker/epita - - go build -buildvcs=false -buildmode=plugin -v -o deploy/repochecker-file-inspector-rules.so srs.epita.fr/fic-server/repochecker/file-inspector - - go build -buildvcs=false -buildmode=plugin -v -o deploy/repochecker-grammalecte-rules.so srs.epita.fr/fic-server/repochecker/grammalecte - - go build -buildvcs=false -buildmode=plugin -v -o deploy/repochecker-pcap-inspector-rules.so srs.epita.fr/fic-server/repochecker/pcap-inspector - - go build -buildvcs=false -buildmode=plugin -v -o deploy/repochecker-videos-rules.so srs.epita.fr/fic-server/repochecker/videos - - grep "const version" repochecker/update.go | sed -r 's/^.*=\s*(\S.*)$/\1/' > deploy/repochecker.version - -build-qa: - extends: - - .build - needs: ["build-qa-ui"] - dependencies: - - build-qa-ui - script: - - go build -v -buildvcs=false -o deploy/qa srs.epita.fr/fic-server/qa diff --git a/.gitlab-ci/image.yml b/.gitlab-ci/image.yml deleted file mode 100644 index 988bb2b0..00000000 --- a/.gitlab-ci/image.yml +++ /dev/null @@ -1,99 +0,0 @@ ---- - -.push: - stage: image - interruptible: true - needs: [] - image: - name: gcr.io/kaniko-project/executor:v1.9.0-debug - entrypoint: [""] - before_script: - - mkdir -p /kaniko/.docker - - echo "{\"auths\":{\"${CI_REGISTRY}\":{\"username\":\"${CI_REGISTRY_USER}\",\"password\":\"${CI_REGISTRY_PASSWORD}\"}}}" > /kaniko/.docker/config.json - script: - - | - /kaniko/executor \ - --context . \ - --dockerfile "${DOCKERFILE}" \ - --destination "${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}/${CI_JOB_NAME}:${CI_COMMIT_SHA}" \ - --destination "${CI_REGISTRY_IMAGE}/${CI_COMMIT_REF_SLUG}/${CI_JOB_NAME}:latest" - only: - - master - -checker: - extends: - - .push - variables: - DOCKERFILE: Dockerfile-checker - -receiver: - extends: - - .push - variables: - DOCKERFILE: Dockerfile-receiver - -generator: - extends: - - .push - variables: - DOCKERFILE: Dockerfile-generator - -admin: - extends: - - .push - variables: - DOCKERFILE: Dockerfile-admin - -fickit-deploy: - extends: - - .push - variables: - DOCKERFILE: Dockerfile-deploy - -get-remote-files: - extends: - - .push - variables: - DOCKERFILE: Dockerfile-get-remote-files - -evdist: - extends: - - .push - variables: - DOCKERFILE: Dockerfile-evdist - -frontend-ui: - extends: - - .push - variables: - DOCKERFILE: Dockerfile-frontend-ui - -nginx: - extends: - - .push - variables: - DOCKERFILE: Dockerfile-nginx - -dashboard: - extends: - - .push - variables: - DOCKERFILE: Dockerfile-dashboard - -repochecker: - extends: - - .push - variables: - DOCKERFILE: Dockerfile-repochecker - -qa: - extends: - - .push - variables: - DOCKERFILE: Dockerfile-qa - -remote-challenge-sync-airbus: - extends: - - .push - variables: - DOCKERFILE: Dockerfile-remote-challenge-sync-airbus diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..03cde9bd --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +# /!\ WARNING: the container generated through this Dockerfile is made only for development purpose; it is NOT SAFE or production ready. + +FROM php:5.5-fpm-alpine +MAINTAINER Pierre-Olivier Mercier + +# Install packages #################################################### + +ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/ + +RUN chmod +x /usr/local/bin/install-php-extensions && \ + install-php-extensions mysql mcrypt + +# ENVIRONNEMENT ####################################################### + +EXPOSE 80/tcp 443/tcp +VOLUME ["/var/www/fic-server/out","/var/www/fic-server/files","/var/www/fic-server/submission","/var/www/fic-server/shared"] + +WORKDIR /var/www/fic-server + +ENTRYPOINT ["/var/www/fic-server/entrypoint.sh"] + +CMD ["php-fpm"] + +# Copying files ####################################################### + +COPY . /var/www/fic-server/ diff --git a/Dockerfile-admin b/Dockerfile-admin deleted file mode 100644 index f2c285f2..00000000 --- a/Dockerfile-admin +++ /dev/null @@ -1,42 +0,0 @@ -FROM golang:1-alpine AS gobuild - -RUN apk add --no-cache git - -WORKDIR /go/src/srs.epita.fr/fic-server/ - -RUN apk add --no-cache binutils-gold build-base - -COPY go.mod go.sum ./ -COPY settings settings/ -COPY libfic ./libfic/ -COPY admin ./admin/ -COPY repochecker ./repochecker/ - -RUN go get -d -v ./admin && \ - go build -v -o admin/admin ./admin && \ - go build -v -buildmode=plugin -o repochecker/epita-rules.so ./repochecker/epita && \ - go build -v -buildmode=plugin -o repochecker/file-inspector.so ./repochecker/file-inspector && \ - go build -v -buildmode=plugin -o repochecker/grammalecte-rules.so ./repochecker/grammalecte && \ - go build -v -buildmode=plugin -o repochecker/videos-rules.so ./repochecker/videos - - -FROM alpine:3.21 - -RUN apk add --no-cache \ - ca-certificates \ - git \ - git-lfs \ - openssh-client-default \ - openssl - -EXPOSE 8081 - -WORKDIR /srv - -ENTRYPOINT ["/srv/admin", "-bind=:8081", "-baseurl=/admin/"] - -COPY --from=gobuild /go/src/srs.epita.fr/fic-server/admin/admin /srv/admin -COPY --from=gobuild /go/src/srs.epita.fr/fic-server/repochecker/epita-rules.so /srv/epita-rules.so -COPY --from=gobuild /go/src/srs.epita.fr/fic-server/repochecker/file-inspector.so /usr/lib/file-inspector.so -COPY --from=gobuild /go/src/srs.epita.fr/fic-server/repochecker/grammalecte-rules.so /usr/lib/grammalecte-rules.so -COPY --from=gobuild /go/src/srs.epita.fr/fic-server/repochecker/videos-rules.so /usr/lib/videos-rules.so diff --git a/Dockerfile-checker b/Dockerfile-checker deleted file mode 100644 index 144ac0a7..00000000 --- a/Dockerfile-checker +++ /dev/null @@ -1,22 +0,0 @@ -FROM golang:1-alpine AS gobuild - -RUN apk add --no-cache git - -WORKDIR /go/src/srs.epita.fr/fic-server/ - -COPY go.mod go.sum ./ -COPY settings settings/ -COPY libfic ./libfic/ -COPY checker ./checker/ - -RUN go get -d -v ./checker && \ - go build -v -buildvcs=false -o checker/checker ./checker - - -FROM alpine:3.21 - -WORKDIR /srv - -ENTRYPOINT ["/srv/checker"] - -COPY --from=gobuild /go/src/srs.epita.fr/fic-server/checker/checker /srv/checker diff --git a/Dockerfile-dashboard b/Dockerfile-dashboard deleted file mode 100644 index b8291bba..00000000 --- a/Dockerfile-dashboard +++ /dev/null @@ -1,32 +0,0 @@ -FROM golang:1-alpine AS gobuild - -RUN apk add --no-cache git - -WORKDIR /go/src/srs.epita.fr/fic-server/ - -COPY go.mod go.sum ./ -COPY settings settings/ -COPY libfic ./libfic/ -COPY dashboard ./dashboard/ - -RUN go get -d -v ./dashboard && \ - go build -v -buildvcs=false -o dashboard/dashboard ./dashboard - - -FROM alpine:3.21 - -EXPOSE 8082 - -WORKDIR /srv - -ENTRYPOINT ["/srv/dashboard", "--bind=:8082"] - -VOLUME /srv/htdocs-dashboard/ - -COPY --from=gobuild /go/src/srs.epita.fr/fic-server/dashboard/dashboard /srv/dashboard -COPY dashboard/static/index.html /srv/htdocs-dashboard/ -COPY admin/static/css/bootstrap.min.css dashboard/static/css/fic.css admin/static/css/glyphicon.css /srv/htdocs-dashboard/css/ -COPY admin/static/fonts /srv/htdocs-dashboard/fonts -COPY dashboard/static/img/srs.png /srv/htdocs-dashboard/img/ -COPY dashboard/static/js/dashboard.js admin/static/js/angular.min.js dashboard/static/js/angular-animate.min.js admin/static/js/angular-route.min.js admin/static/js/angular-sanitize.min.js admin/static/js/bootstrap.min.js admin/static/js/common.js admin/static/js/d3.v3.min.js admin/static/js/jquery.min.js /srv/htdocs-dashboard/js/ -COPY admin/static/js/i18n/* /srv/htdocs-dashboard/js/i18n/ diff --git a/Dockerfile-deploy b/Dockerfile-deploy deleted file mode 100644 index 99765543..00000000 --- a/Dockerfile-deploy +++ /dev/null @@ -1,24 +0,0 @@ -FROM alpine:3.21 - -EXPOSE 67/udp -EXPOSE 69/udp -EXPOSE 80/tcp - -ENTRYPOINT ["/usr/sbin/initial-config.sh"] -CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"] - -WORKDIR /srv/s - -RUN apk add --no-cache \ - busybox-extras \ - supervisor \ - syslinux \ - tftp-hpa - -RUN touch /var/lib/udhcpd/udhcpd.leases && \ - mv /usr/share/syslinux/* /srv - -COPY configs/deploy-initial-config.sh /usr/sbin/initial-config.sh -COPY configs/deploy-supervisord.conf /etc/supervisord.conf -COPY configs/udhcpd-sample.conf /etc/udhcpd.conf -COPY configs/pxelinux.cfg /srv/pxelinux.cfg/default \ No newline at end of file diff --git a/Dockerfile-evdist b/Dockerfile-evdist deleted file mode 100644 index 45a2c506..00000000 --- a/Dockerfile-evdist +++ /dev/null @@ -1,21 +0,0 @@ -FROM golang:1-alpine AS gobuild - -RUN apk add --no-cache git - -WORKDIR /go/src/srs.epita.fr/fic-server/ - -COPY go.mod go.sum ./ -COPY settings settings/ -COPY evdist ./evdist/ - -RUN go get -d -v ./evdist && \ - go build -v -buildvcs=false -o evdist/evdist ./evdist - - -FROM alpine:3.21 - -WORKDIR /srv - -ENTRYPOINT ["/srv/evdist"] - -COPY --from=gobuild /go/src/srs.epita.fr/fic-server/evdist/evdist /srv/evdist diff --git a/Dockerfile-frontend-ui b/Dockerfile-frontend-ui deleted file mode 100644 index a14b0c58..00000000 --- a/Dockerfile-frontend-ui +++ /dev/null @@ -1,13 +0,0 @@ -FROM node:23-alpine AS nodebuild - -WORKDIR /ui - -COPY frontend/fic/ . - -RUN npm install --network-timeout=100000 && \ - npm run build - - -FROM scratch - -COPY --from=nodebuild /ui/build/ /www/htdocs-frontend diff --git a/Dockerfile-generator b/Dockerfile-generator deleted file mode 100644 index 2574e614..00000000 --- a/Dockerfile-generator +++ /dev/null @@ -1,22 +0,0 @@ -FROM golang:1-alpine AS gobuild - -RUN apk add --no-cache git - -WORKDIR /go/src/srs.epita.fr/fic-server/ - -COPY go.mod go.sum ./ -COPY settings settings/ -COPY libfic ./libfic/ -COPY generator ./generator/ - -RUN go get -d -v ./generator && \ - go build -v -buildvcs=false -o generator/generator ./generator - - -FROM alpine:3.21 - -WORKDIR /srv - -ENTRYPOINT ["/srv/generator"] - -COPY --from=gobuild /go/src/srs.epita.fr/fic-server/generator/generator /srv/generator diff --git a/Dockerfile-get-remote-files b/Dockerfile-get-remote-files deleted file mode 100644 index 95e1c5f3..00000000 --- a/Dockerfile-get-remote-files +++ /dev/null @@ -1,27 +0,0 @@ -FROM golang:1-alpine AS gobuild - -RUN apk add --no-cache git - -WORKDIR /go/src/srs.epita.fr/fic-server/ - -RUN apk add --no-cache build-base - -COPY go.mod go.sum ./ -COPY settings settings/ -COPY libfic ./libfic/ -COPY admin ./admin/ - -RUN go get -d -v ./admin && \ - go build -v -o get-remote-files ./admin/get-remote-files - - -FROM alpine:3.21 - -RUN apk add --no-cache \ - ca-certificates - -WORKDIR /srv - -ENTRYPOINT ["/srv/get-remote-files", "/mnt/fic/"] - -COPY --from=gobuild /go/src/srs.epita.fr/fic-server/get-remote-files /srv/get-remote-files diff --git a/Dockerfile-nginx b/Dockerfile-nginx deleted file mode 100644 index 41326840..00000000 --- a/Dockerfile-nginx +++ /dev/null @@ -1,32 +0,0 @@ -FROM node:23-alpine AS nodebuild - -WORKDIR /ui - -COPY frontend/fic/ . - -RUN npm install --network-timeout=100000 && \ - npm run build - - -FROM nginx:stable-alpine-slim - -ENV FIC_BASEURL / -ENV HOST_RECEIVER receiver:8080 -ENV HOST_ADMIN admin:8081 -ENV HOST_DASHBOARD dashboard:8082 -ENV HOST_QA qa:8083 -ENV PATH_FILES /srv/FILES -ENV PATH_STARTINGBLOCK /srv/STARTINGBLOCK -ENV PATH_STATIC /srv/htdocs-frontend -ENV PATH_SETTINGS /srv/SETTINGSDIST -ENV PATH_TEAMS /srv/TEAMS - -EXPOSE 80 - -COPY configs/nginx-chbase.sh /docker-entrypoint.d/40-update-baseurl.sh - -COPY configs/nginx/get-team/upstream.conf /etc/nginx/fic-get-team.conf -COPY configs/nginx/auth/none.conf /etc/nginx/fic-auth.conf -COPY configs/nginx/base/docker.conf /etc/nginx/templates/default.conf.template - -COPY --from=nodebuild /ui/build/ /srv/htdocs-frontend diff --git a/Dockerfile-qa b/Dockerfile-qa deleted file mode 100644 index 37f3a1b0..00000000 --- a/Dockerfile-qa +++ /dev/null @@ -1,38 +0,0 @@ -FROM node:23-alpine AS nodebuild - -WORKDIR /ui - -COPY qa/ui/ . - -RUN npm install --network-timeout=100000 && \ - npm run build - - -FROM golang:1-alpine AS gobuild - -RUN apk add --no-cache git - -WORKDIR /go/src/srs.epita.fr/fic-server/ - -COPY go.mod go.sum ./ -COPY settings settings/ -COPY libfic ./libfic/ -COPY --from=nodebuild /ui ./qa/ui -COPY qa ./qa/ -COPY admin ./admin/ - -RUN go get -d -v ./qa && \ - go build -v -buildvcs=false -o qa/qa ./qa - - -FROM alpine:3.21 - -EXPOSE 8083 - -WORKDIR /srv - -ENTRYPOINT ["/srv/qa", "--bind=:8083"] - -VOLUME /srv/htdocs-qa/ - -COPY --from=gobuild /go/src/srs.epita.fr/fic-server/qa/qa /srv/qa diff --git a/Dockerfile-receiver b/Dockerfile-receiver deleted file mode 100644 index f2cac038..00000000 --- a/Dockerfile-receiver +++ /dev/null @@ -1,27 +0,0 @@ -FROM golang:1-alpine AS gobuild - -RUN apk add --no-cache git - -WORKDIR /go/src/srs.epita.fr/fic-server/ - -COPY go.mod go.sum ./ -COPY settings settings/ -COPY libfic ./libfic/ -COPY receiver ./receiver/ - -RUN go get -d -v ./receiver && \ - go build -v -buildvcs=false -o ./receiver/receiver ./receiver - - -FROM alpine:3.21 - -EXPOSE 8080 - -WORKDIR /srv - -ENTRYPOINT ["/usr/sbin/entrypoint.sh"] -CMD ["--bind=:8080"] - -COPY entrypoint-receiver.sh /usr/sbin/entrypoint.sh - -COPY --from=gobuild /go/src/srs.epita.fr/fic-server/receiver/receiver /srv/receiver diff --git a/Dockerfile-remote-challenge-sync-airbus b/Dockerfile-remote-challenge-sync-airbus deleted file mode 100644 index 47a5e167..00000000 --- a/Dockerfile-remote-challenge-sync-airbus +++ /dev/null @@ -1,24 +0,0 @@ -FROM golang:1-alpine AS gobuild - -RUN apk add --no-cache git - -WORKDIR /go/src/srs.epita.fr/fic-server/ - -COPY go.mod go.sum ./ -COPY libfic ./libfic/ -COPY settings ./settings/ -COPY remote/challenge-sync-airbus ./remote/challenge-sync-airbus/ - -RUN go get -d -v ./remote/challenge-sync-airbus && \ - go build -v -buildvcs=false -o ./challenge-sync-airbus ./remote/challenge-sync-airbus - - -FROM alpine:3.21 - -RUN apk add --no-cache openssl ca-certificates - -WORKDIR /srv - -ENTRYPOINT ["/srv/challenge-sync-airbus"] - -COPY --from=gobuild /go/src/srs.epita.fr/fic-server/challenge-sync-airbus /srv/challenge-sync-airbus diff --git a/Dockerfile-remote-scores-sync-zqds b/Dockerfile-remote-scores-sync-zqds deleted file mode 100644 index e5ff87fb..00000000 --- a/Dockerfile-remote-scores-sync-zqds +++ /dev/null @@ -1,24 +0,0 @@ -FROM golang:1-alpine AS gobuild - -RUN apk add --no-cache git - -WORKDIR /go/src/srs.epita.fr/fic-server/ - -COPY go.mod go.sum ./ -COPY libfic ./libfic/ -COPY settings ./settings/ -COPY remote/scores-sync-zqds ./remote/scores-sync-zqds/ - -RUN go get -d -v ./remote/scores-sync-zqds && \ - go build -v -buildvcs=false -o ./scores-sync-zqds ./remote/scores-sync-zqds - - -FROM alpine:3.21 - -RUN apk add --no-cache openssl ca-certificates - -WORKDIR /srv - -ENTRYPOINT ["/srv/scores-sync-zqds"] - -COPY --from=gobuild /go/src/srs.epita.fr/fic-server/scores-sync-zqds /srv/scores-sync-zqds diff --git a/Dockerfile-repochecker b/Dockerfile-repochecker deleted file mode 100644 index d02bc1de..00000000 --- a/Dockerfile-repochecker +++ /dev/null @@ -1,42 +0,0 @@ -FROM golang:1-alpine AS gobuild - -RUN apk add --no-cache git - -WORKDIR /go/src/srs.epita.fr/fic-server/ - -RUN apk add --no-cache binutils-gold build-base - -COPY go.mod go.sum ./ -COPY settings settings/ -COPY libfic ./libfic/ -COPY admin ./admin/ -COPY repochecker ./repochecker/ - -RUN go get -d -v ./repochecker && \ - go build -v -o repochecker/repochecker ./repochecker && \ - go build -v -buildmode=plugin -o repochecker/epita-rules.so ./repochecker/epita && \ - go build -v -buildmode=plugin -o repochecker/file-inspector.so ./repochecker/file-inspector && \ - go build -v -buildmode=plugin -o repochecker/grammalecte-rules.so ./repochecker/grammalecte && \ - go build -v -buildmode=plugin -o repochecker/pcap-inspector.so ./repochecker/pcap-inspector && \ - go build -v -buildmode=plugin -o repochecker/videos-rules.so ./repochecker/videos - - -ENV GRAMMALECTE_VERSION 2.1.1 - -ADD https://web.archive.org/web/20240926154729if_/https://grammalecte.net/zip/Grammalecte-fr-v$GRAMMALECTE_VERSION.zip /srv/grammalecte.zip - -RUN mkdir /srv/grammalecte && cd /srv/grammalecte && unzip /srv/grammalecte.zip && sed -i 's/if sys.version_info.major < (3, 7):/if False:/' /srv/grammalecte/grammalecte-server.py - -FROM alpine:3.19 - -ENTRYPOINT ["/usr/bin/repochecker", "--rules-plugins=/usr/lib/epita-rules.so", "--rules-plugins=/usr/lib/file-inspector.so", "--rules-plugins=/usr/lib/grammalecte-rules.so", "--rules-plugins=/usr/lib/pcap-inspector.so", "--rules-plugins=/usr/lib/videos-rules.so"] - -RUN apk add --no-cache git python3 ffmpeg - -COPY --from=gobuild /srv/grammalecte /srv/grammalecte -COPY --from=gobuild /go/src/srs.epita.fr/fic-server/repochecker/repochecker /usr/bin/repochecker -COPY --from=gobuild /go/src/srs.epita.fr/fic-server/repochecker/epita-rules.so /usr/lib/epita-rules.so -COPY --from=gobuild /go/src/srs.epita.fr/fic-server/repochecker/file-inspector.so /usr/lib/file-inspector.so -COPY --from=gobuild /go/src/srs.epita.fr/fic-server/repochecker/grammalecte-rules.so /usr/lib/grammalecte-rules.so -COPY --from=gobuild /go/src/srs.epita.fr/fic-server/repochecker/pcap-inspector.so /usr/lib/pcap-inspector.so -COPY --from=gobuild /go/src/srs.epita.fr/fic-server/repochecker/videos-rules.so /usr/lib/videos-rules.so diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 5b7eaadc..00000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016-2018 Pierre-Olivier Mercier - -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/README.md b/README.md deleted file mode 100644 index d908ce27..00000000 --- a/README.md +++ /dev/null @@ -1,238 +0,0 @@ -FIC Forensic CTF Platform -========================= - -This is a CTF server for distributing and validating challenges. It is design -to be robust, so it uses some uncommon technics like client certificate for -authentication, lots of state of the art cryptographic methods and aims to be -deployed in a DMZ network architecture. - -## Features - -- **Collaborative Challenge Design and Review:** Facilitates large team collaboration for challenge creation and review. -- **Versatile Flag Formats:** Supports flags as strings, numbers, multiple-choice questions, unique-choice questions, selects, multiline inputs, and strings with capture regexp. -- **Engaging Challenge Interface:** A visually appealing interface that incorporates images to illustrate exercises. -- **Public Dashboard:** Allow spectators to follow the competition alongside players. -- **Archival Mode:** Preserve past challenges and data in a static form, with no code. Your archive can lied on a S3 bucket. -- **Export Capabilities:** Export challenges to other CTF platforms. -- **Security-Focused:** Designed with security as a top priority. Each service aims to be isolated with right restrictions. Answers are not stored in the database, ... -- **Choose your Authentication:** Authentication is not part of this project, integrate your own authentication methods. -- **Extensible:** Easily extend and customize the platform. The main codebase in Golang is highly documented, each frontend part can be recreated in another language with ease. -- **Comprehensive Settings:** A wide range of settings for challenge customization. You can have first blood or not, dynamic exercice gain, evenemential bonus, ... -- **Git Integration:** Seamless verification and integration with Git. -- **Infrastructure as Code (IaC):** Ensure read-only and reproducible infrastructure. -- **Last-Minute Checks:** Ensure your challenge is ready with a comprehensive set of checks that can be performed anytime, verifying that downloadable files are as expected by the challenge creators. -- **Lightweight:** Optimized for minimal resource consumption, supporting features like serving gzipped files directly to browsers without CPU usage. -- **Scalable:** Designed to handle large-scale competitions with multiple receivers and frontend servers, smoothly queuing activity peaks on the backend. -- **Offline Capability:** Run your challenges offline. -- **Integrated Exercise Issue Ticketing System:** Manage and track issues related to exercises during the competition directly with teams. During designing phase, this transform in a complete dedicated QA platform. -- **Detailed Statistics:** Provide administrators with insights into exercise preferences and complexity. -- **Change Planning:** Schedule events in advance, such as new exercise availability or ephemeral bonuses, with second-by-second precision. -- **Frontend Time Synchronization:** Ensure accurate remaining time and event synchronization between servers and players. - - -## Overview - -This is a [monorepo](https://danluu.com/monorepo/), containing several -micro-services : - -- `admin` is the web interface and API used to control the challenge - and doing synchronization. -- `checker` is an inotify reacting service that handles submissions - checking. -- `dashboard` is a public interface to explain and follow the - conquest, aims to animate the challenge for visitors. -- `evdist` is an inotify reacting service that handles settings - changes during the challenge (eg. a 30 minutes event where hints are - free, ...). -- `generator` takes care of global and team's files generation. -- `qa` is an interface dedicated to challenge development, it stores - reports to be treated by challenges creators. -- `receiver` is only responsible for receiving submissions. It is the - only dynamic part accessibe to players, so it's codebase is reduce - to the minimum. It does not parse or try to understand players - submissions, it just write it down to a file in the file - system. Parsing and treatment is made by the `checker`. -- `remote/challenge-sync-airbus` is an inotify reacting service that - allows us to synchronize scores and exercice validations with the - Airbus scoring platform. -- `remote/scores-sync-zqds` is an inotify reacting service that allows - us to synchronize scores with the ZQDS scoring platform. -- `repochecker` is a side project to check offline for synchronization - issues. - -Here is how thoses services speak to each others: - -![Overview of the micro-services](doc/micro-services.png) - -In the production setup, each micro-service runs in a dedicated -container, isolated from each other. Moreover, two physical machines -should be used: - -- `phobos` communicates with players: displaying the web interface, - authenticate teams and players, storing contest files and handling - submissions retrieval without understanding them. It can't access - `deimos` so its job stops after writing requests on the filesystem. -- `deimos` is hidden from players, isolated from the network. It can - only access `phobos` via a restricted ssh connection, to retrieve - requests from `phobos` filesystem and pushing to it newly generated - static files. - -Concretely, the L2 looks like this: - -![Layer 2 connections](doc/l2.png) - -So, the general filesystem is organized this way: - -- `DASHBOARD` contains files structuring the content of the dashboard - screen(s). -- `FILES` stores the contest file to be downloaded by players. To be - accessible without authentication and to avoid bruteforce, each file - is placed into a directory with a hashed name (the original file - name is preserved). It's rsynced as is to `deimos`. -- `GENERATOR` contains a socket to allow other services to communicate - with the `generator`. -- `PKI` takes care of the PKI used for the client certiciate - authorization process, and more generaly, all authentication related - files (htpasswd, dexidp config, ...). Only the `shared` subdirectory - is shared with `deimos`, private key and teams P12 don't go out. -- `SETTINGS` stores the challenge config as wanted by admins. It's not - always the config in use: it uses can be delayed waiting for a - trigger. -- `SETTINGSDIST` is the challenge configuration in use. It is the one - shared with players. -- `startingblock` keep the `started` state of the challenge. This - helps `nginx` to know when it can start distributing exercices - related files. -- `TEAMS` stores the static files generated by the `generator`, there is - one subdirectory by team (id of the team), plus some files at the - root, which are common to all teams. There is also symlink pointing - to team directory, each symlink represent an authentication - association (certificate ID, OpenID username, htpasswd user, ...). -- `submissions` is the directory where the `receiver` writes - requests. It creates subdirectories at the name of the - authentication association, as seen in `TEAMS`, `checker` then - resolve the association regarding `TEAMS` directory. There is also a - special directory to handle team registration. - -Here is a diagram showing how each micro-service uses directories it has access to (blue for read access, red for write access): - -![Usage of directories by each micro-service](doc/directories.png) - -Local developer setup ---------------------- - -### Using Docker - -Use `docker-compose build`, then `docker-compose up` to launch the infrastructure. - -After booting, you'll be able to reach the main interface at: - and the admin one at: (or at ). -The dashboard is available at and the QA service at . - -In this setup, there is no authentication. You are identfied [as a team](./configs/nginx/get-team/team-1.conf). On first use you'll need to register. - -#### Import folder - -##### Local import folder -The following changes is only required if your are trying to change the local import folder `~/fic` location. - -Make the following changes inside this file `docker-compose.yml`: - - 23 volumes: - 24 - - ~/fic:/mnt/fic:ro - 24 + - /fic:/mnt/fic:ro - -##### Git import -A git repository can be used: - - 29 - command: --baseurl /admin/ -localimport /mnt/fic -localimportsymlink - 29 + command: --baseurl /admin/ -localimport /mnt/fic -localimportsymlink -git-import-remote git@gitlab.cri.epita.fr:ing/majeures/srs/fic/2042/challenges.git - -##### Owncloud import folder -If your are trying to use the folder available with the Owncloud service, make the following changes inside this file `docker-compose.yml`: - - 29 - command: --baseurl /admin/ -localimport /mnt/fic -localimportsymlink - 29 + command: --baseurl /admin/ -clouddav=https://owncloud.srs.epita.fr/remote.php/webdav/FIC%202019/ -clouduser -cloudpass '' - -### Manual builds - -Running this project requires a web server (configuration is given for nginx), -a database (currently supporting only MySQL/MariaDB), a Go compiler for the -revision 1.18 at least and a `inotify`-aware system. You'll also need NodeJS to -compile some user interfaces. - -1. Above all, you need to build Node projects: - - cd frontend/fic; npm install && npm run build - cd qa/ui; npm install && npm run build - -2. First, you'll need to retrieve the dependencies: - - go mod vendor - -2. Then, build the three Go projects: - - go build -o fic-admin ./admin - go build -o fic-checker ./checker - go build -o fic-dashboard ./dashboard - go build -o fic-generator ./generator - go build -o fic-qa ./qa - go build -o fic-receiver ./receiver - go build -o fic-repochecker ./repochecker - ... - -3. Before launching anything, you need to create a database: - - mysql -u root -p <: this is the administration part. - - ./fic-generator & - - This daemon generates static and team related files and then waits - another process to tell it to regenerate some files. - - ./fic-receiver & - - This one exposes an API that gives time synchronization to clients and - handle submission reception (but without treating them). - - ./fic-checker & - - This service waits for new submissions (expected in `submissions` - directory). It only watchs modifications on the file system, it has no web - interface. - - ./fic-dashboard & - - This last server runs the public dashboard. It serves all file, without the - need of a webserver. It listens on port 8082 by default. - - ./fic-qa & - - If you need it, this will launch a web interface on the port 8083 by - default, to perform quality control. - -For the moment, a web server is mandatory to serve static files, look -at the samples given in the `configs/nginx` directory. You need to -pick one base configation flavor in the `configs/nginx/base` -directory, and associated with an authentication mechanism in -`configs/nginx/auth` (named the file `fic-auth.conf` in `/etc/nginx`), -and also pick the corresponding `configs/nginx/get-team` file, you -named `fic-get-team.conf`. diff --git a/admin/.gitignore b/admin/.gitignore deleted file mode 100644 index 0ccbc66d..00000000 --- a/admin/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -admin -fic.db -PKI/ -FILES/ -static/full_import_report.json diff --git a/admin/api/certificate.go b/admin/api/certificate.go deleted file mode 100644 index 5c98c116..00000000 --- a/admin/api/certificate.go +++ /dev/null @@ -1,479 +0,0 @@ -package api - -import ( - "crypto/rand" - "crypto/sha1" - "crypto/x509" - "crypto/x509/pkix" - "encoding/base32" - "encoding/base64" - "errors" - "fmt" - "io/ioutil" - "log" - "math" - "math/big" - "net/http" - "os" - "path" - "strconv" - "strings" - "time" - - "srs.epita.fr/fic-server/admin/pki" - "srs.epita.fr/fic-server/libfic" - - "github.com/gin-gonic/gin" -) - -var TeamsDir string - -func declareCertificateRoutes(router *gin.RouterGroup) { - router.GET("/htpasswd", func(c *gin.Context) { - ret, err := genHtpasswd(true) - if err != nil { - c.AbortWithError(http.StatusInternalServerError, err) - return - } - c.String(http.StatusOK, ret) - }) - router.POST("/htpasswd", func(c *gin.Context) { - if htpasswd, err := genHtpasswd(true); err != nil { - log.Println("Unable to generate htpasswd:", err) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } else if err := ioutil.WriteFile(path.Join(pki.PKIDir, "shared", "ficpasswd"), []byte(htpasswd), 0644); err != nil { - log.Println("Unable to write htpasswd:", err) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - c.AbortWithStatus(http.StatusOK) - }) - router.DELETE("/htpasswd", func(c *gin.Context) { - if err := os.Remove(path.Join(pki.PKIDir, "shared", "ficpasswd")); err != nil { - log.Println("Unable to remove htpasswd:", err) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - c.AbortWithStatus(http.StatusOK) - }) - router.GET("/htpasswd.apr1", func(c *gin.Context) { - ret, err := genHtpasswd(false) - if err != nil { - c.AbortWithError(http.StatusInternalServerError, err) - return - } - c.String(http.StatusOK, ret) - }) - router.GET("/ca", infoCA) - router.GET("/ca.pem", getCAPEM) - router.POST("/ca/new", func(c *gin.Context) { - var upki PKISettings - err := c.ShouldBindJSON(&upki) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - if err := pki.GenerateCA(upki.NotBefore, upki.NotAfter); err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusCreated, true) - }) - - router.GET("/certs", getCertificates) - router.POST("/certs", generateClientCert) - router.DELETE("/certs", func(c *gin.Context) { - v, err := fic.ClearCertificates() - if err != nil { - log.Println("Unable to ClearCertificates:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, v) - }) - - apiCertificatesRoutes := router.Group("/certs/:certid") - apiCertificatesRoutes.Use(CertificateHandler) - apiCertificatesRoutes.HEAD("", getTeamP12File) - apiCertificatesRoutes.GET("", getTeamP12File) - apiCertificatesRoutes.PUT("", updateCertificateAssociation) - apiCertificatesRoutes.DELETE("", func(c *gin.Context) { - cert := c.MustGet("cert").(*fic.Certificate) - - v, err := cert.Revoke() - if err != nil { - log.Println("Unable to Revoke:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, v) - }) -} - -func declareTeamCertificateRoutes(router *gin.RouterGroup) { - router.GET("/certificates", func(c *gin.Context) { - team := c.MustGet("team").(*fic.Team) - - if serials, err := pki.GetTeamSerials(TeamsDir, team.Id); err != nil { - log.Println("Unable to GetTeamSerials:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } else { - var certs []CertExported - for _, serial := range serials { - if cert, err := fic.GetCertificate(serial); err == nil { - certs = append(certs, CertExported{fmt.Sprintf("%0[2]*[1]X", cert.Id, int(math.Ceil(math.Log2(float64(cert.Id))/8)*2)), cert.Creation, cert.Password, &team.Id, cert.Revoked}) - } else { - log.Println("Unable to get back certificate, whereas an association exists on disk: ", err) - } - } - c.JSON(http.StatusOK, certs) - } - }) - - router.GET("/associations", func(c *gin.Context) { - team := c.MustGet("team").(*fic.Team) - - assocs, err := pki.GetTeamAssociations(TeamsDir, team.Id) - if err != nil { - log.Println("Unable to GetTeamAssociations:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, assocs) - }) - - apiTeamAssociationsRoutes := router.Group("/associations/:assoc") - apiTeamAssociationsRoutes.POST("", func(c *gin.Context) { - team := c.MustGet("team").(*fic.Team) - - if err := os.Symlink(fmt.Sprintf("%d", team.Id), path.Join(TeamsDir, c.Params.ByName("assoc"))); err != nil { - log.Println("Unable to create association symlink:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to create association symlink: %s", err.Error())}) - return - } - - c.JSON(http.StatusOK, c.Params.ByName("assoc")) - }) - apiTeamAssociationsRoutes.DELETE("", func(c *gin.Context) { - err := pki.DeleteTeamAssociation(TeamsDir, c.Params.ByName("assoc")) - if err != nil { - log.Printf("Unable to DeleteTeamAssociation(%s): %s", c.Params.ByName("assoc"), err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to delete association symlink: %s", err.Error())}) - return - } - - c.JSON(http.StatusOK, nil) - }) - -} - -func CertificateHandler(c *gin.Context) { - var certid uint64 - var err error - - cid := strings.TrimSuffix(string(c.Params.ByName("certid")), ".p12") - if certid, err = strconv.ParseUint(cid, 10, 64); err != nil { - if certid, err = strconv.ParseUint(cid, 16, 64); err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid certficate identifier"}) - return - } - } - - cert, err := fic.GetCertificate(certid) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Certificate not found"}) - return - } - - c.Set("cert", cert) - - c.Next() -} - -func genHtpasswd(ssha bool) (ret string, err error) { - var teams []*fic.Team - teams, err = fic.GetTeams() - if err != nil { - return - } - - for _, team := range teams { - var serials []uint64 - serials, err = pki.GetTeamSerials(TeamsDir, team.Id) - if err != nil { - return - } - - if len(serials) == 0 { - // Don't include teams that don't have associated certificates - continue - } - - for _, serial := range serials { - var cert *fic.Certificate - cert, err = fic.GetCertificate(serial) - if err != nil { - // Ignore invalid/incorrect/non-existant certificates - continue - } - - if cert.Revoked != nil { - continue - } - - salt := make([]byte, 5) - if _, err = rand.Read(salt); err != nil { - return - } - - if ssha { - hash := sha1.New() - hash.Write([]byte(cert.Password)) - hash.Write([]byte(salt)) - - passwdline := fmt.Sprintf(":{SSHA}%s\n", base64.StdEncoding.EncodeToString(append(hash.Sum(nil), salt...))) - - ret += strings.ToLower(team.Name) + passwdline - ret += fmt.Sprintf("%0[2]*[1]x", cert.Id, int(math.Ceil(math.Log2(float64(cert.Id))/8)*2)) + passwdline - ret += fmt.Sprintf("%0[2]*[1]X", cert.Id, int(math.Ceil(math.Log2(float64(cert.Id))/8)*2)) + passwdline - teamAssociations, _ := pki.GetTeamAssociations(TeamsDir, team.Id) - log.Println(path.Join(TeamsDir, fmt.Sprintf("%d", team.Id)), teamAssociations) - for _, ta := range teamAssociations { - ret += strings.Replace(ta, ":", "", -1) + passwdline - } - } else { - salt32 := base32.StdEncoding.EncodeToString(salt) - ret += fmt.Sprintf( - "%s:$apr1$%s$%s\n", - strings.ToLower(team.Name), - salt32, - fic.Apr1Md5(cert.Password, salt32), - ) - } - } - } - - return -} - -type PKISettings struct { - Version int `json:"version"` - SerialNumber *big.Int `json:"serialnumber"` - Issuer pkix.Name `json:"issuer"` - Subject pkix.Name `json:"subject"` - NotBefore time.Time `json:"notbefore"` - NotAfter time.Time `json:"notafter"` - SignatureAlgorithm x509.SignatureAlgorithm `json:"signatureAlgorithm,"` - PublicKeyAlgorithm x509.PublicKeyAlgorithm `json:"publicKeyAlgorithm"` -} - -func infoCA(c *gin.Context) { - _, cacert, err := pki.LoadCA() - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "CA not found"}) - return - } - - c.JSON(http.StatusOK, PKISettings{ - Version: cacert.Version, - SerialNumber: cacert.SerialNumber, - Issuer: cacert.Issuer, - Subject: cacert.Subject, - NotBefore: cacert.NotBefore, - NotAfter: cacert.NotAfter, - SignatureAlgorithm: cacert.SignatureAlgorithm, - PublicKeyAlgorithm: cacert.PublicKeyAlgorithm, - }) -} - -func getCAPEM(c *gin.Context) { - if _, err := os.Stat(pki.CACertPath()); os.IsNotExist(err) { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Unable to locate the CA root certificate. Have you generated it?"}) - return - } else if fd, err := os.Open(pki.CACertPath()); err != nil { - log.Println("Unable to open CA root certificate:", err) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } else { - defer fd.Close() - - cnt, err := ioutil.ReadAll(fd) - if err != nil { - log.Println("Unable to read CA root certificate:", err) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.String(http.StatusOK, string(cnt)) - } -} - -func getTeamP12File(c *gin.Context) { - cert := c.MustGet("cert").(*fic.Certificate) - - // Create p12 if necessary - if _, err := os.Stat(pki.ClientP12Path(cert.Id)); os.IsNotExist(err) { - if err := pki.WriteP12(cert.Id, cert.Password); err != nil { - log.Println("Unable to WriteP12:", err.Error()) - c.AbortWithError(http.StatusInternalServerError, err) - return - } - } - - if _, err := os.Stat(pki.ClientP12Path(cert.Id)); os.IsNotExist(err) { - log.Println("Unable to compute ClientP12Path:", err.Error()) - c.AbortWithError(http.StatusInternalServerError, errors.New("Unable to locate the p12. Have you generated it?")) - return - } else if fd, err := os.Open(pki.ClientP12Path(cert.Id)); err != nil { - log.Println("Unable to open ClientP12Path:", err.Error()) - c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("Unable to open the p12: %w", err)) - return - } else { - defer fd.Close() - - data, err := ioutil.ReadAll(fd) - if err != nil { - log.Println("Unable to open ClientP12Path:", err.Error()) - c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("Unable to open the p12: %w", err)) - return - } - - c.Data(http.StatusOK, "application/x-pkcs12", data) - } -} - -func generateClientCert(c *gin.Context) { - // First, generate a new, unique, serial - var serial_gen [8]byte - if _, err := rand.Read(serial_gen[:]); err != nil { - log.Println("Unable to read enough entropy to generate client certificate:", err) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to read enough entropy"}) - return - } - for fic.ExistingCertSerial(serial_gen) { - if _, err := rand.Read(serial_gen[:]); err != nil { - log.Println("Unable to read enough entropy to generate client certificate:", err) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to read enough entropy"}) - return - } - } - - var serial_b big.Int - serial_b.SetBytes(serial_gen[:]) - serial := serial_b.Uint64() - - // Let's pick a random password - password, err := fic.GeneratePassword() - if err != nil { - log.Println("Unable to generate password:", err) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to generate password: " + err.Error()}) - return - } - - // Ok, now load CA - capriv, cacert, err := pki.LoadCA() - if err != nil { - log.Println("Unable to load the CA:", err) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to load the CA"}) - return - } - - // Generate our privkey - if err := pki.GenerateClient(serial, cacert.NotBefore, cacert.NotAfter, &cacert, &capriv); err != nil { - log.Println("Unable to generate private key:", err) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to generate private key: " + err.Error()}) - return - } - - // Save in DB - cert, err := fic.RegisterCertificate(serial, password) - if err != nil { - log.Println("Unable to register certificate:", err) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to register certificate."}) - return - } - - c.JSON(http.StatusOK, CertExported{fmt.Sprintf("%0[2]*[1]X", cert.Id, int(math.Ceil(math.Log2(float64(cert.Id))/8)*2)), cert.Creation, cert.Password, nil, cert.Revoked}) -} - -type CertExported struct { - Id string `json:"id"` - Creation time.Time `json:"creation"` - Password string `json:"password,omitempty"` - IdTeam *int64 `json:"id_team"` - Revoked *time.Time `json:"revoked"` -} - -func getCertificates(c *gin.Context) { - certificates, err := fic.GetCertificates() - if err != nil { - log.Println("Unable to retrieve certificates list:", err) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during certificates retrieval."}) - return - } - ret := make([]CertExported, 0) - for _, cert := range certificates { - dstLinkPath := path.Join(TeamsDir, pki.GetCertificateAssociation(cert.Id)) - - var idTeam *int64 = nil - if lnk, err := os.Readlink(dstLinkPath); err == nil { - if tid, err := strconv.ParseInt(lnk, 10, 64); err == nil { - idTeam = &tid - } - } - - ret = append(ret, CertExported{fmt.Sprintf("%0[2]*[1]X", cert.Id, int(math.Ceil(math.Log2(float64(cert.Id))/8)*2)), cert.Creation, "", idTeam, cert.Revoked}) - } - - c.JSON(http.StatusOK, ret) -} - -type CertUploaded struct { - Team *int64 `json:"id_team"` -} - -func updateCertificateAssociation(c *gin.Context) { - cert := c.MustGet("cert").(*fic.Certificate) - - var uc CertUploaded - err := c.ShouldBindJSON(&uc) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - dstLinkPath := path.Join(TeamsDir, pki.GetCertificateAssociation(cert.Id)) - - if uc.Team != nil { - srcLinkPath := fmt.Sprintf("%d", *uc.Team) - if err := os.Symlink(srcLinkPath, dstLinkPath); err != nil { - log.Println("Unable to create certificate symlink:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to create certificate symlink: %s", err.Error())}) - return - } - - // Mark team as active to ensure it'll be generated - if ut, err := fic.GetTeam(*uc.Team); err != nil { - log.Println("Unable to GetTeam:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during team retrieval."}) - return - } else if !ut.Active { - ut.Active = true - _, err := ut.Update() - if err != nil { - log.Println("Unable to UpdateTeam after updateCertificateAssociation:", err.Error()) - } - } - } else { - os.Remove(dstLinkPath) - } - - c.JSON(http.StatusOK, cert) -} diff --git a/admin/api/claim.go b/admin/api/claim.go deleted file mode 100644 index cf545fff..00000000 --- a/admin/api/claim.go +++ /dev/null @@ -1,499 +0,0 @@ -package api - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" - "path" - "strconv" - "time" - - "srs.epita.fr/fic-server/admin/generation" - "srs.epita.fr/fic-server/libfic" - - "github.com/gin-gonic/gin" -) - -func declareClaimsRoutes(router *gin.RouterGroup) { - // Tasks - router.GET("/claims", getClaims) - router.POST("/claims", newClaim) - router.DELETE("/claims", clearClaims) - - apiClaimsRoutes := router.Group("/claims/:cid") - apiClaimsRoutes.Use(ClaimHandler) - apiClaimsRoutes.GET("", showClaim) - apiClaimsRoutes.PUT("", updateClaim) - apiClaimsRoutes.POST("", addClaimDescription) - apiClaimsRoutes.DELETE("", deleteClaim) - - apiClaimsRoutes.GET("/last_update", getClaimLastUpdate) - apiClaimsRoutes.PUT("/descriptions", updateClaimDescription) - - // Assignees - router.GET("/claims-assignees", getAssignees) - router.POST("/claims-assignees", newAssignee) - - apiClaimAssigneesRoutes := router.Group("/claims-assignees/:aid") - apiClaimAssigneesRoutes.Use(ClaimAssigneeHandler) - router.GET("/claims-assignees/:aid", showClaimAssignee) - router.PUT("/claims-assignees/:aid", updateClaimAssignee) - router.DELETE("/claims-assignees/:aid", deleteClaimAssignee) -} - -func declareExerciceClaimsRoutes(router *gin.RouterGroup) { - router.GET("/claims", getExerciceClaims) -} - -func declareTeamClaimsRoutes(router *gin.RouterGroup) { - router.GET("/api/teams/:tid/issue.json", func(c *gin.Context) { - team := c.MustGet("team").(*fic.Team) - - issues, err := team.MyIssueFile() - if err != nil { - log.Printf("Unable to MyIssueFile(tid=%d): %s", team.Id, err.Error()) - c.JSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to generate issues.json."}) - return - } - - c.JSON(http.StatusOK, issues) - }) - - router.GET("/claims", getTeamClaims) -} - -func ClaimHandler(c *gin.Context) { - cid, err := strconv.ParseInt(string(c.Params.ByName("cid")), 10, 64) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid claim identifier"}) - return - } - - claim, err := fic.GetClaim(cid) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Requested claim not found"}) - return - } - - c.Set("claim", claim) - - c.Next() -} - -func ClaimAssigneeHandler(c *gin.Context) { - aid, err := strconv.ParseInt(string(c.Params.ByName("aid")), 10, 64) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid claim assignee identifier"}) - return - } - - assignee, err := fic.GetAssignee(aid) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Requested claim-assignee not found"}) - return - } - - c.Set("claim-assignee", assignee) - - c.Next() -} - -func getClaims(c *gin.Context) { - claims, err := fic.GetClaims() - - if err != nil { - log.Println("Unable to getClaims:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during claims retrieval."}) - return - } - - c.JSON(http.StatusOK, claims) -} - -func getTeamClaims(c *gin.Context) { - team := c.MustGet("team").(*fic.Team) - - claims, err := team.GetClaims() - if err != nil { - log.Printf("Unable to GetClaims(tid=%d): %s", team.Id, err.Error()) - c.JSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve claim list."}) - return - } - - c.JSON(http.StatusOK, claims) -} - -func getExerciceClaims(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - - claims, err := exercice.GetClaims() - if err != nil { - log.Printf("Unable to GetClaims(eid=%d): %s", exercice.Id, err.Error()) - c.JSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve claim list."}) - return - } - - c.JSON(http.StatusOK, claims) -} - -func getClaimLastUpdate(c *gin.Context) { - claim := c.MustGet("claim").(*fic.Claim) - - v, err := claim.GetLastUpdate() - if err != nil { - log.Printf("Unable to GetLastUpdate: %s", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during claim last update retrieval."}) - return - } - - c.JSON(http.StatusOK, v) -} - -type ClaimExported struct { - Id int64 `json:"id"` - Subject string `json:"subject"` - IdTeam *int64 `json:"id_team"` - Team *fic.Team `json:"team"` - IdExercice *int64 `json:"id_exercice"` - Exercice *fic.Exercice `json:"exercice"` - IdAssignee *int64 `json:"id_assignee"` - Assignee *fic.ClaimAssignee `json:"assignee"` - Creation time.Time `json:"creation"` - LastUpdate time.Time `json:"last_update"` - State string `json:"state"` - Priority string `json:"priority"` - Descriptions []*fic.ClaimDescription `json:"descriptions"` -} - -func showClaim(c *gin.Context) { - claim := c.MustGet("claim").(*fic.Claim) - - var e ClaimExported - var err error - if e.Team, err = claim.GetTeam(); err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to find associated team: %s", err.Error())}) - return - } - if e.Exercice, err = claim.GetExercice(); err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to find associated exercice: %s", err.Error())}) - return - } - if e.Assignee, err = claim.GetAssignee(); err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to find associated assignee: %s", err.Error())}) - return - } - if e.Descriptions, err = claim.GetDescriptions(); err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to find claim's descriptions: %s", err.Error())}) - return - } - - e.LastUpdate = e.Creation - for _, d := range e.Descriptions { - if d.Date.After(e.LastUpdate) { - e.LastUpdate = d.Date - } - } - - e.Id = claim.Id - e.IdAssignee = claim.IdAssignee - e.IdTeam = claim.IdTeam - e.IdExercice = claim.IdExercice - e.Subject = claim.Subject - e.Creation = claim.Creation - e.State = claim.State - e.Priority = claim.Priority - - c.JSON(http.StatusOK, e) -} - -type ClaimUploaded struct { - fic.Claim - Whoami *int64 `json:"whoami"` -} - -func newClaim(c *gin.Context) { - var uc ClaimUploaded - err := c.ShouldBindJSON(&uc) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - if uc.Subject == "" { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Claim's subject cannot be empty."}) - return - } - - var t *fic.Team - if uc.IdTeam != nil { - if team, err := fic.GetTeam(*uc.IdTeam); err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to get associated team: %s", err.Error())}) - return - } else { - t = team - } - } else { - t = nil - } - - var e *fic.Exercice - if uc.IdExercice != nil { - if exercice, err := fic.GetExercice(*uc.IdExercice); err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to get associated exercice: %s", err.Error())}) - return - } else { - e = exercice - } - } else { - e = nil - } - - var a *fic.ClaimAssignee - if uc.IdAssignee != nil { - if assignee, err := fic.GetAssignee(*uc.IdAssignee); err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to get associated assignee: %s", err.Error())}) - return - } else { - a = assignee - } - } else { - a = nil - } - - if uc.Priority == "" { - uc.Priority = "medium" - } - - claim, err := fic.NewClaim(uc.Subject, t, e, a, uc.Priority) - if err != nil { - log.Println("Unable to newClaim:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to register new claim"}) - return - } - - c.JSON(http.StatusOK, claim) -} - -func clearClaims(c *gin.Context) { - nb, err := fic.ClearClaims() - if err != nil { - log.Printf("Unable to clearClaims: %s", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during claims clearing."}) - return - } - - c.JSON(http.StatusOK, nb) -} - -func generateTeamIssuesFile(team fic.Team) error { - if generation.GeneratorSocket == "" { - if my, err := team.MyIssueFile(); err != nil { - return fmt.Errorf("Unable to generate issue FILE (tid=%d): %w", team.Id, err) - } else if j, err := json.Marshal(my); err != nil { - return fmt.Errorf("Unable to encode issues' file JSON: %w", err) - } else if err = ioutil.WriteFile(path.Join(TeamsDir, fmt.Sprintf("%d", team.Id), "issues.json"), j, 0644); err != nil { - return fmt.Errorf("Unable to write issues' file: %w", err) - } - } else { - resp, err := generation.PerformGeneration(fic.GenStruct{Type: fic.GenTeamIssues, TeamId: team.Id}) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - v, _ := ioutil.ReadAll(resp.Body) - return fmt.Errorf("%s", string(v)) - } - } - return nil -} - -func addClaimDescription(c *gin.Context) { - claim := c.MustGet("claim").(*fic.Claim) - - var ud fic.ClaimDescription - err := c.ShouldBindJSON(&ud) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - assignee, err := fic.GetAssignee(ud.IdAssignee) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to get associated assignee: %s", err.Error())}) - return - } - - description, err := claim.AddDescription(ud.Content, assignee, ud.Publish) - if err != nil { - log.Println("Unable to addClaimDescription:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to add description"}) - return - } - - if team, _ := claim.GetTeam(); team != nil { - err = generateTeamIssuesFile(*team) - if err != nil { - log.Println("Unable to generateTeamIssuesFile after addClaimDescription:", err.Error()) - } - } - - c.JSON(http.StatusOK, description) -} - -func updateClaimDescription(c *gin.Context) { - claim := c.MustGet("claim").(*fic.Claim) - - var ud fic.ClaimDescription - err := c.ShouldBindJSON(&ud) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - if _, err := ud.Update(); err != nil { - log.Println("Unable to updateClaimDescription:", err.Error()) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "An error occurs during claim description updating."}) - return - } - if team, _ := claim.GetTeam(); team != nil { - err = generateTeamIssuesFile(*team) - if err != nil { - log.Println("Unable to generateTeamIssuesFile:", err.Error()) - } - } - - c.JSON(http.StatusOK, ud) -} - -func updateClaim(c *gin.Context) { - claim := c.MustGet("claim").(*fic.Claim) - - var uc ClaimUploaded - err := c.ShouldBindJSON(&uc) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - uc.Id = claim.Id - - _, err = uc.Update() - if err != nil { - log.Printf("Unable to updateClaim: %s", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during claim update."}) - return - } - - if claim.State != uc.State { - if uc.Whoami != nil { - if assignee, err := fic.GetAssignee(*uc.Whoami); err == nil { - claim.AddDescription(fmt.Sprintf("%s a changé l'état de la tâche vers %q (était %q).", assignee.Name, uc.State, claim.State), assignee, true) - } - } - } - - if claim.IdAssignee != uc.IdAssignee { - if uc.Whoami != nil { - if whoami, err := fic.GetAssignee(*uc.Whoami); err == nil { - if uc.IdAssignee != nil { - if assignee, err := fic.GetAssignee(*uc.IdAssignee); err == nil { - if assignee.Id != whoami.Id { - claim.AddDescription(fmt.Sprintf("%s a assigné la tâche à %s.", whoami.Name, assignee.Name), whoami, false) - } else { - claim.AddDescription(fmt.Sprintf("%s s'est assigné la tâche.", assignee.Name), whoami, false) - } - } - } else { - claim.AddDescription(fmt.Sprintf("%s a retiré l'attribution de la tâche.", whoami.Name), whoami, false) - } - } - } - } - - if team, _ := claim.GetTeam(); team != nil { - err = generateTeamIssuesFile(*team) - } - - c.JSON(http.StatusOK, uc) -} - -func deleteClaim(c *gin.Context) { - claim := c.MustGet("claim").(*fic.Claim) - - if nb, err := claim.Delete(); err != nil { - log.Println("Unable to deleteClaim:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during claim deletion."}) - return - } else { - c.JSON(http.StatusOK, nb) - } -} - -func getAssignees(c *gin.Context) { - assignees, err := fic.GetAssignees() - if err != nil { - log.Println("Unable to getAssignees:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during assignees retrieval."}) - return - } - - c.JSON(http.StatusOK, assignees) -} - -func showClaimAssignee(c *gin.Context) { - c.JSON(http.StatusOK, c.MustGet("claim-assignee").(*fic.ClaimAssignee)) -} -func newAssignee(c *gin.Context) { - var ua fic.ClaimAssignee - err := c.ShouldBindJSON(&ua) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - assignee, err := fic.NewClaimAssignee(ua.Name) - if err != nil { - log.Println("Unable to newAssignee:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during assignee creation."}) - return - } - - c.JSON(http.StatusOK, assignee) -} - -func updateClaimAssignee(c *gin.Context) { - assignee := c.MustGet("claim-assignee").(*fic.ClaimAssignee) - - var ua fic.ClaimAssignee - err := c.ShouldBindJSON(&ua) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - ua.Id = assignee.Id - - if _, err := ua.Update(); err != nil { - log.Println("Unable to updateClaimAssignee:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during claim assignee update."}) - return - } - - c.JSON(http.StatusOK, ua) -} - -func deleteClaimAssignee(c *gin.Context) { - assignee := c.MustGet("claim-assignee").(*fic.ClaimAssignee) - - if _, err := assignee.Delete(); err != nil { - log.Println("Unable to deleteClaimAssignee:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("An error occurs during claim assignee deletion: %s", err.Error())}) - return - } - - c.JSON(http.StatusOK, true) -} diff --git a/admin/api/events.go b/admin/api/events.go deleted file mode 100644 index 17500535..00000000 --- a/admin/api/events.go +++ /dev/null @@ -1,154 +0,0 @@ -package api - -import ( - "encoding/json" - "io/ioutil" - "log" - "net/http" - "path" - "strconv" - - "srs.epita.fr/fic-server/libfic" - - "github.com/gin-gonic/gin" -) - -func declareEventsRoutes(router *gin.RouterGroup) { - router.GET("/events", getEvents) - router.GET("/events.json", getLastEvents) - router.POST("/events", newEvent) - router.DELETE("/events", clearEvents) - - apiEventsRoutes := router.Group("/events/:evid") - apiEventsRoutes.Use(EventHandler) - apiEventsRoutes.GET("", showEvent) - apiEventsRoutes.PUT("", updateEvent) - apiEventsRoutes.DELETE("", deleteEvent) -} - -func EventHandler(c *gin.Context) { - evid, err := strconv.ParseInt(string(c.Params.ByName("evid")), 10, 64) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid event identifier"}) - return - } - - event, err := fic.GetEvent(evid) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Event not found"}) - return - } - - c.Set("event", event) - - c.Next() -} - -func genEventsFile() error { - if evts, err := fic.GetLastEvents(); err != nil { - return err - } else if j, err := json.Marshal(evts); err != nil { - return err - } else if err := ioutil.WriteFile(path.Join(TeamsDir, "events.json"), j, 0666); err != nil { - return err - } - - return nil -} - -func getEvents(c *gin.Context) { - evts, err := fic.GetEvents() - if err != nil { - log.Println("Unable to GetEvents:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve events list"}) - return - } - - c.JSON(http.StatusOK, evts) -} - -func getLastEvents(c *gin.Context) { - evts, err := fic.GetLastEvents() - - if err != nil { - log.Println("Unable to GetLastEvents:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve last events list"}) - return - } - - c.JSON(http.StatusOK, evts) -} - -func newEvent(c *gin.Context) { - var ue fic.Event - err := c.ShouldBindJSON(&ue) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - event, err := fic.NewEvent(ue.Text, ue.Kind) - if err != nil { - log.Printf("Unable to newEvent: %s", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during event creation."}) - return - } - - genEventsFile() - - c.JSON(http.StatusOK, event) -} - -func clearEvents(c *gin.Context) { - nb, err := fic.ClearEvents() - if err != nil { - log.Printf("Unable to clearEvent: %s", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during event clearing."}) - return - } - - c.JSON(http.StatusOK, nb) -} - -func showEvent(c *gin.Context) { - event := c.MustGet("event").(*fic.Event) - c.JSON(http.StatusOK, event) -} - -func updateEvent(c *gin.Context) { - event := c.MustGet("event").(*fic.Event) - - var ue fic.Event - err := c.ShouldBindJSON(&ue) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - ue.Id = event.Id - - if _, err := ue.Update(); err != nil { - log.Printf("Unable to updateEvent: %s", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during event update."}) - return - } - - genEventsFile() - - c.JSON(http.StatusOK, ue) -} - -func deleteEvent(c *gin.Context) { - event := c.MustGet("event").(*fic.Event) - - _, err := event.Delete() - if err != nil { - log.Printf("Unable to deleteEvent: %s", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during event deletion."}) - return - } - - genEventsFile() - - c.JSON(http.StatusOK, true) -} diff --git a/admin/api/exercice.go b/admin/api/exercice.go deleted file mode 100644 index 2c196153..00000000 --- a/admin/api/exercice.go +++ /dev/null @@ -1,1749 +0,0 @@ -package api - -import ( - "bytes" - "fmt" - "log" - "net/http" - "path" - "reflect" - "strconv" - "strings" - "time" - - "srs.epita.fr/fic-server/admin/sync" - "srs.epita.fr/fic-server/libfic" - - "github.com/gin-gonic/gin" -) - -func declareGlobalExercicesRoutes(router *gin.RouterGroup) { - router.GET("/resolutions.json", exportResolutionMovies) - router.GET("/exercices_stats.json", getExercicesStats) - router.GET("/exercices_forge_bindings.json", getExercicesForgeLinks) - router.GET("/tags", listTags) -} - -func declareExercicesRoutes(router *gin.RouterGroup) { - router.GET("/exercices", listExercices) - router.POST("/exercices", createExercice) - - apiExercicesRoutes := router.Group("/exercices/:eid") - apiExercicesRoutes.Use(ExerciceHandler) - apiExercicesRoutes.GET("", showExercice) - apiExercicesRoutes.PUT("", updateExercice) - apiExercicesRoutes.PATCH("", partUpdateExercice) - apiExercicesRoutes.DELETE("", deleteExercice) - - apiExercicesRoutes.POST("/diff-sync", APIDiffExerciceWithRemote) - - apiExercicesRoutes.GET("/history.json", getExerciceHistory) - - apiExercicesRoutes.GET("/stats.json", getExerciceStats) - - apiExercicesRoutes.GET("/tries", listTries) - - apiTriesRoutes := apiExercicesRoutes.Group("/tries/:trid") - apiTriesRoutes.Use(ExerciceTryHandler) - apiTriesRoutes.GET("", getExerciceTry) - apiTriesRoutes.DELETE("", deleteExerciceTry) - - apiHistoryRoutes := apiExercicesRoutes.Group("/history.json") - apiHistoryRoutes.Use(AssigneeCookieHandler) - apiHistoryRoutes.PUT("", appendExerciceHistory) - apiHistoryRoutes.PATCH("", updateExerciceHistory) - apiHistoryRoutes.DELETE("", delExerciceHistory) - - apiExercicesRoutes.GET("/hints", listExerciceHints) - apiExercicesRoutes.POST("/hints", createExerciceHint) - - apiHintsRoutes := apiExercicesRoutes.Group("/hints/:hid") - apiHintsRoutes.Use(HintHandler) - apiHintsRoutes.GET("", showExerciceHint) - apiHintsRoutes.PUT("", updateExerciceHint) - apiHintsRoutes.DELETE("", deleteExerciceHint) - apiHintsRoutes.GET("/dependancies", showExerciceHintDeps) - - apiExercicesRoutes.GET("/flags", listExerciceFlags) - apiExercicesRoutes.POST("/flags", createExerciceFlag) - - apiFlagsRoutes := apiExercicesRoutes.Group("/flags/:kid") - apiFlagsRoutes.Use(FlagKeyHandler) - apiFlagsRoutes.GET("", showExerciceFlag) - apiFlagsRoutes.PUT("", updateExerciceFlag) - apiFlagsRoutes.POST("/try", tryExerciceFlag) - apiFlagsRoutes.DELETE("/", deleteExerciceFlag) - apiFlagsRoutes.GET("/dependancies", showExerciceFlagDeps) - apiFlagsRoutes.GET("/statistics", showExerciceFlagStats) - apiFlagsRoutes.DELETE("/tries", deleteExerciceFlagTries) - apiFlagsRoutes.GET("/choices/", listFlagChoices) - apiFlagsChoicesRoutes := apiExercicesRoutes.Group("/choices/:cid") - apiFlagsChoicesRoutes.Use(FlagChoiceHandler) - apiFlagsChoicesRoutes.GET("", showFlagChoice) - apiFlagsRoutes.POST("/choices/", createFlagChoice) - apiFlagsChoicesRoutes.PUT("", updateFlagChoice) - apiFlagsChoicesRoutes.DELETE("", deleteFlagChoice) - - apiQuizRoutes := apiExercicesRoutes.Group("/quiz/:qid") - apiQuizRoutes.Use(FlagQuizHandler) - apiExercicesRoutes.GET("/quiz", listExerciceQuiz) - apiQuizRoutes.GET("", showExerciceQuiz) - apiQuizRoutes.PUT("", updateExerciceQuiz) - apiQuizRoutes.DELETE("", deleteExerciceQuiz) - apiQuizRoutes.GET("/dependancies", showExerciceQuizDeps) - apiQuizRoutes.GET("/statistics", showExerciceQuizStats) - apiQuizRoutes.DELETE("/tries", deleteExerciceQuizTries) - - apiExercicesRoutes.GET("/tags", listExerciceTags) - apiExercicesRoutes.POST("/tags", addExerciceTag) - apiExercicesRoutes.PUT("/tags", updateExerciceTags) - - declareFilesRoutes(apiExercicesRoutes) - declareExerciceClaimsRoutes(apiExercicesRoutes) - - // Remote - router.GET("/remote/themes/:thid/exercices/:exid", sync.ApiGetRemoteExercice) - router.GET("/remote/themes/:thid/exercices/:exid/flags", sync.ApiGetRemoteExerciceFlags) - router.GET("/remote/themes/:thid/exercices/:exid/hints", sync.ApiGetRemoteExerciceHints) -} - -type Exercice struct { - *fic.Exercice - ForgeLink string `json:"forge_link,omitempty"` -} - -func ExerciceHandler(c *gin.Context) { - eid, err := strconv.ParseInt(string(c.Params.ByName("eid")), 10, 32) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid exercice identifier"}) - return - } - - var exercice *fic.Exercice - if theme, exists := c.Get("theme"); exists { - exercice, err = theme.(*fic.Theme).GetExercice(int(eid)) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Exercice not found"}) - return - } - } else { - exercice, err = fic.GetExercice(eid) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Exercice not found"}) - return - } - - if exercice.IdTheme != nil { - theme, err = exercice.GetTheme() - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to find the attached theme."}) - return - } - - c.Set("theme", theme) - } else { - c.Set("theme", &fic.StandaloneExercicesTheme) - } - } - - c.Set("exercice", exercice) - - c.Next() -} - -func HintHandler(c *gin.Context) { - hid, err := strconv.ParseInt(string(c.Params.ByName("hid")), 10, 32) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid hint identifier"}) - return - } - - exercice := c.MustGet("exercice").(*fic.Exercice) - hint, err := exercice.GetHint(hid) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Hint not found"}) - return - } - - c.Set("hint", hint) - - c.Next() -} - -func FlagKeyHandler(c *gin.Context) { - kid, err := strconv.ParseInt(string(c.Params.ByName("kid")), 10, 32) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid flag identifier"}) - return - } - - var flag *fic.FlagKey - if exercice, exists := c.Get("exercice"); exists { - flag, err = exercice.(*fic.Exercice).GetFlagKey(int(kid)) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Flag not found"}) - return - } - } else { - flag, err = fic.GetFlagKey(int(kid)) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Flag not found"}) - return - } - } - - c.Set("flag-key", flag) - - c.Next() -} - -func FlagChoiceHandler(c *gin.Context) { - cid, err := strconv.ParseInt(string(c.Params.ByName("cid")), 10, 32) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid choice identifier"}) - return - } - - flagkey := c.MustGet("flag-key").(*fic.FlagKey) - choice, err := flagkey.GetChoice(int(cid)) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Choice not found"}) - return - } - - c.Set("flag-choice", choice) - - c.Next() -} - -func FlagQuizHandler(c *gin.Context) { - qid, err := strconv.ParseInt(string(c.Params.ByName("qid")), 10, 64) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid quiz identifier"}) - return - } - - var quiz *fic.MCQ - if exercice, exists := c.Get("exercice"); exists { - quiz, err = exercice.(*fic.Exercice).GetMCQById(int(qid)) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Quiz not found"}) - return - } - } else { - quiz, err = fic.GetMCQ(int(qid)) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Quiz not found"}) - return - } - } - - c.Set("flag-quiz", quiz) - - c.Next() -} - -func listExercices(c *gin.Context) { - if theme, exists := c.Get("theme"); exists { - exercices, err := theme.(*fic.Theme).GetExercices() - if err != nil { - log.Println("Unable to listThemedExercices:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during exercices listing."}) - return - } - - c.JSON(http.StatusOK, exercices) - } else { - exercices, err := fic.GetExercices() - if err != nil { - log.Println("Unable to listThemedExercices:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during exercices listing."}) - return - } - - c.JSON(http.StatusOK, exercices) - } -} - -func listTags(c *gin.Context) { - exercices, err := fic.GetExercices() - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - ret := map[string][]*fic.Exercice{} - for _, exercice := range exercices { - tags, err := exercice.GetTags() - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - for _, t := range tags { - if _, ok := ret[t]; !ok { - ret[t] = []*fic.Exercice{} - } - - ret[t] = append(ret[t], exercice) - } - } - - c.JSON(http.StatusOK, ret) -} - -// Generate the csv to export with: -// -// curl -s http://127.0.0.1:8081/api/resolutions.json | jq -r ".[] | [ .theme,.level,.title, @uri \"https://fic.srs.epita.fr/$(date +%Y)/\\(.videoURI)\" ] | join(\";\")" -func exportResolutionMovies(c *gin.Context) { - exercices, err := fic.GetExercices() - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - export := []map[string]string{} - for _, exercice := range exercices { - var tname string - if exercice.IdTheme != nil { - theme, err := fic.GetTheme(*exercice.IdTheme) - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - tname = theme.Name - } - if len(exercice.VideoURI) > 0 { - level, _ := exercice.GetLevel() - export = append(export, map[string]string{ - "videoURI": strings.Replace(exercice.VideoURI, "$FILES$/", "files/", 1), - "theme": tname, - "title": exercice.Title, - "level": fmt.Sprintf("%d", level), - }) - } - } - - c.JSON(http.StatusOK, export) -} - -func loadFlags(n func() ([]fic.Flag, error)) (interface{}, error) { - if flags, err := n(); err != nil { - return nil, err - } else { - var ret []fic.Flag - - for _, flag := range flags { - if f, ok := flag.(*fic.FlagKey); ok { - if k, err := fic.GetFlagKey(f.Id); err != nil { - return nil, err - } else { - ret = append(ret, k) - } - } else if f, ok := flag.(*fic.MCQ); ok { - if m, err := fic.GetMCQ(f.Id); err != nil { - return nil, err - } else { - ret = append(ret, m) - } - } else { - return nil, fmt.Errorf("Flag type %T not implemented for this flag.", f) - } - } - - return ret, nil - } -} - -func listExerciceHints(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - - hints, err := exercice.GetHints() - if err != nil { - log.Println("Unable to listExerciceHints:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving hints"}) - return - } - - c.JSON(http.StatusOK, hints) -} - -func listExerciceFlags(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - - flags, err := exercice.GetFlagKeys() - if err != nil { - log.Println("Unable to listExerciceFlags:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving exercice flags"}) - return - } - - c.JSON(http.StatusOK, flags) -} - -func listFlagChoices(c *gin.Context) { - flag := c.MustGet("flag-key").(*fic.FlagKey) - - choices, err := flag.GetChoices() - if err != nil { - log.Println("Unable to listFlagChoices:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving flag choices"}) - return - } - - c.JSON(http.StatusOK, choices) -} - -func listExerciceQuiz(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - - quiz, err := exercice.GetMCQ() - if err != nil { - log.Println("Unable to listExerciceQuiz:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving quiz list"}) - return - } - - c.JSON(http.StatusOK, quiz) -} - -func showExercice(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - - var forgelink string - if fli, ok := sync.GlobalImporter.(sync.ForgeLinkedImporter); ok { - if u, _ := fli.GetExerciceLink(exercice); u != nil { - forgelink = u.String() - } - } - - c.JSON(http.StatusOK, Exercice{exercice, forgelink}) -} - -func getExerciceHistory(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - - history, err := exercice.GetHistory() - if err != nil { - log.Println("Unable to getExerciceHistory:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving exercice history"}) - return - } - - c.JSON(http.StatusOK, history) -} - -type exerciceStats struct { - IdExercice int64 `json:"id_exercice,omitempty"` - TeamTries int64 `json:"team_tries"` - TotalTries int64 `json:"total_tries"` - SolvedCount int64 `json:"solved_count"` - FlagSolved []int64 `json:"flag_solved"` - MCQSolved []int64 `json:"mcq_solved"` - CurrentGain int64 `json:"current_gain"` -} - -func getExerciceStats(c *gin.Context) { - e := c.MustGet("exercice").(*fic.Exercice) - - current_gain := e.Gain - if fic.DiscountedFactor > 0 { - decoted_exercice, err := fic.GetDiscountedExercice(e.Id) - if err == nil { - current_gain = decoted_exercice.Gain - } else { - log.Println("Unable to fetch decotedExercice:", err.Error()) - } - } - - c.JSON(http.StatusOK, exerciceStats{ - TeamTries: e.TriedTeamCount(), - TotalTries: e.TriedCount(), - SolvedCount: e.SolvedCount(), - FlagSolved: e.FlagSolved(), - MCQSolved: e.MCQSolved(), - CurrentGain: current_gain, - }) -} - -func getExercicesStats(c *gin.Context) { - exercices, err := fic.GetDiscountedExercices() - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - ret := []exerciceStats{} - for _, e := range exercices { - ret = append(ret, exerciceStats{ - IdExercice: e.Id, - TeamTries: e.TriedTeamCount(), - TotalTries: e.TriedCount(), - SolvedCount: e.SolvedCount(), - FlagSolved: e.FlagSolved(), - MCQSolved: e.MCQSolved(), - CurrentGain: e.Gain, - }) - } - - c.JSON(http.StatusOK, ret) -} - -type themeForgeBinding struct { - ThemeName string `json:"name"` - ThemePath string `json:"path"` - ForgeLink string `json:"forge_link"` - Exercices []exerciceForgeBinding `json:"exercices"` -} - -type exerciceForgeBinding struct { - ExerciceName string `json:"name"` - ExercicePath string `json:"path"` - ForgeLink string `json:"forge_link"` -} - -func getExercicesForgeLinks(c *gin.Context) { - themes, err := fic.GetThemesExtended() - if err != nil { - log.Println("Unable to listThemes:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during themes listing."}) - return - } - - fli, ok := sync.GlobalImporter.(sync.ForgeLinkedImporter) - if !ok { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Current importer is not compatible with ForgeLinkedImporter"}) - return - } - - ret := []themeForgeBinding{} - for _, theme := range themes { - exercices, err := theme.GetExercices() - if err != nil { - log.Println("Unable to listExercices:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during exercice listing."}) - return - } - - var exlinks []exerciceForgeBinding - for _, exercice := range exercices { - var forgelink string - if u, _ := fli.GetExerciceLink(exercice); u != nil { - forgelink = u.String() - } - - exlinks = append(exlinks, exerciceForgeBinding{ - ExerciceName: exercice.Title, - ExercicePath: exercice.Path, - ForgeLink: forgelink, - }) - } - - var forgelink string - if u, _ := fli.GetThemeLink(theme); u != nil { - forgelink = u.String() - } - ret = append(ret, themeForgeBinding{ - ThemeName: theme.Name, - ThemePath: theme.Path, - ForgeLink: forgelink, - Exercices: exlinks, - }) - } - - c.JSON(http.StatusOK, ret) -} - -func AssigneeCookieHandler(c *gin.Context) { - myassignee, err := c.Cookie("myassignee") - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "You must be authenticated to perform this action."}) - return - } - - aid, err := strconv.ParseInt(myassignee, 10, 32) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "You must be authenticated to perform this action: invalid assignee identifier."}) - return - } - - assignee, err := fic.GetAssignee(aid) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "You must be authenticated to perform this action: assignee not found."}) - return - } - - c.Set("assignee", assignee) - - c.Next() -} - -type uploadedExerciceHistory struct { - IdTeam int64 `json:"team_id"` - Kind string - Time time.Time - Secondary *int64 - Coeff float32 -} - -func appendExerciceHistory(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - myassignee := c.MustGet("assignee").(*fic.ClaimAssignee) - - var uh uploadedExerciceHistory - err := c.ShouldBindJSON(&uh) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - err = exercice.AppendHistoryItem(uh.IdTeam, uh.Kind, uh.Secondary) - if err != nil { - log.Println("Unable to appendExerciceHistory:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during history moditication."}) - return - } - log.Printf("AUDIT: %s performs an history append: %s for team %d, exercice %d and optional %v", myassignee.Name, uh.Kind, uh.IdTeam, exercice.Id, uh.Secondary) - - c.JSON(http.StatusOK, uh) -} - -func updateExerciceHistory(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - myassignee := c.MustGet("assignee").(*fic.ClaimAssignee) - - var uh uploadedExerciceHistory - err := c.ShouldBindJSON(&uh) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - _, err = exercice.UpdateHistoryItem(uh.Coeff, uh.IdTeam, uh.Kind, uh.Time, uh.Secondary) - if err != nil { - log.Println("Unable to updateExerciceHistory:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during history update."}) - return - } - log.Printf("AUDIT: %s performs an history update: %s for team %d, exercice %d and optional %v, with coeff %f", myassignee.Name, uh.Kind, uh.IdTeam, exercice.Id, uh.Secondary, uh.Coeff) - - c.JSON(http.StatusOK, uh) -} - -func delExerciceHistory(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - myassignee := c.MustGet("assignee").(*fic.ClaimAssignee) - - var uh uploadedExerciceHistory - err := c.ShouldBindJSON(&uh) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - _, err = exercice.DelHistoryItem(uh.IdTeam, uh.Kind, uh.Time, uh.Secondary) - if err != nil { - log.Println("Unable to delExerciceHistory:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during history deletion."}) - return - } - log.Printf("AUDIT: %s performs an history deletion: %s for team %d, exercice %d and optional %v", myassignee.Name, uh.Kind, uh.IdTeam, exercice.Id, uh.Secondary) - - c.JSON(http.StatusOK, true) -} - -func deleteExercice(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - - _, err := exercice.DeleteCascade() - if err != nil { - log.Println("Unable to deleteExercice:", err.Error()) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "An error occurs during exercice deletion"}) - return - } - - c.JSON(http.StatusOK, true) -} - -func updateExercice(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - - var ue fic.Exercice - err := c.ShouldBindJSON(&ue) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - ue.Id = exercice.Id - - if len(ue.Title) == 0 { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Exercice's title not filled"}) - return - } - - if _, err := ue.Update(); err != nil { - log.Println("Unable to updateExercice:", err.Error()) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "An error occurs during exercice update"}) - return - } - - c.JSON(http.StatusOK, ue) -} - -type patchExercice struct { - Language *string `json:"lang,omitempty"` - Title *string `json:"title"` - Disabled *bool `json:"disabled"` - WIP *bool `json:"wip"` - URLId *string `json:"urlid"` - Statement *string `json:"statement"` - Overview *string `json:"overview"` - Headline *string `json:"headline"` - Finished *string `json:"finished"` - Issue *string `json:"issue"` - IssueKind *string `json:"issuekind"` - Gain *int64 `json:"gain"` - Coefficient *float64 `json:"coefficient"` -} - -func partUpdateExercice(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - - var ue patchExercice - err := c.ShouldBindJSON(&ue) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - for _, field := range reflect.VisibleFields(reflect.TypeOf(ue)) { - if !reflect.ValueOf(ue).FieldByName(field.Name).IsNil() { - reflect.ValueOf(exercice).Elem().FieldByName(field.Name).Set(reflect.ValueOf(reflect.ValueOf(ue).FieldByName(field.Name).Elem().Interface())) - } - } - - if _, err := exercice.Update(); err != nil { - log.Println("Unable to partUpdateExercice:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during exercice update."}) - return - } - - c.JSON(http.StatusOK, exercice) -} - -func createExercice(c *gin.Context) { - theme := c.MustGet("theme").(*fic.Theme) - - // Create a new exercice - var ue fic.Exercice - err := c.ShouldBindJSON(&ue) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - if len(ue.Title) == 0 { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Title not filled"}) - return - } - - var depend *fic.Exercice = nil - if ue.Depend != nil { - if d, err := fic.GetExercice(*ue.Depend); err != nil { - log.Println("Unable to createExercice:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during exercice creation."}) - return - } else { - depend = d - } - } - - exercice, err := theme.AddExercice(ue.Title, ue.Authors, ue.Image, ue.BackgroundColor, ue.WIP, ue.URLId, ue.Path, ue.Statement, ue.Overview, ue.Headline, depend, ue.Gain, ue.VideoURI, ue.Resolution, ue.SeeAlso, ue.Finished) - if err != nil { - log.Println("Unable to createExercice:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during exercice creation."}) - return - } - - c.JSON(http.StatusOK, exercice) -} - -type uploadedHint struct { - Title string - Path string - Content string - Cost int64 - URI string -} - -func createExerciceHint(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - - var uh uploadedHint - err := c.ShouldBindJSON(&uh) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - if len(uh.Content) != 0 { - hint, err := exercice.AddHint(uh.Title, uh.Content, uh.Cost) - if err != nil { - log.Println("Unable to AddHint in createExerciceHint:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to add hint."}) - return - } - - c.JSON(http.StatusOK, hint) - } else if len(uh.URI) != 0 { - hint, err := sync.ImportFile(sync.GlobalImporter, uh.URI, - func(filePath string, origin string) (interface{}, error) { - return exercice.AddHint(uh.Title, "$FILES"+strings.TrimPrefix(filePath, fic.FilesDir), uh.Cost) - }) - - if err != nil { - log.Println("Unable to AddHint (after ImportFile) in createExerciceHint:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to add hint."}) - return - } - - c.JSON(http.StatusOK, hint) - } else { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Hint's content not filled"}) - return - } -} - -func showExerciceHint(c *gin.Context) { - c.JSON(http.StatusOK, c.MustGet("hint").(*fic.EHint)) -} - -func showExerciceHintDeps(c *gin.Context) { - hint := c.MustGet("hint").(*fic.EHint) - - deps, err := loadFlags(hint.GetDepends) - if err != nil { - log.Println("Unable to loaddeps:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to retrieve hint dependencies."}) - return - } - - c.JSON(http.StatusOK, deps) -} - -func updateExerciceHint(c *gin.Context) { - hint := c.MustGet("hint").(*fic.EHint) - - var uh fic.EHint - err := c.ShouldBindJSON(&uh) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - uh.Id = hint.Id - - if len(uh.Title) == 0 { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Hint's title not filled"}) - return - } - - if _, err := uh.Update(); err != nil { - log.Println("Unable to updateExerciceHint:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to update hint."}) - return - } - - c.JSON(http.StatusOK, uh) -} - -func deleteExerciceHint(c *gin.Context) { - hint := c.MustGet("hint").(*fic.EHint) - - _, err := hint.Delete() - if err != nil { - log.Println("Unable to deleteExerciceHint:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to delete hint."}) - return - } - - c.JSON(http.StatusOK, true) -} - -type uploadedFlag struct { - Type string - Label string - Placeholder string - Help string - IgnoreCase bool - Multiline bool - NoTrim bool - CaptureRe *string `json:"capture_regexp"` - SortReGroups bool `json:"sort_re_grps"` - Flag string - Value []byte - ChoicesCost int32 `json:"choices_cost"` - BonusGain int32 `json:"bonus_gain"` -} - -func createExerciceFlag(c *gin.Context) { - var uk uploadedFlag - err := c.ShouldBindJSON(&uk) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - if len(uk.Flag) == 0 { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Flag not filled"}) - return - } - - var vre *string = nil - if uk.CaptureRe != nil && len(*uk.CaptureRe) > 0 { - vre = uk.CaptureRe - } - - exercice := c.MustGet("exercice").(*fic.Exercice) - - flag, err := exercice.AddRawFlagKey(uk.Label, uk.Type, uk.Placeholder, uk.IgnoreCase, uk.NoTrim, uk.Multiline, vre, uk.SortReGroups, []byte(uk.Flag), uk.ChoicesCost, uk.BonusGain) - if err != nil { - log.Println("Unable to createExerciceFlag:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to create flag."}) - return - } - - c.JSON(http.StatusOK, flag) -} - -func showExerciceFlag(c *gin.Context) { - c.JSON(http.StatusOK, c.MustGet("flag-key").(*fic.FlagKey)) -} - -func showExerciceFlagDeps(c *gin.Context) { - flag := c.MustGet("flag-key").(*fic.FlagKey) - - deps, err := loadFlags(flag.GetDepends) - if err != nil { - log.Println("Unable to loaddeps:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to retrieve hint dependencies."}) - return - } - - c.JSON(http.StatusOK, deps) -} - -func showExerciceFlagStats(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - flag := c.MustGet("flag-key").(*fic.FlagKey) - - history, err := exercice.GetHistory() - if err != nil { - log.Println("Unable to getExerciceHistory:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving exercice history"}) - return - } - - var completed int64 - - for _, hline := range history { - if hline["kind"].(string) == "flag_found" { - if int(*hline["secondary"].(*int64)) == flag.Id { - completed += 1 - } - } - } - - tries, err := flag.NbTries() - if err != nil { - log.Println("Unable to nbTries:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving flag tries"}) - return - } - - teams, err := flag.TeamsOnIt() - if err != nil { - log.Println("Unable to teamsOnIt:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving flag related teams"}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "completed": completed, - "tries": tries, - "teams": teams, - }) -} - -func deleteExerciceFlagTries(c *gin.Context) { - flag := c.MustGet("flag-key").(*fic.FlagKey) - - err := flag.DeleteTries() - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - c.AbortWithStatusJSON(http.StatusOK, true) -} - -func tryExerciceFlag(c *gin.Context) { - flag := c.MustGet("flag-key").(*fic.FlagKey) - - var uk uploadedFlag - err := c.ShouldBindJSON(&uk) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - if len(uk.Flag) == 0 { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Empty submission"}) - return - } - - if flag.Check([]byte(uk.Flag)) == 0 { - c.AbortWithStatusJSON(http.StatusOK, true) - return - } - - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Bad submission"}) -} - -func updateExerciceFlag(c *gin.Context) { - flag := c.MustGet("flag-key").(*fic.FlagKey) - - var uk uploadedFlag - err := c.ShouldBindJSON(&uk) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - if len(uk.Label) == 0 { - flag.Label = "Flag" - } else { - flag.Label = uk.Label - } - - flag.Placeholder = uk.Placeholder - flag.Help = uk.Help - flag.IgnoreCase = uk.IgnoreCase - flag.Multiline = uk.Multiline - flag.ChoicesCost = uk.ChoicesCost - flag.BonusGain = uk.BonusGain - - if uk.CaptureRe != nil && len(*uk.CaptureRe) > 0 { - if flag.CaptureRegexp != uk.CaptureRe && uk.Flag == "" { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Pour changer la capture_regexp, vous devez rentrer la réponse attendue à nouveau, car le flag doit être recalculé."}) - return - } - flag.CaptureRegexp = uk.CaptureRe - } else { - if flag.CaptureRegexp != nil && uk.Flag == "" { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Pour changer la capture_regexp, vous devez rentrer la réponse attendue à nouveau, car le flag doit être recalculé."}) - return - } - flag.CaptureRegexp = nil - } - - if len(uk.Flag) > 0 { - var err error - flag.Checksum, err = flag.ComputeChecksum([]byte(uk.Flag)) - if err != nil { - log.Println("Unable to ComputeChecksum:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to compute flag checksum"}) - return - } - } else { - flag.Checksum = uk.Value - } - - if _, err := flag.Update(); err != nil { - log.Println("Unable to updateExerciceFlag:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to update flag."}) - return - } - - c.JSON(http.StatusOK, flag) -} - -func deleteExerciceFlag(c *gin.Context) { - flag := c.MustGet("flag-key").(*fic.FlagKey) - - _, err := flag.Delete() - if err != nil { - log.Println("Unable to deleteExerciceFlag:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to delete flag."}) - return - } - - c.JSON(http.StatusOK, true) -} - -func createFlagChoice(c *gin.Context) { - flag := c.MustGet("flag-key").(*fic.FlagKey) - - var uc fic.FlagChoice - err := c.ShouldBindJSON(&uc) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - if len(uc.Label) == 0 { - uc.Label = uc.Value - } - - choice, err := flag.AddChoice(&uc) - if err != nil { - log.Println("Unable to createFlagChoice:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to create flag choice."}) - return - } - - c.JSON(http.StatusOK, choice) -} - -func showFlagChoice(c *gin.Context) { - c.JSON(http.StatusOK, c.MustGet("flag-choice").(*fic.FlagChoice)) -} - -func updateFlagChoice(c *gin.Context) { - choice := c.MustGet("flag-choice").(*fic.FlagChoice) - - var uc fic.FlagChoice - err := c.ShouldBindJSON(&uc) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - if len(uc.Label) == 0 { - choice.Label = uc.Value - } else { - choice.Label = uc.Label - } - - choice.Value = uc.Value - - if _, err := choice.Update(); err != nil { - log.Println("Unable to updateFlagChoice:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to update flag choice."}) - return - } - - c.JSON(http.StatusOK, choice) -} - -func deleteFlagChoice(c *gin.Context) { - choice := c.MustGet("flag-choice").(*fic.FlagChoice) - - _, err := choice.Delete() - if err != nil { - log.Println("Unable to deleteExerciceChoice:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to delete choice."}) - return - } - - c.JSON(http.StatusOK, true) -} - -func showExerciceQuiz(c *gin.Context) { - c.JSON(http.StatusOK, c.MustGet("flag-quiz").(*fic.MCQ)) -} - -func showExerciceQuizDeps(c *gin.Context) { - quiz := c.MustGet("flag-quiz").(*fic.MCQ) - - deps, err := loadFlags(quiz.GetDepends) - if err != nil { - log.Println("Unable to loaddeps:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to retrieve quiz dependencies."}) - return - } - - c.JSON(http.StatusOK, deps) -} - -func showExerciceQuizStats(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - quiz := c.MustGet("flag-quiz").(*fic.MCQ) - - history, err := exercice.GetHistory() - if err != nil { - log.Println("Unable to getExerciceHistory:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving exercice history"}) - return - } - - var completed int64 - - for _, hline := range history { - if hline["kind"].(string) == "mcq_found" { - if *hline["secondary"].(*int) == quiz.Id { - completed += 1 - } - } - } - - tries, err := quiz.NbTries() - if err != nil { - log.Println("Unable to nbTries:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving flag tries"}) - return - } - - teams, err := quiz.TeamsOnIt() - if err != nil { - log.Println("Unable to teamsOnIt:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving flag related teams"}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "completed": completed, - "tries": tries, - "teams": teams, - }) -} - -func deleteExerciceQuizTries(c *gin.Context) { - quiz := c.MustGet("flag-quiz").(*fic.MCQ) - - err := quiz.DeleteTries() - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - c.AbortWithStatusJSON(http.StatusOK, true) -} - -func updateExerciceQuiz(c *gin.Context) { - quiz := c.MustGet("flag-quiz").(*fic.MCQ) - - var uq fic.MCQ - err := c.ShouldBindJSON(&uq) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - quiz.Title = uq.Title - - if _, err := quiz.Update(); err != nil { - log.Println("Unable to updateExerciceQuiz:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to update quiz."}) - return - } - - // Update and remove old entries - var delete []int - for i, cur := range quiz.Entries { - seen := false - for _, next := range uq.Entries { - if cur.Id == next.Id { - seen = true - - if cur.Label != next.Label || cur.Response != next.Response { - cur.Label = next.Label - cur.Response = next.Response - if _, err := cur.Update(); err != nil { - log.Println("Unable to update MCQ entry:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to update some MCQ entry"}) - return - } - } - - break - } - } - - if seen == false { - if _, err := cur.Delete(); err != nil { - log.Println("Unable to delete MCQ entry:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to delete some MCQ entry"}) - return - } else { - delete = append(delete, i) - } - } - } - for n, i := range delete { - quiz.Entries = append(quiz.Entries[:i-n-1], quiz.Entries[:i-n+1]...) - } - - // Add new choices - for _, choice := range uq.Entries { - if choice.Id == 0 { - if ch, err := quiz.AddEntry(choice); err != nil { - log.Println("Unable to add MCQ entry:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to add some MCQ entry"}) - return - } else { - quiz.Entries = append(quiz.Entries, ch) - } - } - } - - c.JSON(http.StatusOK, quiz) -} - -func deleteExerciceQuiz(c *gin.Context) { - quiz := c.MustGet("flag-quiz").(*fic.MCQ) - - for _, choice := range quiz.Entries { - if _, err := choice.Delete(); err != nil { - log.Println("Unable to deleteExerciceQuiz (entry):", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to delete quiz entry."}) - return - } - } - - _, err := quiz.Delete() - if err != nil { - log.Println("Unable to deleteExerciceQuiz:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to delete quiz."}) - return - } - - c.JSON(http.StatusOK, true) -} - -func listExerciceTags(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - - tags, err := exercice.GetTags() - if err != nil { - log.Println("Unable to listExerciceTags:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to get tags."}) - return - } - - c.JSON(http.StatusOK, tags) -} - -func addExerciceTag(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - - var ut []string - err := c.ShouldBindJSON(&ut) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - // TODO: a DB transaction should be done here: on error we should rollback - for _, t := range ut { - if _, err := exercice.AddTag(t); err != nil { - log.Println("Unable to addExerciceTag:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to add some tag."}) - return - } - } - - c.JSON(http.StatusOK, ut) -} - -func updateExerciceTags(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - - exercice.WipeTags() - addExerciceTag(c) -} - -type syncDiff struct { - Field string `json:"field"` - Link string `json:"link"` - Before interface{} `json:"be"` - After interface{} `json:"af"` -} - -func diffExerciceWithRemote(exercice *fic.Exercice, theme *fic.Theme) ([]syncDiff, error) { - var diffs []syncDiff - - // Compare exercice attributes - thid := exercice.Path[:strings.Index(exercice.Path, "/")] - exid := exercice.Path[strings.Index(exercice.Path, "/")+1:] - exercice_remote, err := sync.GetRemoteExercice(thid, exid, theme) - if err != nil { - return nil, err - } - - for _, field := range reflect.VisibleFields(reflect.TypeOf(*exercice)) { - if ((field.Name == "Image") && path.Base(reflect.ValueOf(*exercice_remote).FieldByName(field.Name).String()) != path.Base(reflect.ValueOf(*exercice).FieldByName(field.Name).String())) || ((field.Name == "Depend") && (((exercice_remote.Depend == nil || exercice.Depend == nil) && exercice.Depend != exercice_remote.Depend) || (exercice_remote.Depend != nil && exercice.Depend != nil && *exercice.Depend != *exercice_remote.Depend))) || (field.Name != "Image" && field.Name != "Depend" && !reflect.ValueOf(*exercice_remote).FieldByName(field.Name).Equal(reflect.ValueOf(*exercice).FieldByName(field.Name))) { - if !field.IsExported() || field.Name == "Id" || field.Name == "IdTheme" || field.Name == "IssueKind" || field.Name == "Coefficient" || field.Name == "BackgroundColor" { - continue - } - - diffs = append(diffs, syncDiff{ - Field: field.Name, - Link: fmt.Sprintf("exercices/%d", exercice.Id), - Before: reflect.ValueOf(*exercice).FieldByName(field.Name).Interface(), - After: reflect.ValueOf(*exercice_remote).FieldByName(field.Name).Interface(), - }) - } - } - - // Compare files - files, err := exercice.GetFiles() - if err != nil { - return nil, fmt.Errorf("Unable to GetFiles: %w", err) - } - - files_remote, err := sync.GetRemoteExerciceFiles(thid, exid) - if err != nil { - return nil, fmt.Errorf("Unable to GetRemoteFiles: %w", err) - } - - for i, file_remote := range files_remote { - if len(files) <= i { - diffs = append(diffs, syncDiff{ - Field: fmt.Sprintf("files[%d]", i), - Link: fmt.Sprintf("exercices/%d", exercice.Id), - Before: nil, - After: file_remote, - }) - continue - } - - for _, field := range reflect.VisibleFields(reflect.TypeOf(*file_remote)) { - if !field.IsExported() || field.Name == "Id" || field.Name == "IdExercice" { - continue - } - if ((field.Name == "Path") && path.Base(reflect.ValueOf(*file_remote).FieldByName(field.Name).String()) != path.Base(reflect.ValueOf(*files[i]).FieldByName(field.Name).String())) || ((field.Name == "Checksum" || field.Name == "ChecksumShown") && !bytes.Equal(reflect.ValueOf(*file_remote).FieldByName(field.Name).Bytes(), reflect.ValueOf(*files[i]).FieldByName(field.Name).Bytes())) || (field.Name != "Checksum" && field.Name != "ChecksumShown" && field.Name != "Path" && !reflect.ValueOf(*file_remote).FieldByName(field.Name).Equal(reflect.ValueOf(*files[i]).FieldByName(field.Name))) { - diffs = append(diffs, syncDiff{ - Field: fmt.Sprintf("files[%d].%s", i, field.Name), - Link: fmt.Sprintf("exercices/%d", exercice.Id), - Before: reflect.ValueOf(*files[i]).FieldByName(field.Name).Interface(), - After: reflect.ValueOf(*file_remote).FieldByName(field.Name).Interface(), - }) - } - } - } - - // Compare flags - flags, err := exercice.GetFlags() - if err != nil { - return nil, fmt.Errorf("Unable to GetFlags: %w", err) - } - - flags_remote, err := sync.GetRemoteExerciceFlags(thid, exid) - if err != nil { - return nil, fmt.Errorf("Unable to GetRemoteFlags: %w", err) - } - - var flags_not_found []interface{} - var flags_extra_found []interface{} - - for i, flag_remote := range flags_remote { - if key_remote, ok := flag_remote.(*fic.FlagKey); ok { - found := false - - for _, flag := range flags { - if key, ok := flag.(*fic.FlagKey); ok && (key.Label == key_remote.Label || key.Order == key_remote.Order) { - found = true - - // Parse flag label - if len(key.Label) > 3 && key.Label[0] == '%' { - spl := strings.Split(key.Label, "%") - key.Label = strings.Join(spl[2:], "%") - } - - for _, field := range reflect.VisibleFields(reflect.TypeOf(*key_remote)) { - if !field.IsExported() || field.Name == "Id" || field.Name == "IdExercice" { - continue - } - if (field.Name == "Checksum" && !bytes.Equal(key.Checksum, key_remote.Checksum)) || (field.Name == "CaptureRegexp" && ((key.CaptureRegexp == nil || key_remote.CaptureRegexp == nil) && key.CaptureRegexp != key_remote.CaptureRegexp) || (key.CaptureRegexp != nil && key_remote.CaptureRegexp != nil && *key.CaptureRegexp != *key_remote.CaptureRegexp)) || (field.Name != "Checksum" && field.Name != "CaptureRegexp" && !reflect.ValueOf(*key_remote).FieldByName(field.Name).Equal(reflect.ValueOf(*key).FieldByName(field.Name))) { - diffs = append(diffs, syncDiff{ - Field: fmt.Sprintf("flags[%d].%s", i, field.Name), - Link: fmt.Sprintf("exercices/%d/flags#flag-%d", exercice.Id, key.Id), - Before: reflect.ValueOf(*key).FieldByName(field.Name).Interface(), - After: reflect.ValueOf(*key_remote).FieldByName(field.Name).Interface(), - }) - } - } - - break - } - } - - if !found { - flags_not_found = append(flags_not_found, key_remote) - } - } else if mcq_remote, ok := flag_remote.(*fic.MCQ); ok { - found := false - - for _, flag := range flags { - if mcq, ok := flag.(*fic.MCQ); ok && (mcq.Title == mcq_remote.Title || mcq.Order == mcq_remote.Order) { - found = true - - for _, field := range reflect.VisibleFields(reflect.TypeOf(*mcq_remote)) { - if !field.IsExported() || field.Name == "Id" || field.Name == "IdExercice" { - continue - } - if field.Name == "Entries" { - var not_found []*fic.MCQ_entry - var extra_found []*fic.MCQ_entry - - for i, entry_remote := range mcq_remote.Entries { - found := false - - for j, entry := range mcq.Entries { - if entry.Label == entry_remote.Label { - for _, field := range reflect.VisibleFields(reflect.TypeOf(*entry_remote)) { - if field.Name == "Id" { - continue - } - - if !reflect.ValueOf(*entry_remote).FieldByName(field.Name).Equal(reflect.ValueOf(*entry).FieldByName(field.Name)) { - diffs = append(diffs, syncDiff{ - Field: fmt.Sprintf("flags[%d].entries[%d].%s", i, j, field.Name), - Link: fmt.Sprintf("exercices/%d/flags#quiz-%d", exercice.Id, mcq.Id), - Before: reflect.ValueOf(*mcq.Entries[j]).FieldByName(field.Name).Interface(), - After: reflect.ValueOf(*entry_remote).FieldByName(field.Name).Interface(), - }) - } - } - - found = true - break - } - } - - if !found { - not_found = append(not_found, entry_remote) - } - } - - for _, entry := range mcq.Entries { - found := false - for _, entry_remote := range mcq_remote.Entries { - if entry.Label == entry_remote.Label { - found = true - break - } - } - - if !found { - extra_found = append(extra_found, entry) - } - } - - if len(not_found) > 0 || len(extra_found) > 0 { - diffs = append(diffs, syncDiff{ - Field: fmt.Sprintf("flags[%d].entries", i), - Link: fmt.Sprintf("exercices/%d/flags", exercice.Id), - Before: extra_found, - After: not_found, - }) - } - } else if !reflect.ValueOf(*mcq_remote).FieldByName(field.Name).Equal(reflect.ValueOf(*mcq).FieldByName(field.Name)) { - diffs = append(diffs, syncDiff{ - Field: fmt.Sprintf("flags[%d].%s", i, field.Name), - Link: fmt.Sprintf("exercices/%d/flags", exercice.Id), - Before: reflect.ValueOf(*mcq).FieldByName(field.Name).Interface(), - After: reflect.ValueOf(*mcq_remote).FieldByName(field.Name).Interface(), - }) - } - } - - break - } - } - - if !found { - flags_not_found = append(flags_not_found, mcq_remote) - } - } else if label_remote, ok := flag_remote.(*fic.FlagLabel); ok { - found := false - - for _, flag := range flags { - if label, ok := flag.(*fic.FlagLabel); ok && (label.Label == label_remote.Label || label.Order == label_remote.Order) { - found = true - - for _, field := range reflect.VisibleFields(reflect.TypeOf(*label_remote)) { - if !field.IsExported() || field.Name == "Id" || field.Name == "IdExercice" { - continue - } - if !reflect.ValueOf(*label_remote).FieldByName(field.Name).Equal(reflect.ValueOf(*label).FieldByName(field.Name)) { - diffs = append(diffs, syncDiff{ - Field: fmt.Sprintf("flags[%d].%s", i, field.Name), - Link: fmt.Sprintf("exercices/%d/flags#flag-%d", exercice.Id, label.Id), - Before: reflect.ValueOf(*label).FieldByName(field.Name).Interface(), - After: reflect.ValueOf(*label_remote).FieldByName(field.Name).Interface(), - }) - } - } - - break - } - } - - if !found { - flags_not_found = append(flags_not_found, label_remote) - } - } else { - log.Printf("unknown flag type: %T", flag_remote) - } - } - - for _, flag := range flags { - if key, ok := flag.(*fic.FlagKey); ok { - found := false - - for _, flag_remote := range flags_remote { - if key_remote, ok := flag_remote.(*fic.FlagKey); ok && (key.Label == key_remote.Label || key.Order == key_remote.Order) { - found = true - break - } - } - - if !found { - flags_extra_found = append(flags_extra_found, flag) - } - } else if mcq, ok := flag.(*fic.MCQ); ok { - found := false - - for _, flag_remote := range flags_remote { - if mcq_remote, ok := flag_remote.(*fic.MCQ); ok && (mcq.Title == mcq_remote.Title || mcq.Order == mcq_remote.Order) { - found = true - break - } - } - - if !found { - flags_extra_found = append(flags_extra_found, flag) - } - } else if label, ok := flag.(*fic.FlagLabel); ok { - found := false - - for _, flag_remote := range flags_remote { - if label_remote, ok := flag_remote.(*fic.FlagLabel); ok && (label.Label == label_remote.Label || label.Order == label_remote.Order) { - found = true - break - } - } - - if !found { - flags_extra_found = append(flags_extra_found, flag) - } - } - } - - if len(flags_not_found) > 0 || len(flags_extra_found) > 0 { - diffs = append(diffs, syncDiff{ - Field: "flags", - Link: fmt.Sprintf("exercices/%d/flags", exercice.Id), - Before: flags_extra_found, - After: flags_not_found, - }) - } - - // Compare hints - hints, err := exercice.GetHints() - if err != nil { - return nil, fmt.Errorf("Unable to GetHints: %w", err) - } - - hints_remote, err := sync.GetRemoteExerciceHints(thid, exid) - if err != nil { - return nil, fmt.Errorf("Unable to GetRemoteHints: %w", err) - } - - for i, hint_remote := range hints_remote { - hint_remote.Hint.TreatHintContent() - - for _, field := range reflect.VisibleFields(reflect.TypeOf(*hint_remote.Hint)) { - if !field.IsExported() || field.Name == "Id" || field.Name == "IdExercice" { - continue - } - if len(hints) <= i { - diffs = append(diffs, syncDiff{ - Field: fmt.Sprintf("hints[%d].%s", i, field.Name), - Link: fmt.Sprintf("exercices/%d", exercice.Id), - Before: nil, - After: reflect.ValueOf(*hint_remote.Hint).FieldByName(field.Name).Interface(), - }) - } else if !reflect.ValueOf(*hint_remote.Hint).FieldByName(field.Name).Equal(reflect.ValueOf(*hints[i]).FieldByName(field.Name)) { - diffs = append(diffs, syncDiff{ - Field: fmt.Sprintf("hints[%d].%s", i, field.Name), - Link: fmt.Sprintf("exercices/%d", exercice.Id), - Before: reflect.ValueOf(*hints[i]).FieldByName(field.Name).Interface(), - After: reflect.ValueOf(*hint_remote.Hint).FieldByName(field.Name).Interface(), - }) - } - } - } - - return diffs, err -} - -func APIDiffExerciceWithRemote(c *gin.Context) { - theme := c.MustGet("theme").(*fic.Theme) - exercice := c.MustGet("exercice").(*fic.Exercice) - - diffs, err := diffExerciceWithRemote(exercice, theme) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, diffs) -} - -func listTries(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - - tries, err := exercice.TriesList() - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, tries) -} - -func ExerciceTryHandler(c *gin.Context) { - trid, err := strconv.ParseInt(string(c.Params.ByName("trid")), 10, 32) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid try identifier"}) - return - } - - exercice := c.MustGet("exercice").(*fic.Exercice) - try, err := exercice.GetTry(trid) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Try not found"}) - return - } - - c.Set("try", try) - - c.Next() -} - -func getExerciceTry(c *gin.Context) { - try := c.MustGet("try").(*fic.ExerciceTry) - - err := try.FillDetails() - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, try) -} - -func deleteExerciceTry(c *gin.Context) { - try := c.MustGet("try").(*fic.ExerciceTry) - - _, err := try.Delete() - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.Status(http.StatusNoContent) -} diff --git a/admin/api/export.go b/admin/api/export.go deleted file mode 100644 index aa24920a..00000000 --- a/admin/api/export.go +++ /dev/null @@ -1,126 +0,0 @@ -package api - -import ( - "archive/zip" - "encoding/json" - "io" - "log" - "net/http" - "path" - - "srs.epita.fr/fic-server/admin/sync" - "srs.epita.fr/fic-server/libfic" - "srs.epita.fr/fic-server/settings" - - "github.com/gin-gonic/gin" -) - -func declareExportRoutes(router *gin.RouterGroup) { - router.GET("/archive.zip", func(c *gin.Context) { - challengeinfo, err := GetChallengeInfo() - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - my, err := fic.MyJSONTeam(nil, true) - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - s, err := settings.ReadSettings(path.Join(settings.SettingsDir, settings.SettingsFile)) - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - s.End = nil - s.NextChangeTime = nil - s.DelegatedQA = []string{} - - teams, err := fic.ExportTeams(false) - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - themes, err := fic.ExportThemes() - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.Writer.WriteHeader(http.StatusOK) - c.Header("Content-Disposition", "attachment; filename=archive.zip") - c.Header("Content-Type", "application/zip") - - w := zip.NewWriter(c.Writer) - - // challenge.json - f, err := w.Create("challenge.json") - if err == nil { - json.NewEncoder(f).Encode(challengeinfo) - } - - // Include partners' logos from challenge.json - if sync.GlobalImporter != nil { - if len(challengeinfo.MainLogo) > 0 { - for _, logo := range challengeinfo.MainLogo { - fd, closer, err := sync.OpenOrGetFile(sync.GlobalImporter, logo) - if err != nil { - log.Printf("Unable to archive main logo %q: %s", logo, err.Error()) - continue - } - - f, err := w.Create(path.Join("logo", path.Base(logo))) - if err == nil { - io.Copy(f, fd) - } - closer() - } - } - - if len(challengeinfo.Partners) > 0 { - for _, partner := range challengeinfo.Partners { - fd, closer, err := sync.OpenOrGetFile(sync.GlobalImporter, partner.Src) - if err != nil { - log.Printf("Unable to archive partner logo %q: %s", partner.Src, err.Error()) - continue - } - - f, err := w.Create(path.Join("partner", path.Base(partner.Src))) - if err == nil { - io.Copy(f, fd) - } - closer() - } - } - } - - // my.json - f, err = w.Create("my.json") - if err == nil { - json.NewEncoder(f).Encode(my) - } - - // settings.json - f, err = w.Create("settings.json") - if err == nil { - json.NewEncoder(f).Encode(s) - } - - // teams.json - f, err = w.Create("teams.json") - if err == nil { - json.NewEncoder(f).Encode(teams) - } - - // themes.json - f, err = w.Create("themes.json") - if err == nil { - json.NewEncoder(f).Encode(themes) - } - - w.Close() - }) -} diff --git a/admin/api/file.go b/admin/api/file.go deleted file mode 100644 index 6d94776c..00000000 --- a/admin/api/file.go +++ /dev/null @@ -1,297 +0,0 @@ -package api - -import ( - "encoding/hex" - "fmt" - "log" - "net/http" - "os" - "path/filepath" - "strconv" - - "srs.epita.fr/fic-server/admin/sync" - "srs.epita.fr/fic-server/libfic" - - "github.com/gin-gonic/gin" -) - -func declareFilesGlobalRoutes(router *gin.RouterGroup) { - router.DELETE("/files/", clearFiles) - - // Remote - router.GET("/remote/themes/:thid/exercices/:exid/files", sync.ApiGetRemoteExerciceFiles) -} - -func declareFilesRoutes(router *gin.RouterGroup) { - router.GET("/files", listFiles) - router.POST("/files", createExerciceFile) - - apiFilesRoutes := router.Group("/files/:fileid") - apiFilesRoutes.Use(FileHandler) - apiFilesRoutes.GET("", showFile) - apiFilesRoutes.PUT("", updateFile) - apiFilesRoutes.DELETE("", deleteFile) - - apiFileDepsRoutes := apiFilesRoutes.Group("/dependancies/:depid") - apiFileDepsRoutes.Use(FileDepHandler) - apiFileDepsRoutes.DELETE("", deleteFileDep) - - // Check - apiFilesRoutes.POST("/check", checkFile) - apiFilesRoutes.POST("/gunzip", gunzipFile) -} - -func FileHandler(c *gin.Context) { - fileid, err := strconv.ParseInt(string(c.Params.ByName("fileid")), 10, 64) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid file identifier"}) - return - } - - var file *fic.EFile - if exercice, exists := c.Get("exercice"); exists { - file, err = exercice.(*fic.Exercice).GetFile(fileid) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "File not found"}) - return - } - } else { - file, err = fic.GetFile(fileid) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "File not found"}) - return - } - } - - c.Set("file", file) - - c.Next() -} - -func FileDepHandler(c *gin.Context) { - depid, err := strconv.ParseInt(string(c.Params.ByName("depid")), 10, 64) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid dependency identifier"}) - return - } - - c.Set("file-depid", depid) - - c.Next() -} - -type APIFile struct { - *fic.EFile - Depends []fic.Flag `json:"depends,omitempty"` -} - -func genFileList(in []*fic.EFile, e error) (out []APIFile, err error) { - if e != nil { - return nil, e - } - - for _, f := range in { - g := APIFile{EFile: f} - - var deps []fic.Flag - deps, err = f.GetDepends() - if err != nil { - return - } - - for _, d := range deps { - if k, ok := d.(*fic.FlagKey); ok { - k, err = fic.GetFlagKey(k.Id) - if err != nil { - return - } - - g.Depends = append(g.Depends, k) - } else if m, ok := d.(*fic.MCQ); ok { - m, err = fic.GetMCQ(m.Id) - if err != nil { - return - } - - g.Depends = append(g.Depends, m) - } else { - err = fmt.Errorf("Unknown type %T to handle file dependancy", k) - return - } - } - - out = append(out, g) - } - - return -} - -func listFiles(c *gin.Context) { - var files []APIFile - var err error - - if exercice, exists := c.Get("exercice"); exists { - files, err = genFileList(exercice.(*fic.Exercice).GetFiles()) - } else { - files, err = genFileList(fic.GetFiles()) - } - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, files) -} - -func clearFiles(c *gin.Context) { - err := os.RemoveAll(fic.FilesDir) - if err != nil { - log.Println("Unable to remove files:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - err = os.MkdirAll(fic.FilesDir, 0751) - if err != nil { - log.Println("Unable to create FILES:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - _, err = fic.ClearFiles() - if err != nil { - log.Println("Unable to clean DB files:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Les fichiers ont bien été effacés. Mais il n'a pas été possible d'effacer la base de données. Refaites une synchronisation maintenant. " + err.Error()}) - return - } - - c.JSON(http.StatusOK, true) -} - -func showFile(c *gin.Context) { - c.JSON(http.StatusOK, c.MustGet("file").(*fic.EFile)) -} - -type uploadedFile struct { - URI string - Digest string -} - -func createExerciceFile(c *gin.Context) { - exercice, exists := c.Get("exercice") - if !exists { - c.AbortWithStatusJSON(http.StatusMethodNotAllowed, gin.H{"errmsg": "File can only be added inside an exercice."}) - return - } - - paramsFiles, err := sync.GetExerciceFilesParams(sync.GlobalImporter, exercice.(*fic.Exercice)) - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - var uf uploadedFile - err = c.ShouldBindJSON(&uf) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - ret, err := sync.ImportFile(sync.GlobalImporter, uf.URI, - func(filePath string, origin string) (interface{}, error) { - if digest, err := hex.DecodeString(uf.Digest); err != nil { - return nil, err - } else { - published := true - disclaimer := "" - - if f, exists := paramsFiles[filepath.Base(filePath)]; exists { - published = !f.Hidden - - if disclaimer, err = sync.ProcessMarkdown(sync.GlobalImporter, f.Disclaimer, exercice.(*fic.Exercice).Path); err != nil { - return nil, fmt.Errorf("error during markdown formating of disclaimer: %w", err) - } - } - - return exercice.(*fic.Exercice).ImportFile(filePath, origin, digest, nil, disclaimer, published) - } - }) - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, ret) -} - -func updateFile(c *gin.Context) { - file := c.MustGet("file").(*fic.EFile) - - var uf fic.EFile - err := c.ShouldBindJSON(&uf) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - uf.Id = file.Id - - if _, err := uf.Update(); err != nil { - log.Println("Unable to updateFile:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to update file."}) - return - } - - c.JSON(http.StatusOK, uf) -} - -func deleteFile(c *gin.Context) { - file := c.MustGet("file").(*fic.EFile) - - _, err := file.Delete() - if err != nil { - log.Println("Unable to updateFile:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to update file."}) - return - } - - c.JSON(http.StatusOK, true) -} - -func deleteFileDep(c *gin.Context) { - file := c.MustGet("file").(*fic.EFile) - depid := c.MustGet("file-depid").(int64) - - err := file.DeleteDepend(&fic.FlagKey{Id: int(depid)}) - if err != nil { - log.Println("Unable to deleteFileDep:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to delete file dependency."}) - return - } - - c.JSON(http.StatusOK, true) -} - -func checkFile(c *gin.Context) { - file := c.MustGet("file").(*fic.EFile) - - err := file.CheckFileOnDisk() - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, true) -} - -func gunzipFile(c *gin.Context) { - file := c.MustGet("file").(*fic.EFile) - - err := file.GunzipFileOnDisk() - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, true) -} diff --git a/admin/api/health.go b/admin/api/health.go deleted file mode 100644 index 13d7a0cc..00000000 --- a/admin/api/health.go +++ /dev/null @@ -1,156 +0,0 @@ -package api - -import ( - "fmt" - "io/ioutil" - "log" - "net/http" - "os" - "path" - "strings" - "time" - - "srs.epita.fr/fic-server/admin/pki" - "srs.epita.fr/fic-server/libfic" - - "github.com/gin-gonic/gin" -) - -var TimestampCheck = "submissions" - -func declareHealthRoutes(router *gin.RouterGroup) { - router.GET("/timestamps.json", func(c *gin.Context) { - stat, err := os.Stat(TimestampCheck) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": fmt.Sprintf("timestamp.json: %s", err.Error())}) - return - } - now := time.Now().UTC() - c.JSON(http.StatusOK, gin.H{ - "frontend": stat.ModTime().UTC(), - "backend": now, - "diffFB": now.Sub(stat.ModTime()), - }) - }) - router.GET("/health.json", GetHealth) - router.GET("/submissions-stats.json", GetSubmissionsStats) - router.GET("/validations-stats.json", GetValidationsStats) - - router.DELETE("/submissions/*path", func(c *gin.Context) { - err := os.Remove(path.Join(TimestampCheck, c.Params.ByName("path"))) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": err.Error()}) - return - } - c.Status(http.StatusOK) - }) -} - -type healthFileReport struct { - IdTeam string `json:"id_team,omitempty"` - Path string `json:"path"` - Error string `json:"error"` -} - -func getHealth(pathname string) (ret []healthFileReport) { - if ds, err := ioutil.ReadDir(pathname); err != nil { - ret = append(ret, healthFileReport{ - Path: strings.TrimPrefix(pathname, TimestampCheck), - Error: fmt.Sprintf("unable to ReadDir: %s", err), - }) - return - } else { - for _, d := range ds { - p := path.Join(pathname, d.Name()) - if d.IsDir() && d.Name() != ".tmp" && d.Mode()&os.ModeSymlink == 0 { - ret = append(ret, getHealth(p)...) - } else if !d.IsDir() && d.Mode()&os.ModeSymlink == 0 && time.Since(d.ModTime()) > 2*time.Second { - if d.Name() == ".locked" { - continue - } - - teamDir := strings.TrimPrefix(pathname, TimestampCheck) - idteam, _ := pki.GetAssociation(path.Join(TeamsDir, teamDir)) - ret = append(ret, healthFileReport{ - IdTeam: idteam, - Path: path.Join(teamDir, d.Name()), - Error: "existing untreated file", - }) - } - } - return - } -} - -func GetHealth(c *gin.Context) { - if _, err := os.Stat(TimestampCheck); err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": fmt.Sprintf("health.json: %s", err.Error())}) - return - } - - c.JSON(http.StatusOK, getHealth(TimestampCheck)) -} - -type SubmissionsStats struct { - NbSubmissionLastMinute uint `json:"nbsubminute"` - NbSubmissionLast5Minute uint `json:"nbsub5minute"` - NbSubmissionLastQuarter uint `json:"nbsubquarter"` - NbSubmissionLastHour uint `json:"nbsubhour"` - NbSubmissionLastDay uint `json:"nbsubday"` -} - -func calcSubmissionsStats(tries []time.Time) (stats SubmissionsStats) { - lastMinute := time.Now().Add(-1 * time.Minute) - last5Minute := time.Now().Add(-5 * time.Minute) - lastQuarter := time.Now().Add(-15 * time.Minute) - lastHour := time.Now().Add(-1 * time.Hour) - lastDay := time.Now().Add(-24 * time.Hour) - - for _, t := range tries { - if lastMinute.Before(t) { - stats.NbSubmissionLastMinute += 1 - stats.NbSubmissionLast5Minute += 1 - stats.NbSubmissionLastQuarter += 1 - stats.NbSubmissionLastHour += 1 - stats.NbSubmissionLastDay += 1 - } else if last5Minute.Before(t) { - stats.NbSubmissionLast5Minute += 1 - stats.NbSubmissionLastQuarter += 1 - stats.NbSubmissionLastHour += 1 - stats.NbSubmissionLastDay += 1 - } else if lastQuarter.Before(t) { - stats.NbSubmissionLastQuarter += 1 - stats.NbSubmissionLastHour += 1 - stats.NbSubmissionLastDay += 1 - } else if lastHour.Before(t) { - stats.NbSubmissionLastHour += 1 - stats.NbSubmissionLastDay += 1 - } else if lastDay.Before(t) { - stats.NbSubmissionLastDay += 1 - } - } - - return -} - -func GetSubmissionsStats(c *gin.Context) { - tries, err := fic.GetTries(nil, nil) - if err != nil { - log.Println("Unable to GetTries:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieves tries."}) - return - } - - c.JSON(http.StatusOK, calcSubmissionsStats(tries)) -} - -func GetValidationsStats(c *gin.Context) { - tries, err := fic.GetValidations(nil, nil) - if err != nil { - log.Println("Unable to GetTries:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieves tries."}) - return - } - - c.JSON(http.StatusOK, calcSubmissionsStats(tries)) -} diff --git a/admin/api/monitor.go b/admin/api/monitor.go deleted file mode 100644 index 3d6a9114..00000000 --- a/admin/api/monitor.go +++ /dev/null @@ -1,98 +0,0 @@ -package api - -import ( - "bufio" - "io/ioutil" - "net/http" - "os" - "strconv" - "strings" - - "github.com/gin-gonic/gin" -) - -func declareMonitorRoutes(router *gin.RouterGroup) { - router.GET("/monitor", func(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "localhost": genLocalConstants(), - }) - }) -} - -func readLoadAvg(fd *os.File) (ret map[string]float64) { - if s, err := ioutil.ReadAll(fd); err == nil { - f := strings.Fields(strings.TrimSpace(string(s))) - if len(f) >= 3 { - ret = map[string]float64{} - ret["1m"], _ = strconv.ParseFloat(f[0], 64) - ret["5m"], _ = strconv.ParseFloat(f[1], 64) - ret["15m"], _ = strconv.ParseFloat(f[2], 64) - } - } - return -} - -func readMeminfo(fd *os.File) (ret map[string]uint64) { - ret = map[string]uint64{} - - scanner := bufio.NewScanner(fd) - for scanner.Scan() { - f := strings.Fields(strings.TrimSpace(scanner.Text())) - if len(f) >= 2 { - if v, err := strconv.ParseUint(f[1], 10, 64); err == nil { - ret[strings.ToLower(strings.TrimSuffix(f[0], ":"))] = v * 1024 - } - } - } - - return -} - -func readCPUStats(fd *os.File) (ret map[string]map[string]uint64) { - ret = map[string]map[string]uint64{} - - scanner := bufio.NewScanner(fd) - for scanner.Scan() { - f := strings.Fields(strings.TrimSpace(scanner.Text())) - if len(f[0]) >= 4 && f[0][0:3] == "cpu" && len(f) >= 8 { - ret[f[0]] = map[string]uint64{} - var total uint64 = 0 - for i, k := range []string{"user", "nice", "system", "idle", "iowait", "irq", "softirq"} { - if v, err := strconv.ParseUint(f[i+1], 10, 64); err == nil { - ret[f[0]][k] = v - total += v - } - } - ret[f[0]]["total"] = total - } - } - - return -} - -func genLocalConstants() interface{} { - ret := map[string]interface{}{} - - fi, err := os.Open("/proc/loadavg") - if err != nil { - return err - } - defer fi.Close() - ret["loadavg"] = readLoadAvg(fi) - - fi, err = os.Open("/proc/meminfo") - if err != nil { - return err - } - defer fi.Close() - ret["meminfo"] = readMeminfo(fi) - - fi, err = os.Open("/proc/stat") - if err != nil { - return err - } - defer fi.Close() - ret["cpustat"] = readCPUStats(fi) - - return ret -} diff --git a/admin/api/password.go b/admin/api/password.go deleted file mode 100644 index d37153e9..00000000 --- a/admin/api/password.go +++ /dev/null @@ -1,360 +0,0 @@ -package api - -import ( - "bytes" - "fmt" - "io/ioutil" - "log" - "net/http" - "os" - "path" - "text/template" - "unicode" - - "srs.epita.fr/fic-server/admin/pki" - "srs.epita.fr/fic-server/libfic" - - "github.com/gin-gonic/gin" -) - -var ( - OidcIssuer = "live.fic.srs.epita.fr" - OidcClientId = "epita-challenge" - OidcSecret = "" -) - -func declarePasswordRoutes(router *gin.RouterGroup) { - router.POST("/password", func(c *gin.Context) { - passwd, err := fic.GeneratePassword() - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, gin.H{"password": passwd}) - }) - router.GET("/oauth-status", func(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "secret_defined": OidcSecret != "", - }) - }) - router.GET("/dex.yaml", func(c *gin.Context) { - cfg, err := genDexConfig() - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.String(http.StatusOK, string(cfg)) - }) - router.POST("/dex.yaml", func(c *gin.Context) { - if dexcfg, err := genDexConfig(); err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } else if err := ioutil.WriteFile(path.Join(pki.PKIDir, "shared", "dex-config.yaml"), []byte(dexcfg), 0644); err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, true) - }) - router.GET("/dex-password.tpl", func(c *gin.Context) { - passtpl, err := genDexPasswordTpl() - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.String(http.StatusOK, string(passtpl)) - }) - router.POST("/dex-password.tpl", func(c *gin.Context) { - if dexcfg, err := genDexPasswordTpl(); err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } else if err := ioutil.WriteFile(path.Join(pki.PKIDir, "shared", "dex-password.tpl"), []byte(dexcfg), 0644); err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, true) - }) - router.GET("/vouch-proxy.yaml", func(c *gin.Context) { - cfg, err := genVouchProxyConfig() - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.String(http.StatusOK, string(cfg)) - }) - router.POST("/vouch-proxy.yaml", func(c *gin.Context) { - if dexcfg, err := genVouchProxyConfig(); err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } else if err := ioutil.WriteFile(path.Join(pki.PKIDir, "shared", "vouch-config.yaml"), []byte(dexcfg), 0644); err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, true) - }) -} - -func declareTeamsPasswordRoutes(router *gin.RouterGroup) { - router.GET("/password", func(c *gin.Context) { - team := c.MustGet("team").(*fic.Team) - - if team.Password != nil { - c.String(http.StatusOK, *team.Password) - } else { - c.AbortWithStatusJSON(http.StatusNotFound, nil) - } - }) - router.POST("/password", func(c *gin.Context) { - team := c.MustGet("team").(*fic.Team) - - if passwd, err := fic.GeneratePassword(); err != nil { - log.Println("Unable to GeneratePassword:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Something went wrong when generating the new team password"}) - return - } else { - team.Password = &passwd - - _, err := team.Update() - if err != nil { - log.Println("Unable to Update Team:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Something went wrong when updating the new team password"}) - return - } - - c.JSON(http.StatusOK, team) - } - }) -} - -const dexcfgtpl = `issuer: {{ .Issuer }} -storage: - type: sqlite3 - config: - file: /var/dex/dex.db -web: - http: 0.0.0.0:5556 -frontend: - issuer: {{ .Name }} - logoURL: {{ .LogoPath }} - dir: /srv/dex/web/ -oauth2: - skipApprovalScreen: true -staticClients: -{{ range $c := .Clients }} -- id: {{ $c.Id }} - name: {{ $c.Name }} - redirectURIs: [{{ range $u := $c.RedirectURIs }}'{{ $u }}'{{ end }}] - secret: {{ $c.Secret }} -{{ end }} -enablePasswordDB: true -staticPasswords: -{{ range $t := .Teams }} - - email: "team{{ printf "%02d" $t.Id }}" - hash: "{{with $t }}{{ .HashedPassword }}{{end}}" -{{ end }} -` - -const dexpasswdtpl = `{{ "{{" }} template "header.html" . {{ "}}" }} - -
-

- Bienvenue au {{ .Name }} ! -

-
-
-
- -
- -
-
-
- -
- -
- - {{ "{{" }} if .Invalid {{ "}}" }} -
- Identifiants incorrects. -
- {{ "{{" }} end {{ "}}" }} - - - -
- {{ "{{" }} if .BackLink {{ "}}" }} - - {{ "{{" }} end {{ "}}" }} -
- -{{ "{{" }} template "footer.html" . {{ "}}" }} -` - -type dexConfigClient struct { - Id string - Name string - RedirectURIs []string - Secret string -} - -type dexConfig struct { - Name string - Issuer string - Clients []dexConfigClient - Teams []*fic.Team - LogoPath string -} - -func genDexConfig() ([]byte, error) { - if OidcSecret == "" { - return nil, fmt.Errorf("Unable to generate dex configuration: OIDC Secret not defined. Please define FICOIDC_SECRET in your environment.") - } - - teams, err := fic.GetTeams() - if err != nil { - return nil, err - } - - b := bytes.NewBufferString("") - - challengeInfo, err := GetChallengeInfo() - if err != nil { - return nil, fmt.Errorf("Cannot create template: %w", err) - } - - // Lower the first letter to be included in a sentence. - name := []rune(challengeInfo.Title) - if len(name) > 0 { - name[0] = unicode.ToLower(name[0]) - } - - logoPath := "" - if len(challengeInfo.MainLogo) > 0 { - logoPath = path.Join("../../files", "logo", path.Base(challengeInfo.MainLogo[len(challengeInfo.MainLogo)-1])) - } - - dexTmpl, err := template.New("dexcfg").Parse(dexcfgtpl) - if err != nil { - return nil, fmt.Errorf("Cannot create template: %w", err) - } - - err = dexTmpl.Execute(b, dexConfig{ - Name: string(name), - Issuer: "https://" + OidcIssuer, - Clients: []dexConfigClient{ - dexConfigClient{ - Id: OidcClientId, - Name: challengeInfo.Title, - RedirectURIs: []string{"https://" + OidcIssuer + "/challenge_access/auth"}, - Secret: OidcSecret, - }, - }, - Teams: teams, - LogoPath: logoPath, - }) - if err != nil { - return nil, fmt.Errorf("An error occurs during template execution: %w", err) - } - - // Also generate team associations - for _, team := range teams { - if _, err := os.Stat(path.Join(TeamsDir, fmt.Sprintf("team%02d", team.Id))); err == nil { - if err = os.Remove(path.Join(TeamsDir, fmt.Sprintf("team%02d", team.Id))); err != nil { - log.Println("Unable to remove existing association symlink:", err.Error()) - return nil, fmt.Errorf("Unable to remove existing association symlink: %s", err.Error()) - } - } - if err := os.Symlink(fmt.Sprintf("%d", team.Id), path.Join(TeamsDir, fmt.Sprintf("team%02d", team.Id))); err != nil { - log.Println("Unable to create association symlink:", err.Error()) - return nil, fmt.Errorf("Unable to create association symlink: %s", err.Error()) - } - } - - return b.Bytes(), nil -} - -func genDexPasswordTpl() ([]byte, error) { - challengeInfo, err := GetChallengeInfo() - if err != nil { - return nil, fmt.Errorf("Cannot create template: %w", err) - } - - if teams, err := fic.GetTeams(); err != nil { - return nil, err - } else { - b := bytes.NewBufferString("") - - if dexTmpl, err := template.New("dexpasswd").Parse(dexpasswdtpl); err != nil { - return nil, fmt.Errorf("Cannot create template: %w", err) - } else if err = dexTmpl.Execute(b, dexConfig{ - Teams: teams, - Name: challengeInfo.Title, - }); err != nil { - return nil, fmt.Errorf("An error occurs during template execution: %w", err) - } else { - return b.Bytes(), nil - } - } -} - -const vouchcfgtpl = `# CONFIGURATION FILE HANDLED BY fic-admin -# DO NOT MODIFY IT BY HAND - -vouch: - logLevel: debug - allowAllUsers: true - document_root: /challenge_access - - cookie: - domain: {{ .Domain }} - -oauth: - provider: oidc - client_id: {{ .ClientId }} - client_secret: {{ .ClientSecret }} - callback_urls: - - https://{{ .Domain }}/challenge_access/auth - auth_url: https://{{ .Domain }}/auth - token_url: http://127.0.0.1:5556/token - user_info_url: http://127.0.0.1:5556/userinfo - scopes: - - openid - - email -` - -type vouchProxyConfig struct { - Domain string - ClientId string - ClientSecret string -} - -func genVouchProxyConfig() ([]byte, error) { - if OidcSecret == "" { - return nil, fmt.Errorf("Unable to generate vouch proxy configuration: OIDC Secret not defined. Please define FICOIDC_SECRET in your environment.") - } - - b := bytes.NewBufferString("") - - if vouchTmpl, err := template.New("vouchcfg").Parse(vouchcfgtpl); err != nil { - return nil, fmt.Errorf("Cannot create template: %w", err) - } else if err = vouchTmpl.Execute(b, vouchProxyConfig{ - Domain: OidcIssuer, - ClientId: OidcClientId, - ClientSecret: OidcSecret, - }); err != nil { - return nil, fmt.Errorf("An error occurs during template execution: %w", err) - } else { - return b.Bytes(), nil - } -} diff --git a/admin/api/public.go b/admin/api/public.go deleted file mode 100644 index f2de4ea0..00000000 --- a/admin/api/public.go +++ /dev/null @@ -1,206 +0,0 @@ -package api - -import ( - "encoding/json" - "fmt" - "log" - "net/http" - "os" - "path" - "strconv" - "strings" - "time" - - "github.com/gin-gonic/gin" -) - -var DashboardDir string - -func declarePublicRoutes(router *gin.RouterGroup) { - router.GET("/public/", listPublic) - router.GET("/public/:sid", getPublic) - router.DELETE("/public/:sid", deletePublic) - router.PUT("/public/:sid", savePublic) -} - -type FICPublicScene struct { - Type string `json:"type"` - Params map[string]interface{} `json:"params"` -} - -type FICPublicDisplay struct { - Scenes []FICPublicScene `json:"scenes"` - Side []FICPublicScene `json:"side"` - CustomCountdown map[string]interface{} `json:"customCountdown"` - HideEvents bool `json:"hideEvents"` - HideCountdown bool `json:"hideCountdown"` - HideCarousel bool `json:"hideCarousel"` - PropagationTime *time.Time `json:"propagationTime,omitempty"` -} - -func InitDashboardPresets(dir string) error { - return nil -} - -func readPublic(path string) (FICPublicDisplay, error) { - var s FICPublicDisplay - if fd, err := os.Open(path); err != nil { - return s, err - } else { - defer fd.Close() - jdec := json.NewDecoder(fd) - - if err := jdec.Decode(&s); err != nil { - return s, err - } - - return s, nil - } -} - -func savePublicTo(path string, s FICPublicDisplay) error { - if fd, err := os.Create(path); err != nil { - return err - } else { - defer fd.Close() - jenc := json.NewEncoder(fd) - - if err := jenc.Encode(s); err != nil { - return err - } - - return nil - } -} - -type DashboardFiles struct { - Presets []string `json:"presets"` - Nexts []*NextDashboardFile `json:"nexts"` -} - -type NextDashboardFile struct { - Name string `json:"name"` - Screen int `json:"screen"` - Date time.Time `json:"date"` -} - -func listPublic(c *gin.Context) { - files, err := os.ReadDir(DashboardDir) - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - var ret DashboardFiles - for _, file := range files { - if strings.HasPrefix(file.Name(), "preset-") { - ret.Presets = append(ret.Presets, strings.TrimSuffix(strings.TrimPrefix(file.Name(), "preset-"), ".json")) - continue - } - - if !strings.HasPrefix(file.Name(), "public") || len(file.Name()) < 18 { - continue - } - - ts, err := strconv.ParseInt(file.Name()[8:18], 10, 64) - if err == nil { - s, _ := strconv.Atoi(file.Name()[6:7]) - ret.Nexts = append(ret.Nexts, &NextDashboardFile{ - Name: file.Name()[6:18], - Screen: s, - Date: time.Unix(ts, 0), - }) - } - } - - c.JSON(http.StatusOK, ret) -} - -func getPublic(c *gin.Context) { - if strings.Contains(c.Params.ByName("sid"), "/") { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "sid cannot contains /"}) - return - } - - filename := fmt.Sprintf("public%s.json", c.Params.ByName("sid")) - if strings.HasPrefix(c.Params.ByName("sid"), "preset-") { - filename = fmt.Sprintf("%s.json", c.Params.ByName("sid")) - } - - if _, err := os.Stat(path.Join(DashboardDir, filename)); !os.IsNotExist(err) { - p, err := readPublic(path.Join(DashboardDir, filename)) - if err != nil { - log.Println("Unable to readPublic in getPublic:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during scene retrieval."}) - return - } - - c.JSON(http.StatusOK, p) - return - } - - c.JSON(http.StatusOK, FICPublicDisplay{Scenes: []FICPublicScene{}, Side: []FICPublicScene{}}) -} - -func deletePublic(c *gin.Context) { - if strings.Contains(c.Params.ByName("sid"), "/") { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "sid cannot contains /"}) - return - } - - filename := fmt.Sprintf("public%s.json", c.Params.ByName("sid")) - if strings.HasPrefix(c.Params.ByName("sid"), "preset-") { - filename = fmt.Sprintf("%s.json", c.Params.ByName("sid")) - } - - if len(filename) == 12 { - if err := savePublicTo(path.Join(DashboardDir, filename), FICPublicDisplay{}); err != nil { - log.Println("Unable to deletePublic:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during scene deletion."}) - return - } - } else { - if err := os.Remove(path.Join(DashboardDir, filename)); err != nil { - log.Println("Unable to deletePublic:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during scene deletion."}) - return - } - } - - c.JSON(http.StatusOK, FICPublicDisplay{Scenes: []FICPublicScene{}, Side: []FICPublicScene{}}) -} - -func savePublic(c *gin.Context) { - if strings.Contains(c.Params.ByName("sid"), "/") { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "sid cannot contains /"}) - return - } - - var scenes FICPublicDisplay - err := c.ShouldBindJSON(&scenes) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - filename := fmt.Sprintf("public%s.json", c.Params.ByName("sid")) - if c.Request.URL.Query().Has("t") { - t, err := time.Parse(time.RFC3339, c.Request.URL.Query().Get("t")) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - filename = fmt.Sprintf("public%s-%d.json", c.Params.ByName("sid"), t.Unix()) - } else if c.Request.URL.Query().Has("p") { - filename = fmt.Sprintf("preset-%s.json", c.Request.URL.Query().Get("p")) - } - - if err := savePublicTo(path.Join(DashboardDir, filename), scenes); err != nil { - log.Println("Unable to savePublicTo:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during scene saving."}) - return - } - - c.JSON(http.StatusOK, scenes) -} diff --git a/admin/api/qa.go b/admin/api/qa.go deleted file mode 100644 index 10bc02c4..00000000 --- a/admin/api/qa.go +++ /dev/null @@ -1,119 +0,0 @@ -package api - -import ( - "log" - "net/http" - "strconv" - - "srs.epita.fr/fic-server/libfic" - - "github.com/gin-gonic/gin" -) - -func declareQARoutes(router *gin.RouterGroup) { - router.POST("/qa/", importExerciceQA) - - apiQARoutes := router.Group("/qa/:qid") - apiQARoutes.Use(QAHandler) - apiQARoutes.POST("/comments", importQAComment) -} - -func QAHandler(c *gin.Context) { - qid, err := strconv.ParseInt(string(c.Params.ByName("qid")), 10, 64) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid QA identifier"}) - return - } - - qa, err := fic.GetQAQuery(qid) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "QA query not found"}) - return - } - - c.Set("qa-query", qa) - - c.Next() -} - -func importExerciceQA(c *gin.Context) { - // Create a new query - var uq fic.QAQuery - err := c.ShouldBindJSON(&uq) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - var exercice *fic.Exercice - if uq.IdExercice == 0 { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "id_exercice not filled"}) - return - } else if exercice, err = fic.GetExercice(uq.IdExercice); err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Unable to find requested exercice"}) - return - } - - if len(uq.State) == 0 { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "State not filled"}) - return - } - - if len(uq.Subject) == 0 { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Subject not filled"}) - return - } - - if qa, err := exercice.NewQAQuery(uq.Subject, uq.IdTeam, uq.User, uq.State, nil); err != nil { - log.Println("Unable to importExerciceQA:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during query creation."}) - return - } else { - qa.Creation = uq.Creation - qa.Solved = uq.Solved - qa.Closed = uq.Closed - - _, err = qa.Update() - if err != nil { - log.Println("Unable to update in importExerciceQA:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during query updating."}) - return - } - - c.JSON(http.StatusOK, qa) - } -} - -func importQAComment(c *gin.Context) { - query := c.MustGet("qa-query").(*fic.QAQuery) - - // Create a new query - var uc fic.QAComment - err := c.ShouldBindJSON(&uc) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - if len(uc.Content) == 0 { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Empty comment"}) - return - } - - if qac, err := query.AddComment(uc.Content, uc.IdTeam, uc.User); err != nil { - log.Println("Unable to AddComment in importQAComment:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during comment creation."}) - return - } else { - qac.Date = uc.Date - - _, err = qac.Update() - if err != nil { - log.Println("Unable to Update comment in importQAComment") - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during comment creation."}) - return - } - - c.JSON(http.StatusOK, qac) - } -} diff --git a/admin/api/repositories.go b/admin/api/repositories.go deleted file mode 100644 index 9afaea5d..00000000 --- a/admin/api/repositories.go +++ /dev/null @@ -1,67 +0,0 @@ -package api - -import ( - "net/http" - "strings" - - "srs.epita.fr/fic-server/admin/sync" - - "github.com/gin-gonic/gin" -) - -func declareRepositoriesRoutes(router *gin.RouterGroup) { - if gi, ok := sync.GlobalImporter.(sync.GitImporter); ok { - router.GET("/repositories", func(c *gin.Context) { - mod, err := gi.GetSubmodules() - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - c.JSON(http.StatusOK, gin.H{"repositories": mod}) - }) - - router.GET("/repositories/*repopath", func(c *gin.Context) { - repopath := strings.TrimPrefix(c.Param("repopath"), "/") - - mod, err := gi.GetSubmodule(repopath) - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - c.JSON(http.StatusOK, mod) - }) - - router.POST("/repositories/*repopath", func(c *gin.Context) { - repopath := strings.TrimPrefix(c.Param("repopath"), "/") - - mod, err := gi.IsRepositoryUptodate(repopath) - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - c.JSON(http.StatusOK, mod) - }) - - router.DELETE("/repositories/*repopath", func(c *gin.Context) { - di, ok := sync.GlobalImporter.(sync.DeletableImporter) - if !ok { - c.AbortWithStatusJSON(http.StatusNotImplemented, gin.H{"errmsg": "Not implemented"}) - return - } - - if strings.Contains(c.Param("repopath"), "..") { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Repopath contains invalid characters"}) - return - } - - repopath := strings.TrimPrefix(c.Param("repopath"), "/") - - err := di.DeleteDir(repopath) - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - c.JSON(http.StatusOK, true) - }) - } -} diff --git a/admin/api/router.go b/admin/api/router.go deleted file mode 100644 index 743bc22e..00000000 --- a/admin/api/router.go +++ /dev/null @@ -1,29 +0,0 @@ -package api - -import ( - "github.com/gin-gonic/gin" -) - -func DeclareRoutes(router *gin.RouterGroup) { - apiRoutes := router.Group("/api") - - declareCertificateRoutes(apiRoutes) - declareClaimsRoutes(apiRoutes) - declareEventsRoutes(apiRoutes) - declareExercicesRoutes(apiRoutes) - declareExportRoutes(apiRoutes) - declareFilesGlobalRoutes(apiRoutes) - declareFilesRoutes(apiRoutes) - declareGlobalExercicesRoutes(apiRoutes) - declareHealthRoutes(apiRoutes) - declareMonitorRoutes(apiRoutes) - declarePasswordRoutes(apiRoutes) - declarePublicRoutes(apiRoutes) - declareQARoutes(apiRoutes) - declareRepositoriesRoutes(apiRoutes) - declareTeamsRoutes(apiRoutes) - declareThemesRoutes(apiRoutes) - declareSettingsRoutes(apiRoutes) - declareSyncRoutes(apiRoutes) - DeclareVersionRoutes(apiRoutes) -} diff --git a/admin/api/settings.go b/admin/api/settings.go deleted file mode 100644 index c16d698d..00000000 --- a/admin/api/settings.go +++ /dev/null @@ -1,425 +0,0 @@ -package api - -import ( - "encoding/json" - "fmt" - "io" - "log" - "net/http" - "os" - "path" - "strconv" - "time" - - "srs.epita.fr/fic-server/admin/generation" - "srs.epita.fr/fic-server/admin/sync" - "srs.epita.fr/fic-server/libfic" - "srs.epita.fr/fic-server/settings" - - "github.com/gin-gonic/gin" -) - -var IsProductionEnv = false - -func declareSettingsRoutes(router *gin.RouterGroup) { - router.GET("/challenge.json", getChallengeInfo) - router.PUT("/challenge.json", saveChallengeInfo) - - router.GET("/settings.json", getSettings) - router.PUT("/settings.json", saveSettings) - router.DELETE("/settings.json", func(c *gin.Context) { - err := ResetSettings() - if err != nil { - log.Println("Unable to ResetSettings:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during setting reset."}) - return - } - - c.JSON(http.StatusOK, true) - }) - - router.GET("/settings-next", listNextSettings) - - apiNextSettingsRoutes := router.Group("/settings-next/:ts") - apiNextSettingsRoutes.Use(NextSettingsHandler) - apiNextSettingsRoutes.GET("", getNextSettings) - apiNextSettingsRoutes.DELETE("", deleteNextSettings) - - router.POST("/reset", reset) - router.POST("/full-generation", fullGeneration) - - router.GET("/prod", func(c *gin.Context) { - c.JSON(http.StatusOK, IsProductionEnv) - }) - router.PUT("/prod", func(c *gin.Context) { - err := c.ShouldBindJSON(&IsProductionEnv) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, IsProductionEnv) - }) -} - -func NextSettingsHandler(c *gin.Context) { - ts, err := strconv.ParseInt(string(c.Params.ByName("ts")), 10, 64) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid next settings identifier"}) - return - } - - nsf, err := settings.ReadNextSettingsFile(path.Join(settings.SettingsDir, fmt.Sprintf("%d.json", ts)), ts) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Next settings not found"}) - return - } - - c.Set("next-settings", nsf) - - c.Next() -} - -func fullGeneration(c *gin.Context) { - resp, err := generation.FullGeneration() - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{ - "errmsg": err.Error(), - }) - return - } - defer resp.Body.Close() - - v, _ := io.ReadAll(resp.Body) - c.JSON(resp.StatusCode, gin.H{ - "errmsg": string(v), - }) -} - -func GetChallengeInfo() (*settings.ChallengeInfo, error) { - var challengeinfo string - var err error - if sync.GlobalImporter == nil { - if fd, err := os.Open(path.Join(settings.SettingsDir, settings.ChallengeFile)); err == nil { - defer fd.Close() - var buf []byte - buf, err = io.ReadAll(fd) - if err == nil { - challengeinfo = string(buf) - } - } - } else { - challengeinfo, err = sync.GetFileContent(sync.GlobalImporter, settings.ChallengeFile) - } - - if err != nil { - log.Println("Unable to retrieve challenge.json:", err.Error()) - return nil, fmt.Errorf("Unable to retrive challenge.json: %w", err) - } - - s, err := settings.ReadChallengeInfo(challengeinfo) - if err != nil { - log.Println("Unable to ReadChallengeInfo:", err.Error()) - return nil, fmt.Errorf("Unable to read challenge info: %w", err) - } - - return s, nil -} - -func getChallengeInfo(c *gin.Context) { - if s, err := GetChallengeInfo(); err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - } else { - c.JSON(http.StatusOK, s) - } -} - -func saveChallengeInfo(c *gin.Context) { - var info *settings.ChallengeInfo - err := c.ShouldBindJSON(&info) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - if sync.GlobalImporter != nil { - jenc, err := json.Marshal(info) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - err = sync.WriteFileContent(sync.GlobalImporter, "challenge.json", jenc) - if err != nil { - log.Println("Unable to SaveChallengeInfo:", err.Error()) - // Ignore the error, try to continue - } - - err = sync.ImportChallengeInfo(info, DashboardDir) - if err != nil { - log.Println("Unable to ImportChallengeInfo:", err.Error()) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Something goes wrong when trying to import related files: %s", err.Error())}) - return - } - } - - if err := settings.SaveChallengeInfo(path.Join(settings.SettingsDir, settings.ChallengeFile), info); err != nil { - log.Println("Unable to SaveChallengeInfo:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to save distributed challenge info: %s", err.Error())}) - return - } - - c.JSON(http.StatusOK, info) -} - -func getSettings(c *gin.Context) { - s, err := settings.ReadSettings(path.Join(settings.SettingsDir, settings.SettingsFile)) - if err != nil { - log.Println("Unable to ReadSettings:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to read settings: %s", err.Error())}) - return - } - - s.WorkInProgress = !IsProductionEnv - c.Writer.Header().Add("X-FIC-Time", fmt.Sprintf("%d", time.Now().Unix())) - c.JSON(http.StatusOK, s) -} - -func saveSettings(c *gin.Context) { - var config *settings.Settings - err := c.ShouldBindJSON(&config) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - // Is this a future setting? - if c.Request.URL.Query().Has("t") { - t, err := time.Parse(time.RFC3339, c.Request.URL.Query().Get("t")) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - // Load current settings to perform diff later - init_settings, err := settings.ReadSettings(path.Join(settings.SettingsDir, settings.SettingsFile)) - if err != nil { - log.Println("Unable to ReadSettings:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to read settings: %s", err.Error())}) - return - } - - current_settings := init_settings - // Apply already registered settings - nsu, err := settings.MergeNextSettingsUntil(&t) - if err == nil { - current_settings = settings.MergeSettings(*init_settings, nsu) - } else { - log.Println("Unable to MergeNextSettingsUntil:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to merge next settings: %s", err.Error())}) - return - } - - // Keep only diff - diff := settings.DiffSettings(current_settings, config) - - hasItems := false - for _, _ = range diff { - hasItems = true - break - } - - if !hasItems { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "No difference to apply."}) - return - } - - if !c.Request.URL.Query().Has("erase") { - // Check if there is already diff to apply at the given time - if nsf, err := settings.ReadNextSettingsFile(path.Join(settings.SettingsDir, fmt.Sprintf("%d.json", t.Unix())), t.Unix()); err == nil { - for k, v := range nsf.Values { - if _, ok := diff[k]; !ok { - diff[k] = v - } - } - } - } - - // Save the diff - settings.SaveSettings(path.Join(settings.SettingsDir, fmt.Sprintf("%d.json", t.Unix())), diff) - - // Return current settings - c.JSON(http.StatusOK, current_settings) - } else { - // Just apply settings right now! - if err := settings.SaveSettings(path.Join(settings.SettingsDir, settings.SettingsFile), config); err != nil { - log.Println("Unable to SaveSettings:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to save settings: %s", err.Error())}) - return - } - - ApplySettings(config) - c.JSON(http.StatusOK, config) - } -} - -func listNextSettings(c *gin.Context) { - nsf, err := settings.ListNextSettingsFiles() - if err != nil { - log.Println("Unable to ListNextSettingsFiles:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to list next settings files: %s", err.Error())}) - return - } - - c.JSON(http.StatusOK, nsf) -} - -func getNextSettings(c *gin.Context) { - c.JSON(http.StatusOK, c.MustGet("next-settings").(*settings.NextSettingsFile)) -} - -func deleteNextSettings(c *gin.Context) { - nsf := c.MustGet("next-settings").(*settings.NextSettingsFile) - - err := os.Remove(path.Join(settings.SettingsDir, fmt.Sprintf("%d.json", nsf.Id))) - if err != nil { - log.Println("Unable to remove the file:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to remove the file: %s", err.Error())}) - return - } - - c.JSON(http.StatusOK, true) -} - -func ApplySettings(config *settings.Settings) { - fic.PartialValidation = config.PartialValidation - fic.UnlockedChallengeDepth = config.UnlockedChallengeDepth - fic.UnlockedChallengeUpTo = config.UnlockedChallengeUpTo - fic.DisplayAllFlags = config.DisplayAllFlags - fic.HideCaseSensitivity = config.HideCaseSensitivity - fic.UnlockedStandaloneExercices = config.UnlockedStandaloneExercices - fic.UnlockedStandaloneExercicesByThemeStepValidation = config.UnlockedStandaloneExercicesByThemeStepValidation - fic.UnlockedStandaloneExercicesByStandaloneExerciceValidation = config.UnlockedStandaloneExercicesByStandaloneExerciceValidation - fic.DisplayMCQBadCount = config.DisplayMCQBadCount - fic.FirstBlood = config.FirstBlood - fic.SubmissionCostBase = config.SubmissionCostBase - fic.HintCoefficient = config.HintCurCoefficient - fic.WChoiceCoefficient = config.WChoiceCurCoefficient - fic.ExerciceCurrentCoefficient = config.ExerciceCurCoefficient - fic.GlobalScoreCoefficient = config.GlobalScoreCoefficient - fic.SubmissionCostBase = config.SubmissionCostBase - fic.SubmissionUniqueness = config.SubmissionUniqueness - fic.CountOnlyNotGoodTries = config.CountOnlyNotGoodTries - fic.QuestionGainRatio = config.QuestionGainRatio - - if config.DiscountedFactor != fic.DiscountedFactor { - fic.DiscountedFactor = config.DiscountedFactor - if err := fic.DBRecreateDiscountedView(); err != nil { - log.Println("Unable to recreate exercices_discounted view:", err.Error()) - } - } -} - -func ResetSettings() error { - return settings.SaveSettings(path.Join(settings.SettingsDir, settings.SettingsFile), &settings.Settings{ - WorkInProgress: IsProductionEnv, - FirstBlood: fic.FirstBlood, - SubmissionCostBase: fic.SubmissionCostBase, - ExerciceCurCoefficient: 1, - HintCurCoefficient: 1, - WChoiceCurCoefficient: 1, - GlobalScoreCoefficient: 1, - DiscountedFactor: 0, - QuestionGainRatio: 0, - UnlockedStandaloneExercices: 10, - UnlockedStandaloneExercicesByThemeStepValidation: 1, - UnlockedStandaloneExercicesByStandaloneExerciceValidation: 0, - AllowRegistration: false, - CanJoinTeam: false, - DenyTeamCreation: false, - DenyNameChange: false, - AcceptNewIssue: true, - QAenabled: false, - EnableResolutionRoute: false, - PartialValidation: true, - UnlockedChallengeDepth: 0, - SubmissionUniqueness: true, - CountOnlyNotGoodTries: true, - DisplayAllFlags: false, - DisplayMCQBadCount: false, - EventKindness: false, - }) -} - -func ResetChallengeInfo() error { - return settings.SaveChallengeInfo(path.Join(settings.SettingsDir, settings.ChallengeFile), &settings.ChallengeInfo{ - Title: "Challenge forensic", - SubTitle: "sous le patronage du commandement de la cyberdéfense", - Authors: "Laboratoire SRS, ÉPITA", - VideosLink: "", - Description: `

Le challenge forensic vous place dans la peau de spécialistes en investigation numérique. Nous mettons à votre disposition une vingtaine de scénarios différents, dans lesquels vous devrez faire les différentes étapes de la caractérisation d’une réponse à incident proposées.

-

Chaque scénario met en scène un contexte d’entreprise, ayant découvert récemment qu’elle a été victime d’une cyberattaque. Elle vous demande alors de l’aider à caractériser, afin de mieux comprendre la situation, notamment le mode opératoire de l’adversaire, les impacts de la cyberattaque, le périmètre technique compromis, etc. Il faudra parfois aussi l’éclairer sur les premières étapes de la réaction.

`, - Rules: `

Déroulement

-

Pendant toute la durée du challenge, vous aurez accès à tous les scénarios, mais seulement à la première des 5 étapes. Chaque étape supplémentaire est débloquée lorsque vous validez l’intégralité de l’étape précédente. Toutefois, pour dynamiser le challenge toutes les étapes et tous les scénarios seront débloquées pour la dernière heure du challenge.

-

Nous mettons à votre disposition une plateforme sur laquelle vous pourrez obtenir les informations sur le contexte de l’entreprise et, généralement, une série de fichiers qui semblent appropriés pour avancer dans l’investigation.

-

La validation d’une étape se fait sur la plateforme, après avoir analysé les informations fournies, en répondant à des questions plus ou moins précises. Il s’agit le plus souvent des mots-clefs que l’on placerait dans un rapport.

-

Pour vous débloquer ou accélérer votre investigation, vous pouvez accéder à quelques indices, en échange d’une décote sur votre score d’un certain nombre de points préalablement affichés.

-

Calcul des points, bonus, malus et classement

-

Chaque équipe dispose d’un compteur de points dans l’intervalle ]-∞;+∞[ (aux détails techniques près), à partir duquel le classement est établi.

-

Vous perdez des points en dévoilant des indices, en demandant des propositions de réponses en remplacement de certains champs de texte, ou en essayant un trop grand nombre de fois une réponse.

-

Le nombre de points que vous fait perdre un indice dépend habituellement de l’aide qu’il vous apportera et est indiqué avant de le dévoiler, car il peut fluctuer en fonction de l’avancement du challenge.

-

Pour chaque champ de texte, vous disposez de 10 tentatives avant de perdre des points (vous perdez les points même si vous ne validez pas l’étape) pour chaque tentative supplémentaire : -0,25 point entre 11 et 20, -0,5 entre 21 et 30, -0,75 entre 31 et 40, …

-

La seule manière de gagner des points est de valider une étape d’un scénario dans son intégralité. Le nombre de points gagnés dépend de la difficulté théorique de l’étape ainsi que d’éventuels bonus. Un bonus de 10 % est accordé à la première équipe qui valide une étape. D’autres bonus peuvent ponctuer le challenge, détaillé dans la partie suivante.

-

Le classement est établi par équipe, selon le nombre de points récoltés et perdus par tous les membres. En cas d’égalité au score, les équipes sont départagées en fonction de leur ordre d’arrivée à ce score.

-

Temps forts

-

Le challenge forensic est jalonné de plusieurs temps forts durant lesquels certains calculs détaillés dans la partie précédente peuvent être altérés. L’équipe d’animation du challenge vous avertira environ 15 minutes avant le début de la modification.

-

Chaque modification se répercute instantanément dans votre interface, attendez simplement qu’elle apparaisse afin d’être certain d’en bénéficier. Un compte à rebours est généralement affiché sur les écrans pour indiquer la fin d’un temps fort. La fin d’application d’un bonus est déterminé par l’heure d’arrivée de votre demande sur nos serveurs.

-

Sans y être limité ou assuré, sachez que durant les précédentes éditions du challenge forensic, nous avons par exemple : doublé les points de défis peu tentés, doublé les points de tous les défis pendant 30 minutes, réduit le coût des indices pendant 15 minutes, etc.

-

-

Tous les étudiants de la majeure Système, Réseaux et Sécurité de l’ÉPITA, son équipe enseignante ainsi que le commandement de la cyberdéfense vous souhaitent bon courage pour cette nouvelle éditions du challenge !

`, - YourMission: `

Bienvenue au challenge forensic !

-

Vous voici aujourd'hui dans la peau de spécialistes en investigation numérique. Vous avez à votre disposition une vingtaine de scénarios différents dans lesquels vous devrez faire les différentes étapes de la caractérisation d’une réponse à incident.

-

Chaque scénario est découpé en 5 grandes étapes de difficulté croissante. Un certain nombre de points est attribué à chaque étape, avec un processus de validation automatique.

-

Un classement est établi en temps réel, tenant compte des différents bonus, en fonction du nombre de points de chaque équipe.

`, - }) -} - -func reset(c *gin.Context) { - var m map[string]string - err := c.ShouldBindJSON(&m) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - t, ok := m["type"] - if !ok { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Field type not found"}) - } - - switch t { - case "teams": - err = fic.ResetTeams() - case "challenges": - err = fic.ResetExercices() - case "game": - err = fic.ResetGame() - case "annexes": - err = fic.ResetAnnexes() - case "settings": - err = ResetSettings() - case "challengeInfo": - err = ResetChallengeInfo() - default: - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Unknown reset type"}) - return - } - - if err != nil { - log.Printf("Unable to reset (type=%q): %s", t, err) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to performe the reset: %s", err.Error())}) - return - } - - c.JSON(http.StatusOK, true) -} diff --git a/admin/api/sync.go b/admin/api/sync.go deleted file mode 100644 index 582c55e0..00000000 --- a/admin/api/sync.go +++ /dev/null @@ -1,411 +0,0 @@ -package api - -import ( - "fmt" - "log" - "net/http" - "net/url" - "os" - "path" - "reflect" - "strings" - - "srs.epita.fr/fic-server/admin/generation" - "srs.epita.fr/fic-server/admin/sync" - "srs.epita.fr/fic-server/libfic" - - "github.com/gin-gonic/gin" - "go.uber.org/multierr" -) - -var lastSyncError = "" - -func flatifySyncErrors(errs error) (ret []string) { - for _, err := range multierr.Errors(errs) { - ret = append(ret, err.Error()) - } - return -} - -func declareSyncRoutes(router *gin.RouterGroup) { - apiSyncRoutes := router.Group("/sync") - - // Return the global sync status - apiSyncRoutes.GET("/status", func(c *gin.Context) { - syncMtd := "Disabled" - if sync.GlobalImporter != nil { - syncMtd = sync.GlobalImporter.Kind() - } - - var syncId *string - if sync.GlobalImporter != nil { - syncId = sync.GlobalImporter.Id() - } - - c.JSON(http.StatusOK, gin.H{ - "sync-type": reflect.TypeOf(sync.GlobalImporter).Name(), - "sync-id": syncId, - "sync": syncMtd, - "pullMutex": !sync.OneGitPullStatus(), - "syncMutex": !sync.OneDeepSyncStatus() && !sync.OneThemeDeepSyncStatus(), - "progress": sync.DeepSyncProgress, - "lastError": lastSyncError, - }) - }) - - // Base sync checks if the local directory is in sync with remote one. - apiSyncRoutes.POST("/base", func(c *gin.Context) { - err := sync.GlobalImporter.Sync() - if err != nil { - lastSyncError = err.Error() - c.JSON(http.StatusExpectationFailed, gin.H{"errmsg": err.Error()}) - } else { - lastSyncError = "" - c.JSON(http.StatusOK, true) - } - }) - - // Speedy sync performs a recursive synchronization without importing files. - apiSyncRoutes.POST("/speed", func(c *gin.Context) { - st := sync.SpeedySyncDeep(sync.GlobalImporter) - sync.EditDeepReport(&st, false) - c.JSON(http.StatusOK, st) - }) - - // Deep sync: a fully recursive synchronization (can be limited by theme). - apiSyncRoutes.POST("/deep", func(c *gin.Context) { - r := sync.SyncDeep(sync.GlobalImporter) - lastSyncError = "" - c.JSON(http.StatusOK, r) - }) - - apiSyncRoutes.POST("/local-diff", APIDiffDBWithRemote) - - apiSyncDeepRoutes := apiSyncRoutes.Group("/deep/:thid") - apiSyncDeepRoutes.Use(ThemeHandler) - // Special route to handle standalone exercices - apiSyncRoutes.POST("/deep/0", func(c *gin.Context) { - var st []string - for _, se := range multierr.Errors(sync.SyncThemeDeep(sync.GlobalImporter, &fic.StandaloneExercicesTheme, 0, 250, nil)) { - st = append(st, se.Error()) - } - sync.EditDeepReport(&sync.SyncReport{Exercices: st}, false) - sync.DeepSyncProgress = 255 - lastSyncError = "" - c.JSON(http.StatusOK, st) - }) - apiSyncDeepRoutes.POST("", func(c *gin.Context) { - theme := c.MustGet("theme").(*fic.Theme) - - exceptions := sync.LoadThemeException(sync.GlobalImporter, theme) - - var st []string - for _, se := range multierr.Errors(sync.SyncThemeDeep(sync.GlobalImporter, theme, 0, 250, exceptions)) { - st = append(st, se.Error()) - } - sync.EditDeepReport(&sync.SyncReport{Themes: map[string][]string{theme.Name: st}}, false) - sync.DeepSyncProgress = 255 - lastSyncError = "" - c.JSON(http.StatusOK, st) - }) - - // Auto sync: to use with continuous deployment, in a development env - apiSyncRoutes.POST("/auto/*p", autoSync) - - // Themes - apiSyncRoutes.POST("/fixurlids", fixAllURLIds) - - apiSyncRoutes.POST("/themes", func(c *gin.Context) { - _, errs := sync.SyncThemes(sync.GlobalImporter) - lastSyncError = "" - c.JSON(http.StatusOK, flatifySyncErrors(errs)) - }) - - apiSyncThemesRoutes := apiSyncRoutes.Group("/themes/:thid") - apiSyncThemesRoutes.Use(ThemeHandler) - apiSyncThemesRoutes.POST("/fixurlid", func(c *gin.Context) { - theme := c.MustGet("theme").(*fic.Theme) - if theme.FixURLId() { - v, err := theme.Update() - if err != nil { - log.Println("Unable to UpdateTheme after fixurlid:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when saving the theme."}) - return - } - - c.JSON(http.StatusOK, v) - } else { - c.AbortWithStatusJSON(http.StatusOK, 0) - } - }) - - // Exercices - declareSyncExercicesRoutes(apiSyncRoutes) - declareSyncExercicesRoutes(apiSyncThemesRoutes) - - // Videos sync imports resolution.mp4 from path stored in database. - apiSyncRoutes.POST("/videos", func(c *gin.Context) { - exercices, err := fic.GetExercices() - if err != nil { - log.Println("Unable to GetExercices:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve exercices list."}) - return - } - - for _, e := range exercices { - if len(e.VideoURI) == 0 || !strings.HasPrefix(e.VideoURI, "$RFILES$/") { - continue - } - - vpath, err := url.PathUnescape(strings.TrimPrefix(e.VideoURI, "$RFILES$/")) - if err != nil { - c.JSON(http.StatusExpectationFailed, gin.H{"errmsg": fmt.Sprintf("Unable to perform URL unescape: %s", err.Error())}) - return - } - - _, err = sync.ImportFile(sync.GlobalImporter, vpath, func(filePath, URI string) (interface{}, error) { - e.VideoURI = path.Join("$FILES$", strings.TrimPrefix(filePath, fic.FilesDir)) - return e.Update() - }) - if err != nil { - c.JSON(http.StatusExpectationFailed, gin.H{"errmsg": err.Error()}) - return - } - } - - c.JSON(http.StatusOK, true) - }) - - // Remove soluces from the database. - apiSyncRoutes.POST("/drop_soluces", func(c *gin.Context) { - exercices, err := fic.GetExercices() - if err != nil { - log.Println("Unable to GetExercices:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve exercices list."}) - return - } - - var errs error - for _, e := range exercices { - // Remove any published video - if len(e.VideoURI) > 0 && strings.HasPrefix(e.VideoURI, "$FILES$") { - vpath := path.Join(fic.FilesDir, strings.TrimPrefix(e.VideoURI, "$FILES$/")) - err = os.Remove(vpath) - if err != nil { - errs = multierr.Append(errs, fmt.Errorf("unable to delete published video (%q): %w", e.VideoURI, err)) - } - } - - // Clean the database - if len(e.VideoURI) > 0 || len(e.Resolution) > 0 { - e.VideoURI = "" - e.Resolution = "" - - _, err = e.Update() - if err != nil { - errs = multierr.Append(errs, fmt.Errorf("unable to update exercice (%d: %s): %w", e.Id, e.Title, err)) - } - } - } - - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"errmsg": flatifySyncErrors(err)}) - } else { - c.JSON(http.StatusOK, true) - } - }) -} - -func declareSyncExercicesRoutes(router *gin.RouterGroup) { - router.POST("/exercices", func(c *gin.Context) { - theme := c.MustGet("theme").(*fic.Theme) - exceptions := sync.LoadThemeException(sync.GlobalImporter, theme) - - _, errs := sync.SyncExercices(sync.GlobalImporter, theme, exceptions) - c.JSON(http.StatusOK, flatifySyncErrors(errs)) - }) - apiSyncExercicesRoutes := router.Group("/exercices/:eid") - apiSyncExercicesRoutes.Use(ExerciceHandler) - apiSyncExercicesRoutes.POST("", func(c *gin.Context) { - theme := c.MustGet("theme").(*fic.Theme) - exercice := c.MustGet("exercice").(*fic.Exercice) - - exceptions := sync.LoadExerciceException(sync.GlobalImporter, theme, exercice, nil) - - _, _, _, errs := sync.SyncExercice(sync.GlobalImporter, theme, exercice.Path, nil, exceptions) - c.JSON(http.StatusOK, flatifySyncErrors(errs)) - }) - apiSyncExercicesRoutes.POST("/files", func(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - theme := c.MustGet("theme").(*fic.Theme) - - exceptions := sync.LoadExerciceException(sync.GlobalImporter, theme, exercice, nil) - - c.JSON(http.StatusOK, flatifySyncErrors(sync.ImportExerciceFiles(sync.GlobalImporter, exercice, exceptions))) - }) - apiSyncExercicesRoutes.POST("/fixurlid", func(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - if exercice.FixURLId() { - v, err := exercice.Update() - if err != nil { - log.Println("Unable to UpdateExercice after fixurlid:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when saving the exercice."}) - return - } - - c.JSON(http.StatusOK, v) - } else { - c.AbortWithStatusJSON(http.StatusOK, 0) - } - }) - apiSyncExercicesRoutes.POST("/hints", func(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - theme := c.MustGet("theme").(*fic.Theme) - - exceptions := sync.LoadExerciceException(sync.GlobalImporter, theme, exercice, nil) - - _, errs := sync.SyncExerciceHints(sync.GlobalImporter, exercice, sync.ExerciceFlagsMap(sync.GlobalImporter, exercice), exceptions) - c.JSON(http.StatusOK, flatifySyncErrors(errs)) - }) - apiSyncExercicesRoutes.POST("/flags", func(c *gin.Context) { - exercice := c.MustGet("exercice").(*fic.Exercice) - theme := c.MustGet("theme").(*fic.Theme) - - exceptions := sync.LoadExerciceException(sync.GlobalImporter, theme, exercice, nil) - _, errs := sync.SyncExerciceFlags(sync.GlobalImporter, exercice, exceptions) - _, herrs := sync.SyncExerciceHints(sync.GlobalImporter, exercice, sync.ExerciceFlagsMap(sync.GlobalImporter, exercice), exceptions) - c.JSON(http.StatusOK, flatifySyncErrors(multierr.Append(errs, herrs))) - }) -} - -// autoSync tries to performs a smart synchronization, when in development environment. -// It'll sync most of modified things, and will delete out of sync data. -// Avoid using it in a production environment. -func autoSync(c *gin.Context) { - p := strings.Split(strings.TrimPrefix(c.Params.ByName("p"), "/"), "/") - - if !IsProductionEnv { - if err := sync.GlobalImporter.Sync(); err != nil { - lastSyncError = err.Error() - log.Println("Unable to sync.GI.Sync:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to perform the pull."}) - return - } - lastSyncError = "" - } - - themes, err := fic.GetThemes() - if err != nil { - log.Println("Unable to GetThemes:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve theme list."}) - return - } - - // No argument, do a deep sync - if len(p) == 0 { - if !IsProductionEnv { - for _, theme := range themes { - theme.DeleteDeep() - } - } - - st := sync.SyncDeep(sync.GlobalImporter) - c.JSON(http.StatusOK, st) - return - } - - var theTheme *fic.Theme - - // Find the given theme - for _, theme := range themes { - if theme.Path == p[0] { - theTheme = theme - break - } - } - - if theTheme == nil { - // The theme doesn't exists locally, perhaps it has not been imported already? - rThemes, err := sync.GetThemes(sync.GlobalImporter) - if err == nil { - for _, theme := range rThemes { - if theme == p[0] { - sync.SyncThemes(sync.GlobalImporter) - - themes, err := fic.GetThemes() - if err == nil { - for _, theme := range themes { - if theme.Path == p[0] { - theTheme = theme - break - } - } - } - - break - } - } - } - - if theTheme == nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": fmt.Sprintf("Theme not found %q", p[0])}) - return - } - } - - if !IsProductionEnv { - exercices, err := theTheme.GetExercices() - if err == nil { - for _, exercice := range exercices { - if len(p) <= 1 || exercice.Path == path.Join(p[0], p[1]) { - exercice.DeleteDeep() - } - } - } - } - - exceptions := sync.LoadThemeException(sync.GlobalImporter, theTheme) - - var st []string - for _, se := range multierr.Errors(sync.SyncThemeDeep(sync.GlobalImporter, theTheme, 0, 250, exceptions)) { - st = append(st, se.Error()) - } - sync.EditDeepReport(&sync.SyncReport{Themes: map[string][]string{theTheme.Name: st}}, false) - sync.DeepSyncProgress = 255 - - resp, err := generation.FullGeneration() - if err == nil { - defer resp.Body.Close() - } - - c.JSON(http.StatusOK, st) -} - -func diffDBWithRemote() (map[string][]syncDiff, error) { - diffs := map[string][]syncDiff{} - - themes, err := fic.GetThemesExtended() - if err != nil { - return nil, err - } - - // Compare inner themes - for _, theme := range themes { - diffs[theme.Name], err = diffThemeWithRemote(theme) - if err != nil { - return nil, fmt.Errorf("Unable to diffThemeWithRemote: %w", err) - } - } - - return diffs, err -} - -func APIDiffDBWithRemote(c *gin.Context) { - diffs, err := diffDBWithRemote() - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, diffs) -} diff --git a/admin/api/team.go b/admin/api/team.go deleted file mode 100644 index d5d21992..00000000 --- a/admin/api/team.go +++ /dev/null @@ -1,641 +0,0 @@ -package api - -import ( - "encoding/json" - "fmt" - "log" - "net/http" - "strconv" - "strings" - "time" - - "srs.epita.fr/fic-server/admin/pki" - "srs.epita.fr/fic-server/libfic" - - "github.com/gin-gonic/gin" -) - -func declareTeamsRoutes(router *gin.RouterGroup) { - router.GET("/teams.json", func(c *gin.Context) { - teams, err := fic.ExportTeams(false) - if err != nil { - log.Println("Unable to ExportTeams:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during teams export."}) - return - } - - c.JSON(http.StatusOK, teams) - }) - router.GET("/teams-members.json", func(c *gin.Context) { - teams, err := fic.ExportTeams(true) - if err != nil { - log.Println("Unable to ExportTeams:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during teams export."}) - return - } - - c.JSON(http.StatusOK, teams) - }) - router.GET("/teams-associations.json", allAssociations) - router.GET("/teams-binding", bindingTeams) - router.GET("/teams-nginx", nginxGenTeams) - router.POST("/refine_colors", refineTeamsColors) - router.POST("/disableinactiveteams", disableInactiveTeams) - router.POST("/enableallteams", enableAllTeams) - router.GET("/teams-members-nginx", nginxGenMember) - router.GET("/teams-tries.json", func(c *gin.Context) { - tries, err := fic.GetTries(nil, nil) - if err != nil { - log.Println("Unable to GetTries:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieves tries."}) - return - } - - c.JSON(http.StatusOK, tries) - }) - - router.GET("/teams", func(c *gin.Context) { - teams, err := fic.GetTeams() - if err != nil { - log.Println("Unable to GetTeams:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during teams listing."}) - return - } - - c.JSON(http.StatusOK, teams) - }) - router.POST("/teams", createTeam) - - apiTeamsRoutes := router.Group("/teams/:tid") - apiTeamsRoutes.Use(TeamHandler) - apiTeamsRoutes.GET("/", func(c *gin.Context) { - c.JSON(http.StatusOK, c.MustGet("team").(*fic.Team)) - }) - apiTeamsRoutes.PUT("/", updateTeam) - apiTeamsRoutes.POST("/", addTeamMember) - apiTeamsRoutes.DELETE("/", deleteTeam) - apiTeamsRoutes.GET("/score-grid.json", func(c *gin.Context) { - team := c.MustGet("team").(*fic.Team) - - sg, err := team.ScoreGrid() - if err != nil { - log.Printf("Unable to get ScoreGrid(tid=%d): %s", team.Id, err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during score grid calculation."}) - return - } - - c.JSON(http.StatusOK, sg) - }) - - apiTeamsPublicRoutes := router.Group("/teams/:tid") - apiTeamsPublicRoutes.Use(TeamPublicHandler) - apiTeamsPublicRoutes.GET("/my.json", func(c *gin.Context) { - var team *fic.Team - if t, ok := c.Get("team"); ok && t != nil { - team = t.(*fic.Team) - } - tfile, err := fic.MyJSONTeam(team, true) - if err != nil { - log.Println("Unable to get MyJSONTeam:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during team JSON generation."}) - return - } - - c.JSON(http.StatusOK, tfile) - }) - apiTeamsPublicRoutes.GET("/wait.json", func(c *gin.Context) { - var team *fic.Team - if t, ok := c.Get("team"); ok && t != nil { - team = t.(*fic.Team) - } - tfile, err := fic.MyJSONTeam(team, false) - if err != nil { - log.Println("Unable to get MyJSONTeam:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during team JSON generation."}) - return - } - - c.JSON(http.StatusOK, tfile) - }) - apiTeamsPublicRoutes.GET("/stats.json", func(c *gin.Context) { - var team *fic.Team - if t, ok := c.Get("team"); ok && t != nil { - team = t.(*fic.Team) - } - if team != nil { - stats, err := team.GetStats() - if err != nil { - log.Println("Unable to get GetStats:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during stats calculation."}) - return - } - - c.JSON(http.StatusOK, stats) - } else { - stats, err := fic.GetTeamsStats(nil) - if err != nil { - log.Println("Unable to get GetTeamsStats:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during global stats calculation."}) - return - } - - c.JSON(http.StatusOK, stats) - } - }) - apiTeamsRoutes.GET("/history.json", func(c *gin.Context) { - team := c.MustGet("team").(*fic.Team) - - history, err := team.GetHistory() - if err != nil { - log.Println("Unable to get GetHistory:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during history calculation."}) - return - } - - c.JSON(http.StatusOK, history) - }) - apiTeamsRoutes.PATCH("/history.json", updateHistory) - apiTeamsRoutes.DELETE("/history.json", delHistory) - apiTeamsPublicRoutes.GET("/tries", func(c *gin.Context) { - team := c.MustGet("team").(*fic.Team) - - tries, err := fic.GetTries(team, nil) - if err != nil { - log.Println("Unable to GetTries:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during tries calculation."}) - return - } - - c.JSON(http.StatusOK, tries) - }) - apiTeamsRoutes.GET("/members", func(c *gin.Context) { - team := c.MustGet("team").(*fic.Team) - - members, err := team.GetMembers() - if err != nil { - log.Println("Unable to GetMembers:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during members retrieval."}) - return - } - - c.JSON(http.StatusOK, members) - }) - apiTeamsRoutes.POST("/members", addTeamMember) - apiTeamsRoutes.PUT("/members", setTeamMember) - - declareTeamsPasswordRoutes(apiTeamsRoutes) - declareTeamClaimsRoutes(apiTeamsRoutes) - declareTeamCertificateRoutes(apiTeamsRoutes) - - // Import teams from cyberrange - router.POST("/cyberrange-teams.json", importTeamsFromCyberrange) -} - -func TeamHandler(c *gin.Context) { - tid, err := strconv.ParseInt(string(c.Params.ByName("tid")), 10, 64) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid team identifier"}) - return - } - - team, err := fic.GetTeam(tid) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Team not found"}) - return - } - - c.Set("team", team) - - c.Next() -} - -func TeamPublicHandler(c *gin.Context) { - tid, err := strconv.ParseInt(string(c.Params.ByName("tid")), 10, 64) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid team identifier"}) - return - } - - if tid != 0 { - team, err := fic.GetTeam(tid) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Team not found"}) - return - } - - c.Set("team", team) - } else { - c.Set("team", nil) - } - - c.Next() -} - -func nginxGenTeams(c *gin.Context) { - teams, err := fic.GetTeams() - if err != nil { - log.Println("Unable to GetTeams:", err.Error()) - c.AbortWithError(http.StatusInternalServerError, err) - return - } - - ret := "" - for _, team := range teams { - ret += fmt.Sprintf(" if ($remote_user = \"%s\") { set $team \"%d\"; }\n", strings.ToLower(team.Name), team.Id) - } - - c.String(http.StatusOK, ret) -} - -func nginxGenMember(c *gin.Context) { - teams, err := fic.GetTeams() - if err != nil { - log.Println("Unable to GetTeams:", err.Error()) - c.AbortWithError(http.StatusInternalServerError, err) - return - } - - ret := "" - for _, team := range teams { - if members, err := team.GetMembers(); err == nil { - for _, member := range members { - ret += fmt.Sprintf(" if ($remote_user = \"%s\") { set $team \"%d\"; }\n", member.Nickname, team.Id) - } - } else { - c.AbortWithError(http.StatusInternalServerError, err) - return - } - } - - c.String(http.StatusOK, ret) -} - -func bindingTeams(c *gin.Context) { - teams, err := fic.GetTeams() - if err != nil { - log.Println("Unable to GetTeams:", err.Error()) - c.AbortWithError(http.StatusInternalServerError, err) - return - } - - ret := "" - for _, team := range teams { - if members, err := team.GetMembers(); err != nil { - c.AbortWithError(http.StatusInternalServerError, err) - return - } else { - var mbs []string - for _, member := range members { - mbs = append(mbs, fmt.Sprintf("%s %s", member.Firstname, member.Lastname)) - } - ret += fmt.Sprintf("%d;%s;%s\n", team.Id, team.Name, strings.Join(mbs, ";")) - } - } - - c.String(http.StatusOK, ret) -} - -type teamAssociation struct { - Association string `json:"association"` - TeamId int64 `json:"team_id"` -} - -func allAssociations(c *gin.Context) { - teams, err := fic.GetTeams() - if err != nil { - log.Println("Unable to GetTeams:", err.Error()) - c.AbortWithError(http.StatusInternalServerError, err) - return - } - - var ret []teamAssociation - - for _, team := range teams { - assocs, err := pki.GetTeamAssociations(TeamsDir, team.Id) - if err != nil { - c.AbortWithError(http.StatusInternalServerError, err) - return - } - - for _, a := range assocs { - ret = append(ret, teamAssociation{a, team.Id}) - } - } - - c.JSON(http.StatusOK, ret) -} - -func importTeamsFromCyberrange(c *gin.Context) { - file, err := c.FormFile("file") - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"errmsg": "Failed to get file: " + err.Error()}) - return - } - - src, err := file.Open() - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"errmsg": "Failed to open file: " + err.Error()}) - return - } - defer src.Close() - - var ut []fic.CyberrangeTeamBase - err = json.NewDecoder(src).Decode(&fic.CyberrangeAPIResponse{Data: &ut}) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - teams, err := fic.GetTeams() - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Impossible de récupérer la liste des équipes actuelles: %s", err.Error())}) - return - } - - for _, crteam := range ut { - var exist_team *fic.Team - for _, team := range teams { - if team.Name == crteam.Name || team.ExternalId == crteam.UUID { - exist_team = team - break - } - } - - if exist_team != nil { - exist_team.Name = crteam.Name - exist_team.ExternalId = crteam.UUID - _, err = exist_team.Update() - } else { - exist_team, err = fic.CreateTeam(crteam.Name, fic.RandomColor().ToRGB(), crteam.UUID) - } - - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Impossible d'ajouter/de modifier l'équipe %v: %s", crteam, err.Error())}) - return - } - - // Import members - if c.DefaultQuery("nomembers", "0") != "" && len(crteam.Members) > 0 { - exist_team.ClearMembers() - - for _, member := range crteam.Members { - _, err = exist_team.AddMember(member.Name, "", member.Nickname, exist_team.Name) - if err != nil { - log.Printf("Unable to add member %q to team %s (tid=%d): %s", member.UUID, exist_team.Name, exist_team.Id, err.Error()) - } - } - } - } - - teams, err = fic.GetTeams() - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Impossible de récupérer la liste des équipes après import: %s", err.Error())}) - return - } - - c.JSON(http.StatusOK, teams) -} - -func createTeam(c *gin.Context) { - var ut fic.Team - err := c.ShouldBindJSON(&ut) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - if ut.Color == 0 { - ut.Color = fic.RandomColor().ToRGB() - } - - team, err := fic.CreateTeam(strings.TrimSpace(ut.Name), ut.Color, ut.ExternalId) - if err != nil { - log.Println("Unable to CreateTeam:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during team creation."}) - return - } - - c.JSON(http.StatusOK, team) -} - -func updateTeam(c *gin.Context) { - team := c.MustGet("team").(*fic.Team) - - var ut fic.Team - err := c.ShouldBindJSON(&ut) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - ut.Id = team.Id - - if ut.Password != nil && *ut.Password == "" { - ut.Password = nil - } - - _, err = ut.Update() - if err != nil { - log.Println("Unable to updateTeam:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during team updating."}) - return - } - - c.JSON(http.StatusOK, ut) -} - -func refineTeamsColors(c *gin.Context) { - teams, err := fic.GetTeams() - if err != nil { - log.Println("Unable to GetTeams:", err.Error()) - c.AbortWithError(http.StatusInternalServerError, err) - return - } - - for i, team := range teams { - team.Color = fic.HSL{ - H: float64(i)/float64(len(teams)) - 0.2, - S: float64(1) / float64(1+i%2), - L: 0.25 + float64(0.5)/float64(1+i%3), - }.ToRGB() - - _, err = team.Update() - if err != nil { - c.AbortWithError(http.StatusInternalServerError, err) - return - } - } - - c.JSON(http.StatusOK, teams) -} - -func disableInactiveTeams(c *gin.Context) { - teams, err := fic.GetTeams() - if err != nil { - log.Println("Unable to GetTeams:", err.Error()) - c.AbortWithError(http.StatusInternalServerError, err) - return - } - - for _, team := range teams { - var serials []uint64 - serials, err = pki.GetTeamSerials(TeamsDir, team.Id) - if err != nil { - c.AbortWithError(http.StatusInternalServerError, err) - return - } - - var assocs []string - assocs, err = pki.GetTeamAssociations(TeamsDir, team.Id) - if err != nil { - c.AbortWithError(http.StatusInternalServerError, err) - return - } - - if len(serials) == 0 && len(assocs) == 0 { - if team.Active { - team.Active = false - team.Update() - } - } else if !team.Active { - team.Active = true - team.Update() - } - } - - c.JSON(http.StatusOK, true) -} - -func enableAllTeams(c *gin.Context) { - teams, err := fic.GetTeams() - if err != nil { - log.Println("Unable to GetTeams:", err.Error()) - c.AbortWithError(http.StatusInternalServerError, err) - return - } - - for _, team := range teams { - if !team.Active { - team.Active = true - team.Update() - } - } - - c.JSON(http.StatusOK, true) -} - -func deleteTeam(c *gin.Context) { - team := c.MustGet("team").(*fic.Team) - - assocs, err := pki.GetTeamAssociations(TeamsDir, team.Id) - if err != nil { - log.Printf("Unable to GetTeamAssociations(tid=%d): %s", team.Id, err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to retrieve team association."}) - return - } - - for _, assoc := range assocs { - err = pki.DeleteTeamAssociation(TeamsDir, assoc) - if err != nil { - log.Printf("Unable to DeleteTeamAssociation(assoc=%s): %s", assoc, err.Error()) - return - } - } - - _, err = team.Delete() - if err != nil { - log.Println("Unable to deleteTeam:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during team deletion."}) - return - } - - c.JSON(http.StatusOK, true) -} - -func addTeamMember(c *gin.Context) { - team := c.MustGet("team").(*fic.Team) - - var members []fic.Member - err := c.ShouldBindJSON(&members) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - for _, member := range members { - _, err := team.AddMember(strings.TrimSpace(member.Firstname), strings.TrimSpace(member.Lastname), strings.TrimSpace(member.Nickname), strings.TrimSpace(member.Company)) - if err != nil { - log.Println("Unable to AddMember:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during member creation."}) - return - } - } - - mmbrs, err := team.GetMembers() - if err != nil { - log.Println("Unable to retrieve members list:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve members list."}) - return - } - - c.JSON(http.StatusOK, mmbrs) -} - -func setTeamMember(c *gin.Context) { - team := c.MustGet("team").(*fic.Team) - team.ClearMembers() - addTeamMember(c) -} - -type uploadedHistory struct { - Kind string - Time time.Time - Primary *int64 - Secondary *int64 - Coefficient float32 -} - -func updateHistory(c *gin.Context) { - team := c.MustGet("team").(*fic.Team) - - var uh uploadedHistory - err := c.ShouldBindJSON(&uh) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - var givenId int64 - if uh.Secondary != nil { - givenId = *uh.Secondary - } else if uh.Primary != nil { - givenId = *uh.Primary - } - - _, err = team.UpdateHistoryCoeff(uh.Kind, uh.Time, givenId, uh.Coefficient) - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to update this history line: %s", err.Error())}) - return - } - - c.JSON(http.StatusOK, true) -} - -func delHistory(c *gin.Context) { - team := c.MustGet("team").(*fic.Team) - - var uh uploadedHistory - err := c.ShouldBindJSON(&uh) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - _, err = team.DelHistoryItem(uh.Kind, uh.Time, uh.Primary, uh.Secondary) - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to delete this history line: %s", err.Error())}) - return - } - - c.JSON(http.StatusOK, true) -} diff --git a/admin/api/theme.go b/admin/api/theme.go deleted file mode 100644 index c1057397..00000000 --- a/admin/api/theme.go +++ /dev/null @@ -1,376 +0,0 @@ -package api - -import ( - "fmt" - "log" - "net/http" - "path" - "reflect" - "strconv" - "strings" - - "srs.epita.fr/fic-server/admin/sync" - "srs.epita.fr/fic-server/libfic" - "srs.epita.fr/fic-server/settings" - - "github.com/gin-gonic/gin" -) - -func declareThemesRoutes(router *gin.RouterGroup) { - router.GET("/themes", listThemes) - router.POST("/themes", createTheme) - router.GET("/themes.json", exportThemes) - router.GET("/session-forensic.yaml", func(c *gin.Context) { - if s, err := settings.ReadSettings(path.Join(settings.SettingsDir, settings.SettingsFile)); err != nil { - log.Printf("Unable to ReadSettings: %s", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during settings reading."}) - return - - } else if challengeinfo, err := sync.GetFileContent(sync.GlobalImporter, "challenge.json"); err != nil { - log.Println("Unable to retrieve challenge.json:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to retrive challenge.json: %s", err.Error())}) - return - } else if ch, err := settings.ReadChallengeInfo(challengeinfo); err != nil { - log.Printf("Unable to ReadChallengeInfo: %s", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during challenge info reading."}) - return - } else if sf, err := fic.GenZQDSSessionFile(ch, s); err != nil { - log.Printf("Unable to GenZQDSSessionFile: %s", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during session file generation."}) - return - } else { - c.JSON(http.StatusOK, sf) - } - }) - router.GET("/files-bindings", bindingFiles) - - apiThemesRoutes := router.Group("/themes/:thid") - apiThemesRoutes.Use(ThemeHandler) - apiThemesRoutes.GET("", showTheme) - apiThemesRoutes.PUT("", updateTheme) - apiThemesRoutes.DELETE("", deleteTheme) - - apiThemesRoutes.POST("/diff-sync", APIDiffThemeWithRemote) - - apiThemesRoutes.GET("/exercices_stats.json", getThemedExercicesStats) - - declareExercicesRoutes(apiThemesRoutes) - - // Remote - router.GET("/remote/themes", sync.ApiListRemoteThemes) - router.GET("/remote/themes/:thid", sync.ApiGetRemoteTheme) - router.GET("/remote/themes/:thid/exercices", sync.ApiListRemoteExercices) -} - -type Theme struct { - *fic.Theme - ForgeLink string `json:"forge_link,omitempty"` -} - -func ThemeHandler(c *gin.Context) { - thid, err := strconv.ParseInt(string(c.Params.ByName("thid")), 10, 64) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid theme identifier"}) - return - } - - if thid == 0 { - c.Set("theme", &fic.StandaloneExercicesTheme) - } else { - theme, err := fic.GetTheme(thid) - if err != nil { - c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Theme not found"}) - return - } - - c.Set("theme", theme) - } - - c.Next() -} - -func fixAllURLIds(c *gin.Context) { - nbFix := 0 - if themes, err := fic.GetThemes(); err == nil { - for _, theme := range themes { - if theme.FixURLId() { - theme.Update() - nbFix += 1 - } - - if exercices, err := theme.GetExercices(); err == nil { - for _, exercice := range exercices { - if exercice.FixURLId() { - exercice.Update() - nbFix += 1 - } - } - } - } - } - - c.JSON(http.StatusOK, nbFix) -} - -func bindingFiles(c *gin.Context) { - files, err := fic.GetFiles() - if err != nil { - c.AbortWithError(http.StatusInternalServerError, err) - return - } - - ret := "" - for _, file := range files { - ret += fmt.Sprintf("%s;%s\n", file.GetOrigin(), file.Path) - } - - c.String(http.StatusOK, ret) -} - -func listThemes(c *gin.Context) { - themes, err := fic.GetThemes() - if err != nil { - log.Println("Unable to listThemes:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to list themes."}) - return - } - - if has, _ := fic.HasStandaloneExercice(); has { - themes = append([]*fic.Theme{&fic.StandaloneExercicesTheme}, themes...) - } - - c.JSON(http.StatusOK, themes) -} - -func exportThemes(c *gin.Context) { - themes, err := fic.ExportThemes() - if err != nil { - log.Println("Unable to exportthemes:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to export themes."}) - return - } - - c.JSON(http.StatusOK, themes) -} - -func showTheme(c *gin.Context) { - theme := c.MustGet("theme").(*fic.Theme) - - var forgelink string - if fli, ok := sync.GlobalImporter.(sync.ForgeLinkedImporter); ok { - if u, _ := fli.GetThemeLink(theme); u != nil { - forgelink = u.String() - } - } - - c.JSON(http.StatusOK, Theme{theme, forgelink}) -} - -func createTheme(c *gin.Context) { - var ut fic.Theme - err := c.ShouldBindJSON(&ut) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - if len(ut.Name) == 0 { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Theme's name not filled"}) - return - } - - th, err := fic.CreateTheme(&ut) - if err != nil { - log.Println("Unable to createTheme:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during theme creation."}) - return - } - - c.JSON(http.StatusOK, th) -} - -func updateTheme(c *gin.Context) { - theme := c.MustGet("theme").(*fic.Theme) - - var ut fic.Theme - err := c.ShouldBindJSON(&ut) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - ut.Id = theme.Id - - if len(ut.Name) == 0 { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Theme's name not filled"}) - return - } - - if _, err := ut.Update(); err != nil { - log.Println("Unable to updateTheme:", err.Error()) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "An error occurs during theme update."}) - return - } - - if theme.Locked != ut.Locked { - exercices, err := theme.GetExercices() - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - for _, exercice := range exercices { - if exercice.Disabled != ut.Locked { - exercice.Disabled = ut.Locked - _, err = exercice.Update() - if err != nil { - log.Println("Unable to enable/disable exercice: ", exercice.Id, err.Error()) - } - } - } - } - - c.JSON(http.StatusOK, ut) -} - -func deleteTheme(c *gin.Context) { - theme := c.MustGet("theme").(*fic.Theme) - - _, err := theme.Delete() - if err != nil { - log.Println("Unable to deleteTheme:", err.Error()) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "An error occurs during theme deletion."}) - return - } - - c.JSON(http.StatusOK, true) -} - -func getThemedExercicesStats(c *gin.Context) { - theme := c.MustGet("theme").(*fic.Theme) - - exercices, err := theme.GetExercices() - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to fetch exercices: %s", err.Error())}) - return - } - - ret := []exerciceStats{} - for _, e := range exercices { - ret = append(ret, exerciceStats{ - IdExercice: e.Id, - TeamTries: e.TriedTeamCount(), - TotalTries: e.TriedCount(), - SolvedCount: e.SolvedCount(), - FlagSolved: e.FlagSolved(), - MCQSolved: e.MCQSolved(), - }) - } - c.JSON(http.StatusOK, ret) -} - -func diffThemeWithRemote(theme *fic.Theme) ([]syncDiff, error) { - var diffs []syncDiff - - // Compare theme attributes - theme_remote, err := sync.GetRemoteTheme(theme.Path) - if err != nil { - return nil, err - } - - for _, field := range reflect.VisibleFields(reflect.TypeOf(*theme)) { - if ((field.Name == "Image") && path.Base(reflect.ValueOf(*theme_remote).FieldByName(field.Name).String()) != path.Base(reflect.ValueOf(*theme).FieldByName(field.Name).String())) || (field.Name != "Image" && !reflect.ValueOf(*theme_remote).FieldByName(field.Name).Equal(reflect.ValueOf(*theme).FieldByName(field.Name))) { - if !field.IsExported() || field.Name == "Id" || field.Name == "IdTheme" || field.Name == "IssueKind" || field.Name == "BackgroundColor" { - continue - } - - diffs = append(diffs, syncDiff{ - Field: field.Name, - Link: fmt.Sprintf("themes/%d", theme.Id), - Before: reflect.ValueOf(*theme).FieldByName(field.Name).Interface(), - After: reflect.ValueOf(*theme_remote).FieldByName(field.Name).Interface(), - }) - } - } - - // Compare exercices list - exercices, err := theme.GetExercices() - if err != nil { - return nil, fmt.Errorf("Unable to GetExercices: %w", err) - } - - exercices_remote, err := sync.ListRemoteExercices(theme.Path) - if err != nil { - return nil, fmt.Errorf("Unable to ListRemoteExercices: %w", err) - } - - var not_found []string - var extra_found []string - - for _, exercice_remote := range exercices_remote { - found := false - for _, exercice := range exercices { - if exercice.Path[strings.Index(exercice.Path, "/")+1:] == exercice_remote { - found = true - break - } - } - - if !found { - not_found = append(not_found, exercice_remote) - } - } - - for _, exercice := range exercices { - found := false - for _, exercice_remote := range exercices_remote { - if exercice.Path[strings.Index(exercice.Path, "/")+1:] == exercice_remote { - found = true - break - } - } - - if !found { - extra_found = append(extra_found, exercice.Path[strings.Index(exercice.Path, "/")+1:]) - } - } - - if len(not_found) > 0 || len(extra_found) > 0 { - diffs = append(diffs, syncDiff{ - Field: "theme.Exercices", - Link: fmt.Sprintf("themes/%d", theme.Id), - Before: strings.Join(extra_found, ", "), - After: strings.Join(not_found, ", "), - }) - } - - // Compare inner exercices - for i, exercice := range exercices { - exdiffs, err := diffExerciceWithRemote(exercice, theme) - if err != nil { - return nil, fmt.Errorf("Unable to diffExerciceWithRemote: %w", err) - } - - for _, exdiff := range exdiffs { - if theme.Id == 0 { - exdiff.Field = fmt.Sprintf("exercices[%d].%s", exercice.Id, exdiff.Field) - } else { - exdiff.Field = fmt.Sprintf("exercices[%d].%s", i, exdiff.Field) - } - diffs = append(diffs, exdiff) - } - } - - return diffs, err -} - -func APIDiffThemeWithRemote(c *gin.Context) { - theme := c.MustGet("theme").(*fic.Theme) - - diffs, err := diffThemeWithRemote(theme) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, diffs) -} diff --git a/admin/api/version.go b/admin/api/version.go deleted file mode 100644 index 52cb0726..00000000 --- a/admin/api/version.go +++ /dev/null @@ -1,15 +0,0 @@ -package api - -import ( - "net/http" - - "github.com/gin-gonic/gin" -) - -func DeclareVersionRoutes(router *gin.RouterGroup) { - router.GET("/version", showVersion) -} - -func showVersion(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{"version": 1.0}) -} diff --git a/admin/app.go b/admin/app.go deleted file mode 100644 index 1ba1910e..00000000 --- a/admin/app.go +++ /dev/null @@ -1,81 +0,0 @@ -package main - -import ( - "context" - "log" - "net/http" - "path/filepath" - "strings" - "time" - - "github.com/gin-gonic/gin" - - "srs.epita.fr/fic-server/admin/api" - "srs.epita.fr/fic-server/settings" -) - -type App struct { - router *gin.Engine - srv *http.Server - cfg *settings.Settings - bind string -} - -func NewApp(cfg *settings.Settings, baseURL string, bind string) App { - if !cfg.WorkInProgress { - gin.SetMode(gin.ReleaseMode) - } - gin.ForceConsoleColor() - router := gin.Default() - - api.DeclareRoutes(router.Group("")) - - var baserouter *gin.RouterGroup - if len(baseURL) > 0 { - router.GET("/", func(c *gin.Context) { - c.Redirect(http.StatusFound, baseURL) - }) - router.GET(filepath.Dir(baseURL)+"/files/*_", func(c *gin.Context) { - path := c.Request.URL.Path - c.Redirect(http.StatusFound, filepath.Join(baseURL, strings.TrimPrefix(path, filepath.Dir(baseURL)))) - }) - - baserouter = router.Group(baseURL) - - api.DeclareRoutes(baserouter) - declareStaticRoutes(baserouter, cfg, baseURL) - } else { - declareStaticRoutes(router.Group(""), cfg, "") - } - - app := App{ - router: router, - bind: bind, - } - - return app -} - -func (app *App) Start() { - app.srv = &http.Server{ - Addr: app.bind, - Handler: app.router, - ReadHeaderTimeout: 15 * time.Second, - ReadTimeout: 15 * time.Second, - WriteTimeout: 10 * time.Second, - IdleTimeout: 30 * time.Second, - } - - log.Printf("Ready, listening on %s\n", app.bind) - if err := app.srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { - log.Fatalf("listen: %s\n", err) - } -} - -func (app *App) Stop() { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - if err := app.srv.Shutdown(ctx); err != nil { - log.Fatal("Server Shutdown:", err) - } -} diff --git a/admin/fill_teams.sh b/admin/fill_teams.sh deleted file mode 100755 index 6a1ea7a5..00000000 --- a/admin/fill_teams.sh +++ /dev/null @@ -1,167 +0,0 @@ -#!/bin/bash - -BASEURL="http://127.0.0.1:8081/admin" -GEN_CERTS=0 -GEN_PASSWD=0 -EXTRA_TEAMS=0 -CSV_SPLITER="," -CSV_COL_LASTNAME=2 -CSV_COL_FIRSTNAME=3 -CSV_COL_NICKNAME=5 -CSV_COL_COMPANY=6 -CSV_COL_TEAM=1 - -usage() { - echo "$0 [options] csv_file" - echo " -B -baseurl BASEURL URL to administration endpoint (default: $BASEURL)" - echo " -S -csv-spliter SEP CSV separator (default: $CSV_SPLITER)" - echo " -e -extra-teams NBS Number of extra teams to generate (default: ${EXTRA_TEAMS})" - echo " -c -generate-certificate Should team certificates be generated? (default: no)" - echo " -p -generate-password Should generate team password to teams.pass? (default: no)" -} - -# Parse options -while [ "${1:0:1}" = "-" ] -do - case "$1" in - -B|-baseurl) - BASEURL=$2 - shift;; - -S|-csv-spliter) - CSV_SPLITER=$2 - shift;; - -e|-extra-teams) - EXTRA_TEAMS=$2 - shift;; - -c|-generate-certificates) - GEN_CERTS=1;; - -p|-generate-password) - GEN_PASSWD=1;; - *) - echo "Unknown option '$1'" - usage - exit 1;; - esac - shift -done - -[ "$#" -lt 1 ] && [ "${EXTRA_TEAMS}" -eq 0 ] && { usage; exit 1; } - -new_team() { - head -n "$1" team-names.txt | tail -1 | sed -E 's/^.*\|\[\[([^|]+\|)?([^|]+)\]\][^|]*\|([A-Fa-f0-9]{1,2})\|([A-Fa-f0-9]{1,2})\|([A-Fa-f0-9]{1,2})\|([0-9]{1,3})\|([0-9]{1,3})\|([0-9]{1,3})\|.*$/\6 \7 \8 \2/' | - while read line; - do - R=`echo $line | cut -d " " -f 1` - G=`echo $line | cut -d " " -f 2` - B=`echo $line | cut -d " " -f 3` - if [ -z "$2" ]; then - N=`echo $line | cut -d " " -f 4` - else - N=`echo -n $2 | tr -d '\r\n'` - fi - - COLOR=$((($R*256 + $G) * 256 + $B)) - - curl -s -d "{\"name\": \"$N\",\"color\": $COLOR}" "${BASEURL}/api/teams" - done | grep -Eo '"id":[0-9]+,' | grep -Eo "[0-9]+" -} - -TNUM=0 - -for i in $(seq $EXTRA_TEAMS) -do - TNUM=$(($TNUM + 1)) - - echo "Doing extra team $TNUM..." - - TID=`new_team $TNUM` - - if [ "${GEN_CERTS}" -eq 1 ] && ! curl -s -f "${BASEURL}/api/teams/${TID}/certificate" > /dev/null - then - curl -s -f "${BASEURL}/api/teams/${TID}/certificate/generate" - elif [ "${GEN_PASSWD}" -eq 1 ] - then - TEAMID=$(curl -s -f "${BASEURL}/api/teams/${TID}/" | jq -r .name) - PASSWD=$(curl -X POST -s -f "${BASEURL}/api/teams/${TID}/password" | jq -r .password) - NP=$(echo "${TEAMID}" | cut -d : -f 1 | sed 's/[[:upper:]]/\l&/g;s/[âáàä]/a/g;s/[êéèë]/e/g') - cat >> teams.pass <> htpasswd.ssha <> htpasswd.apr1 < /dev/null - then - curl -s -f "${BASEURL}/api/teams/${TID}/certificate/generate" - elif [ "${GEN_PASSWD}" -eq 1 ] - then - PASSWD=$(curl -X POST -s -f "${BASEURL}/api/teams/${TID}/password" | jq -r .password) - NP=$(echo "${TEAMID}" | cut -d : -f 1 | sed 's/[[:upper:]]/\l&/g;s/[âáàä]/a/g;s/[êéèë]/e/g') - cat >> teams.pass <> htpasswd.ssha <> htpasswd.apr1 < - - - - {{ .title }} - Administration - - - - - - - - - -
-
-
- -
- -
- -
- - - - - - - - - - - - -` diff --git a/admin/main.go b/admin/main.go deleted file mode 100644 index 06470166..00000000 --- a/admin/main.go +++ /dev/null @@ -1,333 +0,0 @@ -package main - -import ( - "flag" - "io/fs" - "io/ioutil" - "log" - "net/http" - "os" - "os/signal" - "path" - "path/filepath" - "strconv" - "strings" - "syscall" - - "srs.epita.fr/fic-server/admin/api" - "srs.epita.fr/fic-server/admin/generation" - "srs.epita.fr/fic-server/admin/pki" - "srs.epita.fr/fic-server/admin/sync" - "srs.epita.fr/fic-server/libfic" - "srs.epita.fr/fic-server/settings" -) - -func main() { - var err error - bind := "127.0.0.1:8081" - cloudDAVBase := "" - cloudUsername := "fic" - cloudPassword := "" - localImporterDirectory := "" - gitImporterRemote := "" - gitImporterBranch := "" - localImporterSymlink := false - baseURL := "/" - checkplugins := sync.CheckPluginList{} - - // Read paremeters from environment - if v, exists := os.LookupEnv("FICOIDC_ISSUER"); exists { - api.OidcIssuer = v - } else if v, exists := os.LookupEnv("FICOIDC_ISSUER_FILE"); exists { - fd, err := os.Open(v) - if err != nil { - log.Fatal("Unable to open FICOIDC_ISSUER_FILE:", err) - } - - b, _ := ioutil.ReadAll(fd) - api.OidcIssuer = strings.TrimSpace(string(b)) - - fd.Close() - } - if v, exists := os.LookupEnv("FICOIDC_SECRET"); exists { - api.OidcSecret = v - } else if v, exists := os.LookupEnv("FICOIDC_SECRET_FILE"); exists { - fd, err := os.Open(v) - if err != nil { - log.Fatal("Unable to open FICOIDC_SECRET_FILE:", err) - } - - b, _ := ioutil.ReadAll(fd) - api.OidcSecret = strings.TrimSpace(string(b)) - - fd.Close() - } - if v, exists := os.LookupEnv("FICCA_PASS"); exists { - pki.SetCAPassword(v) - } else if v, exists := os.LookupEnv("FICCA_PASS_FILE"); exists { - fd, err := os.Open(v) - if err != nil { - log.Fatal("Unable to open FICCA_PASS_FILE:", err) - } - - b, _ := ioutil.ReadAll(fd) - pki.SetCAPassword(strings.TrimSpace(string(b))) - - fd.Close() - } else { - log.Println("WARNING: no password defined for the CA, will use empty password to secure CA private key") - log.Println("WARNING: PLEASE DEFINE ENVIRONMENT VARIABLE: FICCA_PASS") - } - if v, exists := os.LookupEnv("FICCLOUD_URL"); exists { - cloudDAVBase = v - } - if v, exists := os.LookupEnv("FICCLOUD_USER"); exists { - cloudUsername = v - } - if v, exists := os.LookupEnv("FICCLOUD_PASS"); exists { - cloudPassword = v - } else if v, exists := os.LookupEnv("FICCLOUD_PASS_FILE"); exists { - fd, err := os.Open(v) - if err != nil { - log.Fatal("Unable to open FICCLOUD_PASS_FILE:", err) - } - - b, _ := ioutil.ReadAll(fd) - cloudPassword = strings.TrimSpace(string(b)) - - fd.Close() - } - if v, exists := os.LookupEnv("FIC_BASEURL"); exists { - baseURL = v - } - if v, exists := os.LookupEnv("FIC_4REAL"); exists { - api.IsProductionEnv, err = strconv.ParseBool(v) - if err != nil { - log.Fatal("Unable to parse FIC_4REAL variable:", err) - } - } - if v, exists := os.LookupEnv("FIC_ADMIN_BIND"); exists { - bind = v - } - if v, exists := os.LookupEnv("FIC_TIMESTAMPCHECK"); exists { - api.TimestampCheck = v - } - if v, exists := os.LookupEnv("FIC_SETTINGS"); exists { - settings.SettingsDir = v - } - if v, exists := os.LookupEnv("FIC_FILES"); exists { - fic.FilesDir = v - } - if v, exists := os.LookupEnv("FIC_SYNC_LOCALIMPORT"); exists { - localImporterDirectory = v - } - if v, exists := os.LookupEnv("FIC_SYNC_LOCALIMPORTSYMLINK"); exists { - localImporterSymlink, err = strconv.ParseBool(v) - if err != nil { - log.Fatal("Unable to parse FIC_SYNC_LOCALIMPORTSYMLINK variable:", err) - } - } - if v, exists := os.LookupEnv("FIC_SYNC_GIT_IMPORT_REMOTE"); exists { - gitImporterRemote = v - } - if v, exists := os.LookupEnv("FIC_SYNC_GIT_BRANCH"); exists { - gitImporterBranch = v - } - if v, exists := os.LookupEnv("FIC_OPTIONALDIGEST"); exists { - fic.OptionalDigest, err = strconv.ParseBool(v) - if err != nil { - log.Fatal("Unable to parse FIC_OPTIONALDIGEST variable:", err) - } - } - if v, exists := os.LookupEnv("FIC_STRONGDIGEST"); exists { - fic.StrongDigest, err = strconv.ParseBool(v) - if err != nil { - log.Fatal("Unable to parse FIC_STRONGDIGEST variable:", err) - } - } - - // Read parameters from command line - flag.StringVar(&bind, "bind", bind, "Bind port/socket") - var dsn = flag.String("dsn", fic.DSNGenerator(), "DSN to connect to the MySQL server") - flag.StringVar(&baseURL, "baseurl", baseURL, "URL prepended to each URL") - flag.StringVar(&api.TimestampCheck, "timestampCheck", api.TimestampCheck, "Path regularly touched by frontend to check time synchronisation") - flag.StringVar(&pki.PKIDir, "pki", "./PKI", "Base directory where found PKI scripts") - var staticDir = flag.String("static", "", "Directory containing static files (default if not provided: use embedded files)") - flag.StringVar(&api.TeamsDir, "teams", "./TEAMS", "Base directory where save teams JSON files") - flag.StringVar(&api.DashboardDir, "dashbord", "./DASHBOARD", "Base directory where save public JSON files") - flag.StringVar(&settings.SettingsDir, "settings", settings.SettingsDir, "Base directory where load and save settings") - flag.StringVar(&fic.FilesDir, "files", fic.FilesDir, "Base directory where found challenges files, local part") - flag.StringVar(&generation.GeneratorSocket, "generator", "./GENERATOR/generator.socket", "Path to the generator socket (used to trigger issues.json generations, use an empty string to generate locally)") - flag.StringVar(&localImporterDirectory, "localimport", localImporterDirectory, - "Base directory where found challenges files to import, local part") - flag.BoolVar(&localImporterSymlink, "localimportsymlink", localImporterSymlink, - "Copy files or just create symlink?") - flag.StringVar(&gitImporterRemote, "git-import-remote", gitImporterRemote, - "Remote URL of the git repository to use as synchronization source") - flag.StringVar(&gitImporterBranch, "git-branch", gitImporterBranch, - "Branch to use in the git repository") - flag.StringVar(&cloudDAVBase, "clouddav", cloudDAVBase, - "Base directory where found challenges files to import, cloud part") - flag.StringVar(&cloudUsername, "clouduser", cloudUsername, "Username used to sync") - flag.StringVar(&cloudPassword, "cloudpass", cloudPassword, "Password used to sync") - flag.BoolVar(&fic.OptionalDigest, "optionaldigest", fic.OptionalDigest, "Is the digest required when importing files?") - flag.BoolVar(&fic.StrongDigest, "strongdigest", fic.StrongDigest, "Are BLAKE2b digests required or is SHA-1 good enough?") - flag.BoolVar(&api.IsProductionEnv, "4real", api.IsProductionEnv, "Set this flag when running for a real challenge (it disallows or avoid most of mass user progression deletion)") - flag.Var(&checkplugins, "rules-plugins", "List of libraries containing others rules to checks") - flag.Var(&sync.RemoteFileDomainWhitelist, "remote-file-domain-whitelist", "List of domains which are allowed to store remote files") - flag.Parse() - - log.SetPrefix("[admin] ") - - // Instantiate importer - if localImporterDirectory != "" && cloudDAVBase != "" { - log.Fatal("Cannot have both --clouddav and --localimport defined.") - return - } else if gitImporterRemote != "" && cloudDAVBase != "" { - log.Fatal("Cannot have both --clouddav and --git-import-remote defined.") - return - } else if gitImporterRemote != "" { - sync.GlobalImporter = sync.NewGitImporter(sync.LocalImporter{Base: localImporterDirectory, Symlink: localImporterSymlink}, gitImporterRemote, gitImporterBranch) - } else if localImporterDirectory != "" { - sync.GlobalImporter = sync.LocalImporter{Base: localImporterDirectory, Symlink: localImporterSymlink} - } else if cloudDAVBase != "" { - sync.GlobalImporter, _ = sync.NewCloudImporter(cloudDAVBase, cloudUsername, cloudPassword) - } - if sync.GlobalImporter != nil { - if err := sync.GlobalImporter.Init(); err != nil { - log.Fatal("Unable to initialize the importer: ", err.Error()) - } - log.Println("Using", sync.GlobalImporter.Kind()) - - challengeinfo, err := sync.GetFileContent(sync.GlobalImporter, settings.ChallengeFile) - if err == nil { - // Initial distribution of challenge.json - if _, err := os.Stat(path.Join(settings.SettingsDir, settings.ChallengeFile)); os.IsNotExist(err) { - if fd, err := os.Create(path.Join(settings.SettingsDir, settings.ChallengeFile)); err != nil { - log.Fatal("Unable to open SETTINGS/challenge.json:", err) - } else { - fd.Write([]byte(challengeinfo)) - err = fd.Close() - if err != nil { - log.Fatal("Something went wrong during SETTINGS/challenge.json writing:", err) - } - } - } - - if ci, err := settings.ReadChallengeInfo(challengeinfo); err == nil { - fic.StandaloneExercicesTheme.Authors = ci.Authors - } - } - } - - // Sanitize options - log.Println("Checking paths...") - if staticDir != nil && *staticDir != "" { - if sDir, err := filepath.Abs(*staticDir); err != nil { - log.Fatal(err) - } else { - log.Println("Serving pages from", sDir) - staticFS = http.Dir(sDir) - sync.DeepReportPath = path.Join(sDir, sync.DeepReportPath) - } - } else { - sub, err := fs.Sub(assets, "static") - if err != nil { - log.Fatal("Unable to cd to static/ directory:", err) - } - log.Println("Serving pages from memory.") - staticFS = http.FS(sub) - - sync.DeepReportPath = path.Join("SYNC", sync.DeepReportPath) - if _, err := os.Stat("SYNC"); os.IsNotExist(err) { - os.MkdirAll("SYNC", 0751) - } - } - if fic.FilesDir, err = filepath.Abs(fic.FilesDir); err != nil { - log.Fatal(err) - } - if pki.PKIDir, err = filepath.Abs(pki.PKIDir); err != nil { - log.Fatal(err) - } - if api.DashboardDir, err = filepath.Abs(api.DashboardDir); err != nil { - log.Fatal(err) - } - if api.TeamsDir, err = filepath.Abs(api.TeamsDir); err != nil { - log.Fatal(err) - } - if api.TimestampCheck, err = filepath.Abs(api.TimestampCheck); err != nil { - log.Fatal(err) - } - if settings.SettingsDir, err = filepath.Abs(settings.SettingsDir); err != nil { - log.Fatal(err) - } - if baseURL != "/" { - baseURL = path.Clean(baseURL) - } else { - baseURL = "" - } - - // Creating minimal directories structure - os.MkdirAll(fic.FilesDir, 0751) - os.MkdirAll(pki.PKIDir, 0711) - os.MkdirAll(api.TeamsDir, 0751) - os.MkdirAll(api.DashboardDir, 0751) - os.MkdirAll(settings.SettingsDir, 0751) - - // Load rules plugins - for _, p := range checkplugins { - if err := sync.LoadChecksPlugin(p); err != nil { - log.Fatalf("Unable to load rule plugin %q: %s", p, err.Error()) - } else { - log.Printf("Rules plugin %q successfully loaded", p) - } - } - - // Initialize settings and load them - if !settings.ExistsSettings(path.Join(settings.SettingsDir, settings.SettingsFile)) { - if err = api.ResetSettings(); err != nil { - log.Fatal("Unable to initialize settings.json:", err) - } - } - var config *settings.Settings - if config, err = settings.ReadSettings(path.Join(settings.SettingsDir, settings.SettingsFile)); err != nil { - log.Fatal("Unable to read settings.json:", err) - } else { - api.ApplySettings(config) - } - - // Initialize dashboard presets - if err = api.InitDashboardPresets(api.DashboardDir); err != nil { - log.Println("Unable to initialize dashboards presets:", err) - } - - // Database connection - log.Println("Opening database...") - if err = fic.DBInit(*dsn); err != nil { - log.Fatal("Cannot open the database: ", err) - } - defer fic.DBClose() - - log.Println("Creating database...") - if err = fic.DBCreate(); err != nil { - log.Fatal("Cannot create database: ", err) - } - - // Update base URL on main page - log.Println("Changing base URL to", baseURL+"/", "...") - genIndex(baseURL) - - // Prepare graceful shutdown - interrupt := make(chan os.Signal, 1) - signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM) - - app := NewApp(config, baseURL, bind) - go app.Start() - - // Wait shutdown signal - <-interrupt - - log.Print("The service is shutting down...") - app.Stop() - log.Println("done") -} diff --git a/admin/pki/ca.go b/admin/pki/ca.go deleted file mode 100644 index dfbb9f1f..00000000 --- a/admin/pki/ca.go +++ /dev/null @@ -1,133 +0,0 @@ -package pki - -import ( - "crypto/ecdsa" - "crypto/rand" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "errors" - "io/ioutil" - "math/big" - "os" - "path" - "time" -) - -var passwordCA string - -func SetCAPassword(pass string) { - passwordCA = pass -} - -func CACertPath() string { - return path.Join(PKIDir, "shared", "ca.pem") -} - -func CAPrivkeyPath() string { - return path.Join(PKIDir, "ca.key") -} - -func GenerateCA(notBefore time.Time, notAfter time.Time) error { - ca := &x509.Certificate{ - SerialNumber: big.NewInt(0), - Subject: pkix.Name{ - Organization: []string{"EPITA"}, - OrganizationalUnit: []string{"SRS laboratory"}, - Country: []string{"FR"}, - Locality: []string{"Paris"}, - CommonName: "FIC CA", - }, - NotBefore: notBefore, - NotAfter: notAfter, - IsCA: true, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, - BasicConstraintsValid: true, - } - - // Ensure directories exists - os.Mkdir(PKIDir, 0751) - os.Mkdir(path.Join(PKIDir, "shared"), 0751) - - pub, priv, err := GeneratePrivKey() - if err != nil { - return err - } - - ca_b, err := x509.CreateCertificate(rand.Reader, ca, ca, pub, priv) - if err != nil { - return err - } - - // Save certificate to file - if err := saveCertificate(CACertPath(), ca_b); err != nil { - return err - } - - // Save private key to file - if err := savePrivateKeyEncrypted(CAPrivkeyPath(), priv, passwordCA); err != nil { - return err - } - - return nil -} - -func LoadCA() (priv ecdsa.PrivateKey, ca x509.Certificate, err error) { - // Load certificate - if fd, errr := os.Open(CACertPath()); errr != nil { - return priv, ca, errr - } else { - defer fd.Close() - if cert, errr := ioutil.ReadAll(fd); errr != nil { - return priv, ca, errr - } else { - block, _ := pem.Decode(cert) - if block == nil || block.Type != "CERTIFICATE" { - return priv, ca, errors.New("failed to decode PEM block containing certificate") - } - if catmp, errr := x509.ParseCertificate(block.Bytes); errr != nil { - return priv, ca, errr - } else if catmp == nil { - return priv, ca, errors.New("failed to parse certificate") - } else { - ca = *catmp - } - } - } - - // Load private key - if fd, errr := os.Open(CAPrivkeyPath()); errr != nil { - return priv, ca, errr - } else { - defer fd.Close() - if privkey, errr := ioutil.ReadAll(fd); errr != nil { - return priv, ca, errr - } else { - block, _ := pem.Decode(privkey) - if block == nil || block.Type != "EC PRIVATE KEY" { - return priv, ca, errors.New("failed to decode PEM block containing EC private key") - } - - var decrypted_der []byte - if x509.IsEncryptedPEMBlock(block) { - decrypted_der, err = x509.DecryptPEMBlock(block, []byte(passwordCA)) - if err != nil { - return - } - } else { - decrypted_der = block.Bytes - } - - if tmppriv, errr := x509.ParseECPrivateKey(decrypted_der); errr != nil { - return priv, ca, errr - } else if tmppriv == nil { - return priv, ca, errors.New("failed to parse private key") - } else { - priv = *tmppriv - } - } - } - - return -} diff --git a/admin/pki/client.go b/admin/pki/client.go deleted file mode 100644 index a64ebc4b..00000000 --- a/admin/pki/client.go +++ /dev/null @@ -1,84 +0,0 @@ -package pki - -import ( - "crypto/ecdsa" - "crypto/rand" - "crypto/x509" - "crypto/x509/pkix" - "fmt" - "math" - "math/big" - "os" - "os/exec" - "path" - "time" -) - -func ClientCertificatePath(serial uint64) string { - return path.Join(PKIDir, fmt.Sprintf("%0[2]*[1]X", serial, int(math.Ceil(math.Log2(float64(serial))/8)*2)), "cert.pem") -} - -func ClientPrivkeyPath(serial uint64) string { - return path.Join(PKIDir, fmt.Sprintf("%0[2]*[1]X", serial, int(math.Ceil(math.Log2(float64(serial))/8)*2)), "privkey.pem") -} - -func ClientP12Path(serial uint64) string { - return path.Join(PKIDir, fmt.Sprintf("%0[2]*[1]X", serial, int(math.Ceil(math.Log2(float64(serial))/8)*2)), "team.p12") -} - -func GenerateClient(serial uint64, notBefore time.Time, notAfter time.Time, parent_cert *x509.Certificate, parent_priv *ecdsa.PrivateKey) error { - var certid big.Int - certid.SetUint64(serial) - client := &x509.Certificate{ - SerialNumber: &certid, - Subject: pkix.Name{ - Organization: []string{"EPITA"}, - OrganizationalUnit: []string{"SRS laboratory"}, - Country: []string{"FR"}, - Locality: []string{"Paris"}, - CommonName: fmt.Sprintf("TEAM-%0[2]*[1]X", serial, int(math.Ceil(math.Log2(float64(serial))/8)*2)), - }, - NotBefore: notBefore, - NotAfter: notAfter, - IsCA: false, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, - KeyUsage: x509.KeyUsageDigitalSignature, - BasicConstraintsValid: true, - } - - pub, priv, err := GeneratePrivKey() - if err != nil { - return err - } - - client_b, err := x509.CreateCertificate(rand.Reader, client, parent_cert, pub, parent_priv) - if err != nil { - return err - } - - // Create intermediate directory - os.MkdirAll(path.Join(PKIDir, fmt.Sprintf("%0[2]*[1]X", serial, int(math.Ceil(math.Log2(float64(serial))/8)*2))), 0777) - - // Save certificate to file - if err := saveCertificate(ClientCertificatePath(serial), client_b); err != nil { - return err - } - - // Save private key to file - if err := savePrivateKey(ClientPrivkeyPath(serial), priv); err != nil { - return err - } - - return nil -} - -func WriteP12(serial uint64, password string) error { - cmd := exec.Command("/usr/bin/openssl", "pkcs12", "-export", - "-inkey", ClientPrivkeyPath(serial), - "-in", ClientCertificatePath(serial), - "-name", fmt.Sprintf("TEAM-%0[2]*[1]X", serial, int(math.Ceil(math.Log2(float64(serial))/8)*2)), - "-passout", "pass:" + password, - "-out", ClientP12Path(serial)) - - return cmd.Run() -} diff --git a/admin/pki/common.go b/admin/pki/common.go deleted file mode 100644 index 4310084a..00000000 --- a/admin/pki/common.go +++ /dev/null @@ -1,62 +0,0 @@ -package pki - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/x509" - "encoding/pem" - "os" -) - -var PKIDir string - -func GeneratePrivKey() (pub *ecdsa.PublicKey, priv *ecdsa.PrivateKey, err error) { - if priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader); err == nil { - pub = &priv.PublicKey - } - return -} - -func saveCertificate(path string, cert []byte) error { - if certOut, err := os.Create(path); err != nil { - return err - } else { - pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: cert}) - certOut.Close() - } - return nil -} - -func savePrivateKey(path string, private *ecdsa.PrivateKey) error { - if keyOut, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600); err != nil { - return err - } else if key_b, err := x509.MarshalECPrivateKey(private); err != nil { - return err - } else { - pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: key_b}) - keyOut.Close() - } - return nil -} - -func savePrivateKeyEncrypted(path string, private *ecdsa.PrivateKey, password string) error { - if password == "" { - return savePrivateKey(path, private) - } - - if keyOut, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600); err != nil { - return err - } else { - defer keyOut.Close() - - if key_b, err := x509.MarshalECPrivateKey(private); err != nil { - return err - } else if key_c, err := x509.EncryptPEMBlock(rand.Reader, "EC PRIVATE KEY", key_b, []byte(password), x509.PEMCipherAES256); err != nil { - return err - } else { - pem.Encode(keyOut, key_c) - } - } - return nil -} diff --git a/admin/pki/team.go b/admin/pki/team.go deleted file mode 100644 index 8ea91535..00000000 --- a/admin/pki/team.go +++ /dev/null @@ -1,79 +0,0 @@ -package pki - -import ( - "fmt" - "io/ioutil" - "math" - "os" - "path" - "strconv" - "strings" -) - -const SymlinkPrefix = "_AUTH_ID_" - -func GetCertificateAssociation(serial uint64) string { - return fmt.Sprintf(SymlinkPrefix+"%0[2]*[1]X", serial, int(math.Ceil(math.Log2(float64(serial))/8)*2)) -} - -func GetAssociation(dirname string) (assocs string, err error) { - return os.Readlink(dirname) -} - -func GetAssociations(dirname string) (assocs []string, err error) { - if ds, errr := ioutil.ReadDir(dirname); errr != nil { - return nil, errr - } else { - for _, d := range ds { - if d.Mode()&os.ModeSymlink == os.ModeSymlink { - assocs = append(assocs, d.Name()) - } - } - return - } -} - -func GetTeamSerials(dirname string, id_team int64) (serials []uint64, err error) { - // As futher comparaisons will be made with strings, convert it only one time - str_tid := fmt.Sprintf("%d", id_team) - - var assocs []string - if assocs, err = GetAssociations(dirname); err != nil { - return - } else { - for _, assoc := range assocs { - var tid string - if tid, err = os.Readlink(path.Join(dirname, assoc)); err == nil && tid == str_tid && strings.HasPrefix(assoc, SymlinkPrefix) { - if serial, err := strconv.ParseUint(assoc[9:], 16, 64); err == nil { - serials = append(serials, serial) - } - } - } - } - return -} - -func GetTeamAssociations(dirname string, id_team int64) (teamAssocs []string, err error) { - // As futher comparaisons will be made with strings, convert it only one time - str_tid := fmt.Sprintf("%d", id_team) - - var assocs []string - if assocs, err = GetAssociations(dirname); err != nil { - return - } else { - for _, assoc := range assocs { - var tid string - if tid, err = os.Readlink(path.Join(dirname, assoc)); err == nil && tid == str_tid && !strings.HasPrefix(assoc, SymlinkPrefix) { - teamAssocs = append(teamAssocs, assoc) - } - } - } - return -} - -func DeleteTeamAssociation(dirname string, assoc string) error { - if err := os.Remove(path.Join(dirname, assoc)); err != nil { - return err - } - return nil -} diff --git a/admin/static.go b/admin/static.go deleted file mode 100644 index 23ad6da6..00000000 --- a/admin/static.go +++ /dev/null @@ -1,160 +0,0 @@ -package main - -import ( - "bytes" - "embed" - "errors" - "log" - "net/http" - "os" - "path" - "strings" - "text/template" - - "srs.epita.fr/fic-server/admin/api" - "srs.epita.fr/fic-server/admin/sync" - "srs.epita.fr/fic-server/libfic" - "srs.epita.fr/fic-server/settings" - - "github.com/gin-gonic/gin" -) - -//go:embed static - -var assets embed.FS - -var indexPage []byte - -func genIndex(baseURL string) { - tplcfg := map[string]string{ - "logo": "img/logo.png", - "title": "Challenge", - "urlbase": path.Clean(path.Join(baseURL+"/", "nuke"))[:len(path.Clean(path.Join(baseURL+"/", "nuke")))-4], - } - - ci, err := api.GetChallengeInfo() - if err == nil && ci != nil { - tplcfg["title"] = ci.Title - if len(ci.MainLogo) > 0 { - tplcfg["logo"] = "/files/logo/" + path.Base(ci.MainLogo[0]) - } - } - - b := bytes.NewBufferString("") - if indexTmpl, err := template.New("index").Parse(indextpl); err != nil { - log.Fatal("Cannot create template:", err) - } else if err = indexTmpl.Execute(b, tplcfg); err != nil { - log.Fatal("An error occurs during template execution:", err) - } else { - indexPage = b.Bytes() - } -} - -func serveIndex(c *gin.Context) { - c.Writer.Write(indexPage) -} - -var staticFS http.FileSystem - -func serveFile(c *gin.Context, url string) { - c.Request.URL.Path = url - http.FileServer(staticFS).ServeHTTP(c.Writer, c.Request) -} - -func declareStaticRoutes(router *gin.RouterGroup, cfg *settings.Settings, baseURL string) { - router.GET("/", func(c *gin.Context) { - serveIndex(c) - }) - router.GET("/auth/*_", func(c *gin.Context) { - serveIndex(c) - }) - router.GET("/claims/*_", func(c *gin.Context) { - serveIndex(c) - }) - router.GET("/exercices/*_", func(c *gin.Context) { - serveIndex(c) - }) - router.GET("/events/*_", func(c *gin.Context) { - serveIndex(c) - }) - router.GET("/files", func(c *gin.Context) { - serveIndex(c) - }) - router.GET("/forge-links", func(c *gin.Context) { - serveIndex(c) - }) - router.GET("/public/*_", func(c *gin.Context) { - serveIndex(c) - }) - router.GET("/pki/*_", func(c *gin.Context) { - serveIndex(c) - }) - router.GET("/repositories", func(c *gin.Context) { - serveIndex(c) - }) - router.GET("/settings", func(c *gin.Context) { - serveIndex(c) - }) - router.GET("/sync", func(c *gin.Context) { - serveIndex(c) - }) - router.GET("/tags/*_", func(c *gin.Context) { - serveIndex(c) - }) - router.GET("/teams/*_", func(c *gin.Context) { - serveIndex(c) - }) - router.GET("/themes/*_", func(c *gin.Context) { - serveIndex(c) - }) - - router.GET("/css/*_", func(c *gin.Context) { - serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL)) - }) - router.GET("/fonts/*_", func(c *gin.Context) { - serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL)) - }) - router.GET("/img/*_", func(c *gin.Context) { - serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL)) - }) - router.GET("/js/*_", func(c *gin.Context) { - serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL)) - }) - router.GET("/views/*_", func(c *gin.Context) { - serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL)) - }) - - router.GET("/files/*_", func(c *gin.Context) { - filepath := path.Join(fic.FilesDir, strings.TrimPrefix(strings.TrimPrefix(c.Request.URL.Path, baseURL), "/files")) - - if st, err := os.Stat(filepath); os.IsNotExist(err) || st.Size() == 0 { - if st, err := os.Stat(filepath + ".gz"); err == nil { - if fd, err := os.Open(filepath + ".gz"); err == nil { - c.DataFromReader(http.StatusOK, st.Size(), "application/octet-stream", fd, map[string]string{ - "Content-Encoding": "gzip", - }) - return - } - } - } - - c.File(filepath) - }) - router.GET("/submissions/*_", func(c *gin.Context) { - http.ServeFile(c.Writer, c.Request, path.Join(api.TimestampCheck, strings.TrimPrefix(c.Request.URL.Path, path.Join(baseURL, "submissions")))) - }) - router.GET("/vids/*_", func(c *gin.Context) { - if importer, ok := sync.GlobalImporter.(sync.DirectAccessImporter); ok { - http.ServeFile(c.Writer, c.Request, importer.GetLocalPath(strings.TrimPrefix(c.Request.URL.Path, path.Join(baseURL, "vids")))) - } else { - c.AbortWithError(http.StatusBadRequest, errors.New("Only available with local importer.")) - } - }) - - router.GET("/check_import.html", func(c *gin.Context) { - serveFile(c, "check_import.html") - }) - router.GET("/full_import_report.json", func(c *gin.Context) { - http.ServeFile(c.Writer, c.Request, sync.DeepReportPath) - }) -} diff --git a/admin/static/check_import.html b/admin/static/check_import.html deleted file mode 100644 index da2176e6..00000000 --- a/admin/static/check_import.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - Rapport d'import FIC - - - - - - -

Rapport d'import FIC

-

- Date du dernier import : -

-
- - - diff --git a/admin/static/css/bootstrap.min.css b/admin/static/css/bootstrap.min.css deleted file mode 100644 index 83a71b1f..00000000 --- a/admin/static/css/bootstrap.min.css +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v4.6.2 (https://getbootstrap.com/) - * Copyright 2011-2022 The Bootstrap Authors - * Copyright 2011-2022 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([class]){color:inherit;text-decoration:none}a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit;text-align:-webkit-match-parent}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:.875em;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-sm-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-md-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-lg-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-xl-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{-webkit-appearance:none;-moz-appearance:none;appearance:none}select.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#28a745}.valid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.form-row>.col>.valid-tooltip,.form-row>[class*=col-]>.valid-tooltip{left:5px}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem)!important;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated select.form-control:valid,select.form-control.is-valid{padding-right:3rem!important;background-position:right 1.5rem center}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem)!important;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right .75rem center/8px 10px no-repeat,#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem) no-repeat}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.form-row>.col>.invalid-tooltip,.form-row>[class*=col-]>.invalid-tooltip{left:5px}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem)!important;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated select.form-control:invalid,select.form-control.is-invalid{padding-right:3rem!important;background-position:right 1.5rem center}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem)!important;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right .75rem center/8px 10px no-repeat,#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem) no-repeat}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#0069d9;border-color:#0062cc;box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{color:#fff;background-color:#5a6268;border-color:#545b62;box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#218838;border-color:#1e7e34;box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#138496;border-color:#117a8b;box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{color:#212529;background-color:#e0a800;border-color:#d39e00;box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c82333;border-color:#bd2130;box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{color:#212529;background-color:#e2e6ea;border-color:#dae0e5;box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{color:#fff;background-color:#23272b;border-color:#1d2124;box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.width{width:0;height:auto;transition:width .35s ease}@media (prefers-reduced-motion:reduce){.collapsing.width{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#e9ecef}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#adb5bd;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group:not(.has-validation)>.custom-file:not(:last-child) .custom-file-label,.input-group:not(.has-validation)>.custom-file:not(:last-child) .custom-file-label::after,.input-group:not(.has-validation)>.custom-select:not(:last-child),.input-group:not(.has-validation)>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.custom-file:nth-last-child(n+3) .custom-file-label,.input-group.has-validation>.custom-file:nth-last-child(n+3) .custom-file-label::after,.input-group.has-validation>.custom-select:nth-last-child(n+3),.input-group.has-validation>.form-control:nth-last-child(n+3){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group.has-validation>.input-group-append:nth-last-child(n+3)>.btn,.input-group.has-validation>.input-group-append:nth-last-child(n+3)>.input-group-text,.input-group:not(.has-validation)>.input-group-append:not(:last-child)>.btn,.input-group:not(.has-validation)>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;z-index:1;display:block;min-height:1.5rem;padding-left:1.5rem;-webkit-print-color-adjust:exact;color-adjust:exact;print-color-adjust:exact}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before,.custom-control-input[disabled]~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:1px solid #adb5bd}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:50%/50% 50% no-repeat}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right .75rem center/8px 10px no-repeat;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;overflow:hidden;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;overflow:hidden;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-ms-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-link{margin-bottom:-1px;background-color:transparent;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{isolation:isolate;border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{background:0 0;border:0;border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item,.nav-fill>.nav-link{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:50%/100% 100% no-repeat}.navbar-nav-scroll{max-height:75vh;overflow-y:auto}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{-ms-flex-negative:0;flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-ms-flex:1 0 0%;flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion{overflow-anchor:none}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;z-index:2;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;line-height:0;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:1s linear infinite progress-bar-stripes;animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0}a.close.disabled{pointer-events:none}.toast{-ms-flex-preferred-size:350px;flex-basis:350px;max-width:350px;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);height:-webkit-min-content;height:-moz-min-content;height:min-content;content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem);height:-webkit-min-content;height:-moz-min-content;height:min-content}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:50%/100% 100% no-repeat}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;border:.25em solid currentcolor;border-right-color:transparent;border-radius:50%;-webkit-animation:.75s linear infinite spinner-border;animation:.75s linear infinite spinner-border}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;background-color:currentcolor;border-radius:50%;opacity:0;-webkit-animation:.75s linear infinite spinner-grow;animation:.75s linear infinite spinner-grow}.spinner-grow-sm{width:1rem;height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{-webkit-animation-duration:1.5s;animation-duration:1.5s}}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;-ms-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;word-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} -/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/admin/static/css/glyphicon.css b/admin/static/css/glyphicon.css deleted file mode 100644 index 1bf9e4de..00000000 --- a/admin/static/css/glyphicon.css +++ /dev/null @@ -1,805 +0,0 @@ -@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'); -} -.glyphicon { - position: relative; - top: 1px; - display: inline-block; - font-family: 'Glyphicons Halflings'; - font-style: normal; - font-weight: normal; - line-height: 1; - - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -.glyphicon-asterisk:before { - content: "\002a"; -} -.glyphicon-plus:before { - content: "\002b"; -} -.glyphicon-euro:before, -.glyphicon-eur:before { - content: "\20ac"; -} -.glyphicon-minus:before { - content: "\2212"; -} -.glyphicon-cloud:before { - content: "\2601"; -} -.glyphicon-envelope:before { - content: "\2709"; -} -.glyphicon-pencil:before { - content: "\270f"; -} -.glyphicon-glass:before { - content: "\e001"; -} -.glyphicon-music:before { - content: "\e002"; -} -.glyphicon-search:before { - content: "\e003"; -} -.glyphicon-heart:before { - content: "\e005"; -} -.glyphicon-star:before { - content: "\e006"; -} -.glyphicon-star-empty:before { - content: "\e007"; -} -.glyphicon-user:before { - content: "\e008"; -} -.glyphicon-film:before { - content: "\e009"; -} -.glyphicon-th-large:before { - content: "\e010"; -} -.glyphicon-th:before { - content: "\e011"; -} -.glyphicon-th-list:before { - content: "\e012"; -} -.glyphicon-ok:before { - content: "\e013"; -} -.glyphicon-remove:before { - content: "\e014"; -} -.glyphicon-zoom-in:before { - content: "\e015"; -} -.glyphicon-zoom-out:before { - content: "\e016"; -} -.glyphicon-off:before { - content: "\e017"; -} -.glyphicon-signal:before { - content: "\e018"; -} -.glyphicon-cog:before { - content: "\e019"; -} -.glyphicon-trash:before { - content: "\e020"; -} -.glyphicon-home:before { - content: "\e021"; -} -.glyphicon-file:before { - content: "\e022"; -} -.glyphicon-time:before { - content: "\e023"; -} -.glyphicon-road:before { - content: "\e024"; -} -.glyphicon-download-alt:before { - content: "\e025"; -} -.glyphicon-download:before { - content: "\e026"; -} -.glyphicon-upload:before { - content: "\e027"; -} -.glyphicon-inbox:before { - content: "\e028"; -} -.glyphicon-play-circle:before { - content: "\e029"; -} -.glyphicon-repeat:before { - content: "\e030"; -} -.glyphicon-refresh:before { - content: "\e031"; -} -.glyphicon-list-alt:before { - content: "\e032"; -} -.glyphicon-lock:before { - content: "\e033"; -} -.glyphicon-flag:before { - content: "\e034"; -} -.glyphicon-headphones:before { - content: "\e035"; -} -.glyphicon-volume-off:before { - content: "\e036"; -} -.glyphicon-volume-down:before { - content: "\e037"; -} -.glyphicon-volume-up:before { - content: "\e038"; -} -.glyphicon-qrcode:before { - content: "\e039"; -} -.glyphicon-barcode:before { - content: "\e040"; -} -.glyphicon-tag:before { - content: "\e041"; -} -.glyphicon-tags:before { - content: "\e042"; -} -.glyphicon-book:before { - content: "\e043"; -} -.glyphicon-bookmark:before { - content: "\e044"; -} -.glyphicon-print:before { - content: "\e045"; -} -.glyphicon-camera:before { - content: "\e046"; -} -.glyphicon-font:before { - content: "\e047"; -} -.glyphicon-bold:before { - content: "\e048"; -} -.glyphicon-italic:before { - content: "\e049"; -} -.glyphicon-text-height:before { - content: "\e050"; -} -.glyphicon-text-width:before { - content: "\e051"; -} -.glyphicon-align-left:before { - content: "\e052"; -} -.glyphicon-align-center:before { - content: "\e053"; -} -.glyphicon-align-right:before { - content: "\e054"; -} -.glyphicon-align-justify:before { - content: "\e055"; -} -.glyphicon-list:before { - content: "\e056"; -} -.glyphicon-indent-left:before { - content: "\e057"; -} -.glyphicon-indent-right:before { - content: "\e058"; -} -.glyphicon-facetime-video:before { - content: "\e059"; -} -.glyphicon-picture:before { - content: "\e060"; -} -.glyphicon-map-marker:before { - content: "\e062"; -} -.glyphicon-adjust:before { - content: "\e063"; -} -.glyphicon-tint:before { - content: "\e064"; -} -.glyphicon-edit:before { - content: "\e065"; -} -.glyphicon-share:before { - content: "\e066"; -} -.glyphicon-check:before { - content: "\e067"; -} -.glyphicon-move:before { - content: "\e068"; -} -.glyphicon-step-backward:before { - content: "\e069"; -} -.glyphicon-fast-backward:before { - content: "\e070"; -} -.glyphicon-backward:before { - content: "\e071"; -} -.glyphicon-play:before { - content: "\e072"; -} -.glyphicon-pause:before { - content: "\e073"; -} -.glyphicon-stop:before { - content: "\e074"; -} -.glyphicon-forward:before { - content: "\e075"; -} -.glyphicon-fast-forward:before { - content: "\e076"; -} -.glyphicon-step-forward:before { - content: "\e077"; -} -.glyphicon-eject:before { - content: "\e078"; -} -.glyphicon-chevron-left:before { - content: "\e079"; -} -.glyphicon-chevron-right:before { - content: "\e080"; -} -.glyphicon-plus-sign:before { - content: "\e081"; -} -.glyphicon-minus-sign:before { - content: "\e082"; -} -.glyphicon-remove-sign:before { - content: "\e083"; -} -.glyphicon-ok-sign:before { - content: "\e084"; -} -.glyphicon-question-sign:before { - content: "\e085"; -} -.glyphicon-info-sign:before { - content: "\e086"; -} -.glyphicon-screenshot:before { - content: "\e087"; -} -.glyphicon-remove-circle:before { - content: "\e088"; -} -.glyphicon-ok-circle:before { - content: "\e089"; -} -.glyphicon-ban-circle:before { - content: "\e090"; -} -.glyphicon-arrow-left:before { - content: "\e091"; -} -.glyphicon-arrow-right:before { - content: "\e092"; -} -.glyphicon-arrow-up:before { - content: "\e093"; -} -.glyphicon-arrow-down:before { - content: "\e094"; -} -.glyphicon-share-alt:before { - content: "\e095"; -} -.glyphicon-resize-full:before { - content: "\e096"; -} -.glyphicon-resize-small:before { - content: "\e097"; -} -.glyphicon-exclamation-sign:before { - content: "\e101"; -} -.glyphicon-gift:before { - content: "\e102"; -} -.glyphicon-leaf:before { - content: "\e103"; -} -.glyphicon-fire:before { - content: "\e104"; -} -.glyphicon-eye-open:before { - content: "\e105"; -} -.glyphicon-eye-close:before { - content: "\e106"; -} -.glyphicon-warning-sign:before { - content: "\e107"; -} -.glyphicon-plane:before { - content: "\e108"; -} -.glyphicon-calendar:before { - content: "\e109"; -} -.glyphicon-random:before { - content: "\e110"; -} -.glyphicon-comment:before { - content: "\e111"; -} -.glyphicon-magnet:before { - content: "\e112"; -} -.glyphicon-chevron-up:before { - content: "\e113"; -} -.glyphicon-chevron-down:before { - content: "\e114"; -} -.glyphicon-retweet:before { - content: "\e115"; -} -.glyphicon-shopping-cart:before { - content: "\e116"; -} -.glyphicon-folder-close:before { - content: "\e117"; -} -.glyphicon-folder-open:before { - content: "\e118"; -} -.glyphicon-resize-vertical:before { - content: "\e119"; -} -.glyphicon-resize-horizontal:before { - content: "\e120"; -} -.glyphicon-hdd:before { - content: "\e121"; -} -.glyphicon-bullhorn:before { - content: "\e122"; -} -.glyphicon-bell:before { - content: "\e123"; -} -.glyphicon-certificate:before { - content: "\e124"; -} -.glyphicon-thumbs-up:before { - content: "\e125"; -} -.glyphicon-thumbs-down:before { - content: "\e126"; -} -.glyphicon-hand-right:before { - content: "\e127"; -} -.glyphicon-hand-left:before { - content: "\e128"; -} -.glyphicon-hand-up:before { - content: "\e129"; -} -.glyphicon-hand-down:before { - content: "\e130"; -} -.glyphicon-circle-arrow-right:before { - content: "\e131"; -} -.glyphicon-circle-arrow-left:before { - content: "\e132"; -} -.glyphicon-circle-arrow-up:before { - content: "\e133"; -} -.glyphicon-circle-arrow-down:before { - content: "\e134"; -} -.glyphicon-globe:before { - content: "\e135"; -} -.glyphicon-wrench:before { - content: "\e136"; -} -.glyphicon-tasks:before { - content: "\e137"; -} -.glyphicon-filter:before { - content: "\e138"; -} -.glyphicon-briefcase:before { - content: "\e139"; -} -.glyphicon-fullscreen:before { - content: "\e140"; -} -.glyphicon-dashboard:before { - content: "\e141"; -} -.glyphicon-paperclip:before { - content: "\e142"; -} -.glyphicon-heart-empty:before { - content: "\e143"; -} -.glyphicon-link:before { - content: "\e144"; -} -.glyphicon-phone:before { - content: "\e145"; -} -.glyphicon-pushpin:before { - content: "\e146"; -} -.glyphicon-usd:before { - content: "\e148"; -} -.glyphicon-gbp:before { - content: "\e149"; -} -.glyphicon-sort:before { - content: "\e150"; -} -.glyphicon-sort-by-alphabet:before { - content: "\e151"; -} -.glyphicon-sort-by-alphabet-alt:before { - content: "\e152"; -} -.glyphicon-sort-by-order:before { - content: "\e153"; -} -.glyphicon-sort-by-order-alt:before { - content: "\e154"; -} -.glyphicon-sort-by-attributes:before { - content: "\e155"; -} -.glyphicon-sort-by-attributes-alt:before { - content: "\e156"; -} -.glyphicon-unchecked:before { - content: "\e157"; -} -.glyphicon-expand:before { - content: "\e158"; -} -.glyphicon-collapse-down:before { - content: "\e159"; -} -.glyphicon-collapse-up:before { - content: "\e160"; -} -.glyphicon-log-in:before { - content: "\e161"; -} -.glyphicon-flash:before { - content: "\e162"; -} -.glyphicon-log-out:before { - content: "\e163"; -} -.glyphicon-new-window:before { - content: "\e164"; -} -.glyphicon-record:before { - content: "\e165"; -} -.glyphicon-save:before { - content: "\e166"; -} -.glyphicon-open:before { - content: "\e167"; -} -.glyphicon-saved:before { - content: "\e168"; -} -.glyphicon-import:before { - content: "\e169"; -} -.glyphicon-export:before { - content: "\e170"; -} -.glyphicon-send:before { - content: "\e171"; -} -.glyphicon-floppy-disk:before { - content: "\e172"; -} -.glyphicon-floppy-saved:before { - content: "\e173"; -} -.glyphicon-floppy-remove:before { - content: "\e174"; -} -.glyphicon-floppy-save:before { - content: "\e175"; -} -.glyphicon-floppy-open:before { - content: "\e176"; -} -.glyphicon-credit-card:before { - content: "\e177"; -} -.glyphicon-transfer:before { - content: "\e178"; -} -.glyphicon-cutlery:before { - content: "\e179"; -} -.glyphicon-header:before { - content: "\e180"; -} -.glyphicon-compressed:before { - content: "\e181"; -} -.glyphicon-earphone:before { - content: "\e182"; -} -.glyphicon-phone-alt:before { - content: "\e183"; -} -.glyphicon-tower:before { - content: "\e184"; -} -.glyphicon-stats:before { - content: "\e185"; -} -.glyphicon-sd-video:before { - content: "\e186"; -} -.glyphicon-hd-video:before { - content: "\e187"; -} -.glyphicon-subtitles:before { - content: "\e188"; -} -.glyphicon-sound-stereo:before { - content: "\e189"; -} -.glyphicon-sound-dolby:before { - content: "\e190"; -} -.glyphicon-sound-5-1:before { - content: "\e191"; -} -.glyphicon-sound-6-1:before { - content: "\e192"; -} -.glyphicon-sound-7-1:before { - content: "\e193"; -} -.glyphicon-copyright-mark:before { - content: "\e194"; -} -.glyphicon-registration-mark:before { - content: "\e195"; -} -.glyphicon-cloud-download:before { - content: "\e197"; -} -.glyphicon-cloud-upload:before { - content: "\e198"; -} -.glyphicon-tree-conifer:before { - content: "\e199"; -} -.glyphicon-tree-deciduous:before { - content: "\e200"; -} -.glyphicon-cd:before { - content: "\e201"; -} -.glyphicon-save-file:before { - content: "\e202"; -} -.glyphicon-open-file:before { - content: "\e203"; -} -.glyphicon-level-up:before { - content: "\e204"; -} -.glyphicon-copy:before { - content: "\e205"; -} -.glyphicon-paste:before { - content: "\e206"; -} -.glyphicon-alert:before { - content: "\e209"; -} -.glyphicon-equalizer:before { - content: "\e210"; -} -.glyphicon-king:before { - content: "\e211"; -} -.glyphicon-queen:before { - content: "\e212"; -} -.glyphicon-pawn:before { - content: "\e213"; -} -.glyphicon-bishop:before { - content: "\e214"; -} -.glyphicon-knight:before { - content: "\e215"; -} -.glyphicon-baby-formula:before { - content: "\e216"; -} -.glyphicon-tent:before { - content: "\26fa"; -} -.glyphicon-blackboard:before { - content: "\e218"; -} -.glyphicon-bed:before { - content: "\e219"; -} -.glyphicon-apple:before { - content: "\f8ff"; -} -.glyphicon-erase:before { - content: "\e221"; -} -.glyphicon-hourglass:before { - content: "\231b"; -} -.glyphicon-lamp:before { - content: "\e223"; -} -.glyphicon-duplicate:before { - content: "\e224"; -} -.glyphicon-piggy-bank:before { - content: "\e225"; -} -.glyphicon-scissors:before { - content: "\e226"; -} -.glyphicon-bitcoin:before { - content: "\e227"; -} -.glyphicon-btc:before { - content: "\e227"; -} -.glyphicon-xbt:before { - content: "\e227"; -} -.glyphicon-yen:before { - content: "\00a5"; -} -.glyphicon-jpy:before { - content: "\00a5"; -} -.glyphicon-ruble:before { - content: "\20bd"; -} -.glyphicon-rub:before { - content: "\20bd"; -} -.glyphicon-scale:before { - content: "\e230"; -} -.glyphicon-ice-lolly:before { - content: "\e231"; -} -.glyphicon-ice-lolly-tasted:before { - content: "\e232"; -} -.glyphicon-education:before { - content: "\e233"; -} -.glyphicon-option-horizontal:before { - content: "\e234"; -} -.glyphicon-option-vertical:before { - content: "\e235"; -} -.glyphicon-menu-hamburger:before { - content: "\e236"; -} -.glyphicon-modal-window:before { - content: "\e237"; -} -.glyphicon-oil:before { - content: "\e238"; -} -.glyphicon-grain:before { - content: "\e239"; -} -.glyphicon-sunglasses:before { - content: "\e240"; -} -.glyphicon-text-size:before { - content: "\e241"; -} -.glyphicon-text-color:before { - content: "\e242"; -} -.glyphicon-text-background:before { - content: "\e243"; -} -.glyphicon-object-align-top:before { - content: "\e244"; -} -.glyphicon-object-align-bottom:before { - content: "\e245"; -} -.glyphicon-object-align-horizontal:before { - content: "\e246"; -} -.glyphicon-object-align-left:before { - content: "\e247"; -} -.glyphicon-object-align-vertical:before { - content: "\e248"; -} -.glyphicon-object-align-right:before { - content: "\e249"; -} -.glyphicon-triangle-right:before { - content: "\e250"; -} -.glyphicon-triangle-left:before { - content: "\e251"; -} -.glyphicon-triangle-bottom:before { - content: "\e252"; -} -.glyphicon-triangle-top:before { - content: "\e253"; -} -.glyphicon-console:before { - content: "\e254"; -} -.glyphicon-superscript:before { - content: "\e255"; -} -.glyphicon-subscript:before { - content: "\e256"; -} -.glyphicon-menu-left:before { - content: "\e257"; -} -.glyphicon-menu-right:before { - content: "\e258"; -} -.glyphicon-menu-down:before { - content: "\e259"; -} -.glyphicon-menu-up:before { - content: "\e260"; -} diff --git a/admin/static/fonts/FantasqueSansMono-Regular.woff b/admin/static/fonts/FantasqueSansMono-Regular.woff deleted file mode 100644 index c88cd830..00000000 Binary files a/admin/static/fonts/FantasqueSansMono-Regular.woff and /dev/null differ diff --git a/admin/static/fonts/LinBiolinum_R.woff b/admin/static/fonts/LinBiolinum_R.woff deleted file mode 100644 index 5c399fd8..00000000 Binary files a/admin/static/fonts/LinBiolinum_R.woff and /dev/null differ diff --git a/admin/static/fonts/LinBiolinum_RB.woff b/admin/static/fonts/LinBiolinum_RB.woff deleted file mode 100644 index fbeead0f..00000000 Binary files a/admin/static/fonts/LinBiolinum_RB.woff and /dev/null differ diff --git a/admin/static/fonts/LinBiolinum_RI.woff b/admin/static/fonts/LinBiolinum_RI.woff deleted file mode 100644 index 40661f13..00000000 Binary files a/admin/static/fonts/LinBiolinum_RI.woff and /dev/null differ diff --git a/admin/static/fonts/glyphicons-halflings-regular.eot b/admin/static/fonts/glyphicons-halflings-regular.eot deleted file mode 100644 index b93a4953..00000000 Binary files a/admin/static/fonts/glyphicons-halflings-regular.eot and /dev/null differ diff --git a/admin/static/fonts/glyphicons-halflings-regular.svg b/admin/static/fonts/glyphicons-halflings-regular.svg deleted file mode 100644 index 94fb5490..00000000 --- a/admin/static/fonts/glyphicons-halflings-regular.svg +++ /dev/null @@ -1,288 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/admin/static/fonts/glyphicons-halflings-regular.ttf b/admin/static/fonts/glyphicons-halflings-regular.ttf deleted file mode 100644 index 1413fc60..00000000 Binary files a/admin/static/fonts/glyphicons-halflings-regular.ttf and /dev/null differ diff --git a/admin/static/fonts/glyphicons-halflings-regular.woff b/admin/static/fonts/glyphicons-halflings-regular.woff deleted file mode 100644 index 9e612858..00000000 Binary files a/admin/static/fonts/glyphicons-halflings-regular.woff and /dev/null differ diff --git a/admin/static/fonts/glyphicons-halflings-regular.woff2 b/admin/static/fonts/glyphicons-halflings-regular.woff2 deleted file mode 100644 index 64539b54..00000000 Binary files a/admin/static/fonts/glyphicons-halflings-regular.woff2 and /dev/null differ diff --git a/admin/static/js/angular-resource.min.js b/admin/static/js/angular-resource.min.js deleted file mode 100644 index 8b924c37..00000000 --- a/admin/static/js/angular-resource.min.js +++ /dev/null @@ -1,15 +0,0 @@ -/* - AngularJS v1.7.9 - (c) 2010-2018 Google, Inc. http://angularjs.org - License: MIT -*/ -(function(T,a){'use strict';function M(m,f){f=f||{};a.forEach(f,function(a,d){delete f[d]});for(var d in m)!m.hasOwnProperty(d)||"$"===d.charAt(0)&&"$"===d.charAt(1)||(f[d]=m[d]);return f}var B=a.$$minErr("$resource"),H=/^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;a.module("ngResource",["ng"]).info({angularVersion:"1.7.9"}).provider("$resource",function(){var m=/^https?:\/\/\[[^\]]*][^/]*/,f=this;this.defaults={stripTrailingSlashes:!0,cancellable:!1,actions:{get:{method:"GET"},save:{method:"POST"},query:{method:"GET", -isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}}};this.$get=["$http","$log","$q","$timeout",function(d,F,G,N){function C(a,d){this.template=a;this.defaults=n({},f.defaults,d);this.urlParams={}}var O=a.noop,r=a.forEach,n=a.extend,R=a.copy,P=a.isArray,D=a.isDefined,x=a.isFunction,I=a.isNumber,y=a.$$encodeUriQuery,S=a.$$encodeUriSegment;C.prototype={setUrlParams:function(a,d,f){var g=this,c=f||g.template,s,h,n="",b=g.urlParams=Object.create(null);r(c.split(/\W/),function(a){if("hasOwnProperty"=== -a)throw B("badname");!/^\d+$/.test(a)&&a&&(new RegExp("(^|[^\\\\]):"+a+"(\\W|$)")).test(c)&&(b[a]={isQueryParamValue:(new RegExp("\\?.*=:"+a+"(?:\\W|$)")).test(c)})});c=c.replace(/\\:/g,":");c=c.replace(m,function(b){n=b;return""});d=d||{};r(g.urlParams,function(b,a){s=d.hasOwnProperty(a)?d[a]:g.defaults[a];D(s)&&null!==s?(h=b.isQueryParamValue?y(s,!0):S(s),c=c.replace(new RegExp(":"+a+"(\\W|$)","g"),function(b,a){return h+a})):c=c.replace(new RegExp("(/?):"+a+"(\\W|$)","g"),function(b,a,e){return"/"=== -e.charAt(0)?e:a+e})});g.defaults.stripTrailingSlashes&&(c=c.replace(/\/+$/,"")||"/");c=c.replace(/\/\.(?=\w+($|\?))/,".");a.url=n+c.replace(/\/(\\|%5C)\./,"/.");r(d,function(b,c){g.urlParams[c]||(a.params=a.params||{},a.params[c]=b)})}};return function(m,y,z,g){function c(b,c){var d={};c=n({},y,c);r(c,function(c,f){x(c)&&(c=c(b));var e;if(c&&c.charAt&&"@"===c.charAt(0)){e=b;var k=c.substr(1);if(null==k||""===k||"hasOwnProperty"===k||!H.test("."+k))throw B("badmember",k);for(var k=k.split("."),h=0, -n=k.length;h/g,">")}function A(a){for(;a;){if(a.nodeType===s.Node.ELEMENT_NODE)for(var e=a.attributes,d=0,b=e.length;d"))},end:function(a){a=q(a);d||!0!==m[a]||!0===r[a]||(b(""));a==d&&(d=!1)},chars:function(a){d|| -b(L(a))}}};J=s.Node.prototype.contains||function(a){return!!(this.compareDocumentPosition(a)&16)};var z=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,u=/([^#-~ |!])/g,r=f("area,br,col,hr,img,wbr"),x=f("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),p=f("rp,rt"),n=h({},p,x),x=h({},x,f("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,section,table,ul")),p=h({},p,f("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")), -l=f("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,radialGradient,rect,stop,svg,switch,text,title,tspan"),w=f("script,style"),m=h({},r,x,p,n),O=f("background,cite,href,longdesc,src,xlink:href,xml:base"),n=f("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,size,span,start,summary,tabindex,target,title,type,valign,value,vspace,width"), -p=f("accent-height,accumulate,additive,alphabetic,arabic-form,ascent,baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan", -!0),M=h({},O,p,n),N=function(a,e){function d(b){b=""+b;try{var d=(new a.DOMParser).parseFromString(b,"text/html").body;d.firstChild.remove();return d}catch(e){}}function b(a){c.innerHTML=a;e.documentMode&&A(c);return c}var g;if(e&&e.implementation)g=e.implementation.createHTMLDocument("inert");else throw D("noinert");var c=(g.documentElement||g.getDocumentElement()).querySelector("body");c.innerHTML='';return c.querySelector("svg")? -(c.innerHTML='

',c.querySelector("svg img")?d:b):function(b){b=""+b;try{b=encodeURI(b)}catch(d){return}var e=new a.XMLHttpRequest;e.responseType="document";e.open("GET","data:text/html;charset=utf-8,"+b,!1);e.send(null);b=e.response.body;b.firstChild.remove();return b}}(s,s.document)}).info({angularVersion:"1.7.9"});c.module("ngSanitize").filter("linky",["$sanitize",function(f){var h=/((s?ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i, -t=/^mailto:/i,q=c.$$minErr("linky"),s=c.isDefined,A=c.isFunction,v=c.isObject,y=c.isString;return function(c,z,u){function r(c){c&&l.push(P(c))}function x(c,g){var f,a=p(c);l.push("');r(g);l.push("")}if(null==c||""===c)return c;if(!y(c))throw q("notstring",c);for(var p=A(u)?u:v(u)?function(){return u}:function(){return{}},n=c,l=[],w,m;c=n.match(h);)w=c[0],c[2]|| -c[4]||(w=(c[3]?"http://":"mailto:")+w),m=c.index,r(n.substr(0,m)),x(w,c[0].replace(t,"")),n=n.substring(m+c[0].length);r(n);return f(l.join(""))}}])})(window,window.angular); -//# sourceMappingURL=angular-sanitize.min.js.map diff --git a/admin/static/js/angular.min.js b/admin/static/js/angular.min.js deleted file mode 100644 index f6bf3370..00000000 --- a/admin/static/js/angular.min.js +++ /dev/null @@ -1,350 +0,0 @@ -/* - AngularJS v1.7.9 - (c) 2010-2018 Google, Inc. http://angularjs.org - License: MIT -*/ -(function(C){'use strict';function re(a){if(D(a))w(a.objectMaxDepth)&&(Wb.objectMaxDepth=Xb(a.objectMaxDepth)?a.objectMaxDepth:NaN),w(a.urlErrorParamsEnabled)&&Ga(a.urlErrorParamsEnabled)&&(Wb.urlErrorParamsEnabled=a.urlErrorParamsEnabled);else return Wb}function Xb(a){return W(a)&&0c)return"...";var d=b.$$hashKey,f;if(H(a)){f=0;for(var g=a.length;f").append(a).html();try{return a[0].nodeType===Pa?K(b):b.match(/^(<[^>]+>)/)[1].replace(/^<([\w-]+)/,function(a,b){return"<"+K(b)})}catch(d){return K(b)}}function Tc(a){try{return decodeURIComponent(a)}catch(b){}}function gc(a){var b={};r((a||"").split("&"), -function(a){var c,e,f;a&&(e=a=a.replace(/\+/g,"%20"),c=a.indexOf("="),-1!==c&&(e=a.substring(0,c),f=a.substring(c+1)),e=Tc(e),w(e)&&(f=w(f)?Tc(f):!0,ta.call(b,e)?H(b[e])?b[e].push(f):b[e]=[b[e],f]:b[e]=f))});return b}function ye(a){var b=[];r(a,function(a,c){H(a)?r(a,function(a){b.push(ba(c,!0)+(!0===a?"":"="+ba(a,!0)))}):b.push(ba(c,!0)+(!0===a?"":"="+ba(a,!0)))});return b.length?b.join("&"):""}function hc(a){return ba(a,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function ba(a, -b){return encodeURIComponent(a).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%3B/gi,";").replace(/%20/g,b?"%20":"+")}function ze(a,b){var d,c,e=Qa.length;for(c=0;c protocol indicates an extension, document.location.href does not match."))}function Uc(a,b,d){D(d)||(d={});d=S({strictDi:!1},d);var c=function(){a=x(a);if(a.injector()){var c=a[0]===C.document?"document":za(a);throw pa("btstrpd",c.replace(//,">"));}b=b||[];b.unshift(["$provide",function(b){b.value("$rootElement",a)}]);d.debugInfoEnabled&&b.push(["$compileProvider", -function(a){a.debugInfoEnabled(!0)}]);b.unshift("ng");c=fb(b,d.strictDi);c.invoke(["$rootScope","$rootElement","$compile","$injector",function(a,b,c,d){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return c},e=/^NG_ENABLE_DEBUG_INFO!/,f=/^NG_DEFER_BOOTSTRAP!/;C&&e.test(C.name)&&(d.debugInfoEnabled=!0,C.name=C.name.replace(e,""));if(C&&!f.test(C.name))return c();C.name=C.name.replace(f,"");ca.resumeBootstrap=function(a){r(a,function(a){b.push(a)});return c()};B(ca.resumeDeferredBootstrap)&& -ca.resumeDeferredBootstrap()}function Ce(){C.name="NG_ENABLE_DEBUG_INFO!"+C.name;C.location.reload()}function De(a){a=ca.element(a).injector();if(!a)throw pa("test");return a.get("$$testability")}function Vc(a,b){b=b||"_";return a.replace(Ee,function(a,c){return(c?b:"")+a.toLowerCase()})}function Fe(){var a;if(!Wc){var b=qb();(rb=z(b)?C.jQuery:b?C[b]:void 0)&&rb.fn.on?(x=rb,S(rb.fn,{scope:Wa.scope,isolateScope:Wa.isolateScope,controller:Wa.controller,injector:Wa.injector,inheritedData:Wa.inheritedData})): -x=Y;a=x.cleanData;x.cleanData=function(b){for(var c,e=0,f;null!=(f=b[e]);e++)(c=(x._data(f)||{}).events)&&c.$destroy&&x(f).triggerHandler("$destroy");a(b)};ca.element=x;Wc=!0}}function gb(a,b,d){if(!a)throw pa("areq",b||"?",d||"required");return a}function sb(a,b,d){d&&H(a)&&(a=a[a.length-1]);gb(B(a),b,"not a function, got "+(a&&"object"===typeof a?a.constructor.name||"Object":typeof a));return a}function Ja(a,b){if("hasOwnProperty"===a)throw pa("badname",b);}function Ge(a,b,d){if(!b)return a;b=b.split("."); -for(var c,e=a,f=b.length,g=0;g")+c[2];for(c=c[0];c--;)d=d.lastChild;f=db(f,d.childNodes);d=e.firstChild;d.textContent=""}else f.push(b.createTextNode(a));e.textContent="";e.innerHTML="";r(f,function(a){e.appendChild(a)});return e}function Y(a){if(a instanceof Y)return a;var b;A(a)&&(a=U(a),b=!0);if(!(this instanceof Y)){if(b&&"<"!==a.charAt(0))throw nc("nosel");return new Y(a)}if(b){b= -C.document;var d;a=(d=og.exec(a))?[b.createElement(d[1])]:(d=ed(a,b))?d.childNodes:[];oc(this,a)}else B(a)?fd(a):oc(this,a)}function pc(a){return a.cloneNode(!0)}function yb(a,b){!b&&lc(a)&&x.cleanData([a]);a.querySelectorAll&&x.cleanData(a.querySelectorAll("*"))}function gd(a){for(var b in a)return!1;return!0}function hd(a){var b=a.ng339,d=b&&Ka[b],c=d&&d.events,d=d&&d.data;d&&!gd(d)||c&&!gd(c)||(delete Ka[b],a.ng339=void 0)}function id(a,b,d,c){if(w(c))throw nc("offargs");var e=(c=zb(a))&&c.events, -f=c&&c.handle;if(f){if(b){var g=function(b){var c=e[b];w(d)&&cb(c||[],d);w(d)&&c&&0l&&this.remove(n.key);return b}},get:function(a){if(l";b=Fa.firstChild.attributes;var d=b[0];b.removeNamedItem(d.name);d.value=c;a.attributes.setNamedItem(d)}function sa(a,b){try{a.addClass(b)}catch(c){}} -function da(a,b,c,d,e){a instanceof x||(a=x(a));var f=Xa(a,b,a,c,d,e);da.$$addScopeClass(a);var g=null;return function(b,c,d){if(!a)throw $("multilink");gb(b,"scope");e&&e.needsNewScope&&(b=b.$parent.$new());d=d||{};var h=d.parentBoundTranscludeFn,k=d.transcludeControllers;d=d.futureParentElement;h&&h.$$boundTransclude&&(h=h.$$boundTransclude);g||(g=(d=d&&d[0])?"foreignobject"!==ua(d)&&la.call(d).match(/SVG/)?"svg":"html":"html");d="html"!==g?x(ja(g,x("

").append(a).html())):c?Wa.clone.call(a): -a;if(k)for(var l in k)d.data("$"+l+"Controller",k[l].instance);da.$$addScopeInfo(d,b);c&&c(d,b);f&&f(b,d,d,h);c||(a=f=null);return d}}function Xa(a,b,c,d,e,f){function g(a,c,d,e){var f,k,l,m,p,I,t;if(n)for(t=Array(c.length),m=0;mu.priority)break;if(O=u.scope)u.templateUrl||(D(O)?(ba("new/isolated scope",s||t,u,y),s=u):ba("new/isolated scope",s,u,y)),t=t||u;Q=u.name;if(!ma&&(u.replace&&(u.templateUrl||u.template)||u.transclude&&!u.$$tlb)){for(O=sa+1;ma=a[O++];)if(ma.transclude&&!ma.$$tlb||ma.replace&&(ma.templateUrl||ma.template)){Ib=!0;break}ma=!0}!u.templateUrl&&u.controller&&(J=J||T(),ba("'"+Q+"' controller", -J[Q],u,y),J[Q]=u);if(O=u.transclude)if(G=!0,u.$$tlb||(ba("transclusion",L,u,y),L=u),"element"===O)N=!0,n=u.priority,M=y,y=d.$$element=x(da.$$createComment(Q,d[Q])),b=y[0],pa(f,Ha.call(M,0),b),R=Z(Ib,M,e,n,g&&g.name,{nonTlbTranscludeDirective:L});else{var ka=T();if(D(O)){M=C.document.createDocumentFragment();var Xa=T(),F=T();r(O,function(a,b){var c="?"===a.charAt(0);a=c?a.substring(1):a;Xa[a]=b;ka[b]=null;F[b]=c});r(y.contents(),function(a){var b=Xa[wa(ua(a))];b?(F[b]=!0,ka[b]=ka[b]||C.document.createDocumentFragment(), -ka[b].appendChild(a)):M.appendChild(a)});r(F,function(a,b){if(!a)throw $("reqslot",b);});for(var K in ka)ka[K]&&(R=x(ka[K].childNodes),ka[K]=Z(Ib,R,e));M=x(M.childNodes)}else M=x(pc(b)).contents();y.empty();R=Z(Ib,M,e,void 0,void 0,{needsNewScope:u.$$isolateScope||u.$$newScope});R.$$slots=ka}if(u.template)if(P=!0,ba("template",v,u,y),v=u,O=B(u.template)?u.template(y,d):u.template,O=Na(O),u.replace){g=u;M=mc.test(O)?rd(ja(u.templateNamespace,U(O))):[];b=M[0];if(1!==M.length||1!==b.nodeType)throw $("tplrt", -Q,"");pa(f,y,b);A={$attr:{}};O=sc(b,[],A);var Dg=a.splice(sa+1,a.length-(sa+1));(s||t)&&fa(O,s,t);a=a.concat(O).concat(Dg);ga(d,A);A=a.length}else y.html(O);if(u.templateUrl)P=!0,ba("template",v,u,y),v=u,u.replace&&(g=u),p=ha(a.splice(sa,a.length-sa),y,d,f,G&&R,h,k,{controllerDirectives:J,newScopeDirective:t!==u&&t,newIsolateScopeDirective:s,templateDirective:v,nonTlbTranscludeDirective:L}),A=a.length;else if(u.compile)try{q=u.compile(y,d,R);var X=u.$$originalDirective||u;B(q)?m(null,Va(X,q),E,ib): -q&&m(Va(X,q.pre),Va(X,q.post),E,ib)}catch(ca){c(ca,za(y))}u.terminal&&(p.terminal=!0,n=Math.max(n,u.priority))}p.scope=t&&!0===t.scope;p.transcludeOnThisElement=G;p.templateOnThisElement=P;p.transclude=R;l.hasElementTranscludeDirective=N;return p}function W(a,b,c,d){var e;if(A(b)){var f=b.match(l);b=b.substring(f[0].length);var g=f[1]||f[3],f="?"===f[2];"^^"===g?c=c.parent():e=(e=d&&d[b])&&e.instance;if(!e){var h="$"+b+"Controller";e="^^"===g&&c[0]&&9===c[0].nodeType?null:g?c.inheritedData(h):c.data(h)}if(!e&& -!f)throw $("ctreq",b,a);}else if(H(b))for(e=[],g=0,f=b.length;gc.priority)&&-1!==c.restrict.indexOf(e)){k&&(c=ac(c,{$$start:k,$$end:l}));if(!c.$$bindings){var I=m=c,t=c.name,u={isolateScope:null,bindToController:null};D(I.scope)&&(!0===I.bindToController?(u.bindToController=d(I.scope,t,!0),u.isolateScope={}):u.isolateScope=d(I.scope,t,!1));D(I.bindToController)&&(u.bindToController=d(I.bindToController, -t,!0));if(u.bindToController&&!I.controller)throw $("noctrl",t);m=m.$$bindings=u;D(m.isolateScope)&&(c.$$isolateBindings=m.isolateScope)}b.push(c);m=c}}return m}function ca(b){if(f.hasOwnProperty(b))for(var c=a.get(b+"Directive"),d=0,e=c.length;d"+b+"";return c.childNodes[0].childNodes;default:return b}}function oa(a,b){if("srcdoc"===b)return u.HTML;if("src"===b||"ngSrc"===b)return-1===["img","video","audio","source","track"].indexOf(a)?u.RESOURCE_URL:u.MEDIA_URL;if("xlinkHref"===b)return"image"===a?u.MEDIA_URL: -"a"===a?u.URL:u.RESOURCE_URL;if("form"===a&&"action"===b||"base"===a&&"href"===b||"link"===a&&"href"===b)return u.RESOURCE_URL;if("a"===a&&("href"===b||"ngHref"===b))return u.URL}function xa(a,b){var c=b.toLowerCase();return v[a+"|"+c]||v["*|"+c]}function ya(a){return ma(u.valueOf(a),"ng-prop-srcset")}function Ea(a,b,c,d){if(m.test(d))throw $("nodomevents");a=ua(a);var e=xa(a,d),f=Ta;"srcset"!==d||"img"!==a&&"source"!==a?e&&(f=u.getTrusted.bind(u,e)):f=ya;b.push({priority:100,compile:function(a,b){var e= -p(b[c]),g=p(b[c],function(a){return u.valueOf(a)});return{pre:function(a,b){function c(){var g=e(a);b[0][d]=f(g)}c();a.$watch(g,c)}}}})}function Ia(a,c,d,e,f){var g=ua(a),k=oa(g,e),l=h[e]||f,p=b(d,!f,k,l);if(p){if("multiple"===e&&"select"===g)throw $("selmulti",za(a));if(m.test(e))throw $("nodomevents");c.push({priority:100,compile:function(){return{pre:function(a,c,f){c=f.$$observers||(f.$$observers=T());var g=f[e];g!==d&&(p=g&&b(g,!0,k,l),d=g);p&&(f[e]=p(a),(c[e]||(c[e]=[])).$$inter=!0,(f.$$observers&& -f.$$observers[e].$$scope||a).$watch(p,function(a,b){"class"===e&&a!==b?f.$updateClass(a,b):f.$set(e,a)}))}}}})}}function pa(a,b,c){var d=b[0],e=b.length,f=d.parentNode,g,h;if(a)for(g=0,h=a.length;g=b)return a;for(;b--;){var d=a[b];(8===d.nodeType||d.nodeType===Pa&&""===d.nodeValue.trim())&&Fg.call(a,b,1)}return a}function Bg(a,b){if(b&&A(b))return b;if(A(a)){var d=ud.exec(a);if(d)return d[3]}}function Ff(){var a={};this.has=function(b){return a.hasOwnProperty(b)};this.register=function(b,d){Ja(b, -"controller");D(b)?S(a,b):a[b]=d};this.$get=["$injector",function(b){function d(a,b,d,g){if(!a||!D(a.$scope))throw F("$controller")("noscp",g,b);a.$scope[b]=d}return function(c,e,f,g){var k,h,l;f=!0===f;g&&A(g)&&(l=g);if(A(c)){g=c.match(ud);if(!g)throw vd("ctrlfmt",c);h=g[1];l=l||g[3];c=a.hasOwnProperty(h)?a[h]:Ge(e.$scope,h,!0);if(!c)throw vd("ctrlreg",h);sb(c,h,!0)}if(f)return f=(H(c)?c[c.length-1]:c).prototype,k=Object.create(f||null),l&&d(e,l,k,h||c.name),S(function(){var a=b.invoke(c,k,e,h); -a!==k&&(D(a)||B(a))&&(k=a,l&&d(e,l,k,h||c.name));return k},{instance:k,identifier:l});k=b.instantiate(c,e,h);l&&d(e,l,k,h||c.name);return k}}]}function Gf(){this.$get=["$window",function(a){return x(a.document)}]}function Hf(){this.$get=["$document","$rootScope",function(a,b){function d(){e=c.hidden}var c=a[0],e=c&&c.hidden;a.on("visibilitychange",d);b.$on("$destroy",function(){a.off("visibilitychange",d)});return function(){return e}}]}function If(){this.$get=["$log",function(a){return function(b, -d){a.error.apply(a,arguments)}}]}function uc(a){return D(a)?ha(a)?a.toISOString():eb(a):a}function Of(){this.$get=function(){return function(a){if(!a)return"";var b=[];Oc(a,function(a,c){null===a||z(a)||B(a)||(H(a)?r(a,function(a){b.push(ba(c)+"="+ba(uc(a)))}):b.push(ba(c)+"="+ba(uc(a))))});return b.join("&")}}}function Pf(){this.$get=function(){return function(a){function b(a,e,f){H(a)?r(a,function(a,c){b(a,e+"["+(D(a)?c:"")+"]")}):D(a)&&!ha(a)?Oc(a,function(a,c){b(a,e+(f?"":"[")+c+(f?"":"]"))}): -(B(a)&&(a=a()),d.push(ba(e)+"="+(null==a?"":ba(uc(a)))))}if(!a)return"";var d=[];b(a,"",!0);return d.join("&")}}}function vc(a,b){if(A(a)){var d=a.replace(Gg,"").trim();if(d){var c=b("Content-Type"),c=c&&0===c.indexOf(wd),e;(e=c)||(e=(e=d.match(Hg))&&Ig[e[0]].test(d));if(e)try{a=Rc(d)}catch(f){if(!c)return a;throw Kb("baddata",a,f);}}}return a}function xd(a){var b=T(),d;A(a)?r(a.split("\n"),function(a){d=a.indexOf(":");var e=K(U(a.substr(0,d)));a=U(a.substr(d+1));e&&(b[e]=b[e]?b[e]+", "+a:a)}):D(a)&& -r(a,function(a,d){var f=K(d),g=U(a);f&&(b[f]=b[f]?b[f]+", "+g:g)});return b}function yd(a){var b;return function(d){b||(b=xd(a));return d?(d=b[K(d)],void 0===d&&(d=null),d):b}}function zd(a,b,d,c){if(B(c))return c(a,b,d);r(c,function(c){a=c(a,b,d)});return a}function Nf(){var a=this.defaults={transformResponse:[vc],transformRequest:[function(a){return D(a)&&"[object File]"!==la.call(a)&&"[object Blob]"!==la.call(a)&&"[object FormData]"!==la.call(a)?eb(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"}, -post:ja(wc),put:ja(wc),patch:ja(wc)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",paramSerializer:"$httpParamSerializer",jsonpCallbackParam:"callback"},b=!1;this.useApplyAsync=function(a){return w(a)?(b=!!a,this):b};var d=this.interceptors=[],c=this.xsrfWhitelistedOrigins=[];this.$get=["$browser","$httpBackend","$$cookieReader","$cacheFactory","$rootScope","$q","$injector","$sce",function(e,f,g,k,h,l,m,p){function n(b){function c(a,b){for(var d=0,e=b.length;da?b:l.reject(b)}if(!D(b))throw F("$http")("badreq",b);if(!A(p.valueOf(b.url)))throw F("$http")("badreq",b.url);var g=S({method:"get",transformRequest:a.transformRequest,transformResponse:a.transformResponse,paramSerializer:a.paramSerializer,jsonpCallbackParam:a.jsonpCallbackParam}, -b);g.headers=function(b){var c=a.headers,e=S({},b.headers),f,g,h,c=S({},c.common,c[K(b.method)]);a:for(f in c){g=K(f);for(h in e)if(K(h)===g)continue a;e[f]=c[f]}return d(e,ja(b))}(b);g.method=ub(g.method);g.paramSerializer=A(g.paramSerializer)?m.get(g.paramSerializer):g.paramSerializer;e.$$incOutstandingRequestCount("$http");var h=[],k=[];b=l.resolve(g);r(v,function(a){(a.request||a.requestError)&&h.unshift(a.request,a.requestError);(a.response||a.responseError)&&k.push(a.response,a.responseError)}); -b=c(b,h);b=b.then(function(b){var c=b.headers,d=zd(b.data,yd(c),void 0,b.transformRequest);z(d)&&r(c,function(a,b){"content-type"===K(b)&&delete c[b]});z(b.withCredentials)&&!z(a.withCredentials)&&(b.withCredentials=a.withCredentials);return s(b,d).then(f,f)});b=c(b,k);return b=b.finally(function(){e.$$completeOutstandingRequest(E,"$http")})}function s(c,d){function e(a){if(a){var c={};r(a,function(a,d){c[d]=function(c){function d(){a(c)}b?h.$applyAsync(d):h.$$phase?d():h.$apply(d)}});return c}}function k(a, -c,d,e,f){function g(){m(c,a,d,e,f)}R&&(200<=a&&300>a?R.put(O,[a,c,xd(d),e,f]):R.remove(O));b?h.$applyAsync(g):(g(),h.$$phase||h.$apply())}function m(a,b,d,e,f){b=-1<=b?b:0;(200<=b&&300>b?L.resolve:L.reject)({data:a,status:b,headers:yd(d),config:c,statusText:e,xhrStatus:f})}function s(a){m(a.data,a.status,ja(a.headers()),a.statusText,a.xhrStatus)}function v(){var a=n.pendingRequests.indexOf(c);-1!==a&&n.pendingRequests.splice(a,1)}var L=l.defer(),u=L.promise,R,q,ma=c.headers,x="jsonp"===K(c.method), -O=c.url;x?O=p.getTrustedResourceUrl(O):A(O)||(O=p.valueOf(O));O=G(O,c.paramSerializer(c.params));x&&(O=t(O,c.jsonpCallbackParam));n.pendingRequests.push(c);u.then(v,v);!c.cache&&!a.cache||!1===c.cache||"GET"!==c.method&&"JSONP"!==c.method||(R=D(c.cache)?c.cache:D(a.cache)?a.cache:N);R&&(q=R.get(O),w(q)?q&&B(q.then)?q.then(s,s):H(q)?m(q[1],q[0],ja(q[2]),q[3],q[4]):m(q,200,{},"OK","complete"):R.put(O,u));z(q)&&((q=jc(c.url)?g()[c.xsrfCookieName||a.xsrfCookieName]:void 0)&&(ma[c.xsrfHeaderName||a.xsrfHeaderName]= -q),f(c.method,O,d,k,ma,c.timeout,c.withCredentials,c.responseType,e(c.eventHandlers),e(c.uploadEventHandlers)));return u}function G(a,b){0=h&&(t.resolve(s),f(r.$$intervalId));G||c.$apply()},k,t,G);return r}}}]}function Ad(a,b){var d=ga(a);b.$$protocol=d.protocol;b.$$host=d.hostname;b.$$port=fa(d.port)||Mg[d.protocol]||null}function Bd(a,b,d){if(Ng.test(a))throw jb("badpath",a);var c="/"!==a.charAt(0);c&&(a="/"+a);a=ga(a);for(var c=(c&&"/"===a.pathname.charAt(0)?a.pathname.substring(1):a.pathname).split("/"),e=c.length;e--;)c[e]=decodeURIComponent(c[e]),d&&(c[e]=c[e].replace(/\//g,"%2F"));d=c.join("/");b.$$path=d;b.$$search=gc(a.search); -b.$$hash=decodeURIComponent(a.hash);b.$$path&&"/"!==b.$$path.charAt(0)&&(b.$$path="/"+b.$$path)}function xc(a,b){return a.slice(0,b.length)===b}function xa(a,b){if(xc(b,a))return b.substr(a.length)}function Da(a){var b=a.indexOf("#");return-1===b?a:a.substr(0,b)}function yc(a,b,d){this.$$html5=!0;d=d||"";Ad(a,this);this.$$parse=function(a){var d=xa(b,a);if(!A(d))throw jb("ipthprfx",a,b);Bd(d,this,!0);this.$$path||(this.$$path="/");this.$$compose()};this.$$normalizeUrl=function(a){return b+a.substr(1)}; -this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;w(f=xa(a,c))?(g=f,g=d&&w(f=xa(d,f))?b+(xa("/",f)||f):a+g):w(f=xa(b,c))?g=b+f:b===c+"/"&&(g=b);g&&this.$$parse(g);return!!g}}function zc(a,b,d){Ad(a,this);this.$$parse=function(c){var e=xa(a,c)||xa(b,c),f;z(e)||"#"!==e.charAt(0)?this.$$html5?f=e:(f="",z(e)&&(a=c,this.replace())):(f=xa(d,e),z(f)&&(f=e));Bd(f,this,!1);c=this.$$path;var e=a,g=/^\/[A-Z]:(\/.*)/;xc(f,e)&&(f=f.replace(e,""));g.exec(f)||(c=(f=g.exec(c))? -f[1]:c);this.$$path=c;this.$$compose()};this.$$normalizeUrl=function(b){return a+(b?d+b:"")};this.$$parseLinkUrl=function(b,d){return Da(a)===Da(b)?(this.$$parse(b),!0):!1}}function Cd(a,b,d){this.$$html5=!0;zc.apply(this,arguments);this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;a===Da(c)?f=c:(g=xa(b,c))?f=a+d+g:b===c+"/"&&(f=b);f&&this.$$parse(f);return!!f};this.$$normalizeUrl=function(b){return a+d+b}}function Lb(a){return function(){return this[a]}}function Dd(a, -b){return function(d){if(z(d))return this[a];this[a]=b(d);this.$$compose();return this}}function Tf(){var a="!",b={enabled:!1,requireBase:!0,rewriteLinks:!0};this.hashPrefix=function(b){return w(b)?(a=b,this):a};this.html5Mode=function(a){if(Ga(a))return b.enabled=a,this;if(D(a)){Ga(a.enabled)&&(b.enabled=a.enabled);Ga(a.requireBase)&&(b.requireBase=a.requireBase);if(Ga(a.rewriteLinks)||A(a.rewriteLinks))b.rewriteLinks=a.rewriteLinks;return this}return b};this.$get=["$rootScope","$browser","$sniffer", -"$rootElement","$window",function(d,c,e,f,g){function k(a,b){return a===b||ga(a).href===ga(b).href}function h(a,b,d){var e=m.url(),f=m.$$state;try{c.url(a,b,d),m.$$state=c.state()}catch(g){throw m.url(e),m.$$state=f,g;}}function l(a,b){d.$broadcast("$locationChangeSuccess",m.absUrl(),a,m.$$state,b)}var m,p;p=c.baseHref();var n=c.url(),s;if(b.enabled){if(!p&&b.requireBase)throw jb("nobase");s=n.substring(0,n.indexOf("/",n.indexOf("//")+2))+(p||"/");p=e.history?yc:Cd}else s=Da(n),p=zc;var r=s.substr(0, -Da(s).lastIndexOf("/")+1);m=new p(s,r,"#"+a);m.$$parseLinkUrl(n,n);m.$$state=c.state();var t=/^\s*(javascript|mailto):/i;f.on("click",function(a){var e=b.rewriteLinks;if(e&&!a.ctrlKey&&!a.metaKey&&!a.shiftKey&&2!==a.which&&2!==a.button){for(var g=x(a.target);"a"!==ua(g[0]);)if(g[0]===f[0]||!(g=g.parent())[0])return;if(!A(e)||!z(g.attr(e))){var e=g.prop("href"),h=g.attr("href")||g.attr("xlink:href");D(e)&&"[object SVGAnimatedString]"===e.toString()&&(e=ga(e.animVal).href);t.test(e)||!e||g.attr("target")|| -a.isDefaultPrevented()||!m.$$parseLinkUrl(e,h)||(a.preventDefault(),m.absUrl()!==c.url()&&d.$apply())}}});m.absUrl()!==n&&c.url(m.absUrl(),!0);var N=!0;c.onUrlChange(function(a,b){xc(a,r)?(d.$evalAsync(function(){var c=m.absUrl(),e=m.$$state,f;m.$$parse(a);m.$$state=b;f=d.$broadcast("$locationChangeStart",a,c,b,e).defaultPrevented;m.absUrl()===a&&(f?(m.$$parse(c),m.$$state=e,h(c,!1,e)):(N=!1,l(c,e)))}),d.$$phase||d.$digest()):g.location.href=a});d.$watch(function(){if(N||m.$$urlUpdatedByLocation){m.$$urlUpdatedByLocation= -!1;var a=c.url(),b=m.absUrl(),f=c.state(),g=m.$$replace,n=!k(a,b)||m.$$html5&&e.history&&f!==m.$$state;if(N||n)N=!1,d.$evalAsync(function(){var b=m.absUrl(),c=d.$broadcast("$locationChangeStart",b,a,m.$$state,f).defaultPrevented;m.absUrl()===b&&(c?(m.$$parse(a),m.$$state=f):(n&&h(b,g,f===m.$$state?null:m.$$state),l(a,f)))})}m.$$replace=!1});return m}]}function Uf(){var a=!0,b=this;this.debugEnabled=function(b){return w(b)?(a=b,this):a};this.$get=["$window",function(d){function c(a){cc(a)&&(a.stack&& -f?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&&(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=d.console||{},e=b[a]||b.log||E;return function(){var a=[];r(arguments,function(b){a.push(c(b))});return Function.prototype.apply.call(e,b,a)}}var f=Ca||/\bEdge\//.test(d.navigator&&d.navigator.userAgent);return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return function(){a&&c.apply(b, -arguments)}}()}}]}function Og(a){return a+""}function Pg(a,b){return"undefined"!==typeof a?a:b}function Ed(a,b){return"undefined"===typeof a?b:"undefined"===typeof b?a:a+b}function Qg(a,b){switch(a.type){case q.MemberExpression:if(a.computed)return!1;break;case q.UnaryExpression:return 1;case q.BinaryExpression:return"+"!==a.operator?1:!1;case q.CallExpression:return!1}return void 0===b?Fd:b}function Z(a,b,d){var c,e,f=a.isPure=Qg(a,d);switch(a.type){case q.Program:c=!0;r(a.body,function(a){Z(a.expression, -b,f);c=c&&a.expression.constant});a.constant=c;break;case q.Literal:a.constant=!0;a.toWatch=[];break;case q.UnaryExpression:Z(a.argument,b,f);a.constant=a.argument.constant;a.toWatch=a.argument.toWatch;break;case q.BinaryExpression:Z(a.left,b,f);Z(a.right,b,f);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.left.toWatch.concat(a.right.toWatch);break;case q.LogicalExpression:Z(a.left,b,f);Z(a.right,b,f);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.constant?[]:[a];break;case q.ConditionalExpression:Z(a.test, -b,f);Z(a.alternate,b,f);Z(a.consequent,b,f);a.constant=a.test.constant&&a.alternate.constant&&a.consequent.constant;a.toWatch=a.constant?[]:[a];break;case q.Identifier:a.constant=!1;a.toWatch=[a];break;case q.MemberExpression:Z(a.object,b,f);a.computed&&Z(a.property,b,f);a.constant=a.object.constant&&(!a.computed||a.property.constant);a.toWatch=a.constant?[]:[a];break;case q.CallExpression:c=d=a.filter?!b(a.callee.name).$stateful:!1;e=[];r(a.arguments,function(a){Z(a,b,f);c=c&&a.constant;e.push.apply(e, -a.toWatch)});a.constant=c;a.toWatch=d?e:[a];break;case q.AssignmentExpression:Z(a.left,b,f);Z(a.right,b,f);a.constant=a.left.constant&&a.right.constant;a.toWatch=[a];break;case q.ArrayExpression:c=!0;e=[];r(a.elements,function(a){Z(a,b,f);c=c&&a.constant;e.push.apply(e,a.toWatch)});a.constant=c;a.toWatch=e;break;case q.ObjectExpression:c=!0;e=[];r(a.properties,function(a){Z(a.value,b,f);c=c&&a.value.constant;e.push.apply(e,a.value.toWatch);a.computed&&(Z(a.key,b,!1),c=c&&a.key.constant,e.push.apply(e, -a.key.toWatch))});a.constant=c;a.toWatch=e;break;case q.ThisExpression:a.constant=!1;a.toWatch=[];break;case q.LocalsExpression:a.constant=!1,a.toWatch=[]}}function Gd(a){if(1===a.length){a=a[0].expression;var b=a.toWatch;return 1!==b.length?b:b[0]!==a?b:void 0}}function Hd(a){return a.type===q.Identifier||a.type===q.MemberExpression}function Id(a){if(1===a.body.length&&Hd(a.body[0].expression))return{type:q.AssignmentExpression,left:a.body[0].expression,right:{type:q.NGValueParameter},operator:"="}} -function Jd(a){this.$filter=a}function Kd(a){this.$filter=a}function Mb(a,b,d){this.ast=new q(a,d);this.astCompiler=d.csp?new Kd(b):new Jd(b)}function Ac(a){return B(a.valueOf)?a.valueOf():Rg.call(a)}function Vf(){var a=T(),b={"true":!0,"false":!1,"null":null,undefined:void 0},d,c;this.addLiteral=function(a,c){b[a]=c};this.setIdentifierFns=function(a,b){d=a;c=b;return this};this.$get=["$filter",function(e){function f(b,c){var d,f;switch(typeof b){case "string":return f=b=b.trim(),d=a[f],d||(d=new Nb(G), -d=(new Mb(d,e,G)).parse(b),a[f]=p(d)),s(d,c);case "function":return s(b,c);default:return s(E,c)}}function g(a,b,c){return null==a||null==b?a===b:"object"!==typeof a||(a=Ac(a),"object"!==typeof a||c)?a===b||a!==a&&b!==b:!1}function k(a,b,c,d,e){var f=d.inputs,h;if(1===f.length){var k=g,f=f[0];return a.$watch(function(a){var b=f(a);g(b,k,f.isPure)||(h=d(a,void 0,void 0,[b]),k=b&&Ac(b));return h},b,c,e)}for(var l=[],m=[],n=0,p=f.length;n=c.$$state.status&&e&&e.length&&a(function(){for(var a,c,f=0,g=e.length;fa)for(b in l++,f)ta.call(e,b)||(t--,delete f[b])}else f!==e&&(f=e,l++);return l}}c.$$pure=g(a).literal;c.$stateful=!c.$$pure;var d=this,e,f,h,k=1r&&(z=4-r,N[z]|| -(N[z]=[]),N[z].push({msg:B(a.exp)?"fn: "+(a.exp.name||a.exp.toString()):a.exp,newVal:g,oldVal:h}));else if(a===c){s=!1;break a}}catch(E){f(E)}if(!(n=!q.$$suspended&&q.$$watchersCount&&q.$$childHead||q!==y&&q.$$nextSibling))for(;q!==y&&!(n=q.$$nextSibling);)q=q.$parent}while(q=n);if((s||w.length)&&!r--)throw v.$$phase=null,d("infdig",b,N);}while(s||w.length);for(v.$$phase=null;JCa)throw Ea("iequirks");var c=ja(V);c.isEnabled=function(){return a}; -c.trustAs=d.trustAs;c.getTrusted=d.getTrusted;c.valueOf=d.valueOf;a||(c.trustAs=c.getTrusted=function(a,b){return b},c.valueOf=Ta);c.parseAs=function(a,d){var e=b(d);return e.literal&&e.constant?e:b(d,function(b){return c.getTrusted(a,b)})};var e=c.parseAs,f=c.getTrusted,g=c.trustAs;r(V,function(a,b){var d=K(b);c[("parse_as_"+d).replace(Cc,wb)]=function(b){return e(a,b)};c[("get_trusted_"+d).replace(Cc,wb)]=function(b){return f(a,b)};c[("trust_as_"+d).replace(Cc,wb)]=function(b){return g(a,b)}}); -return c}]}function ag(){this.$get=["$window","$document",function(a,b){var d={},c=!((!a.nw||!a.nw.process)&&a.chrome&&(a.chrome.app&&a.chrome.app.runtime||!a.chrome.app&&a.chrome.runtime&&a.chrome.runtime.id))&&a.history&&a.history.pushState,e=fa((/android (\d+)/.exec(K((a.navigator||{}).userAgent))||[])[1]),f=/Boxee/i.test((a.navigator||{}).userAgent),g=b[0]||{},k=g.body&&g.body.style,h=!1,l=!1;k&&(h=!!("transition"in k||"webkitTransition"in k),l=!!("animation"in k||"webkitAnimation"in k));return{history:!(!c|| -4>e||f),hasEvent:function(a){if("input"===a&&Ca)return!1;if(z(d[a])){var b=g.createElement("div");d[a]="on"+a in b}return d[a]},csp:Aa(),transitions:h,animations:l,android:e}}]}function bg(){this.$get=ia(function(a){return new Tg(a)})}function Tg(a){function b(){var a=e.pop();return a&&a.cb}function d(a){for(var b=e.length-1;0<=b;--b){var c=e[b];if(c.type===a)return e.splice(b,1),c.cb}}var c={},e=[],f=this.ALL_TASKS_TYPE="$$all$$",g=this.DEFAULT_TASK_TYPE="$$default$$";this.completeTask=function(e, -h){h=h||g;try{e()}finally{var l;l=h||g;c[l]&&(c[l]--,c[f]--);l=c[h];var m=c[f];if(!m||!l)for(l=m?d:b;m=l(h);)try{m()}catch(p){a.error(p)}}};this.incTaskCount=function(a){a=a||g;c[a]=(c[a]||0)+1;c[f]=(c[f]||0)+1};this.notifyWhenNoPendingTasks=function(a,b){b=b||f;c[b]?e.push({type:b,cb:a}):a()}}function dg(){var a;this.httpOptions=function(b){return b?(a=b,this):a};this.$get=["$exceptionHandler","$templateCache","$http","$q","$sce",function(b,d,c,e,f){function g(k,h){g.totalPendingRequests++;if(!A(k)|| -z(d.get(k)))k=f.getTrustedResourceUrl(k);var l=c.defaults&&c.defaults.transformResponse;H(l)?l=l.filter(function(a){return a!==vc}):l===vc&&(l=null);return c.get(k,S({cache:d,transformResponse:l},a)).finally(function(){g.totalPendingRequests--}).then(function(a){return d.put(k,a.data)},function(a){h||(a=Ug("tpload",k,a.status,a.statusText),b(a));return e.reject(a)})}g.totalPendingRequests=0;return g}]}function eg(){this.$get=["$rootScope","$browser","$location",function(a,b,d){return{findBindings:function(a, -b,d){a=a.getElementsByClassName("ng-binding");var g=[];r(a,function(a){var c=ca.element(a).data("$binding");c&&r(c,function(c){d?(new RegExp("(^|\\s)"+Md(b)+"(\\s|\\||$)")).test(c)&&g.push(a):-1!==c.indexOf(b)&&g.push(a)})});return g},findModels:function(a,b,d){for(var g=["ng-","data-ng-","ng\\:"],k=0;kc&&(c=e),c+=+a.slice(e+1),a=a.substring(0,e)):0>c&&(c=a.length);for(e=0;a.charAt(e)===Ec;e++);if(e===(g=a.length))d=[0],c=1;else{for(g--;a.charAt(g)===Ec;)g--;c-=e;d=[];for(f=0;e<=g;e++,f++)d[f]=+a.charAt(e)}c>Wd&&(d=d.splice(0,Wd-1),b=c-1,c=1);return{d:d,e:b,i:c}}function dh(a,b,d,c){var e=a.d,f=e.length-a.i;b=z(b)?Math.min(Math.max(d,f),c):+b;d=b+a.i;c=e[d];if(0d-1){for(c=0;c>d;c--)e.unshift(0),a.i++;e.unshift(1);a.i++}else e[d-1]++;for(;fk;)h.unshift(0),k++;0=b.lgSize&&k.unshift(h.splice(-b.lgSize,h.length).join(""));h.length>b.gSize;)k.unshift(h.splice(-b.gSize,h.length).join(""));h.length&&k.unshift(h.join(""));h=k.join(d);f.length&&(h+=c+f.join(""));e&&(h+="e+"+e)}return 0>a&&!g?b.negPre+h+b.negSuf:b.posPre+h+b.posSuf}function Ob(a,b,d,c){var e="";if(0>a||c&&0>=a)c?a=-a+1:(a=-a,e="-");for(a=""+a;a.length-d)f+=d;0===f&&-12===d&&(f=12);return Ob(f,b,c,e)}}function kb(a,b,d){return function(c,e){var f=c["get"+a](),g=ub((d?"STANDALONE":"")+(b?"SHORT":"")+a);return e[g][f]}}function Xd(a){var b=(new Date(a,0,1)).getDay();return new Date(a,0,(4>=b?5:12)-b)}function Yd(a){return function(b){var d=Xd(b.getFullYear());b=+new Date(b.getFullYear(),b.getMonth(),b.getDate()+(4-b.getDay()))-+d;b=1+Math.round(b/6048E5);return Ob(b,a)}}function Fc(a,b){return 0>= -a.getFullYear()?b.ERAS[0]:b.ERAS[1]}function Rd(a){function b(a){var b;if(b=a.match(d)){a=new Date(0);var f=0,g=0,k=b[8]?a.setUTCFullYear:a.setFullYear,h=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=fa(b[9]+b[10]),g=fa(b[9]+b[11]));k.call(a,fa(b[1]),fa(b[2])-1,fa(b[3]));f=fa(b[4]||0)-f;g=fa(b[5]||0)-g;k=fa(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));h.call(a,f,g,k,b)}return a}var d=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c, -d,f){var g="",k=[],h,l;d=d||"mediumDate";d=a.DATETIME_FORMATS[d]||d;A(c)&&(c=eh.test(c)?fa(c):b(c));W(c)&&(c=new Date(c));if(!ha(c)||!isFinite(c.getTime()))return c;for(;d;)(l=fh.exec(d))?(k=db(k,l,1),d=k.pop()):(k.push(d),d=null);var m=c.getTimezoneOffset();f&&(m=ec(f,m),c=fc(c,f,!0));r(k,function(b){h=gh[b];g+=h?h(c,a.DATETIME_FORMATS,m):"''"===b?"'":b.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function Yg(){return function(a,b){z(b)&&(b=2);return eb(a,b)}}function Zg(){return function(a, -b,d){b=Infinity===Math.abs(Number(b))?Number(b):fa(b);if(X(b))return a;W(a)&&(a=a.toString());if(!ya(a))return a;d=!d||isNaN(d)?0:fa(d);d=0>d?Math.max(0,a.length+d):d;return 0<=b?Gc(a,d,d+b):0===d?Gc(a,b,a.length):Gc(a,Math.max(0,d+b),d)}}function Gc(a,b,d){return A(a)?a.slice(b,d):Ha.call(a,b,d)}function Td(a){function b(b){return b.map(function(b){var c=1,d=Ta;if(B(b))d=b;else if(A(b)){if("+"===b.charAt(0)||"-"===b.charAt(0))c="-"===b.charAt(0)?-1:1,b=b.substring(1);if(""!==b&&(d=a(b),d.constant))var e= -d(),d=function(a){return a[e]}}return{get:d,descending:c}})}function d(a){switch(typeof a){case "number":case "boolean":case "string":return!0;default:return!1}}function c(a,b){var c=0,d=a.type,h=b.type;if(d===h){var h=a.value,l=b.value;"string"===d?(h=h.toLowerCase(),l=l.toLowerCase()):"object"===d&&(D(h)&&(h=a.index),D(l)&&(l=b.index));h!==l&&(c=hb||37<=b&&40>=b||m(a,this,this.value)});if(e.hasEvent("paste"))b.on("paste cut drop",m)}b.on("change",l);if(ce[g]&&c.$$hasNativeValidators&&g===d.type)b.on("keydown wheel mousedown",function(a){if(!h){var b=this.validity, -c=b.badInput,d=b.typeMismatch;h=f.defer(function(){h=null;b.badInput===c&&b.typeMismatch===d||l(a)})}});c.$render=function(){var a=c.$isEmpty(c.$viewValue)?"":c.$viewValue;b.val()!==a&&b.val(a)}}function Qb(a,b){return function(d,c){var e,f;if(ha(d))return d;if(A(d)){'"'===d.charAt(0)&&'"'===d.charAt(d.length-1)&&(d=d.substring(1,d.length-1));if(hh.test(d))return new Date(d);a.lastIndex=0;if(e=a.exec(d))return e.shift(),f=c?{yyyy:c.getFullYear(),MM:c.getMonth()+1,dd:c.getDate(),HH:c.getHours(),mm:c.getMinutes(), -ss:c.getSeconds(),sss:c.getMilliseconds()/1E3}:{yyyy:1970,MM:1,dd:1,HH:0,mm:0,ss:0,sss:0},r(e,function(a,c){cf.yyyy&&e.setFullYear(f.yyyy),e}return NaN}}function nb(a,b,d,c){return function(e,f,g,k,h,l,m,p){function n(a){return a&&!(a.getTime&&a.getTime()!==a.getTime())}function s(a){return w(a)&&!ha(a)?r(a)||void 0:a}function r(a,b){var c=k.$options.getOption("timezone");v&&v!==c&&(b=Sc(b,ec(v)));var e=d(a, -b);!isNaN(e)&&c&&(e=fc(e,c));return e}Ic(e,f,g,k,a);Sa(e,f,g,k,h,l);var t="time"===a||"datetimelocal"===a,q,v;k.$parsers.push(function(c){if(k.$isEmpty(c))return null;if(b.test(c))return r(c,q);k.$$parserName=a});k.$formatters.push(function(a){if(a&&!ha(a))throw ob("datefmt",a);if(n(a)){q=a;var b=k.$options.getOption("timezone");b&&(v=b,q=fc(q,b,!0));var d=c;t&&A(k.$options.getOption("timeSecondsFormat"))&&(d=c.replace("ss.sss",k.$options.getOption("timeSecondsFormat")).replace(/:$/,""));a=m("date")(a, -d,b);t&&k.$options.getOption("timeStripZeroSeconds")&&(a=a.replace(/(?::00)?(?:\.000)?$/,""));return a}v=q=null;return""});if(w(g.min)||g.ngMin){var x=g.min||p(g.ngMin)(e),B=s(x);k.$validators.min=function(a){return!n(a)||z(B)||d(a)>=B};g.$observe("min",function(a){a!==x&&(B=s(a),x=a,k.$validate())})}if(w(g.max)||g.ngMax){var y=g.max||p(g.ngMax)(e),J=s(y);k.$validators.max=function(a){return!n(a)||z(J)||d(a)<=J};g.$observe("max",function(a){a!==y&&(J=s(a),y=a,k.$validate())})}}}function Ic(a,b,d, -c,e){(c.$$hasNativeValidators=D(b[0].validity))&&c.$parsers.push(function(a){var d=b.prop("validity")||{};if(d.badInput||d.typeMismatch)c.$$parserName=e;else return a})}function de(a){a.$parsers.push(function(b){if(a.$isEmpty(b))return null;if(ih.test(b))return parseFloat(b);a.$$parserName="number"});a.$formatters.push(function(b){if(!a.$isEmpty(b)){if(!W(b))throw ob("numfmt",b);b=b.toString()}return b})}function na(a){w(a)&&!W(a)&&(a=parseFloat(a));return X(a)?void 0:a}function Jc(a){var b=a.toString(), -d=b.indexOf(".");return-1===d?-1a&&(a=/e-(\d+)$/.exec(b))?Number(a[1]):0:b.length-d-1}function ee(a,b,d){a=Number(a);var c=(a|0)!==a,e=(b|0)!==b,f=(d|0)!==d;if(c||e||f){var g=c?Jc(a):0,k=e?Jc(b):0,h=f?Jc(d):0,g=Math.max(g,k,h),g=Math.pow(10,g);a*=g;b*=g;d*=g;c&&(a=Math.round(a));e&&(b=Math.round(b));f&&(d=Math.round(d))}return 0===(a-b)%d}function fe(a,b,d,c,e){if(w(c)){a=a(c);if(!a.constant)throw ob("constexpr",d,c);return a(b)}return e}function Kc(a,b){function d(a,b){if(!a||!a.length)return[]; -if(!b||!b.length)return a;var c=[],d=0;a:for(;d(?:<\/\1>|)$/,mc=/<|&#?\w+;/,mg=/<([\w:-]+)/,ng=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,oa={option:[1,'"],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"", -"
"],_default:[0,"",""]};oa.optgroup=oa.option;oa.tbody=oa.tfoot=oa.colgroup=oa.caption=oa.thead;oa.th=oa.td;var ug=C.Node.prototype.contains||function(a){return!!(this.compareDocumentPosition(a)&16)},Wa=Y.prototype={ready:fd,toString:function(){var a=[];r(this,function(b){a.push(""+b)});return"["+a.join(", ")+"]"},eq:function(a){return 0<=a?x(this[a]):x(this[this.length+a])},length:0,push:kh,sort:[].sort,splice:[].splice},Gb={};r("multiple selected checked disabled readOnly required open".split(" "), -function(a){Gb[K(a)]=a});var md={};r("input select option textarea button form details".split(" "),function(a){md[a]=!0});var td={ngMinlength:"minlength",ngMaxlength:"maxlength",ngMin:"min",ngMax:"max",ngPattern:"pattern",ngStep:"step"};r({data:rc,removeData:qc,hasData:function(a){for(var b in Ka[a.ng339])return!0;return!1},cleanData:function(a){for(var b=0,d=a.length;b/,xg=/^[^(]*\(\s*([^)]*)\)/m,nh=/,/,oh=/^\s*(_?)(\S+?)\1\s*$/,vg=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Ba=F("$injector"); -fb.$$annotate=function(a,b,d){var c;if("function"===typeof a){if(!(c=a.$inject)){c=[];if(a.length){if(b)throw A(d)&&d||(d=a.name||yg(a)),Ba("strictdi",d);b=od(a);r(b[1].split(nh),function(a){a.replace(oh,function(a,b,d){c.push(d)})})}a.$inject=c}}else H(a)?(b=a.length-1,sb(a[b],"fn"),c=a.slice(0,b)):sb(a,"fn",!0);return c};var je=F("$animate"),zf=function(){this.$get=E},Af=function(){var a=new Hb,b=[];this.$get=["$$AnimateRunner","$rootScope",function(d,c){function e(a,b,c){var d=!1;b&&(b=A(b)?b.split(" "): -H(b)?b:[],r(b,function(b){b&&(d=!0,a[b]=c)}));return d}function f(){r(b,function(b){var c=a.get(b);if(c){var d=zg(b.attr("class")),e="",f="";r(c,function(a,b){a!==!!d[b]&&(a?e+=(e.length?" ":"")+b:f+=(f.length?" ":"")+b)});r(b,function(a){e&&Db(a,e);f&&Cb(a,f)});a.delete(b)}});b.length=0}return{enabled:E,on:E,off:E,pin:E,push:function(g,k,h,l){l&&l();h=h||{};h.from&&g.css(h.from);h.to&&g.css(h.to);if(h.addClass||h.removeClass)if(k=h.addClass,l=h.removeClass,h=a.get(g)||{},k=e(h,k,!0),l=e(h,l,!1), -k||l)a.set(g,h),b.push(g),1===b.length&&c.$$postDigest(f);g=new d;g.complete();return g}}}]},xf=["$provide",function(a){var b=this,d=null,c=null;this.$$registeredAnimations=Object.create(null);this.register=function(c,d){if(c&&"."!==c.charAt(0))throw je("notcsel",c);var g=c+"-animation";b.$$registeredAnimations[c.substr(1)]=g;a.factory(g,d)};this.customFilter=function(a){1===arguments.length&&(c=B(a)?a:null);return c};this.classNameFilter=function(a){if(1===arguments.length&&(d=a instanceof RegExp? -a:null)&&/[(\s|\/)]ng-animate[(\s|\/)]/.test(d.toString()))throw d=null,je("nongcls","ng-animate");return d};this.$get=["$$animateQueue",function(a){function b(a,c,d){if(d){var e;a:{for(e=0;e <= >= && || ! = |".split(" "),function(a){Ub[a]=!0});var rh={n:"\n",f:"\f",r:"\r",t:"\t",v:"\v","'":"'",'"':'"'},Nb=function(a){this.options=a};Nb.prototype={constructor:Nb, -lex:function(a){this.text=a;this.index=0;for(this.tokens=[];this.index=a&&"string"===typeof a},isWhitespace:function(a){return" "===a||"\r"===a||"\t"===a||"\n"===a||"\v"===a||"\u00a0"===a},isIdentifierStart:function(a){return this.options.isIdentifierStart? -this.options.isIdentifierStart(a,this.codePointAt(a)):this.isValidIdentifierStart(a)},isValidIdentifierStart:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isIdentifierContinue:function(a){return this.options.isIdentifierContinue?this.options.isIdentifierContinue(a,this.codePointAt(a)):this.isValidIdentifierContinue(a)},isValidIdentifierContinue:function(a,b){return this.isValidIdentifierStart(a,b)||this.isNumber(a)},codePointAt:function(a){return 1===a.length?a.charCodeAt(0): -(a.charCodeAt(0)<<10)+a.charCodeAt(1)-56613888},peekMultichar:function(){var a=this.text.charAt(this.index),b=this.peek();if(!b)return a;var d=a.charCodeAt(0),c=b.charCodeAt(0);return 55296<=d&&56319>=d&&56320<=c&&57343>=c?a+b:a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,b,d){d=d||this.index;b=w(b)?"s "+b+"-"+this.index+" ["+this.text.substring(b,d)+"]":" "+d;throw Ya("lexerr",a,b,this.text);},readNumber:function(){for(var a="",b=this.index;this.index< -this.text.length;){var d=K(this.text.charAt(this.index));if("."===d||this.isNumber(d))a+=d;else{var c=this.peek();if("e"===d&&this.isExpOperator(c))a+=d;else if(this.isExpOperator(d)&&c&&this.isNumber(c)&&"e"===a.charAt(a.length-1))a+=d;else if(!this.isExpOperator(d)||c&&this.isNumber(c)||"e"!==a.charAt(a.length-1))break;else this.throwError("Invalid exponent")}this.index++}this.tokens.push({index:b,text:a,constant:!0,value:Number(a)})},readIdent:function(){var a=this.index;for(this.index+=this.peekMultichar().length;this.index< -this.text.length;){var b=this.peekMultichar();if(!this.isIdentifierContinue(b))break;this.index+=b.length}this.tokens.push({index:a,text:this.text.slice(a,this.index),identifier:!0})},readString:function(a){var b=this.index;this.index++;for(var d="",c=a,e=!1;this.index","<=",">=");)a={type:q.BinaryExpression,operator:b.text,left:a,right:this.additive()};return a},additive:function(){for(var a=this.multiplicative(),b;b=this.expect("+","-");)a={type:q.BinaryExpression, -operator:b.text,left:a,right:this.multiplicative()};return a},multiplicative:function(){for(var a=this.unary(),b;b=this.expect("*","/","%");)a={type:q.BinaryExpression,operator:b.text,left:a,right:this.unary()};return a},unary:function(){var a;return(a=this.expect("+","-","!"))?{type:q.UnaryExpression,operator:a.text,prefix:!0,argument:this.unary()}:this.primary()},primary:function(){var a;this.expect("(")?(a=this.filterChain(),this.consume(")")):this.expect("[")?a=this.arrayDeclaration():this.expect("{")? -a=this.object():this.selfReferential.hasOwnProperty(this.peek().text)?a=Ia(this.selfReferential[this.consume().text]):this.options.literals.hasOwnProperty(this.peek().text)?a={type:q.Literal,value:this.options.literals[this.consume().text]}:this.peek().identifier?a=this.identifier():this.peek().constant?a=this.constant():this.throwError("not a primary expression",this.peek());for(var b;b=this.expect("(","[",".");)"("===b.text?(a={type:q.CallExpression,callee:a,arguments:this.parseArguments()},this.consume(")")): -"["===b.text?(a={type:q.MemberExpression,object:a,property:this.expression(),computed:!0},this.consume("]")):"."===b.text?a={type:q.MemberExpression,object:a,property:this.identifier(),computed:!1}:this.throwError("IMPOSSIBLE");return a},filter:function(a){a=[a];for(var b={type:q.CallExpression,callee:this.identifier(),arguments:a,filter:!0};this.expect(":");)a.push(this.expression());return b},parseArguments:function(){var a=[];if(")"!==this.peekToken().text){do a.push(this.filterChain());while(this.expect(",")) -}return a},identifier:function(){var a=this.consume();a.identifier||this.throwError("is not a valid identifier",a);return{type:q.Identifier,name:a.text}},constant:function(){return{type:q.Literal,value:this.consume().value}},arrayDeclaration:function(){var a=[];if("]"!==this.peekToken().text){do{if(this.peek("]"))break;a.push(this.expression())}while(this.expect(","))}this.consume("]");return{type:q.ArrayExpression,elements:a}},object:function(){var a=[],b;if("}"!==this.peekToken().text){do{if(this.peek("}"))break; -b={type:q.Property,kind:"init"};this.peek().constant?(b.key=this.constant(),b.computed=!1,this.consume(":"),b.value=this.expression()):this.peek().identifier?(b.key=this.identifier(),b.computed=!1,this.peek(":")?(this.consume(":"),b.value=this.expression()):b.value=b.key):this.peek("[")?(this.consume("["),b.key=this.expression(),this.consume("]"),b.computed=!0,this.consume(":"),b.value=this.expression()):this.throwError("invalid key",this.peek());a.push(b)}while(this.expect(","))}this.consume("}"); -return{type:q.ObjectExpression,properties:a}},throwError:function(a,b){throw Ya("syntax",b.text,a,b.index+1,this.text,this.text.substring(b.index));},consume:function(a){if(0===this.tokens.length)throw Ya("ueoe",this.text);var b=this.expect(a);b||this.throwError("is unexpected, expecting ["+a+"]",this.peek());return b},peekToken:function(){if(0===this.tokens.length)throw Ya("ueoe",this.text);return this.tokens[0]},peek:function(a,b,d,c){return this.peekAhead(0,a,b,d,c)},peekAhead:function(a,b,d,c, -e){if(this.tokens.length>a){a=this.tokens[a];var f=a.text;if(f===b||f===d||f===c||f===e||!(b||d||c||e))return a}return!1},expect:function(a,b,d,c){return(a=this.peek(a,b,d,c))?(this.tokens.shift(),a):!1},selfReferential:{"this":{type:q.ThisExpression},$locals:{type:q.LocalsExpression}}};var Fd=2;Jd.prototype={compile:function(a){var b=this;this.state={nextId:0,filters:{},fn:{vars:[],body:[],own:{}},assign:{vars:[],body:[],own:{}},inputs:[]};Z(a,b.$filter);var d="",c;this.stage="assign";if(c=Id(a))this.state.computing= -"assign",d=this.nextId(),this.recurse(c,d),this.return_(d),d="fn.assign="+this.generateFunction("assign","s,v,l");c=Gd(a.body);b.stage="inputs";r(c,function(a,c){var d="fn"+c;b.state[d]={vars:[],body:[],own:{}};b.state.computing=d;var k=b.nextId();b.recurse(a,k);b.return_(k);b.state.inputs.push({name:d,isPure:a.isPure});a.watchId=c});this.state.computing="fn";this.stage="main";this.recurse(a);a='"'+this.USE+" "+this.STRICT+'";\n'+this.filterPrefix()+"var fn="+this.generateFunction("fn","s,l,a,i")+ -d+this.watchFns()+"return fn;";a=(new Function("$filter","getStringValue","ifDefined","plus",a))(this.$filter,Og,Pg,Ed);this.state=this.stage=void 0;return a},USE:"use",STRICT:"strict",watchFns:function(){var a=[],b=this.state.inputs,d=this;r(b,function(b){a.push("var "+b.name+"="+d.generateFunction(b.name,"s"));b.isPure&&a.push(b.name,".isPure="+JSON.stringify(b.isPure)+";")});b.length&&a.push("fn.inputs=["+b.map(function(a){return a.name}).join(",")+"];");return a.join("")},generateFunction:function(a, -b){return"function("+b+"){"+this.varsPrefix(a)+this.body(a)+"};"},filterPrefix:function(){var a=[],b=this;r(this.state.filters,function(d,c){a.push(d+"=$filter("+b.escape(c)+")")});return a.length?"var "+a.join(",")+";":""},varsPrefix:function(a){return this.state[a].vars.length?"var "+this.state[a].vars.join(",")+";":""},body:function(a){return this.state[a].body.join("")},recurse:function(a,b,d,c,e,f){var g,k,h=this,l,m,p;c=c||E;if(!f&&w(a.watchId))b=b||this.nextId(),this.if_("i",this.lazyAssign(b, -this.computedMember("i",a.watchId)),this.lazyRecurse(a,b,d,c,e,!0));else switch(a.type){case q.Program:r(a.body,function(b,c){h.recurse(b.expression,void 0,void 0,function(a){k=a});c!==a.body.length-1?h.current().body.push(k,";"):h.return_(k)});break;case q.Literal:m=this.escape(a.value);this.assign(b,m);c(b||m);break;case q.UnaryExpression:this.recurse(a.argument,void 0,void 0,function(a){k=a});m=a.operator+"("+this.ifDefined(k,0)+")";this.assign(b,m);c(m);break;case q.BinaryExpression:this.recurse(a.left, -void 0,void 0,function(a){g=a});this.recurse(a.right,void 0,void 0,function(a){k=a});m="+"===a.operator?this.plus(g,k):"-"===a.operator?this.ifDefined(g,0)+a.operator+this.ifDefined(k,0):"("+g+")"+a.operator+"("+k+")";this.assign(b,m);c(m);break;case q.LogicalExpression:b=b||this.nextId();h.recurse(a.left,b);h.if_("&&"===a.operator?b:h.not(b),h.lazyRecurse(a.right,b));c(b);break;case q.ConditionalExpression:b=b||this.nextId();h.recurse(a.test,b);h.if_(b,h.lazyRecurse(a.alternate,b),h.lazyRecurse(a.consequent, -b));c(b);break;case q.Identifier:b=b||this.nextId();d&&(d.context="inputs"===h.stage?"s":this.assign(this.nextId(),this.getHasOwnProperty("l",a.name)+"?l:s"),d.computed=!1,d.name=a.name);h.if_("inputs"===h.stage||h.not(h.getHasOwnProperty("l",a.name)),function(){h.if_("inputs"===h.stage||"s",function(){e&&1!==e&&h.if_(h.isNull(h.nonComputedMember("s",a.name)),h.lazyAssign(h.nonComputedMember("s",a.name),"{}"));h.assign(b,h.nonComputedMember("s",a.name))})},b&&h.lazyAssign(b,h.nonComputedMember("l", -a.name)));c(b);break;case q.MemberExpression:g=d&&(d.context=this.nextId())||this.nextId();b=b||this.nextId();h.recurse(a.object,g,void 0,function(){h.if_(h.notNull(g),function(){a.computed?(k=h.nextId(),h.recurse(a.property,k),h.getStringValue(k),e&&1!==e&&h.if_(h.not(h.computedMember(g,k)),h.lazyAssign(h.computedMember(g,k),"{}")),m=h.computedMember(g,k),h.assign(b,m),d&&(d.computed=!0,d.name=k)):(e&&1!==e&&h.if_(h.isNull(h.nonComputedMember(g,a.property.name)),h.lazyAssign(h.nonComputedMember(g, -a.property.name),"{}")),m=h.nonComputedMember(g,a.property.name),h.assign(b,m),d&&(d.computed=!1,d.name=a.property.name))},function(){h.assign(b,"undefined")});c(b)},!!e);break;case q.CallExpression:b=b||this.nextId();a.filter?(k=h.filter(a.callee.name),l=[],r(a.arguments,function(a){var b=h.nextId();h.recurse(a,b);l.push(b)}),m=k+"("+l.join(",")+")",h.assign(b,m),c(b)):(k=h.nextId(),g={},l=[],h.recurse(a.callee,k,g,function(){h.if_(h.notNull(k),function(){r(a.arguments,function(b){h.recurse(b,a.constant? -void 0:h.nextId(),void 0,function(a){l.push(a)})});m=g.name?h.member(g.context,g.name,g.computed)+"("+l.join(",")+")":k+"("+l.join(",")+")";h.assign(b,m)},function(){h.assign(b,"undefined")});c(b)}));break;case q.AssignmentExpression:k=this.nextId();g={};this.recurse(a.left,void 0,g,function(){h.if_(h.notNull(g.context),function(){h.recurse(a.right,k);m=h.member(g.context,g.name,g.computed)+a.operator+k;h.assign(b,m);c(b||m)})},1);break;case q.ArrayExpression:l=[];r(a.elements,function(b){h.recurse(b, -a.constant?void 0:h.nextId(),void 0,function(a){l.push(a)})});m="["+l.join(",")+"]";this.assign(b,m);c(b||m);break;case q.ObjectExpression:l=[];p=!1;r(a.properties,function(a){a.computed&&(p=!0)});p?(b=b||this.nextId(),this.assign(b,"{}"),r(a.properties,function(a){a.computed?(g=h.nextId(),h.recurse(a.key,g)):g=a.key.type===q.Identifier?a.key.name:""+a.key.value;k=h.nextId();h.recurse(a.value,k);h.assign(h.member(b,g,a.computed),k)})):(r(a.properties,function(b){h.recurse(b.value,a.constant?void 0: -h.nextId(),void 0,function(a){l.push(h.escape(b.key.type===q.Identifier?b.key.name:""+b.key.value)+":"+a)})}),m="{"+l.join(",")+"}",this.assign(b,m));c(b||m);break;case q.ThisExpression:this.assign(b,"s");c(b||"s");break;case q.LocalsExpression:this.assign(b,"l");c(b||"l");break;case q.NGValueParameter:this.assign(b,"v"),c(b||"v")}},getHasOwnProperty:function(a,b){var d=a+"."+b,c=this.current().own;c.hasOwnProperty(d)||(c[d]=this.nextId(!1,a+"&&("+this.escape(b)+" in "+a+")"));return c[d]},assign:function(a, -b){if(a)return this.current().body.push(a,"=",b,";"),a},filter:function(a){this.state.filters.hasOwnProperty(a)||(this.state.filters[a]=this.nextId(!0));return this.state.filters[a]},ifDefined:function(a,b){return"ifDefined("+a+","+this.escape(b)+")"},plus:function(a,b){return"plus("+a+","+b+")"},return_:function(a){this.current().body.push("return ",a,";")},if_:function(a,b,d){if(!0===a)b();else{var c=this.current().body;c.push("if(",a,"){");b();c.push("}");d&&(c.push("else{"),d(),c.push("}"))}}, -not:function(a){return"!("+a+")"},isNull:function(a){return a+"==null"},notNull:function(a){return a+"!=null"},nonComputedMember:function(a,b){var d=/[^$_a-zA-Z0-9]/g;return/^[$_a-zA-Z][$_a-zA-Z0-9]*$/.test(b)?a+"."+b:a+'["'+b.replace(d,this.stringEscapeFn)+'"]'},computedMember:function(a,b){return a+"["+b+"]"},member:function(a,b,d){return d?this.computedMember(a,b):this.nonComputedMember(a,b)},getStringValue:function(a){this.assign(a,"getStringValue("+a+")")},lazyRecurse:function(a,b,d,c,e,f){var g= -this;return function(){g.recurse(a,b,d,c,e,f)}},lazyAssign:function(a,b){var d=this;return function(){d.assign(a,b)}},stringEscapeRegex:/[^ a-zA-Z0-9]/g,stringEscapeFn:function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)},escape:function(a){if(A(a))return"'"+a.replace(this.stringEscapeRegex,this.stringEscapeFn)+"'";if(W(a))return a.toString();if(!0===a)return"true";if(!1===a)return"false";if(null===a)return"null";if("undefined"===typeof a)return"undefined";throw Ya("esc");},nextId:function(a, -b){var d="v"+this.state.nextId++;a||this.current().vars.push(d+(b?"="+b:""));return d},current:function(){return this.state[this.state.computing]}};Kd.prototype={compile:function(a){var b=this;Z(a,b.$filter);var d,c;if(d=Id(a))c=this.recurse(d);d=Gd(a.body);var e;d&&(e=[],r(d,function(a,c){var d=b.recurse(a);d.isPure=a.isPure;a.input=d;e.push(d);a.watchId=c}));var f=[];r(a.body,function(a){f.push(b.recurse(a.expression))});a=0===a.body.length?E:1===a.body.length?f[0]:function(a,b){var c;r(f,function(d){c= -d(a,b)});return c};c&&(a.assign=function(a,b,d){return c(a,d,b)});e&&(a.inputs=e);return a},recurse:function(a,b,d){var c,e,f=this,g;if(a.input)return this.inputs(a.input,a.watchId);switch(a.type){case q.Literal:return this.value(a.value,b);case q.UnaryExpression:return e=this.recurse(a.argument),this["unary"+a.operator](e,b);case q.BinaryExpression:return c=this.recurse(a.left),e=this.recurse(a.right),this["binary"+a.operator](c,e,b);case q.LogicalExpression:return c=this.recurse(a.left),e=this.recurse(a.right), -this["binary"+a.operator](c,e,b);case q.ConditionalExpression:return this["ternary?:"](this.recurse(a.test),this.recurse(a.alternate),this.recurse(a.consequent),b);case q.Identifier:return f.identifier(a.name,b,d);case q.MemberExpression:return c=this.recurse(a.object,!1,!!d),a.computed||(e=a.property.name),a.computed&&(e=this.recurse(a.property)),a.computed?this.computedMember(c,e,b,d):this.nonComputedMember(c,e,b,d);case q.CallExpression:return g=[],r(a.arguments,function(a){g.push(f.recurse(a))}), -a.filter&&(e=this.$filter(a.callee.name)),a.filter||(e=this.recurse(a.callee,!0)),a.filter?function(a,c,d,f){for(var p=[],n=0;n":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)>b(c,e,f,g);return d?{value:c}:c}},"binary<=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)<=b(c,e,f,g);return d?{value:c}:c}},"binary>=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)>=b(c,e,f,g);return d?{value:c}:c}},"binary&&":function(a,b,d){return function(c,e,f,g){c= -a(c,e,f,g)&&b(c,e,f,g);return d?{value:c}:c}},"binary||":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)||b(c,e,f,g);return d?{value:c}:c}},"ternary?:":function(a,b,d,c){return function(e,f,g,k){e=a(e,f,g,k)?b(e,f,g,k):d(e,f,g,k);return c?{value:e}:e}},value:function(a,b){return function(){return b?{context:void 0,name:void 0,value:a}:a}},identifier:function(a,b,d){return function(c,e,f,g){c=e&&a in e?e:c;d&&1!==d&&c&&null==c[a]&&(c[a]={});e=c?c[a]:void 0;return b?{context:c,name:a,value:e}: -e}},computedMember:function(a,b,d,c){return function(e,f,g,k){var h=a(e,f,g,k),l,m;null!=h&&(l=b(e,f,g,k),l+="",c&&1!==c&&h&&!h[l]&&(h[l]={}),m=h[l]);return d?{context:h,name:l,value:m}:m}},nonComputedMember:function(a,b,d,c){return function(e,f,g,k){e=a(e,f,g,k);c&&1!==c&&e&&null==e[b]&&(e[b]={});f=null!=e?e[b]:void 0;return d?{context:e,name:b,value:f}:f}},inputs:function(a,b){return function(d,c,e,f){return f?f[b]:a(d,c,e)}}};Mb.prototype={constructor:Mb,parse:function(a){a=this.getAst(a);var b= -this.astCompiler.compile(a.ast),d=a.ast;b.literal=0===d.body.length||1===d.body.length&&(d.body[0].expression.type===q.Literal||d.body[0].expression.type===q.ArrayExpression||d.body[0].expression.type===q.ObjectExpression);b.constant=a.ast.constant;b.oneTime=a.oneTime;return b},getAst:function(a){var b=!1;a=a.trim();":"===a.charAt(0)&&":"===a.charAt(1)&&(b=!0,a=a.substring(2));return{ast:this.ast.ast(a),oneTime:b}}};var Ea=F("$sce"),V={HTML:"html",CSS:"css",MEDIA_URL:"mediaUrl",URL:"url",RESOURCE_URL:"resourceUrl", -JS:"js"},Cc=/_([a-z])/g,Ug=F("$templateRequest"),Vg=F("$timeout"),aa=C.document.createElement("a"),Od=ga(C.location.href),Na;aa.href="http://[::1]";var Wg="[::1]"===aa.hostname;Pd.$inject=["$document"];dd.$inject=["$provide"];var Wd=22,Vd=".",Ec="0";Qd.$inject=["$locale"];Sd.$inject=["$locale"];var gh={yyyy:ea("FullYear",4,0,!1,!0),yy:ea("FullYear",2,0,!0,!0),y:ea("FullYear",1,0,!1,!0),MMMM:kb("Month"),MMM:kb("Month",!0),MM:ea("Month",2,1),M:ea("Month",1,1),LLLL:kb("Month",!1,!0),dd:ea("Date",2), -d:ea("Date",1),HH:ea("Hours",2),H:ea("Hours",1),hh:ea("Hours",2,-12),h:ea("Hours",1,-12),mm:ea("Minutes",2),m:ea("Minutes",1),ss:ea("Seconds",2),s:ea("Seconds",1),sss:ea("Milliseconds",3),EEEE:kb("Day"),EEE:kb("Day",!0),a:function(a,b){return 12>a.getHours()?b.AMPMS[0]:b.AMPMS[1]},Z:function(a,b,d){a=-1*d;return a=(0<=a?"+":"")+(Ob(Math[0=a.getFullYear()?b.ERANAMES[0]:b.ERANAMES[1]}}, -fh=/((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))([\s\S]*)/,eh=/^-?\d+$/;Rd.$inject=["$locale"];var $g=ia(K),ah=ia(ub);Td.$inject=["$parse"];var Me=ia({restrict:"E",compile:function(a,b){if(!b.href&&!b.xlinkHref)return function(a,b){if("a"===b[0].nodeName.toLowerCase()){var e="[object SVGAnimatedString]"===la.call(b.prop("href"))?"xlink:href":"href";b.on("click",function(a){b.attr(e)||a.preventDefault()})}}}}),vb={};r(Gb,function(a,b){function d(a,d,e){a.$watch(e[c], -function(a){e.$set(b,!!a)})}if("multiple"!==a){var c=wa("ng-"+b),e=d;"checked"===a&&(e=function(a,b,e){e.ngModel!==e[c]&&d(a,b,e)});vb[c]=function(){return{restrict:"A",priority:100,link:e}}}});r(td,function(a,b){vb[b]=function(){return{priority:100,link:function(a,c,e){if("ngPattern"===b&&"/"===e.ngPattern.charAt(0)&&(c=e.ngPattern.match(ie))){e.$set("ngPattern",new RegExp(c[1],c[2]));return}a.$watch(e[b],function(a){e.$set(b,a)})}}}});r(["src","srcset","href"],function(a){var b=wa("ng-"+a);vb[b]= -["$sce",function(d){return{priority:99,link:function(c,e,f){var g=a,k=a;"href"===a&&"[object SVGAnimatedString]"===la.call(e.prop("href"))&&(k="xlinkHref",f.$attr[k]="xlink:href",g=null);f.$set(b,d.getTrustedMediaUrl(f[b]));f.$observe(b,function(b){b?(f.$set(k,b),Ca&&g&&e.prop(g,f[k])):"href"===a&&f.$set(k,null)})}}}]});var lb={$addControl:E,$getControls:ia([]),$$renameControl:function(a,b){a.$name=b},$removeControl:E,$setValidity:E,$setDirty:E,$setPristine:E,$setSubmitted:E,$$setSubmitted:E};Pb.$inject= -["$element","$attrs","$scope","$animate","$interpolate"];Pb.prototype={$rollbackViewValue:function(){r(this.$$controls,function(a){a.$rollbackViewValue()})},$commitViewValue:function(){r(this.$$controls,function(a){a.$commitViewValue()})},$addControl:function(a){Ja(a.$name,"input");this.$$controls.push(a);a.$name&&(this[a.$name]=a);a.$$parentForm=this},$getControls:function(){return ja(this.$$controls)},$$renameControl:function(a,b){var d=a.$name;this[d]===a&&delete this[d];this[b]=a;a.$name=b},$removeControl:function(a){a.$name&& -this[a.$name]===a&&delete this[a.$name];r(this.$pending,function(b,d){this.$setValidity(d,null,a)},this);r(this.$error,function(b,d){this.$setValidity(d,null,a)},this);r(this.$$success,function(b,d){this.$setValidity(d,null,a)},this);cb(this.$$controls,a);a.$$parentForm=lb},$setDirty:function(){this.$$animate.removeClass(this.$$element,Za);this.$$animate.addClass(this.$$element,Vb);this.$dirty=!0;this.$pristine=!1;this.$$parentForm.$setDirty()},$setPristine:function(){this.$$animate.setClass(this.$$element, -Za,Vb+" ng-submitted");this.$dirty=!1;this.$pristine=!0;this.$submitted=!1;r(this.$$controls,function(a){a.$setPristine()})},$setUntouched:function(){r(this.$$controls,function(a){a.$setUntouched()})},$setSubmitted:function(){for(var a=this;a.$$parentForm&&a.$$parentForm!==lb;)a=a.$$parentForm;a.$$setSubmitted()},$$setSubmitted:function(){this.$$animate.addClass(this.$$element,"ng-submitted");this.$submitted=!0;r(this.$$controls,function(a){a.$$setSubmitted&&a.$$setSubmitted()})}};ae({clazz:Pb,set:function(a, -b,d){var c=a[b];c?-1===c.indexOf(d)&&c.push(d):a[b]=[d]},unset:function(a,b,d){var c=a[b];c&&(cb(c,d),0===c.length&&delete a[b])}});var ke=function(a){return["$timeout","$parse",function(b,d){function c(a){return""===a?d('this[""]').assign:d(a).assign||E}return{name:"form",restrict:a?"EAC":"E",require:["form","^^?form"],controller:Pb,compile:function(d,f){d.addClass(Za).addClass(mb);var g=f.name?"name":a&&f.ngForm?"ngForm":!1;return{pre:function(a,d,e,f){var p=f[0];if(!("action"in e)){var n=function(b){a.$apply(function(){p.$commitViewValue(); -p.$setSubmitted()});b.preventDefault()};d[0].addEventListener("submit",n);d.on("$destroy",function(){b(function(){d[0].removeEventListener("submit",n)},0,!1)})}(f[1]||p.$$parentForm).$addControl(p);var s=g?c(p.$name):E;g&&(s(a,p),e.$observe(g,function(b){p.$name!==b&&(s(a,void 0),p.$$parentForm.$$renameControl(p,b),s=c(p.$name),s(a,p))}));d.on("$destroy",function(){p.$$parentForm.$removeControl(p);s(a,void 0);S(p,lb)})}}}}}]},Ne=ke(),Ze=ke(!0),hh=/^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)$/, -sh=/^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i,th=/^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+(\.[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/,ih=/^\s*(-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/,le=/^(\d{4,})-(\d{2})-(\d{2})$/,me=/^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,Mc=/^(\d{4,})-W(\d\d)$/,ne=/^(\d{4,})-(\d\d)$/, -oe=/^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,ce=T();r(["date","datetime-local","month","time","week"],function(a){ce[a]=!0});var pe={text:function(a,b,d,c,e,f){Sa(a,b,d,c,e,f);Hc(c)},date:nb("date",le,Qb(le,["yyyy","MM","dd"]),"yyyy-MM-dd"),"datetime-local":nb("datetimelocal",me,Qb(me,"yyyy MM dd HH mm ss sss".split(" ")),"yyyy-MM-ddTHH:mm:ss.sss"),time:nb("time",oe,Qb(oe,["HH","mm","ss","sss"]),"HH:mm:ss.sss"),week:nb("week",Mc,function(a,b){if(ha(a))return a;if(A(a)){Mc.lastIndex=0;var d=Mc.exec(a); -if(d){var c=+d[1],e=+d[2],f=d=0,g=0,k=0,h=Xd(c),e=7*(e-1);b&&(d=b.getHours(),f=b.getMinutes(),g=b.getSeconds(),k=b.getMilliseconds());return new Date(c,0,h.getDate()+e,d,f,g,k)}}return NaN},"yyyy-Www"),month:nb("month",ne,Qb(ne,["yyyy","MM"]),"yyyy-MM"),number:function(a,b,d,c,e,f,g,k){Ic(a,b,d,c,"number");de(c);Sa(a,b,d,c,e,f);var h;if(w(d.min)||d.ngMin){var l=d.min||k(d.ngMin)(a);h=na(l);c.$validators.min=function(a,b){return c.$isEmpty(b)||z(h)||b>=h};d.$observe("min",function(a){a!==l&&(h=na(a), -l=a,c.$validate())})}if(w(d.max)||d.ngMax){var m=d.max||k(d.ngMax)(a),p=na(m);c.$validators.max=function(a,b){return c.$isEmpty(b)||z(p)||b<=p};d.$observe("max",function(a){a!==m&&(p=na(a),m=a,c.$validate())})}if(w(d.step)||d.ngStep){var n=d.step||k(d.ngStep)(a),s=na(n);c.$validators.step=function(a,b){return c.$isEmpty(b)||z(s)||ee(b,h||0,s)};d.$observe("step",function(a){a!==n&&(s=na(a),n=a,c.$validate())})}},url:function(a,b,d,c,e,f){Sa(a,b,d,c,e,f);Hc(c);c.$validators.url=function(a,b){var d= -a||b;return c.$isEmpty(d)||sh.test(d)}},email:function(a,b,d,c,e,f){Sa(a,b,d,c,e,f);Hc(c);c.$validators.email=function(a,b){var d=a||b;return c.$isEmpty(d)||th.test(d)}},radio:function(a,b,d,c){var e=!d.ngTrim||"false"!==U(d.ngTrim);z(d.name)&&b.attr("name",++pb);b.on("change",function(a){var g;b[0].checked&&(g=d.value,e&&(g=U(g)),c.$setViewValue(g,a&&a.type))});c.$render=function(){var a=d.value;e&&(a=U(a));b[0].checked=a===c.$viewValue};d.$observe("value",c.$render)},range:function(a,b,d,c,e,f){function g(a, -c){b.attr(a,d[a]);var e=d[a];d.$observe(a,function(a){a!==e&&(e=a,c(a))})}function k(a){p=na(a);X(c.$modelValue)||(m?(a=b.val(),p>a&&(a=p,b.val(a)),c.$setViewValue(a)):c.$validate())}function h(a){n=na(a);X(c.$modelValue)||(m?(a=b.val(),n=p},g("min",k));e&&(n=na(d.max),c.$validators.max=m?function(){return!0}:function(a,b){return c.$isEmpty(b)||z(n)||b<=n},g("max",h));f&&(s=na(d.step),c.$validators.step=m?function(){return!r.stepMismatch}: -function(a,b){return c.$isEmpty(b)||z(s)||ee(b,p||0,s)},g("step",l))},checkbox:function(a,b,d,c,e,f,g,k){var h=fe(k,a,"ngTrueValue",d.ngTrueValue,!0),l=fe(k,a,"ngFalseValue",d.ngFalseValue,!1);b.on("change",function(a){c.$setViewValue(b[0].checked,a&&a.type)});c.$render=function(){b[0].checked=c.$viewValue};c.$isEmpty=function(a){return!1===a};c.$formatters.push(function(a){return va(a,h)});c.$parsers.push(function(a){return a?h:l})},hidden:E,button:E,submit:E,reset:E,file:E},Yc=["$browser","$sniffer", -"$filter","$parse",function(a,b,d,c){return{restrict:"E",require:["?ngModel"],link:{pre:function(e,f,g,k){k[0]&&(pe[K(g.type)]||pe.text)(e,f,g,k[0],b,a,d,c)}}}}],vf=function(){var a={configurable:!0,enumerable:!1,get:function(){return this.getAttribute("value")||""},set:function(a){this.setAttribute("value",a)}};return{restrict:"E",priority:200,compile:function(b,d){if("hidden"===K(d.type))return{pre:function(b,d,f,g){b=d[0];b.parentNode&&b.parentNode.insertBefore(b,b.nextSibling);Object.defineProperty&& -Object.defineProperty(b,"value",a)}}}}},uh=/^(true|false|\d+)$/,sf=function(){function a(a,d,c){var e=w(c)?c:9===Ca?"":null;a.prop("value",e);d.$set("value",c)}return{restrict:"A",priority:100,compile:function(b,d){return uh.test(d.ngValue)?function(b,d,f){b=b.$eval(f.ngValue);a(d,f,b)}:function(b,d,f){b.$watch(f.ngValue,function(b){a(d,f,b)})}}}},Re=["$compile",function(a){return{restrict:"AC",compile:function(b){a.$$addBindingClass(b);return function(b,c,e){a.$$addBindingInfo(c,e.ngBind);c=c[0]; -b.$watch(e.ngBind,function(a){c.textContent=ic(a)})}}}}],Te=["$interpolate","$compile",function(a,b){return{compile:function(d){b.$$addBindingClass(d);return function(c,d,f){c=a(d.attr(f.$attr.ngBindTemplate));b.$$addBindingInfo(d,c.expressions);d=d[0];f.$observe("ngBindTemplate",function(a){d.textContent=z(a)?"":a})}}}}],Se=["$sce","$parse","$compile",function(a,b,d){return{restrict:"A",compile:function(c,e){var f=b(e.ngBindHtml),g=b(e.ngBindHtml,function(b){return a.valueOf(b)});d.$$addBindingClass(c); -return function(b,c,e){d.$$addBindingInfo(c,e.ngBindHtml);b.$watch(g,function(){var d=f(b);c.html(a.getTrustedHtml(d)||"")})}}}}],rf=ia({restrict:"A",require:"ngModel",link:function(a,b,d,c){c.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),Ue=Kc("",!0),We=Kc("Odd",0),Ve=Kc("Even",1),Xe=Ra({compile:function(a,b){b.$set("ngCloak",void 0);a.removeClass("ng-cloak")}}),Ye=[function(){return{restrict:"A",scope:!0,controller:"@",priority:500}}],cd={},vh={blur:!0,focus:!0};r("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "), -function(a){var b=wa("ng-"+a);cd[b]=["$parse","$rootScope","$exceptionHandler",function(d,c,e){return qd(d,c,e,b,a,vh[a])}]});var af=["$animate","$compile",function(a,b){return{multiElement:!0,transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(d,c,e,f,g){var k,h,l;d.$watch(e.ngIf,function(d){d?h||g(function(d,f){h=f;d[d.length++]=b.$$createComment("end ngIf",e.ngIf);k={clone:d};a.enter(d,c.parent(),c)}):(l&&(l.remove(),l=null),h&&(h.$destroy(),h=null),k&&(l=tb(k.clone), -a.leave(l).done(function(a){!1!==a&&(l=null)}),k=null))})}}}],bf=["$templateRequest","$anchorScroll","$animate",function(a,b,d){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",controller:ca.noop,compile:function(c,e){var f=e.ngInclude||e.src,g=e.onload||"",k=e.autoscroll;return function(c,e,m,p,n){var r=0,q,t,x,v=function(){t&&(t.remove(),t=null);q&&(q.$destroy(),q=null);x&&(d.leave(x).done(function(a){!1!==a&&(t=null)}),t=x,x=null)};c.$watch(f,function(f){var m=function(a){!1=== -a||!w(k)||k&&!c.$eval(k)||b()},t=++r;f?(a(f,!0).then(function(a){if(!c.$$destroyed&&t===r){var b=c.$new();p.template=a;a=n(b,function(a){v();d.enter(a,null,e).done(m)});q=b;x=a;q.$emit("$includeContentLoaded",f);c.$eval(g)}},function(){c.$$destroyed||t!==r||(v(),c.$emit("$includeContentError",f))}),c.$emit("$includeContentRequested",f)):(v(),p.template=null)})}}}}],uf=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(b,d,c,e){la.call(d[0]).match(/SVG/)? -(d.empty(),a(ed(e.template,C.document).childNodes)(b,function(a){d.append(a)},{futureParentElement:d})):(d.html(e.template),a(d.contents())(b))}}}],cf=Ra({priority:450,compile:function(){return{pre:function(a,b,d){a.$eval(d.ngInit)}}}}),qf=function(){return{restrict:"A",priority:100,require:"ngModel",link:function(a,b,d,c){var e=d.ngList||", ",f="false"!==d.ngTrim,g=f?U(e):e;c.$parsers.push(function(a){if(!z(a)){var b=[];a&&r(a.split(g),function(a){a&&b.push(f?U(a):a)});return b}});c.$formatters.push(function(a){if(H(a))return a.join(e)}); -c.$isEmpty=function(a){return!a||!a.length}}}},mb="ng-valid",$d="ng-invalid",Za="ng-pristine",Vb="ng-dirty",ob=F("ngModel");Rb.$inject="$scope $exceptionHandler $attrs $element $parse $animate $timeout $q $interpolate".split(" ");Rb.prototype={$$initGetterSetters:function(){if(this.$options.getOption("getterSetter")){var a=this.$$parse(this.$$attr.ngModel+"()"),b=this.$$parse(this.$$attr.ngModel+"($$$p)");this.$$ngModelGet=function(b){var c=this.$$parsedNgModel(b);B(c)&&(c=a(b));return c};this.$$ngModelSet= -function(a,c){B(this.$$parsedNgModel(a))?b(a,{$$$p:c}):this.$$parsedNgModelAssign(a,c)}}else if(!this.$$parsedNgModel.assign)throw ob("nonassign",this.$$attr.ngModel,za(this.$$element));},$render:E,$isEmpty:function(a){return z(a)||""===a||null===a||a!==a},$$updateEmptyClasses:function(a){this.$isEmpty(a)?(this.$$animate.removeClass(this.$$element,"ng-not-empty"),this.$$animate.addClass(this.$$element,"ng-empty")):(this.$$animate.removeClass(this.$$element,"ng-empty"),this.$$animate.addClass(this.$$element, -"ng-not-empty"))},$setPristine:function(){this.$dirty=!1;this.$pristine=!0;this.$$animate.removeClass(this.$$element,Vb);this.$$animate.addClass(this.$$element,Za)},$setDirty:function(){this.$dirty=!0;this.$pristine=!1;this.$$animate.removeClass(this.$$element,Za);this.$$animate.addClass(this.$$element,Vb);this.$$parentForm.$setDirty()},$setUntouched:function(){this.$touched=!1;this.$untouched=!0;this.$$animate.setClass(this.$$element,"ng-untouched","ng-touched")},$setTouched:function(){this.$touched= -!0;this.$untouched=!1;this.$$animate.setClass(this.$$element,"ng-touched","ng-untouched")},$rollbackViewValue:function(){this.$$timeout.cancel(this.$$pendingDebounce);this.$viewValue=this.$$lastCommittedViewValue;this.$render()},$validate:function(){if(!X(this.$modelValue)){var a=this.$$lastCommittedViewValue,b=this.$$rawModelValue,d=this.$valid,c=this.$modelValue,e=this.$options.getOption("allowInvalid"),f=this;this.$$runValidators(b,a,function(a){e||d===a||(f.$modelValue=a?b:void 0,f.$modelValue!== -c&&f.$$writeModelToScope())})}},$$runValidators:function(a,b,d){function c(){var c=!0;r(h.$validators,function(d,e){var g=Boolean(d(a,b));c=c&&g;f(e,g)});return c?!0:(r(h.$asyncValidators,function(a,b){f(b,null)}),!1)}function e(){var c=[],d=!0;r(h.$asyncValidators,function(e,g){var h=e(a,b);if(!h||!B(h.then))throw ob("nopromise",h);f(g,void 0);c.push(h.then(function(){f(g,!0)},function(){d=!1;f(g,!1)}))});c.length?h.$$q.all(c).then(function(){g(d)},E):g(!0)}function f(a,b){k===h.$$currentValidationRunId&& -h.$setValidity(a,b)}function g(a){k===h.$$currentValidationRunId&&d(a)}this.$$currentValidationRunId++;var k=this.$$currentValidationRunId,h=this;(function(){var a=h.$$parserName;if(z(h.$$parserValid))f(a,null);else return h.$$parserValid||(r(h.$validators,function(a,b){f(b,null)}),r(h.$asyncValidators,function(a,b){f(b,null)})),f(a,h.$$parserValid),h.$$parserValid;return!0})()?c()?e():g(!1):g(!1)},$commitViewValue:function(){var a=this.$viewValue;this.$$timeout.cancel(this.$$pendingDebounce);if(this.$$lastCommittedViewValue!== -a||""===a&&this.$$hasNativeValidators)this.$$updateEmptyClasses(a),this.$$lastCommittedViewValue=a,this.$pristine&&this.$setDirty(),this.$$parseAndValidate()},$$parseAndValidate:function(){var a=this.$$lastCommittedViewValue,b=this;this.$$parserValid=z(a)?void 0:!0;this.$setValidity(this.$$parserName,null);this.$$parserName="parse";if(this.$$parserValid)for(var d=0;dg||e.$isEmpty(b)||b.length<=g}}}}}],ad=["$parse",function(a){return{restrict:"A",require:"?ngModel",link:function(b,d,c,e){if(e){var f=c.minlength||a(c.ngMinlength)(b),g=Tb(f)||-1;c.$observe("minlength",function(a){f!== -a&&(g=Tb(a)||-1,f=a,e.$validate())});e.$validators.minlength=function(a,b){return e.$isEmpty(b)||b.length>=g}}}}}];C.angular.bootstrap?C.console&&console.log("WARNING: Tried to load AngularJS more than once."):(Fe(),Je(ca),ca.module("ngLocale",[],["$provide",function(a){function b(a){a+="";var b=a.indexOf(".");return-1==b?0:a.length-b-1}a.value("$locale",{DATETIME_FORMATS:{AMPMS:["AM","PM"],DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),ERANAMES:["Before Christ","Anno Domini"], -ERAS:["BC","AD"],FIRSTDAYOFWEEK:6,MONTH:"January February March April May June July August September October November December".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),STANDALONEMONTH:"January February March April May June July August September October November December".split(" "),WEEKENDRANGE:[5,6],fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",medium:"MMM d, y h:mm:ss a",mediumDate:"MMM d, y",mediumTime:"h:mm:ss a", -"short":"M/d/yy h:mm a",shortDate:"M/d/yy",shortTime:"h:mm a"},NUMBER_FORMATS:{CURRENCY_SYM:"$",DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{gSize:3,lgSize:3,maxFrac:3,minFrac:0,minInt:1,negPre:"-",negSuf:"",posPre:"",posSuf:""},{gSize:3,lgSize:3,maxFrac:2,minFrac:2,minInt:1,negPre:"-\u00a4",negSuf:"",posPre:"\u00a4",posSuf:""}]},id:"en-us",localeID:"en_US",pluralCat:function(a,c){var e=a|0,f=c;void 0===f&&(f=Math.min(b(a),3));Math.pow(10,f);return 1==e&&0==f?"one":"other"}})}]),x(function(){Ae(C.document, -Uc)}))})(window);!window.angular.$$csp().noInlineStyle&&window.angular.element(document.head).prepend(''); -//# sourceMappingURL=angular.min.js.map diff --git a/admin/static/js/app.js b/admin/static/js/app.js deleted file mode 100644 index 9b6fc13b..00000000 --- a/admin/static/js/app.js +++ /dev/null @@ -1,2988 +0,0 @@ -angular.module("FICApp", ["ngRoute", "ngResource", "ngSanitize"]) - .config(function ($routeProvider, $locationProvider) { - $routeProvider - .when("/themes", { - controller: "ThemesListController", - templateUrl: "views/theme-list.html" - }) - .when("/themes/:themeId", { - controller: "ThemeController", - templateUrl: "views/theme.html" - }) - .when("/themes/:themeId/exercices/:exerciceId", { - controller: "ExerciceController", - templateUrl: "views/exercice.html" - }) - .when("/repositories", { - controller: "RepositoriesController", - templateUrl: "views/repositories.html" - }) - .when("/sync", { - controller: "SyncController", - templateUrl: "views/sync.html" - }) - .when("/settings", { - controller: "SettingsController", - templateUrl: "views/settings.html" - }) - .when("/auth", { - controller: "AuthController", - templateUrl: "views/auth.html" - }) - .when("/pki", { - controller: "PKIController", - templateUrl: "views/pki.html" - }) - .when("/exercices", { - controller: "AllExercicesListController", - templateUrl: "views/exercice-list.html" - }) - .when("/exercices/:exerciceId", { - controller: "ExerciceController", - templateUrl: "views/exercice.html" - }) - .when("/exercices/:exerciceId/flags", { - controller: "ExerciceController", - templateUrl: "views/exercice-flags.html" - }) - .when("/exercices/:exerciceId/resolution", { - controller: "ExerciceController", - templateUrl: "views/exercice-resolution.html" - }) - .when("/forge-links", { - controller: "ForgeLinksController", - templateUrl: "views/exercices-forgelink.html" - }) - .when("/tags", { - controller: "TagsListController", - templateUrl: "views/tags.html" - }) - .when("/teams", { - controller: "TeamsListController", - templateUrl: "views/team-list.html" - }) - .when("/teams/print", { - controller: "TeamsListController", - templateUrl: "views/team-print.html" - }) - .when("/teams/export", { - controller: "TeamsListController", - templateUrl: "views/team-export.html" - }) - .when("/teams/:teamId", { - controller: "TeamController", - templateUrl: "views/team-edit.html" - }) - .when("/teams/:teamId/stats", { - controller: "TeamController", - templateUrl: "views/team-stats.html" - }) - .when("/teams/:teamId/score", { - controller: "TeamController", - templateUrl: "views/team-score.html" - }) - .when("/public/:screenId", { - controller: "PublicController", - templateUrl: "views/public.html" - }) - .when("/files", { - controller: "FilesListController", - templateUrl: "views/file-list.html" - }) - .when("/events", { - controller: "EventsListController", - templateUrl: "views/event-list.html" - }) - .when("/events/:eventId", { - controller: "EventController", - templateUrl: "views/event.html" - }) - .when("/claims", { - controller: "ClaimsListController", - templateUrl: "views/claim-list.html" - }) - .when("/claims/:claimId", { - controller: "ClaimController", - templateUrl: "views/claim.html" - }) - .when("/", { - templateUrl: "views/home.html" - }); - $locationProvider.html5Mode(true); - }); - -function setCookie(name, value, days) { - var expires; - - if (days) { - var date = new Date(); - date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); - expires = "; expires=" + date.toGMTString(); - } else { - expires = ""; - } - document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/"; -} - -function getCookie(name) { - var nameEQ = encodeURIComponent(name) + "="; - var ca = document.cookie.split(';'); - for (var i = 0; i < ca.length; i++) { - var c = ca[i]; - while (c.charAt(0) === ' ') - c = c.substring(1, c.length); - if (c.indexOf(nameEQ) === 0) - return decodeURIComponent(c.substring(nameEQ.length, c.length)); - } - return null; -} - -angular.module("FICApp") - .directive('autofocus', ['$timeout', function ($timeout) { - return { - restrict: 'A', - link: function ($scope, $element) { - $timeout(function () { - $element[0].focus(); - }); - } - } - }]) - - .component('toast', { - bindings: { - date: '=', - msg: '=', - timeout: '=', - title: '=', - variant: '=', - yesNo: '=', - onyes: '=', - onno: '=', - }, - controller: function ($element) { - if (this.timeout === 0) - $element.children(0).toast({ autohide: false }); - else if (!this.timeout && this.timeout !== 0) - $element.children(0).toast({ delay: 7000 }); - else - $element.children(0).toast({ delay: this.timeout }); - $element.children(0).toast('show'); - $element.children(0).on('hidden.bs.toast', function () { - $element.children(0).toast('dispose'); - }); - this.yesFunc = function () { - $element.children(0).toast('hide'); - if (this.onyes) - this.onyes(); - } - this.noFunc = function () { - $element.children(0).toast('hide'); - if (this.onno) - this.onno(); - } - }, - template: `` - }); - -angular.module("FICApp") - .factory("Version", function ($resource) { - return $resource("api/version") - }) - .factory("Timestamp", function ($resource) { - return $resource("api/timestamps.json") - }) - .factory("Health", function ($resource) { - return $resource("api/health.json") - }) - .factory("Monitor", function ($resource) { - return $resource("api/monitor/:machineId", { machineId: '@id' }) - }) - .factory("Event", function ($resource) { - return $resource("api/events/:eventId", { eventId: '@id' }, { - 'update': { method: 'PUT' }, - }) - }) - .factory("Claim", function ($resource) { - return $resource("api/claims/:claimId", { claimId: '@id' }, { - 'update': { method: 'PUT' }, - }) - }) - .factory("ClaimAssignee", function ($resource) { - return $resource("api/claims-assignees/:assigneeId", { assigneeId: '@id' }, { - 'update': { method: 'PUT' }, - }) - }) - .factory("Certificate", function ($resource) { - return $resource("api/certs/:serial", { serial: '@id' }, { - 'update': { method: 'PUT' }, - }) - }) - .factory("CACertificate", function ($resource) { - return $resource("api/ca/:serial", { serial: '@id' }) - }) - .factory("File", function ($resource) { - return $resource("api/files/:fileId", { fileId: '@id' }) - }) - .factory("Settings", function ($resource) { - return $resource("api/settings.json", null, { - 'update': { method: 'PUT' }, - }) - }) - .factory("NextSettings", function ($resource) { - return $resource("api/settings-next/:tsId", { tsId: '@id' }, { - 'update': { method: 'PUT' }, - }) - }) - .factory("SettingsChallenge", function ($resource) { - return $resource("api/challenge.json", null, { - 'update': { method: 'PUT' }, - }) - }) - .factory("Scene", function ($resource) { - return $resource("api/public/:screenId", { screenId: '@id' }, { - 'update': { method: 'PUT' }, - }) - }) - .factory("Team", function ($resource) { - return $resource("api/teams/:teamId", { teamId: '@id' }, { - 'update': { method: 'PUT' }, - }) - }) - .factory("TeamCertificate", function ($resource) { - return $resource("api/teams/:teamId/certificates", { teamId: '@id' }) - }) - .factory("TeamAssociation", function ($resource) { - return $resource("api/teams/:teamId/associations/:assoc", { teamId: '@teamId', assoc: '@assoc' }) - }) - .factory("TeamMember", function ($resource) { - return $resource("api/teams/:teamId/members", { teamId: '@id' }, { - 'save': { method: 'PUT' }, - }) - }) - .factory("TeamMy", function ($resource) { - return $resource("api/teams/:teamId/my.json", { teamId: '@id' }) - }) - .factory("Teams", function ($resource) { - return $resource("api/teams.json") - }) - .factory("TeamHistory", function ($resource) { - return $resource("api/teams/:teamId/history.json", { teamId: '@id' }) - }) - .factory("TeamScore", function ($resource) { - return $resource("api/teams/:teamId/score-grid.json", { teamId: '@id' }) - }) - .factory("TeamStats", function ($resource) { - return $resource("api/teams/:teamId/stats.json", { teamId: '@id' }) - }) - .factory("TeamPresence", function ($resource) { - return $resource("api/teams/:teamId/tries", { teamId: '@id' }) - }) - .factory("Theme", function ($resource) { - return $resource("api/themes/:themeId", { themeId: '@id' }, { - update: { method: 'PUT' } - }); - }) - .factory("Themes", function ($resource) { - return $resource("api/themes.json", null, { - 'get': { method: 'GET' }, - }) - }) - .factory("ThemedExercice", function ($resource) { - return $resource("api/themes/:themeId/exercices/:exerciceId", { themeId: '@id', exerciceId: '@idExercice' }, { - update: { method: 'PUT' } - }) - }) - .factory("Exercice", function ($resource) { - return $resource("api/exercices/:exerciceId", { exerciceId: '@id' }, { - update: { method: 'PUT' }, - patch: { method: 'PATCH' } - }) - }) - .factory("ExerciceClaims", function ($resource) { - return $resource("api/exercices/:exerciceId/claims", { exerciceId: '@idExercice' }) - }) - .factory("ExerciceTags", function ($resource) { - return $resource("api/exercices/:exerciceId/tags", { exerciceId: '@idExercice' }, { - update: { method: 'PUT' } - }) - }) - .factory("ExerciceHistory", function ($resource) { - return $resource("api/exercices/:exerciceId/history.json", { exerciceId: '@id' }) - }) - .factory("ExerciceTries", function ($resource) { - return $resource("api/exercices/:exerciceId/tries/:tryId", { exerciceId: '@idExercice', tryId: '@id' }) - }) - .factory("ExercicesStats", function ($resource) { - return $resource("api/exercices_stats.json", { themeId: '@id' }) - }) - .factory("ExerciceStats", function ($resource) { - return $resource("api/exercices/:exerciceId/stats.json", { exerciceId: '@id' }) - }) - .factory("ExerciceFile", function ($resource) { - return $resource("api/exercices/:exerciceId/files/:fileId", { exerciceId: '@idExercice', fileId: '@id' }, { - update: { method: 'PUT' } - }) - }) - .factory("ExerciceHint", function ($resource) { - return $resource("api/exercices/:exerciceId/hints/:hintId", { exerciceId: '@idExercice', hintId: '@id' }, { - update: { method: 'PUT' } - }) - }) - .factory("ExerciceHintDeps", function ($resource) { - return $resource("api/exercices/:exerciceId/hints/:hintId/dependancies", { exerciceId: '@idExercice', hintId: '@id' }) - }) - .factory("ExerciceFlag", function ($resource) { - return $resource("api/exercices/:exerciceId/flags/:flagId", { exerciceId: '@idExercice', flagId: '@id' }, { - update: { method: 'PUT' } - }) - }) - .factory("ExerciceFlagChoices", function ($resource) { - return $resource("api/exercices/:exerciceId/flags/:flagId/choices/:choiceId", { exerciceId: '@idExercice', flagId: '@idFlag', choiceId: '@id' }, { - 'update': { method: 'PUT' }, - }) - }) - .factory("ExerciceFlagDeps", function ($resource) { - return $resource("api/exercices/:exerciceId/flags/:flagId/dependancies", { exerciceId: '@idExercice', flagId: '@id' }) - }) - .factory("ExerciceFlagStats", function ($resource) { - return $resource("api/exercices/:exerciceId/flags/:flagId/statistics", { exerciceId: '@idExercice', flagId: '@id' }) - }) - .factory("ExerciceMCQFlag", function ($resource) { - return $resource("api/exercices/:exerciceId/quiz/:mcqId", { exerciceId: '@idExercice', mcqId: '@id' }, { - update: { method: 'PUT' } - }) - }) - .factory("ExerciceMCQDeps", function ($resource) { - return $resource("api/exercices/:exerciceId/quiz/:mcqId/dependancies", { exerciceId: '@idExercice', mcqId: '@id' }) - }) - .factory("ExerciceMCQStats", function ($resource) { - return $resource("api/exercices/:exerciceId/quiz/:mcqId/statistics", { exerciceId: '@idExercice', mcqId: '@id' }) - }); - -angular.module("FICApp") - .filter("countHints", function () { - return function (input) { - if (input == undefined) - return 0; - return input.reduce(function (sum, n) { return sum + (n.content || n.file) ? 1 : 0; }, 0); - } - }) - .filter("toColor", function () { - return function (num) { - num >>>= 0; - var b = (num & 0xFF).toString(16), - g = ((num & 0xFF00) >>> 8).toString(16), - r = ((num & 0xFF0000) >>> 16).toString(16), - a = ((num & 0xFF000000) >>> 24) / 255; - if (r.length <= 1) r = "0" + r; - if (g.length <= 1) g = "0" + g; - if (b.length <= 1) b = "0" + b; - return "#" + r + g + b; - } - }) - .filter("cksum", function () { - return function (input) { - if (input == undefined) - return input; - var raw = atob(input).toString(16); - var hex = ''; - for (var i = 0; i < raw.length; i++) { - var _hex = raw.charCodeAt(i).toString(16) - hex += (_hex.length == 2 ? _hex : '0' + _hex); - } - return hex - } - }) - - .component('dependancy', { - bindings: { - dep: '=', - deleteDep: '=', - }, - controller: function () { }, - template: ` -
  • - Flag {{ $ctrl.dep.label }} - QCM {{ $ctrl.dep.title }} - -
  • - ` - }) - - .directive('color', function () { - return { - require: 'ngModel', - link: function (scope, ele, attr, ctrl) { - ctrl.$formatters.unshift(function (num) { - num >>>= 0; - var b = (num & 0xFF).toString(16), - g = ((num & 0xFF00) >>> 8).toString(16), - r = ((num & 0xFF0000) >>> 16).toString(16), - a = ((num & 0xFF000000) >>> 24) / 255; - if (r.length <= 1) r = "0" + r; - if (g.length <= 1) g = "0" + g; - if (b.length <= 1) b = "0" + b; - return "#" + r + g + b; - }); - ctrl.$parsers.unshift(function (viewValue) { - var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(viewValue); - return result ? ( - parseInt(result[1], 16) * 256 * 256 + - parseInt(result[2], 16) * 256 + - parseInt(result[3], 16) - - ) : 0; - }); - } - }; - }) - - .directive('integer', function () { - return { - require: 'ngModel', - link: function (scope, ele, attr, ctrl) { - ctrl.$parsers.unshift(function (viewValue) { - return parseInt(viewValue, 10); - }); - } - }; - }) - - .directive('float', function () { - return { - require: 'ngModel', - link: function (scope, ele, attr, ctrl) { - ctrl.$parsers.unshift(function (viewValue) { - return parseFloat(viewValue, 10); - }); - } - }; - }) - - .run(function ($rootScope, $http, $interval, $timeout, Settings, $location) { - $rootScope.$location = $location; - $rootScope.Utils = { - keys: Object.keys - }; - $rootScope.refreshSettings = function () { - $http.get("api/settings.json").then(function (response) { - response.data.start = new Date(response.data.start); - response.data.end = new Date(response.data.end); - response.data.generation = new Date(response.data.generation); - if ($rootScope.settings && $rootScope.settings.activateTime instanceof Date) - response.data.activateTime = $rootScope.settings.activateTime; - $rootScope.settings = response.data; - $rootScope.recvTime(response); - }) - } - $rootScope.refreshSettings(); - $interval($rootScope.refreshSettings, 10000); - - $rootScope.toasts = []; - $rootScope.addToast = function (kind, title, msg, yesFunc, noFunc, tmout) { - $rootScope.toasts.unshift({ - variant: kind, - title: title, - msg: msg, - timeout: tmout, - yesFunc: yesFunc, - noFunc: noFunc, - }); - } - - $rootScope.staticFilesNeedUpdate = 0; - $rootScope.staticRegenerationInProgress = false; - $rootScope.regenerateStaticFiles = function () { - $rootScope.staticRegenerationInProgress = true; - $http.post("api/full-generation").then(function (response) { - $rootScope.staticFilesNeedUpdate = 0; - $rootScope.staticRegenerationInProgress = false; - $rootScope.addToast('success', 'Regeneration ended'); - }, function (response) { - $rootScope.staticRegenerationInProgress = false; - $rootScope.addToast('error', 'An error occurs when saving settings:', response.data.errmsg); - }) - } - - $rootScope.$on('$locationChangeStart', function (event, next, current) { - if ($rootScope.staticFilesNeedUpdate) { - $timeout(function () { - document.getElementById("circle1").classList.add("play"); - }, 10); - $timeout(function () { - document.getElementById("circle1").classList.remove("play"); - }, 710); - - $timeout(function () { - document.getElementById("circle2").classList.add("play"); - }, 50); - $timeout(function () { - document.getElementById("circle2").classList.remove("play"); - }, 750); - } - }); - - $rootScope.logged = parseInt(getCookie("myassignee")) > 0; - }) - - .controller("VersionController", function ($scope, Version) { - $scope.v = Version.get(); - }) - - .controller("TimestampController", function ($scope, $interval, Timestamp) { - $scope.t = Timestamp.get(); - var refresh = function () { - $scope.t = Timestamp.get(); - } - var myinterval = $interval(refresh, 2500); - $scope.$on('$destroy', function () { $interval.cancel(myinterval); }); - }) - - .controller("HealthController", function ($scope, $interval, Health, $http) { - var refresh = function () { - $scope.health = Health.query(); - } - refresh(); - var myinterval = $interval(refresh, 2500); - $scope.drop_submission = function (path) { - $scope.addToast('info', 'Delete submission', 'Ensure this submission is not interesting. Continue?', - function () { - $http.delete("api/submissions" + path).then(function (response) { - refresh(); - }, function (response) { - $scope.addToast('danger', 'An error occurs when deleting submission:', response.data.errmsg); - }); - } - ); - } - $scope.$on('$destroy', function () { $interval.cancel(myinterval); }); - }) - - .controller("MonitorController", function ($scope, Monitor) { - $scope.monitor = Monitor.get(); - }) - - .controller("AllTeamAssociationsController", function ($scope, $http) { - $scope.newdqa = ""; - $scope.addDelegatedQA = function () { - if ($scope.newdqa.length) { - if (!$scope.config.delegated_qa) - $scope.config.delegated_qa = []; - - $scope.config.delegated_qa.push($scope.newdqa); - $scope.saveSettings(); - $scope.newdqa = ""; - } - } - - $scope.allAssociations = []; - $http.get("api/teams-associations.json").then(function (response) { - $scope.allAssociations = response.data; - }) - }) - - .controller("SettingsController", function ($scope, $rootScope, NextSettings, Settings, SettingsChallenge, $location, $http, $interval) { - $scope.nextsettings = NextSettings.query(); - $scope.erase = false; - $scope.editNextSettings = function (ns) { - $scope.activateTime = new Date(ns.date); - $rootScope.settings.activateTime = $scope.activateTime; - $scope.erase = true; - Object.keys(ns.values).forEach(function (k) { - $scope.config[k] = ns.values[k]; - }); - $scope.config.enableExerciceDepend = $scope.config.unlockedChallengeDepth >= 0; - $scope.config.disabledsubmitbutton = $scope.config.disablesubmitbutton && $scope.config.disablesubmitbutton.length > 0; - } - $scope.deleteNextSettings = function (ns) { - ns.$delete().then(function () { - $scope.nextsettings = NextSettings.query(); - }) - } - - $scope.displayDangerousActions = false; - $scope.config = Settings.get(); - $scope.dist_config = {}; - $scope.config.$promise.then(function (response) { - response.start = new Date(response.start); - if (response.end) response.end = new Date(response.end); - else response.end = null; - response.generation = new Date(response.generation); - response.activateTime = new Date(response.activateTime); - $scope.dist_config = Object.assign({}, response); - - response.enableExerciceDepend = response.unlockedChallengeDepth >= 0; - response.disabledsubmitbutton = response.disablesubmitbutton && response.disablesubmitbutton.length > 0; - if (response.end) { - $scope.duration = (response.end - response.start)/60000; - } - }) - $scope.challenge = SettingsChallenge.get(); - $scope.duration = 360; - $scope.durationChange = function(endChanged) { - if (endChanged) - $scope.duration = (new Date($scope.config.end).getTime() - new Date($scope.config.start).getTime())/60000; - else - $scope.config.end = new Date(new Date($scope.config.start).getTime() + $scope.duration * 60000); - } - $scope.activateTime = ""; - $scope.challenge.$promise.then(function (c) { - if (c.duration) - $scope.duration = c.duration; - }); - - $scope.exerciceDependChange = function () { - if ($scope.config.enableExerciceDepend) - $scope.config.unlockedChallengeDepth = 0; - else - $scope.config.unlockedChallengeDepth = -1; - }; - - $scope.submitButtonStateChange = function () { - if ($scope.config.disabledsubmitbutton) - $scope.config.disablesubmitbutton = "Mise à jour en cours..."; - else - $scope.config.disablesubmitbutton = ""; - }; - - $scope.dropDelegatedQA = function (member) { - if (!$scope.config.delegated_qa) { - $scope.config.delegated_qa = []; - } - angular.forEach($scope.config.delegated_qa, function (m, k) { - if (member == m) - $scope.config.delegated_qa.splice(k, 1); - }); - $scope.saveSettings(); - } - - $scope.saveChallengeInfo = function () { - this.challenge.duration = $scope.duration; - this.challenge.$update(function (response) { - $scope.addToast('success', 'Infos du challenge mises à jour avec succès !'); - }, function (response) { - $scope.addToast('danger', 'An error occurs when saving challenge info:', response.data.errmsg); - }); - } - $scope.saveSettings = function (msg) { - if (msg === undefined) { msg = 'New settings saved!'; } - if (this.config.end == "") this.config.end = null; - - var nStart = this.config.start; - var nEnd = this.config.end; - var nGen = this.config.generation; - var state = this.config.enableExerciceDepend; - this.config.unlockedChallengeDepth = (this.config.enableExerciceDepend ? this.config.unlockedChallengeDepth : -1) - this.config.disablesubmitbutton = (this.config.disabledsubmitbutton ? this.config.disablesubmitbutton : '') - var updateQuery = {}; - if (this.activateTime && this.activateTime != '') { - updateQuery['t'] = this.activateTime; - this.activateTime = null; - } - if (this.erase) { - updateQuery['erase'] = true; - this.erase = false; - } - this.config.$update(updateQuery, function (response) { - $scope.dist_config = Object.assign({}, response); - $scope.addToast('success', msg); - $scope.nextsettings = NextSettings.query(); - response.enableExerciceDepend = response.unlockedChallengeDepth >= 0; - response.disabledsubmitbutton = response.disablesubmitbutton && response.disablesubmitbutton.length > 0; - $rootScope.settings.start = new Date(nStart); - if (nEnd) { - $rootScope.settings.end = new Date(nEnd); - } else { - $rootScope.settings.end = null; - } - $rootScope.settings.generation = new Date(nGen); - $scope.updateActivateTime(); - }, function (response) { - $scope.addToast('danger', 'An error occurs when saving settings:', response.data.errmsg); - }); - } - $scope.launchChallenge = function () { - var ts = $rootScope.getSrvTime().getTime() - $rootScope.getSrvTime().getTime() % 60000; - this.config.start = new Date(ts + 120000); - this.config.end = new Date(ts + 120000 + this.duration * 60000); - - $scope.addToast('info', 'Challenge ready to start,', 'propagate the changes?', - function () { - $scope.saveSettings(); - }); - } - $scope.updateActivateTime = function () { - $rootScope.settings.activateTime = this.activateTime; - } - $scope.updActivateTime = function (modulo) { - if (modulo) { - var ts = Math.floor((new Date(this.config.end) - $rootScope.getSrvTime().getTime() - (60000 * modulo / 2)) / (60000 * modulo)) * (60000 * modulo); - var d = new Date(this.config.end) - ts; - this.activateTime = new Date(d); - this.updateActivateTime(); - } else { - this.activateTime = null; - this.updateActivateTime(); - } - } - $scope.reset = function (type) { - var txts = { - "settings": "En validant, vous remettrez les paramètres de cette page à leur valeur initiale, y compris la date de début du challenge.", - "challengeInfo": "En validant, vous effacerez les informations descriptives du challenge.", - "challenges": "En validant, vous retirerez toutes les données statiques des challenges.", - "teams": "En validant, vous supprimerez l'ensemble des équipes enregistreées.", - "game": "En validant, vous supprimerez toutes les tentatives, les validations, ... faites par les équipes.", - } - $scope.addToast('warning', txts[type], 'Êtes-vous sûr de vouloir continuer ?', - function () { - if (confirm("Êtes-vous vraiment sûr ?\n" + txts[type])) { - $http.post("api/reset", { "type": type }).then(function (time) { - $scope.addToast('success', type + 'reseted'); - $location.url("/"); - }, function (response) { - $scope.addToast('danger', 'An error occurs when reseting ' + type + ':', response.data.errmsg); - }); - - } - }); - }; - $scope.switchToProd = function () { - $scope.addToast('warning', "Activer le mode challenge ?", "L'activation du mode challenge est temporaire (vous devriez plutôt relancer le daemon avec l'option `-4real`). Ce mode permet d'éviter les mauvaises manipulations et désactive le hook git de synchronisation automatique. Êtes-vous sûr de vouloir continuer ?", - function () { - $http.put("api/prod", true).then(function (time) { - $rootScope.refreshSettings() - $scope.addToast('success', 'Mode challenge activé'); - }, function (response) { - $scope.addToast('danger', 'An error occurs when activating challenge mode:', response.data.errmsg); - }); - }); - }; - }) - - .controller("RepositoriesController", function ($scope, $http) { - $http.get("api/repositories").then(function (response) { - $scope.repositories = response.data.repositories; - }); - - $scope.deleteRepository = function(repo) { - $http.delete("api/repositories/" + repo.path).then(function (response) { - $scope.repositories[$scope.repositories.indexOf(repo)].hash = "- DELETED -"; - }); - }; - }) - .component('teamLink', { - bindings: { - idTeam: '=', - }, - controller: function (Team) { - var ctrl = this; - ctrl.team = {}; - - ctrl.$onInit = function () { - ctrl.team = Team.get({teamId: ctrl.idTeam}); - }; - }, - template: `{{ $ctrl.team.name }} ` - }) - .component('repositoryUptodate', { - bindings: { - repository: '<', - }, - controller: function ($http) { - var ctrl = this; - - ctrl.status = {}; - ctrl.color = "badge-secondary"; - - ctrl.$onInit = function () { - $http.post("api/repositories/" + ctrl.repository.path).then(function (response) { - ctrl.status = response.data; - - if (ctrl.repository.hash.startsWith(ctrl.status.hash)) { - ctrl.color = "badge-success"; - } else { - ctrl.color = "badge-danger"; - } - }); - }; - }, - template: `{{ $ctrl.status.hash }} {{ $ctrl.status.text }}` - }) - - .controller("SyncController", function ($scope, $rootScope, $location, $http, $interval) { - $scope.displayDangerousActions = false; - - var needRefreshSyncReportWhenReady = false; - var refreshSyncReport = function () { - needRefreshSyncReportWhenReady = false; - $http.get("full_import_report.json").then(function (response) { - $scope.syncReport = response.data; - }) - }; - refreshSyncReport() - - $scope.deepSyncInProgress = false; - - var progressInterval = $interval(function () { - $http.get("api/sync/status").then(function (response) { - if (response.data.progress && response.data.progress != 255) - needRefreshSyncReportWhenReady = true; - else if (needRefreshSyncReportWhenReady) - refreshSyncReport(); - if (response.data && response.data.progress) { - $scope.syncPercent = Math.floor(response.data.progress * 100 / 255); - $scope.deepSyncInProgress = response.data.pullMutex && response.data.syncMutex; - } else { - $scope.syncPercent = 0; - } - $scope.syncStatus = response.data; - }, function (response) { - $scope.syncPercent = 0; - $scope.syncStatus = response.data; - }) - }, 1500); - $scope.$on('$destroy', function () { $interval.cancel(progressInterval); }); - - $scope.deepSync = function (theme) { - if (theme) { - question = 'Faire une synchronisation intégrale du thème ' + theme.name + ' ?' - url = "api/sync/deep/" + theme.id - } else { - question = 'Faire une synchronisation intégrale ?' - url = "api/sync/deep" - } - $scope.addToast('warning', question, '', - function () { - $scope.deepSyncInProgress = true; - $http.post(url).then(function () { - $scope.deepSyncInProgress = false; - $scope.addToast('success', 'Synchronisation intégrale terminée.', 'Voir le rapport.', null, null, 15000); - }, function (response) { - $scope.deepSyncInProgress = false; - $scope.addToast('warning', 'Synchronisation intégrale terminée.', 'Voir le rapport.', null, null, 15000); - }); - }); - }; - $scope.speedyDeepSync = function () { - $scope.addToast('warning', 'Faire une synchronisation profonde rapide, sans s\'occuper des fichiers ?', '', - function () { - $scope.deepSyncInProgress = true; - $http.post("api/sync/speed").then(function () { - $scope.deepSyncInProgress = false; - $scope.addToast('success', 'Synchronisation profonde rapide terminée.', 'Voir le rapport.', null, null, 15000); - }, function (response) { - $scope.deepSyncInProgress = false; - $scope.addToast('warning', 'Synchronisation profinde rapide terminée.', 'Voir le rapport.', null, null, 15000); - }); - }); - }; - $scope.baseSync = function () { - $scope.addToast('warning', 'Tirer les mises à jour du dépôt ?', '', - function () { - $scope.deepSyncInProgress = true; - $http.post("api/sync/base").then(function () { - $scope.deepSyncInProgress = false; - $scope.addToast('success', 'Mise à jour terminée.'); - }, function (response) { - $scope.deepSyncInProgress = false; - $scope.addToast('danger', 'Mise à jour terminée.', response.data.errmsg); - }); - }); - }; - $scope.syncVideos = function () { - $scope.addToast('warning', 'Synchroniser les vidéos de résolution ?', 'ATTENTION il ne faut pas lancer cette synchronisation durant le challenge. Seulement une fois le challenge terminé, cela permet de rendre les vidéos accessibles dans l\'interface joueurs.', - function () { - $scope.deepSyncInProgress = true; - $http.post("api/sync/videos").then(function () { - $scope.deepSyncInProgress = false; - $scope.addToast('success', 'Import des vidéos terminé.'); - }, function (response) { - $scope.deepSyncInProgress = false; - $scope.addToast('danger', 'Import des vidéos terminé.', response.data.errmsg); - }); - }); - }; - $scope.dropSoluces = function () { - $scope.addToast('warning', 'Effacer les solutions', 'Ceci va retirer les textes de résolution de la base de données ainsi que les liens vers les vidéos.', - function () { - $scope.deepSyncInProgress = true; - $http.post("api/sync/drop_soluces").then(function () { - $scope.deepSyncInProgress = false; - $scope.addToast('success', 'Effacement des solutions terminé.'); - }, function (response) { - $scope.deepSyncInProgress = false; - $scope.addToast('danger', 'Effacement des solutions terminé avec des erreurs.', response.data.errmsg); - }); - }); - }; - $scope.diffWithRepo = function () { - $scope.diff = null; - $http({ - url: "api/sync/local-diff", - method: "POST" - }).then(function (response) { - $scope.diff = response.data; - if (response.data === null) { - $scope.addToast('success', 'Changements par rapport au dépôt', "Tout est pareil !"); - } - }, function (response) { - $scope.diff = null; - $scope.addToast('danger', 'An error occurs when synchronizing exercice:', response.data.errmsg); - }); - }; - }) - - .controller("AuthController", function ($scope, $http) { - $scope.generateHtpasswd = function () { - $http.post("api/htpasswd").then(function () { - $scope.addToast('success', 'Fichier htpasswd généré avec succès'); - }, function (response) { - $scope.addToast('danger', 'An error occurs when generating htpasswd file:', response.data.errmsg); - }); - }; - $scope.removeHtpasswd = function () { - $http.delete("api/htpasswd").then(function () { - $scope.addToast('success', 'Fichier htpasswd supprimé avec succès'); - }, function (response) { - $scope.addToast('danger', 'An error occurs when deleting htpasswd file:', response.data.errmsg); - }); - }; - }) - - .controller("OAuthController", function ($scope, $http) { - $scope.oauth_status = {}; - $scope.refreshOAuthStatus = function () { - $http.get("api/oauth-status").then(function (res) { - $scope.oauth_status = res.data; - }); - }; - $scope.refreshOAuthStatus(); - - $scope.genDexCfg = function () { - $http.post("api/dex.yaml").then(function () { - $http.post("api/dex-password.tpl").then(function () { - $scope.addToast('success', 'Dex config refreshed.', "Don't forget to reload/reboot frontend host."); - }, function (response) { - $scope.addToast('danger', 'An error occurs when generating dex password tpl:', response.data.errmsg); - }); - }, function (response) { - $scope.addToast('danger', 'An error occurs when generating dex config:', response.data.errmsg); - }); - $http.post("api/vouch-proxy.yaml").then(function () { - $scope.addToast('success', 'VouchProxy config refreshed.', "Don't forget to reload/reboot frontend host."); - }, function (response) { - $scope.addToast('danger', 'An error occurs when generating VouchProxy config:', response.data.errmsg); - }); - } - }) - - .controller("PKIController", function ($scope, $rootScope, Certificate, CACertificate, Team, $location, $http) { - var ts = Date.now() - Date.now() % 86400000; - var d = new Date(ts); - var f = new Date(ts + 3 * 86400000); - $scope.newca = { - notAfter: f.toISOString(), - notBefore: d.toISOString(), - }; - - $scope.teams = Team.query(); - $scope.certificates = Certificate.query(); - $scope.ca = CACertificate.get(); - - $scope.revoke = function () { - var targetserial = $("#revokeModal").data("certificate"); - if (targetserial) { - Certificate.delete({ serial: targetserial }).$promise.then( - function () { - $('#revokeModal').modal('hide'); - $scope.certificates = Certificate.query().$promise.then(function (certificates) { - certificates.forEach(function (certificate, cid) { - certificate.serial = parseInt(certificate.id).toString(16); - }); - }); - }, function (response) { - $scope.addToast('danger', 'An error occurs when trying to associate certificate:', response.data.errmsg); - } - ); - } - }; - - $scope.validateSearch = function (keyEvent) { - if (keyEvent.which === 13) { - var myCertificate = null; - $scope.certificates.forEach(function (certificate) { - if (String(certificate.id).indexOf($scope.query.toUpperCase()) >= 0) { - if (myCertificate === null) - myCertificate = certificate; - else - myCertificate = false; - } - }); - if (myCertificate && myCertificate.id_team == null) { - $('#associationModal').data('certificate', myCertificate.id) - $('#associationModal').modal() - } - } - }; - $scope.validatePKIForm = function (keyEvent) { - if (keyEvent.which === 13) - $scope.associate() - }; - $scope.associate = function () { - var targetserial = $("#associationModal").data("certificate"); - if (!targetserial) return; - Certificate.update({ serial: targetserial }, { id_team: $scope.selectedTeam }).$promise.then( - function () { - $('#associationModal').modal('hide'); - $scope.certificates = Certificate.query(); - $scope.selectedTeam = null; - }, function (response) { - $scope.addToast('danger', 'An error occurs when trying to associate certificate:', response.data.errmsg); - } - ); - }; - - $scope.generateCA = function () { - $http.post("api/ca/new", $scope.newca).then(function () { - $scope.ca = CACertificate.get(); - }, function (response) { - $scope.addToast('danger', 'An error occurs when generating CA:', response.data.errmsg); - }); - }; - $scope.renewCA = function () { - $scope.ca = {}; - }; - - $scope.generateCert = function () { - $http.post("api/certs").then(function () { - $scope.certificates = Certificate.query(); - }, function (response) { - $scope.addToast('danger', 'An error occurs when generating certificate:', response.data.errmsg); - }); - }; - }) - - .controller("PublicController", function ($scope, $rootScope, $routeParams, $location, Scene, Theme, Teams, Exercice) { - $scope.propagationtime = null; - $scope.presetName = ""; - $scope.screens = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - $scope.screenid = $routeParams.screenId; - $scope.display = Scene.get({ screenId: $routeParams.screenId }); - $scope.listScenes = Scene.get(); - $scope.themes = Theme.query(); - $scope.teams = Teams.get(); - - $scope.chScreen = function (sid) { - if ($scope.screenid) - $location.url("/public/" + $scope.screenid); - else - $location.url("/public/"); - } - - $scope.types = { - "welcome": "Messages de bienvenue", - "countdown": "Compte à rebours", - "message": "Message", - "panel": "Boîte", - "carousel": "Carousel", - "exercice": "Exercice", - "table": "Tableau", - "rank": "Classement", - "graph": "Graphique", - }; - $scope.typeside = { - "welcome": "Messages de bienvenue", - "themes": "Présentation des thèmes", - "exercice_follow": "Dernier exercice des événements", - "exercice": "Exercice", - "rank": "Classement", - "graph": "Graphique", - "message": "Message", - "panel": "Boîte", - }; - $scope.welcome_types = { - "teams": "Accueil des équipes", - "public": "Accueil du public", - }; - $scope.carousel_types = { - "exercices": "Exercices", - "teams": "Équipes", - "themes": "Thèmes", - "ranking": "Classement", - }; - $scope.colors = { - "primary": "Primaire", - "secondary": "Secondaire", - "info": "Info", - "success": "Success", - "warning": "Warning", - "danger": "Danger", - "light": "Clair", - "dark": "Foncé", - }; - $scope.rank_types = { - "general": "Classement général", - "final": "Classement final", - }; - $scope.rank_types_side = { - "carousel": "Classement général carousel", - "general": "Classement général", - }; - $scope.table_types = { - "levels": "Niveaux d'exercices", - "teams": "Équipes", - }; - $scope.exercices = Exercice.query(); - - $scope.clearScene = function () { - $scope.someUpdt = true; - $scope.display.scenes = []; - }; - $scope.presetScene = function (scene) { - $scope.someUpdt = true; - if (scene == "registration") { - $scope.display.scenes = [ - { - type: "welcome", - params: { kind: "teams", url: "https://fic.srs.epita.fr/" }, - }, - { - type: "welcome", - params: { kind: "public", notitle: true }, - }, - ]; - $scope.display.side = [ - { - type: "themes", - params: {}, - }, - ]; - } - else if (scene == "welcome") { - $scope.display.scenes = [ - { - type: "carousel", - params: { color: "info", kind: "themes", title: "Présentation des entreprises ciblées" }, - }, - ]; - $scope.display.side = [ - { - type: "welcome", - params: { kind: "public" }, - }, - ]; - } - else if (scene == "start") { - $scope.display.scenes = [ - { - type: "welcome", - params: { kind: "public" }, - }, - { - type: "countdown", - params: { color: "success", end: null, lead: "Go, go, go !", title: "Le challenge forensic va bientôt commencer !" }, - }, - ]; - $scope.display.side = [ - { - type: "themes", - params: {}, - }, - ]; - } - else if (scene == "end") { - $scope.display.scenes = [ - { - type: "rank", - params: { which: "final" }, - }, - { - type: "table", - params: { kind: "teams", themes: $scope.themes.map(function (z, i) { return z.id; }), total: true, teams: [] }, - }, - ]; - angular.forEach($scope.teams, function (team, tid) { - if (team.rank >= 1 && team.rank <= 4) - $scope.display.scenes[1].params.teams.push(tid) - }); - $scope.display.side = [ - { - type: "rank", - params: { which: "carousel" }, - }, - { - type: "graph", - params: { teams: [], height: 400, legend: false, hide: true }, - }, - { - type: "message", - params: { html: '
    Epita
    Réserves de cyberdéfense
    ', hide: true }, - }, - ]; - angular.forEach($scope.teams, function (team, tid) { - if (team.rank >= 1 && team.rank <= 9) - $scope.display.side[1].params.teams.push(tid) - }); - $scope.display.hideEvents = true; - $scope.display.hideCountdown = true; - } - else if (scene == "summary") { - $scope.display.scenes = [ - { - type: "table", - params: { kind: "levels", levels: [1, 2, 3, 4, 5], themes: $scope.themes.map(function (z, i) { return z.id; }), total: true }, - }, - { - type: "graph", - params: { teams: [], height: 337, legend: true }, - }, - ]; - angular.forEach($scope.teams, function (team, tid) { - if (team.rank >= 1 && team.rank <= 9) - $scope.display.scenes[1].params.teams.push(tid) - }); - $scope.display.side = [ - { - type: "exercice_follow", - params: {}, - }, - ]; - $scope.display.hideEvents = false; - $scope.display.hideCountdown = false; - } - else if (scene == "summary2") { - $scope.display.scenes = [ - { - type: "graph", - params: { teams: [], height: 400, legend: false }, - }, - { - type: "rank", - params: { limit: 10, which: "general", legend: true }, - }, - ]; - angular.forEach($scope.teams, function (team, tid) { - if (team.rank >= 1 && team.rank <= 9) - $scope.display.scenes[0].params.teams.push(tid) - }); - $scope.display.side = [ - { - type: "exercice_follow", - params: {}, - }, - ]; - $scope.display.hideEvents = false; - $scope.display.hideCountdown = false; - } - else if (scene == "summary3") { - $scope.display.scenes = [ - { - type: "table", - params: { kind: "levels", levels: [1, 2, 3, 4, 5], themes: $scope.themes.map(function (z, i) { return z.id; }), total: true }, - }, - { - type: "rank", - params: { limit: 10, which: "general", legend: false }, - }, - ]; - $scope.display.side = [ - { - type: "exercice_follow", - params: {}, - }, - ]; - $scope.display.hideEvents = false; - $scope.display.hideCountdown = false; - } - else if (scene == "happyhour") { - $scope.display.customCountdown = { - show: true, - shadow: "#E8CF5C", - end: new Date($rootScope.getSrvTime().getTime() + 1802000).toISOString(), - before: "Heure joyeuse : chaque résolution compte double !", - after: "Heure joyeuse terminée !", - } - } - else if (scene == "freehintquarter") { - $scope.display.customCountdown = { - show: true, - shadow: "#3DD28F", - end: new Date($rootScope.getSrvTime().getTime() + 902000).toISOString(), - before: "Quart d'heure facile : indices dévoilés !", - after: "Quart d'heure facile terminée !", - } - } - }; - - $scope.genSceneCountdownDate = function (scene, duration) { - scene.params.end = (new Date($rootScope.getSrvTime().getTime() + duration)).toISOString(); - } - $scope.genCustomCountdownDate = function (duration) { - if (duration == null) { - $scope.display.customCountdown.end = $rootScope.settings.activateTime; - } else { - $scope.display.customCountdown.end = (new Date($rootScope.getSrvTime().getTime() + duration)).toISOString(); - } - } - - $scope.loadFile = function (fname) { - $scope.display = Scene.get({ screenId: fname }); - $scope.someUpdt = true; - }; - $scope.deleteFile = function (fname) { - Scene.delete({ screenId: fname }); - $scope.listScenes = Scene.get(); - }; - $scope.latePropagation = function () { - $scope.someUpdt = false; - var prms = Scene.update({ screenId: $scope.screenid, t: $scope.propagationTime }, $scope.display); - prms.$promise.then(function () { - $scope.addToast('success', 'Scene successfully planned!'); - }, function (response) { - $scope.addToast('danger', 'An error occurs when planning scene:', response.data.errmsg); - }); - }; - $scope.savePreset = function () { - $scope.someUpdt = false; - var prms = Scene.update({ screenId: $scope.screenid, p: $scope.presetName }, $scope.display); - prms.$promise.then(function () { - $scope.addToast('success', 'Preset successfully saved!'); - }, function (response) { - $scope.addToast('danger', 'An error occurs when saving preset:', response.data.errmsg); - }); - }; - $scope.saveScenes = function () { - $scope.someUpdt = false; - var prms = Scene.update({ screenId: $scope.screenid }, $scope.display); - prms.$promise.then(function () { - $scope.addToast('success', 'Scene successfully published!'); - }, function (response) { - $scope.addToast('danger', 'An error occurs when saving scene:', response.data.errmsg); - }); - }; - $scope.addSide = function () { - $scope.someUpdt = true; - $scope.display.side.push({ params: {} }); - }; - $scope.delSide = function (s) { - $scope.someUpdt = true; - angular.forEach($scope.display.side, function (scene, k) { - if (scene == s) - $scope.display.side.splice(k, 1); - }); - }; - $scope.upSide = function (s) { - $scope.someUpdt = true; - angular.forEach($scope.display.side, function (scene, k) { - if (scene == s && k > 0) { - $scope.display.side.splice(k, 1); - $scope.display.side.splice(k - 1, 0, scene); - } - }); - }; - $scope.downSide = function (s) { - $scope.someUpdt = true; - var move = true; - angular.forEach($scope.display.side, function (scene, k) { - if (move && scene == s) { - $scope.display.side.splice(k, 1); - $scope.display.side.splice(k + 1, 0, scene); - move = false; - } - }); - }; - $scope.addScene = function () { - $scope.someUpdt = true; - $scope.display.scenes.push({ params: {} }); - }; - $scope.delScene = function (s) { - $scope.someUpdt = true; - angular.forEach($scope.display.scenes, function (scene, k) { - if (scene == s) - $scope.display.scenes.splice(k, 1); - }); - }; - $scope.upScene = function (s) { - $scope.someUpdt = true; - angular.forEach($scope.display.scenes, function (scene, k) { - if (scene == s && k > 0) { - $scope.display.scenes.splice(k, 1); - $scope.display.scenes.splice(k - 1, 0, scene); - } - }); - }; - $scope.downScene = function (s) { - $scope.someUpdt = true; - var move = true; - angular.forEach($scope.display.scenes, function (scene, k) { - if (move && scene == s) { - $scope.display.scenes.splice(k, 1); - $scope.display.scenes.splice(k + 1, 0, scene); - move = false; - } - }); - }; - }) - - .controller("FilesListController", function ($scope, File, $location, $http, $rootScope) { - $scope.files = File.query(); - $scope.errfnd = null; - $scope.errzip = null; - $scope.clearFilesWIP = false; - $scope.fields = ["id", "path", "name", "size"]; - - $scope.clearFiles = function (id) { - File.delete(function () { - $rootScope.staticFilesNeedUpdate++; - $scope.files = []; - }); - }; - $scope.clearFilesDir = function () { - $scope.addToast('warning', 'Êtes-vous sûr de vouloir continuer ?', "Ceci va supprimer tout le contenu du dossier FILES. Il s'agit des fichiers ci-dessous, il faudra refaire une synchronisation ensuite.", - function () { - $scope.clearFilesWIP = true; - $http({ - url: "api/files", - method: "DELETE" - }).then(function (response) { - $scope.clearFilesWIP = false; - }, function (response) { - $scope.clearFilesWIP = false; - $scope.addToast('danger', 'An error occurs when trying to clear files:', response.data.errmsg); - }); - }); - }; - $scope.gunzipFile = function (f) { - f.gunzipWIP = true; - $http({ - url: "api/files/" + f.id + "/gunzip", - method: "POST" - }).then(function (response) { - f.gunzipWIP = false; - f.err = true; - }, function (response) { - f.gunzipWIP = false; - $scope.inSync = false; - $scope.errzip += 1; - f.err = response.data.errmsg; - }) - }; - $scope.checksum = function (f) { - f.checkWIP = true; - $http({ - url: "api/files/" + f.id + "/check", - method: "POST" - }).then(function (response) { - f.checkWIP = false; - f.err = true; - }, function (response) { - f.checkWIP = false; - $scope.inSync = false; - $scope.errfnd += 1; - f.err = response.data.errmsg; - }) - }; - $scope.checksumAll = function () { - $scope.errfnd = null; - angular.forEach($scope.files, function (file) { - $scope.checksum(file); - }); - if ($scope.errfnd === null) $scope.errfnd = 0; - }; - $scope.gunzipFiles = function () { - $scope.errzip = null; - angular.forEach($scope.files, function (file) { - $scope.gunzipFile(file); - }); - if ($scope.errzip === null) $scope.errzip = 0; - }; - $scope.show = function (f) { - $location.url("/exercices/" + f.idExercice); - }; - }) - - .controller("EventsListController", function ($scope, Event, $location) { - $scope.events = Event.query(); - $scope.fields = ["id", "kind", "txt", "time"]; - - $scope.clearEvents = function (id) { - Event.delete(function () { - $scope.events = []; - }); - }; - $scope.show = function (id) { - $location.url("/events/" + id); - }; - }) - .controller("EventController", function ($scope, Event, $routeParams, $location) { - $scope.event = Event.get({ eventId: $routeParams.eventId }); - $scope.fields = ["kind", "txt", "time"]; - $scope.kinds = { - "secondary": "Par défaut", - "primary": "Mise en valeur", - "info": "Info", - "warning": "Warning", - "success": "Success", - "danger": "Danger", - "light": "Clair", - "dark": "Foncé", - }; - - $scope.saveEvent = function () { - if (this.event.id) { - this.event.$update(); - } else { - this.event.$save(function () { - $location.url("/events/" + $scope.event.id); - }); - } - } - $scope.deleteEvent = function () { - this.event.$remove(function () { $location.url("/events/"); }); - } - }) - - .controller("AssigneesListController", function ($scope, ClaimAssignee, $location) { - $scope.assignees = ClaimAssignee.query(); - - $scope.setMyAId = function (aid) { - setCookie("myassignee", aid, 5); - $location.url("/claims/"); - } - $scope.whoami = getCookie("myassignee"); - $scope.newAssignee = function () { - $scope.assignees.push(new ClaimAssignee()); - } - $scope.edit = function (a) { - a.edit = true; - } - $scope.updateAssignee = function (a) { - if (a.id) { - a.$update(function () { $location.url("/claims/"); }); - } else { - a.$save() - } - } - $scope.removeAssignee = function (a) { - a.$remove(function () { $location.url("/claims/"); }); - } - }) - .controller("ClaimsTinyListController", function ($scope, Claim, ClaimAssignee, $interval) { - $scope.whoami = getCookie("myassignee"); - - var priorities = { - "low": 1, - "medium": 2, - "high": 3, - "critical": 4, - }; - $scope.priorities = [ - "secondary", - "light", - "info", - "warning", - "danger", - ]; - - var refresh = function () { - Claim.query().$promise.then(function (claims) { - $scope.newClaims = 0; - $scope.newClaimsMaxLevel = 0; - $scope.myClaims = 0; - $scope.myClaimsMaxLevel = 0; - - claims.forEach(function (claim, cid) { - if ($scope.whoami && !claim.id_assignee && claim.state == 'new') { - $scope.newClaims++; - if (priorities[claim.priority] > $scope.newClaimsMaxLevel) - $scope.newClaimsMaxLevel = priorities[claim.priority]; - } - else if ($scope.whoami && claim.id_assignee == $scope.whoami && claim.state != 'closed' && claim.state != 'invalid') { - $scope.myClaims++; - if (claim.state == 'new' && priorities[claim.priority] > $scope.myClaimsMaxLevel) - $scope.myClaimsMaxLevel = priorities[claim.priority]; - } - }) - }); - }; - refresh(); - $interval(refresh, 10000); - }) - .controller("ClaimsListController", function ($scope, Claim, ClaimAssignee, Teams, $interval, $location) { - var refresh = function () { - $scope.claims = Claim.query(); - $scope.assignees = ClaimAssignee.query(); - } - refresh(); - var myinterval = $interval(refresh, 10000); - $scope.$on('$destroy', function () { $interval.cancel(myinterval); }); - - $scope.whoami = getCookie("myassignee"); - $scope.teams = Teams.get(); - $scope.fields = ["subject", "id_team", "state", "id_assignee", "last_update", "id"]; - - $scope.order = "priority"; - $scope.chOrder = function (no) { - $scope.order = no; - }; - - $scope.clearClaims = function (id) { - Claim.delete(function () { - $scope.claims = []; - }); - }; - $scope.show = function (id) { - $location.url("/claims/" + id); - }; - }) - .controller("ClaimLastUpdateController", function ($scope, $http) { - $scope.init = function (claim) { - $http.get("api/claims/" + claim.id + "/last_update").then(function (response) { - if (response.data) - $scope.last_update = response.data; - else - $scope.last_update = claim.creation; - claim.last_update = $scope.last_update; - }, function (response) { - $scope.last_update = claim.creation; - }) - } - }) - .controller("ClaimController", function ($scope, Claim, ClaimAssignee, Teams, Exercice, $routeParams, $location, $http, $rootScope) { - $scope.claim = Claim.get({ claimId: $routeParams.claimId }, function (v) { - v.id_team = "" + v.id_team; - if (!v.priority) - v.priority = "medium"; - }); - if ($routeParams.claimId == "new") - $scope.fields = ["id_team", "id_exercice", "subject", "priority", "id_assignee"]; - else - $scope.fields = ["subject", "priority", "id_exercice", "id_assignee", "id_team", "creation", "state"]; - $scope.assignees = ClaimAssignee.query(); - $scope.comm = { ndescription: "" }; - $scope.whoami = Math.floor(getCookie("myassignee")); - $scope.teams = Teams.get(); - $scope.exercices = Exercice.query(); - $scope.namedFields = { - "subject": "Objet", - "id_assignee": "Assigné à", - "state": "État", - "id_team": "Équipe", - "id_exercice": "Challenge", - "creation": "Création", - "priority": "Priorité", - "description": "Description", - }; - $scope.states = { - "new": "Nouveau", - "need-info": "Besoin d'infos", - "confirmed": "Confirmé", - "in-progress": "En cours", - "need-review": "Fait", - "closed": "Clos", - "invalid": "Invalide", - }; - $scope.priorities = { - "low": "Basse", - "medium": "Moyenne", - "high": "Haute", - "critical": "Critique", - }; - - $scope.changeState = function (state) { - this.claim.state = state; - if ((state == "in-progress" || state == "invalid") && this.claim.id_assignee) - this.claim.id_assignee = $scope.whoami; - if (this.claim.id) - this.saveClaim(state == "invalid" || state == "closed"); - } - $scope.assignToMe = function () { - this.claim.id_assignee = $scope.whoami; - if (this.claim.id) - this.saveClaim(false); - } - $scope.updateDescription = function (description) { - $http({ - url: "api/claims/" + $scope.claim.id + "/descriptions", - method: "PUT", - data: description - }).then(function (response) { - $scope.claim = Claim.get({ claimId: $routeParams.claimId }, function (v) { - v.id_team = "" + v.id_team; - if (!v.priority) - v.priority = "medium"; - }); - }); - } - $scope.saveDescription = function () { - $http({ - url: "api/claims/" + $scope.claim.id, - method: "POST", - data: { - "id_assignee": $scope.whoami, - "content": $scope.comm.ndescription - } - }).then(function (response) { - $location.url("/claims/" + $scope.claim.id + "/"); - }); - } - $scope.saveClaim = function (backToList) { - this.claim.whoami = $scope.whoami; - if (this.claim.id_team) { - this.claim.id_team = parseInt(this.claim.id_team, 10); - } else { - this.claim.id_team = null; - } - if (this.claim.id) { - this.claim.$update(function (v) { - v.id_team = "" + v.id_team; - if ($scope.comm.ndescription) - $scope.saveDescription(); - else if (backToList) - $location.url("/claims/"); - else - $scope.claim = Claim.get({ claimId: $routeParams.claimId }, function (v) { - v.id_team = "" + v.id_team; - if (!v.priority) - v.priority = "medium"; - }); - }); - } else { - this.claim.$save(function () { - if (!$scope.comm.ndescription) - $scope.comm.ndescription = "Création de la tâche"; - $scope.saveDescription(); - }, function (response) { - $scope.addToast('danger', 'An error occurs when trying to save claim:', response.data.errmsg); - }); - } - } - $scope.deleteClaim = function () { - this.claim.$remove(function () { $location.url("/claims/"); }); - } - }) - - .controller("ThemesListController", function ($scope, Theme, $location, $rootScope, $http) { - $scope.themes = Theme.query(); - $scope.fields = ["name", "authors", "headline", "path"]; - - $scope.validateSearch = function (keyEvent) { - if (keyEvent.which === 13) { - var myTheme = null; - $scope.themes.forEach(function (theme) { - if (String(theme.name.toLowerCase()).indexOf($scope.query.toLowerCase()) >= 0) { - if (myTheme === null) - myTheme = theme; - else - myTheme = false; - } - }); - if (myTheme) - $location.url("themes/" + myTheme.id); - } - }; - - $scope.show = function (id) { - $location.url("/themes/" + id); - }; - $scope.inSync = false; - $scope.sync = function () { - $scope.inSync = true; - $http({ - url: "api/sync/themes", - method: "POST" - }).then(function (response) { - $scope.inSync = false; - $scope.themes = Theme.query(); - $rootScope.staticFilesNeedUpdate++; - if (response.data) - $scope.addToast('danger', 'An error occurs when synchronizing theme list:', response.data); - else - $scope.addToast('success', 'Synchronisation de la liste des thèmes terminée avec succès.'); - }, function (response) { - $scope.inSync = false; - $scope.addToast('danger', 'An error occurs when synchronizing theme list:', response.data.errmsg); - }); - }; - }) - .controller("ThemeController", function ($scope, Theme, $routeParams, $location, $rootScope, $http) { - $scope.theme = Theme.get({ themeId: $routeParams.themeId }); - $scope.fields = ["name", "urlid", "locked", "authors", "headline", "intro", "image", "background_color", "partner_txt", "partner_href", "partner_img"]; - - $scope.saveTheme = function () { - if (this.theme.id) { - this.theme.$update(); - } else { - this.theme.$save(function () { - $location.url("/themes/" + $scope.theme.id); - }); - } - $rootScope.staticFilesNeedUpdate++; - } - $scope.deleteTheme = function () { - this.theme.$remove(function () { - $rootScope.staticFilesNeedUpdate++; - $location.url("/themes/"); - }, function (response) { - $scope.addToast('danger', 'An error occurs when trying to delete theme:', response.data.errmsg); - }); - } - $scope.checkExoSync = function () { - $scope.diff = null; - $http({ - url: "api/themes/" + $scope.theme.id + "/diff-sync", - method: "POST" - }).then(function (response) { - $scope.diff = response.data; - if (response.data === null) { - $scope.addToast('success', 'Changements par rapport au dépôt', "Tout est pareil !"); - } - }, function (response) { - $scope.diff = null; - $scope.addToast('danger', 'An error occurs when synchronizing exercice:', response.data.errmsg); - }); - }; - }) - - .controller("TagsListController", function ($scope, $http) { - $scope.tags = []; - $http({ - url: "api/tags", - method: "GET" - }).then(function (response) { - $scope.tags = response.data - }); - }) - - .controller("AllExercicesListController", function ($scope, Exercice, Theme, $routeParams, $location, $rootScope, $http, $filter) { - $http({ - url: "api/themes.json", - method: "GET" - }).then(function (response) { - $scope.themes = response.data - }); - - $scope.exercices = Exercice.query(); - $scope.exercice = {}; // Array used to save fields to updates in selected exercices - $scope.fields = ["title", "headline"]; - - $scope.validateSearch = function (keyEvent) { - if (keyEvent.which === 13) { - var myExercice = null; - $scope.exercices.forEach(function (exercice) { - if (String(exercice.title.toLowerCase()).indexOf($scope.query.toLowerCase()) >= 0) { - if (myExercice === null) - myExercice = exercice; - else - myExercice = false; - } - }); - if (myExercice) - $location.url("exercices/" + myExercice.id); - } - }; - - $scope.toggleSelectAll = function () { - angular.forEach($filter('filter')($scope.exercices, $scope.query), function (ex) { - ex.selected = !$scope.selectall - }) - } - - $scope.updateExercices = function () { - angular.forEach($scope.exercices, function (ex) { - if (ex.selected) { - Exercice.patch({ exerciceId: ex.id }, $scope.exercice); - } - }) - $scope.exercice = {}; - $rootScope.staticFilesNeedUpdate++; - $scope.addToast('success', 'Édition de masse terminée avec succès'); - } - - $scope.show = function (id) { - $location.url("/exercices/" + id); - }; - $scope.inSync = false; - $scope.syncFull = function () { - $scope.inSync = true; - $scope.done = -1; - $scope.total = 0; - var work = []; - var go = function () { - if (!work.length) { - $scope.addToast('info', "Synchronisation des exercices terminée."); - $scope.inSync = false; - return; - } - var u = work.pop(); - - $http({ - url: u, - method: "GET" - }).then(function (response) { - $rootScope.staticFilesNeedUpdate++; - $scope.done += 1; - go(); - }, function (response) { - $scope.done += 1; - go(); - }); - }; - - angular.forEach($scope.exercices, function (ex) { - if ($scope.syncFiles) - work.push("api/sync/exercices/" + ex.id + "/files"); - if ($scope.syncHints) - work.push("api/sync/exercices/" + ex.id + "/hints"); - if ($scope.syncFlags) - work.push("api/sync/exercices/" + ex.id + "/flags"); - }); - $scope.total = work.length; - go(); - - }; - $scope.syncFiles = true; - $scope.syncHints = true; - $scope.syncFlags = true; - }) - .controller("ExercicesListController", function ($scope, ThemedExercice, $location, $rootScope, $http) { - $scope.exercices = ThemedExercice.query({ themeId: $scope.theme.id }); - $scope.fields = ["title", "headline", "issue"]; - - $scope.show = function (id) { - $location.url("/themes/" + $scope.theme.id + "/exercices/" + id); - }; - - $scope.inSync = false; - $scope.syncExo = function () { - $scope.inSync = true; - $http({ - url: "api/sync/themes/" + $scope.theme.id + "/exercices", - method: "POST" - }).then(function (response) { - $scope.inSync = false; - $scope.exercices = ThemedExercice.query({ themeId: $scope.theme.id }); - $rootScope.staticFilesNeedUpdate++; - if (response.data) - $scope.addToast('warning', 'An error occurs when synchrinizing exercices:', response.data); - else - $scope.addToast('success', 'Synchronisation de la liste des exercices terminée avec succès.'); - }, function (response) { - $scope.inSync = false; - $scope.addToast('danger', 'An error occurs when synchrinizing exercices:', response.data.errmsg); - }); - }; - }) - .controller("ExerciceController", function ($scope, $rootScope, Exercice, ThemedExercice, $routeParams, $location, $http) { - if ($routeParams.themeId && $routeParams.exerciceId == "new") { - $scope.exercice = new ThemedExercice(); - } else { - $scope.exercice = Exercice.get({ exerciceId: $routeParams.exerciceId }); - } - - $scope.my_ex_num = {}; - - $http({ - url: "api/themes.json", - method: "GET" - }).then(function (response) { - $scope.themes = response.data; - if ($scope.exercice.id_theme) { - for (var k in $scope.themes[$scope.exercice.id_theme].exercices) { - var exercice = $scope.themes[$scope.exercice.id_theme].exercices[k]; - $scope.my_ex_num[exercice.id] = k; - } - } else { - for (var k in $scope.themes["0"].exercices) { - var exercice = $scope.themes["0"].exercices[k]; - $scope.my_ex_num[exercice.id] = k; - } - } - }); - $scope.exercices = Exercice.query(); - $scope.fields = ["title", "urlid", "authors", "disabled", "statement", "headline", "overview", "finished", "depend", "gain", "coefficient", "videoURI", "image", "background_color", "resolution", "issue", "issuekind", "wip"]; - - $scope.inSync = false; - $scope.syncExo = function () { - $scope.inSync = true; - $http({ - url: $scope.exercice.id_theme ? ("api/sync/themes/" + $scope.exercice.id_theme + "/exercices/" + $routeParams.exerciceId) : ("api/sync/exercices/" + $routeParams.exerciceId), - method: "POST" - }).then(function (response) { - $scope.inSync = false; - $scope.exercice = Exercice.get({ exerciceId: $routeParams.exerciceId }); - $rootScope.staticFilesNeedUpdate++; - if (response.data) - $scope.addToast('danger', 'An error occurs when synchronizing exercice:', response.data); - else - $scope.addToast('success', "Synchronisation de l'exercice terminée avec succès."); - }, function (response) { - $scope.inSync = false; - $scope.addToast('danger', 'An error occurs when synchronizing exercice:', response.data.errmsg); - }); - }; - $scope.checkExoSync = function () { - $scope.diff = null; - $http({ - url: ($scope.exercice.id_theme ? ("api/themes/" + $scope.exercice.id_theme + "/exercices/" + $routeParams.exerciceId) : ("api/exercices/" + $routeParams.exerciceId)) + "/diff-sync", - method: "POST" - }).then(function (response) { - $scope.diff = response.data; - if (response.data === null) { - $scope.addToast('success', 'Changements par rapport au dépôt', "Tout est pareil !"); - } - }, function (response) { - $scope.diff = null; - $scope.addToast('danger', 'An error occurs when synchronizing exercice:', response.data.errmsg); - }); - }; - - $scope.deleteExercice = function () { - var tid = $scope.exercice.id_theme; - this.exercice.$remove(function () { - $rootScope.staticFilesNeedUpdate++; - $location.url("/themes/" + tid); - }, function (response) { - $scope.addToast('danger', 'An error occurs when trying to delete exercice:', response.data.errmsg); - }); - } - $scope.saveExercice = function () { - if (this.exercice.id) { - this.exercice.$update(); - $rootScope.staticFilesNeedUpdate++; - } else if ($routeParams.themeId) { - this.exercice.$save({ themeId: $routeParams.themeId }, function () { - $rootScope.staticFilesNeedUpdate++; - $location.url("/themes/" + $scope.exercice.idTheme + "/exercices/" + $scope.exercice.id); - }, function (response) { - $scope.addToast('danger', 'An error occurs when trying to save exercice:', response.data.errmsg); - }); - } - } - $scope.selectedTeam = ""; - $scope.validateForTeam = function () { - var flagid = $("#validationModal").data("flagid"); - if (!flagid) return; - var target = { - team_id: parseInt($("#tteam").val().replace(/number:/, '')), - kind: $("#validationModal").data("kind"), - secondary: flagid, - }; - $http({ - url: "api/exercices/" + $scope.exercice.id + "/history.json", - method: "PUT", - data: target - }).then(function (response) { - $rootScope.staticFilesNeedUpdate++; - $("#validationModal").modal('hide'); - $scope.addToast('success', 'Flag validé avec succès'); - }, function (response) { - $scope.addToast('danger', 'An error occurs when trying to validate flag for team:', response.data.errmsg); - }); - } - $scope.historyAppend = function () { - var secondary = null; - if ($("#historyEvent").val() == "hint") - secondary = parseInt($("#historySecondaryHint").val().replace(/number:/, '')); - else if ($("#historyEvent").val() == "wchoices" || $("#historyEvent").val() == "flag_found") - secondary = parseInt($("#historySecondaryFlag").val().replace(/number:/, '')); - else if ($("#historyEvent").val() == "mcq_found") - secondary = parseInt($("#historySecondaryQuiz").val().replace(/number:/, '')); - - var target = { - team_id: parseInt($("#tteam").val().replace(/number:/, '')), - kind: $("#historyEvent").val(), - secondary: secondary, - }; - $http({ - url: "api/exercices/" + $scope.exercice.id + "/history.json", - method: "PUT", - data: target - }).then(function (response) { - $rootScope.staticFilesNeedUpdate++; - $("#appendHistoryModal").modal('hide'); - $scope.addToast('success', 'Événement ajouté avec succès'); - $scope.refreshHistory(); - }, function (response) { - $scope.addToast('danger', 'An error occurs when trying to add event in history:', response.data.errmsg); - }); - } - }) - - .controller("SearchTryController", function ($scope, ExerciceTries) { - $scope.tr = ExerciceTries.get({ exerciceId: $scope.exercice.id, tryId: $scope.row.secondary }); - }) - - .controller("SubmissionsStatsController", function ($scope, $http, $interval) { - var refresh = function () { - $http({ - url: "api/submissions-stats.json", - }).then(function (response) { - $scope.submissionsstats = response.data; - }); - } - var myinterval = $interval(refresh, 15000); - refresh(); - $scope.$on('$destroy', function () { $interval.cancel(myinterval); }); - }) - - .controller("ValidationsStatsController", function ($scope, $http, $interval) { - var refresh = function () { - $http({ - url: "api/validations-stats.json", - }).then(function (response) { - $scope.validationsstats = response.data; - }); - } - var myinterval = $interval(refresh, 15000); - refresh(); - $scope.$on('$destroy', function () { $interval.cancel(myinterval); }); - }) - - .controller("ExercicesStatsController", function ($scope, Themes, ExercicesStats) { - $scope.themes = Themes.get(); - $scope.exercices = {}; - ExercicesStats.query().$promise.then(function (exs) { - exs.forEach(function (ex) { - $scope.exercices[ex.id_exercice] = ex; - }) - }); - $scope.lenExoArray = []; - $scope.themes.$promise.then(function (themes) { - if (themes['0'] && themes['0'].exercices.length) { - var j = 0; - for (var i = themes['0'].exercices.length / 10; i >= 0; i--) { - $scope.lenExoArray.push(j++); - } - } - }); - }) - - .controller("ExerciceStatsController", function ($scope, ExerciceStats, $routeParams) { - $scope.stats = ExerciceStats.get({ exerciceId: $routeParams.exerciceId }); - }) - - .controller("ExerciceClaimsController", function ($scope, ExerciceClaims, Team, ClaimAssignee, $routeParams, $location) { - $scope.claims = ExerciceClaims.query({ exerciceId: $routeParams.exerciceId }); - $scope.assignees = ClaimAssignee.query(); - - $scope.claims.$promise.then(function (claims) { - claims.forEach(function (claim, cid) { - $scope.claims[cid].team = Team.get({ teamId: claim.id_team }); - }) - }); - - $scope.showClosed = false; - $scope.show = function (id) { - $location.url("/claims/" + id); - }; - }) - - .controller("ForgeLinksController", function ($scope, $http) { - $http({ - url: "api/exercices_forge_bindings.json", - }).then(function (response) { - $scope.forge_links = response.data; - }, function (response) { - $scope.addToast('danger', 'An error occurs when generating exercice forge links: ', response.data.errmsg); - }); - }) - - .controller("ExerciceTagsController", function ($scope, ExerciceTags, $routeParams, $rootScope) { - $scope.tags = ExerciceTags.query({ exerciceId: $routeParams.exerciceId }); - - $scope.addTag = function () { - $scope.tags.push(""); - } - $scope.deleteTag = function () { - $scope.tags.splice($scope.tags.indexOf(this.tag), 1); - return $scope.saveTags(); - } - $scope.saveTags = function () { - ExerciceTags.update({ exerciceId: $routeParams.exerciceId }, this.tags); - $rootScope.staticFilesNeedUpdate++; - } - }) - - .controller("ExerciceHistoryController", function ($scope, ExerciceHistory, $routeParams, $http, $rootScope) { - $scope.history = []; - $scope.refreshHistory = function () { - $scope.history = ExerciceHistory.query({ exerciceId: $routeParams.exerciceId }); - } - $scope.$parent.refreshHistory = $scope.refreshHistory; - $scope.refreshHistory(); - $scope.updHistory = function () { - var target = { - team_id: $("#updHistory").data("idteam"), - kind: $("#updHistory").data("kind"), - time: $("#updHistory").data("time"), - secondary: $("#updHistory").data("secondary") != "" ? $("#updHistory").data("secondary") : null, - coeff: parseFloat($('#historycoeff').val()), - }; - if (target) { - $http({ - url: "api/exercices/" + $routeParams.exerciceId + "/history.json", - method: "PATCH", - data: target - }).then(function (response) { - $rootScope.staticFilesNeedUpdate++; - $scope.history = ExerciceHistory.query({ exerciceId: $routeParams.exerciceId }); - $('#updHistory').modal('hide'); - }, function (response) { - $scope.addToast('danger', 'An error occurs when updating history item: ', response.data.errmsg); - }); - } - } - $scope.delHistory = function (row) { - $http({ - url: "api/exercices/" + $routeParams.exerciceId + "/history.json", - method: "DELETE", - data: row - }).then(function (response) { - $rootScope.staticFilesNeedUpdate++; - $scope.history = ExerciceHistory.query({ exerciceId: $routeParams.exerciceId }); - }, function (response) { - $scope.addToast('danger', 'An error occurs when removing history item: ', response.data.errmsg); - }); - } - }) - - .controller("ExerciceFilesController", function ($scope, ExerciceFile, $routeParams, $rootScope, $http) { - $scope.files = ExerciceFile.query({ exerciceId: $routeParams.exerciceId }); - - $scope.deleteFile = function () { - this.file.$delete(function () { - $scope.files = ExerciceFile.query({ exerciceId: $routeParams.exerciceId }); - }); - $rootScope.staticFilesNeedUpdate++; - return false; - } - $scope.deleteFileDep = function () { - $http({ - url: "api//files/" + this.file.id + "/dependancies/" + this.dep.id, - method: "DELETE" - }).then(function (response) { - $scope.files = ExerciceFile.query({ exerciceId: $routeParams.exerciceId }); - }, function (response) { - $scope.addToast('danger', 'An error occurs when removing file dep:', response.data.errmsg); - }); - $rootScope.staticFilesNeedUpdate++; - return false; - } - $scope.saveFile = function () { - this.file.$update(); - $rootScope.staticFilesNeedUpdate++; - } - $scope.inSync = false; - $scope.syncFiles = function () { - $scope.inSync = true; - $http({ - url: "api/sync/exercices/" + $routeParams.exerciceId + "/files", - method: "POST" - }).then(function (response) { - $scope.inSync = false; - $rootScope.staticFilesNeedUpdate++; - $scope.files = ExerciceFile.query({ exerciceId: $routeParams.exerciceId }); - if (response.data) - $scope.addToast('danger', 'An error occurs when synchronizing flags list:', response.data); - else - $scope.addToast('success', "Synchronisation de la liste de fichiers terminée avec succès."); - }, function (response) { - $scope.inSync = false; - $scope.addToast('danger', 'An error occurs when synchronizing flags list:', response.data.errmsg); - }); - }; - }) - - .controller("ExerciceHintsController", function ($scope, ExerciceHint, $routeParams, $rootScope, $http) { - $scope.hints = ExerciceHint.query({ exerciceId: $routeParams.exerciceId }); - - $scope.addHint = function () { - $scope.hints.push(new ExerciceHint()); - } - $scope.deleteHint = function () { - this.hint.$delete(function () { - $scope.hints = ExerciceHint.query({ exerciceId: $routeParams.exerciceId }); - $rootScope.staticFilesNeedUpdate++; - }, function (response) { - $scope.addToast('danger', 'An error occurs when trying to delete hint:', response.data.errmsg); - }); - } - $scope.saveHint = function () { - if (this.hint.id) { - this.hint.$update(); - } else { - this.hint.$save({ exerciceId: $routeParams.exerciceId }); - } - $rootScope.staticFilesNeedUpdate++; - } - $scope.inSync = false; - $scope.syncHints = function () { - $scope.inSync = true; - $http({ - url: "api/sync/exercices/" + $routeParams.exerciceId + "/hints", - method: "POST" - }).then(function (response) { - $scope.inSync = false; - $scope.hints = ExerciceHint.query({ exerciceId: $routeParams.exerciceId }); - $rootScope.staticFilesNeedUpdate++; - if (response.data) - $scope.addToast('danger', 'An error occurs when synchronizing hints list:', response.data); - else - $scope.addToast('success', "Synchronisation de la liste d'indices terminée avec succès."); - }, function (response) { - $scope.inSync = false; - $scope.addToast('danger', 'An error occurs when synchronizing hints list:', response.data.errmsg); - }); - }; - }) - - .controller("ExerciceHintDepsController", function ($scope, $routeParams, ExerciceHintDeps) { - $scope.init = function (hint) { - $scope.deps = ExerciceHintDeps.query({ exerciceId: $routeParams.exerciceId, hintId: hint.id }); - } - }) - - .controller("ExerciceFlagsController", function ($scope, ExerciceFlag, $routeParams, $rootScope, $http) { - $scope.flags = ExerciceFlag.query({ exerciceId: $routeParams.exerciceId }); - - $scope.flags.$promise.then(function (flags) { - flags.forEach(function (flag, fid) { - flags[fid].values = ['']; - }); - }); - - $scope.changeValue = function (flag) { - flag.value = undefined; - flag.show_raw = true; - } - $scope.addFlag = function () { - $scope.flags.push(new ExerciceFlag()); - } - $scope.deleteFlag = function () { - this.flag.$delete(function () { - $scope.flags = ExerciceFlag.query({ exerciceId: $routeParams.exerciceId }); - $rootScope.staticFilesNeedUpdate++; - }, function (response) { - $scope.addToast('danger', 'An error occurs when trying to delete flag:', response.data.errmsg); - }); - } - $scope.saveFlag = function () { - if (this.flag.id) { - this.flag.$update().then(function() {}, function(error) { $scope.addToast('danger', 'Impossible de mettre à jour le flag :', error.data.errmsg); }); - } else { - this.flag.$save({ exerciceId: $routeParams.exerciceId }).then(function() {}, function(error) { $scope.addToast('danger', 'Impossible de créer le flag :', error.data.errmsg); }); - } - $rootScope.staticFilesNeedUpdate++; - } - $scope.testFlag = function (flag) { - if (flag.values) { - var val = flag.value; - treatFlagKey(flag); - flag.test_str = flag.value; - flag.value = val; - } - - if (flag.test_str) { - $http({ - url: "api/exercices/" + $routeParams.exerciceId + "/flags/" + flag.id + "/try", - data: { "flag": flag.test_str }, - method: "POST" - }).then(function (response) { - flag.test_str = ""; - $scope.addToast('success', "Flag Ok !"); - }, function (response) { - flag.test_str = ""; - $scope.addToast('danger', 'An error occurs: ', response.data.errmsg); - }); - } - } - $scope.inSync = false; - $scope.syncFlags = function () { - $scope.inSync = true; - $http({ - url: "api/sync/exercices/" + $routeParams.exerciceId + "/flags", - method: "POST" - }).then(function (response) { - $scope.inSync = false; - $scope.flags = ExerciceFlag.query({ exerciceId: $routeParams.exerciceId }); - $rootScope.staticFilesNeedUpdate++; - if (response.data) - $scope.addToast('danger', 'An error occurs when synchronizing flags list:', response.data); - else - $scope.addToast('success', "Synchronisation de la liste de drapeaux terminée avec succès."); - }, function (response) { - $scope.inSync = false; - $scope.addToast('danger', 'An error occurs when synchronizing flags list:', response.data.errmsg); - }); - }; - }) - - .controller("ExerciceFlagChoicesController", function ($scope, ExerciceFlagChoices, $routeParams) { - $scope.choices = ExerciceFlagChoices.query({ exerciceId: $routeParams.exerciceId, flagId: $scope.flag.id }) - - $scope.flag.wantchoices = function () { - $scope.flag.choices = {}; - $scope.choices.forEach(function (choice) { - $scope.flag.choices[choice.value] = choice.label - }) - } - $scope.choices.$promise.then(function (choices) { - if ($scope.flag.choices_cost == 0 && choices.length > 0) { - $scope.flag.wantchoices() - } - }) - - - $scope.addChoice = function () { - $scope.choices.push(new ExerciceFlagChoices()) - } - - $scope.saveChoice = function () { - if (this.choice.id) - this.choice.$update({ exerciceId: $routeParams.exerciceId, flagId: this.flag.id }) - else - this.choice.$save({ exerciceId: $routeParams.exerciceId, flagId: this.flag.id }) - } - - $scope.deleteChoice = function () { - if (this.choice.id) - this.choice.$delete({ exerciceId: $routeParams.exerciceId, flagId: this.flag.id }, function () { - $scope.choices = ExerciceFlagChoices.query({ exerciceId: $routeParams.exerciceId, flagId: $scope.flag.id }) - }) - } - }) - - .controller("ExerciceFlagDepsController", function ($scope, $routeParams, ExerciceFlagDeps) { - $scope.init = function (flag) { - $scope.deps = ExerciceFlagDeps.query({ exerciceId: $routeParams.exerciceId, flagId: flag.id }); - } - }) - - .controller("ExerciceFlagStatsController", function ($scope, $routeParams, ExerciceFlagStats, $http) { - $scope.init = function (flag) { - $scope.flag_id = flag.id; - $scope.stats = ExerciceFlagStats.get({ exerciceId: $routeParams.exerciceId, flagId: flag.id }); - } - $scope.deleteTries = function () { - $http.delete(`/api/exercices/${$routeParams.exerciceId}/flags/${$scope.flag_id}/tries`).then(function () { - $scope.stats = ExerciceFlagStats.get({ exerciceId: $routeParams.exerciceId, flagId: $scope.flag_id }); - }); - } - }) - - .controller("ExerciceMCQFlagsController", function ($scope, ExerciceMCQFlag, $routeParams, $rootScope) { - $scope.quiz = ExerciceMCQFlag.query({ exerciceId: $routeParams.exerciceId }); - - $scope.addQuiz = function () { - $scope.quiz.push(new ExerciceMCQFlag()); - } - $scope.deleteQuiz = function () { - this.q.$delete(function () { - $scope.quiz = ExerciceMCQFlag.query({ exerciceId: $routeParams.exerciceId }); - $rootScope.staticFilesNeedUpdate++; - }, function (response) { - $scope.addToast('danger', 'An error occurs when trying to delete flag:', response.data.errmsg); - }); - } - $scope.saveQuiz = function () { - if (this.q.id) { - this.q.$update(); - } else { - this.q.$save({ exerciceId: $routeParams.exerciceId }); - } - $rootScope.staticFilesNeedUpdate++; - } - - $scope.addChoice = function () { - this.quiz[this.qk].entries.push({ label: "", response: false }) - } - $scope.deleteChoice = function () { - this.quiz[this.qk].entries.splice(this.quiz[this.qk].entries.indexOf(this.choice), 1); - } - }) - - .controller("ExerciceMCQDepsController", function ($scope, $routeParams, ExerciceMCQDeps) { - $scope.init = function (flag) { - $scope.deps = ExerciceMCQDeps.query({ exerciceId: $routeParams.exerciceId, mcqId: flag.id }); - } - }) - - .controller("ExerciceMCQStatsController", function ($scope, $routeParams, ExerciceMCQStats, $http) { - $scope.init = function (mcq) { - $scope.mcq_id = mcq.id; - $scope.stats = ExerciceMCQStats.get({ exerciceId: $routeParams.exerciceId, mcqId: mcq.id }); - } - $scope.deleteTries = function () { - $http.delete(`/api/exercices/${$routeParams.exerciceId}/quiz/${$scope.mcq_id}/tries`).then(function () { - $scope.stats = ExerciceMCQStats.get({ exerciceId: $routeParams.exerciceId, mcqId: $scope.mcq_id }); - }); - } - }) - - .controller("TeamsListController", function ($scope, $rootScope, Team, $location, $http) { - $scope.teams = Team.query(); - $scope.fields = ["id", "name"]; - - $scope.order = []; - $scope.teams.$promise.then(function (teams) { - teams.forEach(function (team) { - $scope.order.push(team.id); - }); - }); - $scope.validateSearch = function (keyEvent) { - if (keyEvent.which === 13) { - var myTeam = null; - $scope.teams.forEach(function (team) { - if (String(team.name.toLowerCase()).indexOf($scope.query.toLowerCase()) >= 0) { - if (myTeam === null) - myTeam = team; - else - myTeam = false; - } - }); - if (myTeam) - $location.url("teams/" + myTeam.id); - } - }; - - $scope.desactiveTeams = function () { - $http.post("api/disableinactiveteams").then(function () { - $scope.teams = Team.query(); - }, function (response) { - $scope.addToast('danger', 'An error occurs when disabling inactive teams:', response.data.errmsg); - }); - } - $scope.refineTeamsColors = function () { - $http.post("api/refine_colors").then(function () { - $scope.teams = Team.query(); - }, function (response) { - $scope.addToast('danger', 'An error occurs when updating teams:', response.data.errmsg); - }); - } - $scope.show = function (id) { - $location.url("/teams/" + id); - }; - $scope.triggerTeamsImport = function() { - document.getElementById('crTeamsInput').click(); - }; - $scope.uploadFile = function() { - var formData = new FormData(); - formData.append('file', $scope.selectedFile); - - $http.post('api/cyberrange-teams.json', formData, { - transformRequest: angular.identity, - headers: {'Content-Type': undefined}, - }).then(function(response) { - $scope.teams = response.data; - $scope.addToast('success', 'Import des équipes', "L'import a été réalisé avec succès !"); - }, function(error) { - console.log(error); - $scope.addToast('danger', 'Import des équipes', error.data.errmsg); - }); - }; - }) - .controller("TeamMembersController", function ($scope, TeamMember) { - $scope.fields = ["firstname", "lastname", "nickname", "company"]; - if ($scope.team != null) { - $scope.members = TeamMember.query({ teamId: $scope.team.id }); - - $scope.newMember = function () { - $scope.members.push(new TeamMember()); - } - $scope.saveTeamMembers = function () { - if (this.team.id) { - TeamMember.save({ teamId: this.team.id }, $scope.members); - } - } - $scope.removeMember = function (member) { - angular.forEach($scope.members, function (m, k) { - if (member == m) - $scope.members.splice(k, 1); - }); - } - } - }) - .controller("TeamController", function ($scope, $rootScope, $location, Team, TeamMember, $routeParams, $http) { - if ($scope.team && $scope.team.id) - $routeParams.teamId = $scope.team.id; - $scope.team = Team.get({ teamId: $routeParams.teamId }); - $scope.fields = ["name", "color", "external_id"]; - - $scope.saveTeam = function () { - if (this.team.id) { - this.team.$update(); - $rootScope.staticFilesNeedUpdate++; - } else { - this.team.$save(function () { - $rootScope.staticFilesNeedUpdate++; - $location.url("/teams/" + $scope.team.id); - }); - } - } - $scope.resetPasswd = function (team) { - $http({ - url: "api/password", - method: "POST" - }).then(function (response) { - team.password = response.data.password; - }); - } - $scope.deleteTeam = function () { - backName = this.team.name; - this.team.$remove(function () { $scope.addToast('success', 'Team ' + backName + ' successfully removed.'); $location.url("/teams/"); $rootScope.staticFilesNeedUpdate++; }, - function (response) { $scope.addToast('danger', 'An error occurs during suppression of the team:', response.data.errmsg); }); - } - $scope.showStats = function () { - $location.url("/teams/" + $scope.team.id + "/stats"); - } - $scope.showScore = function () { - $location.url("/teams/" + $scope.team.id + "/score"); - } - }) - .controller("TeamCertificatesController", function ($scope, $rootScope, TeamCertificate, $routeParams, $http) { - $scope.certificates = TeamCertificate.query({ teamId: $routeParams.teamId }); - $scope.certificates.$promise.then(function (certificates) { - certificates.forEach(function (certificate, cid) { - certificate.serial = parseInt(certificate.id).toString(16); - }); - }); - - $scope.dissociateCertificate = function (certificate) { - $http({ - url: "api/certs/" + certificate.id, - method: "PUT", - data: { - id_team: null - } - }).then(function (response) { - $scope.certificates = TeamCertificate.query({ teamId: $routeParams.teamId }).$promise.then(function (certificates) { - certificates.forEach(function (certificate, cid) { - certificate.serial = parseInt(certificate.id).toString(16); - }); - }); - $scope.addToast('success', 'Certificate successfully dissociated!'); - }, function (response) { - $scope.addToast('danger', 'An error occurs when dissociating certiticate:', response.data.errmsg); - }); - } - }) - .controller("TeamAssociationsController", function ($scope, $rootScope, TeamAssociation, $routeParams, $http) { - $scope.associations = TeamAssociation.query({ teamId: $routeParams.teamId }); - $scope.form = { "newassoc": "" }; - - $scope.addAssociation = function () { - if ($scope.form.newassoc) { - TeamAssociation.save({ teamId: $scope.team.id, assoc: $scope.form.newassoc }).$promise.then( - function () { - $scope.form.newassoc = ""; - $scope.associations = TeamAssociation.query({ teamId: $routeParams.teamId }); - }, function (response) { - if (response.data) - $scope.addToast('danger', 'An error occurs when creating user association: ', response.data.errmsg); - }); - } - } - - $scope.dropAssociation = function (assoc) { - TeamAssociation.delete({ teamId: $routeParams.teamId, assoc: assoc }).$promise.then( - function () { - $scope.associations = TeamAssociation.query({ teamId: $routeParams.teamId }); - }, function (response) { - $scope.addToast('danger', 'An error occurs when removing user association: ', response.data.errmsg); - }); - } - }) - .controller("TeamHistoryController", function ($scope, TeamHistory, $routeParams, $http, $rootScope) { - $scope.history = TeamHistory.query({ teamId: $routeParams.teamId }); - $scope.updHistory = function () { - var target = { - team_id: parseInt($routeParams.teamId), - kind: $("#updHistory").data("kind"), - time: $("#updHistory").data("time"), - secondary: $("#updHistory").data("secondary") != "" ? $("#updHistory").data("secondary") : null, - coeff: parseFloat($('#historycoeff').val()), - }; - if (target) { - $http({ - url: "api/exercices/" + $("#updHistory").data("primary") + "/history.json", - method: "PATCH", - data: target - }).then(function (response) { - $rootScope.staticFilesNeedUpdate++; - $scope.history = TeamHistory.query({ teamId: $routeParams.teamId }); - $('#updHistory').modal('hide'); - }, function (response) { - $scope.addToast('danger', 'An error occurs when updating history item: ', response.data.errmsg); - }); - } - } - $scope.delHistory = function (row) { - $http({ - url: "api/teams/" + $routeParams.teamId + "/history.json", - method: "DELETE", - data: row - }).then(function (response) { - $rootScope.staticFilesNeedUpdate++; - $scope.history = TeamHistory.query({ teamId: $routeParams.teamId }); - }, function (response) { - $scope.addToast('danger', 'An error occurs when removing history item: ', response.data.errmsg); - }); - } - }) - .controller("TeamScoreController", function ($scope, TeamScore, TeamMy, Exercice, $routeParams) { - $scope.scores = TeamScore.query({ teamId: $routeParams.teamId }); - $scope.my = TeamMy.get({ teamId: $routeParams.teamId }); - $scope.exercices = Exercice.query(); - }) - .controller("TeamStatsController", function ($scope, TeamStats, $routeParams) { - $scope.teamstats = TeamStats.get({ teamId: $routeParams.teamId }); - $scope.teamstats.$promise.then(function (res) { - solvedByLevelPie("#pieLevels", res.levels); - var themes = []; - angular.forEach(res.themes, function (theme, tid) { - themes.push(theme); - }) - solvedByThemesPie("#pieThemes", themes); - }); - }) - .controller("TeamsJSONController", function ($scope, Teams) { - $scope.teams = Teams.get(); - $scope.teams.$promise.then(function (teams) { - $scope.rank = []; - Object.keys(teams).forEach(function (team) { - $scope.teams[team].id = team; - $scope.rank.push($scope.teams[team]); - }) - }); - }) - .controller("TeamExercicesController", function ($scope, Teams, Themes, TeamMy, Exercice, $routeParams) { - $scope.teams = Teams.get(); - $scope.themes = Themes.get(); - $scope.exercices = Exercice.query(); - $scope.my = TeamMy.get({ teamId: $routeParams.teamId }); - - $scope.teams.$promise.then(function (res) { - $scope.nb_teams = 0; - $scope.nb_reg_teams = Object.keys(res).length; - angular.forEach(res, function (team, tid) { - if (team.rank) - $scope.nb_teams += 1; - }, 0); - }); - - $scope.my.$promise.then(function (res) { - $scope.solved_exercices = 0; - angular.forEach(res.exercices, function (exercice, eid) { - if (exercice.solved_rank) { - $scope.solved_exercices += 1; - } - }, 0); - }); - - }) - - .controller("PresenceController", function ($scope, TeamPresence, $routeParams) { - $scope.presence = TeamPresence.query({ teamId: $routeParams.teamId }); - $scope.presence.$promise.then(function (res) { - presenceCal($scope, "#presenceCal", res); - }); - }); - -function solvedByLevelPie(location, data) { - var width = d3.select(location).node().getBoundingClientRect().width - parseInt(d3.select(location).style("padding-right")) - parseInt(d3.select(location).style("padding-left")), - height = d3.select(location).node().getBoundingClientRect().width - 10, - radius = Math.min(width, height) / 2, - innerRadius = 0.1 * radius; - - var color = d3.scale.ordinal() - .range(["#9E0041", "#C32F4B", "#E1514B", "#F47245", "#FB9F59", "#FEC574", "#FAE38C", "#EAD195", "#C7E89E", "#9CD6A4", "#6CC4A4", "#4D9DB4", "#4776B4", "#5E4EA1"]); - - var pie = d3.layout.pie() - .sort(null) - .value(function (d) { return d.width; }); - - var arc = d3.svg.arc() - .innerRadius(innerRadius) - .outerRadius(function (d) { - return (radius - innerRadius) * (d.data.score / 100.0) + innerRadius; - }); - - var outlineArc = d3.svg.arc() - .innerRadius(innerRadius) - .outerRadius(radius); - - var svg = d3.select(location).append("svg") - .attr("width", width) - .attr("height", height) - .append("g") - .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); - - data.forEach(function (d) { - d.score = d.solved * 100 / d.total; - d.width = d.tries + 1; - }); - - var path = svg.selectAll(".solidArc") - .data(pie(data)) - .enter().append("path") - .attr("fill", function (d) { return color(d.data.tip); }) - .attr("class", "solidArc") - .attr("stroke", "gray") - .attr("d", arc); - - var outerPath = svg.selectAll(".outlineArc") - .data(pie(data)) - .enter().append("path") - .attr("fill", "none") - .attr("stroke", "gray") - .attr("class", "outlineArc") - .attr("d", outlineArc); - - var labelArc = d3.svg.arc() - .outerRadius(0.8 * radius) - .innerRadius(0.8 * radius); - - svg.selectAll(".labelArc") - .data(pie(data)) - .enter().append("text") - .attr("transform", function (d) { return "translate(" + labelArc.centroid(d) + ")"; }) - .attr("dy", ".35em") - .attr("text-anchor", "middle") - .text(function (d) { return d.data.tip + ": " + d.data.solved + "/" + d.data.total; }); - - svg.selectAll(".label2Arc") - .data(pie(data)) - .enter().append("text") - .attr("transform", function (d) { return "translate(" + arc.centroid(d) + ")"; }) - .attr("dy", ".35em") - .attr("text-anchor", "middle") - .text(function (d) { return d.data.tries; }); -} - -function solvedByThemesPie(location, data) { - var width = d3.select(location).node().getBoundingClientRect().width - parseInt(d3.select(location).style("padding-right")) - parseInt(d3.select(location).style("padding-left")), - height = d3.select(location).node().getBoundingClientRect().width, - radius = Math.min(width, height) / 2, - innerRadius = 0.1 * radius; - - var color = d3.scale.ordinal() - .range(["#9E0041", "#C32F4B", "#E1514B", "#F47245", "#FB9F59", "#FEC574", "#FAE38C", "#EAD195", "#C7E89E", "#9CD6A4", "#6CC4A4", "#4D9DB4", "#4776B4", "#5E4EA1"]); - - var pie = d3.layout.pie() - .sort(null) - .value(function (d) { return d.width; }); - - var arc = d3.svg.arc() - .innerRadius(innerRadius) - .outerRadius(function (d) { - return (radius - innerRadius) * (d.data.score / 100.0) + innerRadius; - }); - - var outlineArc = d3.svg.arc() - .innerRadius(innerRadius) - .outerRadius(radius); - - var svg = d3.select(location).append("svg") - .attr("width", width) - .attr("height", height) - .append("g") - .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); - - data.forEach(function (d) { - d.score = d.solved * 100 / d.total; - d.width = d.tries + 0.5; - }); - - var path = svg.selectAll(".solidArc") - .data(pie(data)) - .enter().append("path") - .attr("fill", function (d) { return color(d.data.tip); }) - .attr("class", "solidArc") - .attr("stroke", "gray") - .attr("d", arc); - - var outerPath = svg.selectAll(".outlineArc") - .data(pie(data)) - .enter().append("path") - .attr("fill", "none") - .attr("stroke", "gray") - .attr("class", "outlineArc") - .attr("d", outlineArc); - - svg.selectAll(".label2Arc") - .data(pie(data)) - .enter().append("text") - .attr("transform", function (d) { return "translate(" + arc.centroid(d) + ")"; }) - .attr("dy", ".35em") - .attr("text-anchor", "middle") - .text(function (d) { return d.data.solved; }); - - var labelArc = d3.svg.arc() - .outerRadius(0.8 * radius) - .innerRadius(0.8 * radius); - - svg.selectAll(".labelArc") - .data(pie(data)) - .enter().append("text") - .attr("transform", function (d) { return "translate(" + labelArc.centroid(d) + ")"; }) - .attr("dy", ".35em") - .attr("text-anchor", "middle") - .text(function (d) { return d.data.tip + ": " + d.data.tries; }); -} - -function presenceCal(scope, location, data) { - var width = d3.select(location).node().getBoundingClientRect().width, - height = 80, - cellSize = 17; // cell size - - var percent = d3.format(".1%"), - format = d3.time.format("%H:%M"); - - var color = d3.scale.quantize() - .domain([0, 16]) - .range(d3.range(8).map(function (d) { return "q" + d + "-8"; })); - - var svg = d3.select(location).selectAll("svg") - .data(d3.range(scope.settings.start, scope.time.start + (scope.settings.start % 86400000 + scope.settings.end - scope.settings.start), 86400000).map(function (t) { return new Date(t); })) - .enter().append("svg") - .attr("width", width) - .attr("height", height) - .attr("class", "RdYlGn") - .append("g") - .attr("transform", "translate(" + ((width - cellSize * 24) / 2) + "," + (height - cellSize * 4 - 1) + ")"); - - svg.append("text") - .attr("transform", "translate(-6," + cellSize * 2.6 + ")rotate(-90)") - .style("text-anchor", "middle") - .text(function (d) { return d.getDate() + "-" + (d.getMonth() + 1); }); - - var rect = svg.selectAll(".quarter") - .data(function (d) { return d3.time.minutes(new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0), new Date(d.getFullYear(), d.getMonth(), d.getDate(), 23), 15); }) - .enter().append("rect") - .attr("width", cellSize) - .attr("height", cellSize) - .attr("transform", function (d) { return "translate(" + (d.getHours() * cellSize) + "," + (d.getMinutes() / 15 * cellSize) + ")"; }) - .attr("class", function (d) { - if (d >= scope.settings.start && d < scope.settings.start + scope.settings.end - scope.settings.start) return color(data.reduce(function (prev, cur) { - cur = new Date(cur).getTime(); - dv = d.getTime(); - return prev + ((dv <= cur && cur < dv + 15 * 60000) ? 1 : 0); - }, 0)); - }); -} diff --git a/admin/static/js/bootstrap.min.js b/admin/static/js/bootstrap.min.js deleted file mode 100644 index 97206dcd..00000000 --- a/admin/static/js/bootstrap.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v4.6.2 (https://getbootstrap.com/) - * Copyright 2011-2022 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap={},t.jQuery,t.Popper)}(this,(function(t,e,n){"use strict";function i(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var o=i(e),a=i(n);function s(t,e){for(var n=0;n=4)throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}};d.jQueryDetection(),o.default.fn.emulateTransitionEnd=function(t){var e=this,n=!1;return o.default(this).one(d.TRANSITION_END,(function(){n=!0})),setTimeout((function(){n||d.triggerTransitionEnd(e)}),t),this},o.default.event.special[d.TRANSITION_END]={bindType:f,delegateType:f,handle:function(t){if(o.default(t.target).is(this))return t.handleObj.handler.apply(this,arguments)}};var c="bs.alert",h=o.default.fn.alert,g=function(){function t(t){this._element=t}var e=t.prototype;return e.close=function(t){var e=this._element;t&&(e=this._getRootElement(t)),this._triggerCloseEvent(e).isDefaultPrevented()||this._removeElement(e)},e.dispose=function(){o.default.removeData(this._element,c),this._element=null},e._getRootElement=function(t){var e=d.getSelectorFromElement(t),n=!1;return e&&(n=document.querySelector(e)),n||(n=o.default(t).closest(".alert")[0]),n},e._triggerCloseEvent=function(t){var e=o.default.Event("close.bs.alert");return o.default(t).trigger(e),e},e._removeElement=function(t){var e=this;if(o.default(t).removeClass("show"),o.default(t).hasClass("fade")){var n=d.getTransitionDurationFromElement(t);o.default(t).one(d.TRANSITION_END,(function(n){return e._destroyElement(t,n)})).emulateTransitionEnd(n)}else this._destroyElement(t)},e._destroyElement=function(t){o.default(t).detach().trigger("closed.bs.alert").remove()},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data(c);i||(i=new t(this),n.data(c,i)),"close"===e&&i[e](this)}))},t._handleDismiss=function(t){return function(e){e&&e.preventDefault(),t.close(this)}},l(t,null,[{key:"VERSION",get:function(){return"4.6.2"}}]),t}();o.default(document).on("click.bs.alert.data-api",'[data-dismiss="alert"]',g._handleDismiss(new g)),o.default.fn.alert=g._jQueryInterface,o.default.fn.alert.Constructor=g,o.default.fn.alert.noConflict=function(){return o.default.fn.alert=h,g._jQueryInterface};var m="bs.button",p=o.default.fn.button,_="active",v='[data-toggle^="button"]',y='input:not([type="hidden"])',b=".btn",E=function(){function t(t){this._element=t,this.shouldAvoidTriggerChange=!1}var e=t.prototype;return e.toggle=function(){var t=!0,e=!0,n=o.default(this._element).closest('[data-toggle="buttons"]')[0];if(n){var i=this._element.querySelector(y);if(i){if("radio"===i.type)if(i.checked&&this._element.classList.contains(_))t=!1;else{var a=n.querySelector(".active");a&&o.default(a).removeClass(_)}t&&("checkbox"!==i.type&&"radio"!==i.type||(i.checked=!this._element.classList.contains(_)),this.shouldAvoidTriggerChange||o.default(i).trigger("change")),i.focus(),e=!1}}this._element.hasAttribute("disabled")||this._element.classList.contains("disabled")||(e&&this._element.setAttribute("aria-pressed",!this._element.classList.contains(_)),t&&o.default(this._element).toggleClass(_))},e.dispose=function(){o.default.removeData(this._element,m),this._element=null},t._jQueryInterface=function(e,n){return this.each((function(){var i=o.default(this),a=i.data(m);a||(a=new t(this),i.data(m,a)),a.shouldAvoidTriggerChange=n,"toggle"===e&&a[e]()}))},l(t,null,[{key:"VERSION",get:function(){return"4.6.2"}}]),t}();o.default(document).on("click.bs.button.data-api",v,(function(t){var e=t.target,n=e;if(o.default(e).hasClass("btn")||(e=o.default(e).closest(b)[0]),!e||e.hasAttribute("disabled")||e.classList.contains("disabled"))t.preventDefault();else{var i=e.querySelector(y);if(i&&(i.hasAttribute("disabled")||i.classList.contains("disabled")))return void t.preventDefault();"INPUT"!==n.tagName&&"LABEL"===e.tagName||E._jQueryInterface.call(o.default(e),"toggle","INPUT"===n.tagName)}})).on("focus.bs.button.data-api blur.bs.button.data-api",v,(function(t){var e=o.default(t.target).closest(b)[0];o.default(e).toggleClass("focus",/^focus(in)?$/.test(t.type))})),o.default(window).on("load.bs.button.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-toggle="buttons"] .btn')),e=0,n=t.length;e0,this._pointerEvent=Boolean(window.PointerEvent||window.MSPointerEvent),this._addEventListeners()}var e=t.prototype;return e.next=function(){this._isSliding||this._slide(N)},e.nextWhenVisible=function(){var t=o.default(this._element);!document.hidden&&t.is(":visible")&&"hidden"!==t.css("visibility")&&this.next()},e.prev=function(){this._isSliding||this._slide(D)},e.pause=function(t){t||(this._isPaused=!0),this._element.querySelector(".carousel-item-next, .carousel-item-prev")&&(d.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},e.cycle=function(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config.interval&&!this._isPaused&&(this._updateInterval(),this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},e.to=function(t){var e=this;this._activeElement=this._element.querySelector(I);var n=this._getItemIndex(this._activeElement);if(!(t>this._items.length-1||t<0))if(this._isSliding)o.default(this._element).one(A,(function(){return e.to(t)}));else{if(n===t)return this.pause(),void this.cycle();var i=t>n?N:D;this._slide(i,this._items[t])}},e.dispose=function(){o.default(this._element).off(".bs.carousel"),o.default.removeData(this._element,w),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},e._getConfig=function(t){return t=r({},k,t),d.typeCheckConfig(T,t,O),t},e._handleSwipe=function(){var t=Math.abs(this.touchDeltaX);if(!(t<=40)){var e=t/this.touchDeltaX;this.touchDeltaX=0,e>0&&this.prev(),e<0&&this.next()}},e._addEventListeners=function(){var t=this;this._config.keyboard&&o.default(this._element).on("keydown.bs.carousel",(function(e){return t._keydown(e)})),"hover"===this._config.pause&&o.default(this._element).on("mouseenter.bs.carousel",(function(e){return t.pause(e)})).on("mouseleave.bs.carousel",(function(e){return t.cycle(e)})),this._config.touch&&this._addTouchEventListeners()},e._addTouchEventListeners=function(){var t=this;if(this._touchSupported){var e=function(e){t._pointerEvent&&j[e.originalEvent.pointerType.toUpperCase()]?t.touchStartX=e.originalEvent.clientX:t._pointerEvent||(t.touchStartX=e.originalEvent.touches[0].clientX)},n=function(e){t._pointerEvent&&j[e.originalEvent.pointerType.toUpperCase()]&&(t.touchDeltaX=e.originalEvent.clientX-t.touchStartX),t._handleSwipe(),"hover"===t._config.pause&&(t.pause(),t.touchTimeout&&clearTimeout(t.touchTimeout),t.touchTimeout=setTimeout((function(e){return t.cycle(e)}),500+t._config.interval))};o.default(this._element.querySelectorAll(".carousel-item img")).on("dragstart.bs.carousel",(function(t){return t.preventDefault()})),this._pointerEvent?(o.default(this._element).on("pointerdown.bs.carousel",(function(t){return e(t)})),o.default(this._element).on("pointerup.bs.carousel",(function(t){return n(t)})),this._element.classList.add("pointer-event")):(o.default(this._element).on("touchstart.bs.carousel",(function(t){return e(t)})),o.default(this._element).on("touchmove.bs.carousel",(function(e){return function(e){t.touchDeltaX=e.originalEvent.touches&&e.originalEvent.touches.length>1?0:e.originalEvent.touches[0].clientX-t.touchStartX}(e)})),o.default(this._element).on("touchend.bs.carousel",(function(t){return n(t)})))}},e._keydown=function(t){if(!/input|textarea/i.test(t.target.tagName))switch(t.which){case 37:t.preventDefault(),this.prev();break;case 39:t.preventDefault(),this.next()}},e._getItemIndex=function(t){return this._items=t&&t.parentNode?[].slice.call(t.parentNode.querySelectorAll(".carousel-item")):[],this._items.indexOf(t)},e._getItemByDirection=function(t,e){var n=t===N,i=t===D,o=this._getItemIndex(e),a=this._items.length-1;if((i&&0===o||n&&o===a)&&!this._config.wrap)return e;var s=(o+(t===D?-1:1))%this._items.length;return-1===s?this._items[this._items.length-1]:this._items[s]},e._triggerSlideEvent=function(t,e){var n=this._getItemIndex(t),i=this._getItemIndex(this._element.querySelector(I)),a=o.default.Event("slide.bs.carousel",{relatedTarget:t,direction:e,from:i,to:n});return o.default(this._element).trigger(a),a},e._setActiveIndicatorElement=function(t){if(this._indicatorsElement){var e=[].slice.call(this._indicatorsElement.querySelectorAll(".active"));o.default(e).removeClass(S);var n=this._indicatorsElement.children[this._getItemIndex(t)];n&&o.default(n).addClass(S)}},e._updateInterval=function(){var t=this._activeElement||this._element.querySelector(I);if(t){var e=parseInt(t.getAttribute("data-interval"),10);e?(this._config.defaultInterval=this._config.defaultInterval||this._config.interval,this._config.interval=e):this._config.interval=this._config.defaultInterval||this._config.interval}},e._slide=function(t,e){var n,i,a,s=this,l=this._element.querySelector(I),r=this._getItemIndex(l),u=e||l&&this._getItemByDirection(t,l),f=this._getItemIndex(u),c=Boolean(this._interval);if(t===N?(n="carousel-item-left",i="carousel-item-next",a="left"):(n="carousel-item-right",i="carousel-item-prev",a="right"),u&&o.default(u).hasClass(S))this._isSliding=!1;else if(!this._triggerSlideEvent(u,a).isDefaultPrevented()&&l&&u){this._isSliding=!0,c&&this.pause(),this._setActiveIndicatorElement(u),this._activeElement=u;var h=o.default.Event(A,{relatedTarget:u,direction:a,from:r,to:f});if(o.default(this._element).hasClass("slide")){o.default(u).addClass(i),d.reflow(u),o.default(l).addClass(n),o.default(u).addClass(n);var g=d.getTransitionDurationFromElement(l);o.default(l).one(d.TRANSITION_END,(function(){o.default(u).removeClass(n+" "+i).addClass(S),o.default(l).removeClass("active "+i+" "+n),s._isSliding=!1,setTimeout((function(){return o.default(s._element).trigger(h)}),0)})).emulateTransitionEnd(g)}else o.default(l).removeClass(S),o.default(u).addClass(S),this._isSliding=!1,o.default(this._element).trigger(h);c&&this.cycle()}},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this).data(w),i=r({},k,o.default(this).data());"object"==typeof e&&(i=r({},i,e));var a="string"==typeof e?e:i.slide;if(n||(n=new t(this,i),o.default(this).data(w,n)),"number"==typeof e)n.to(e);else if("string"==typeof a){if("undefined"==typeof n[a])throw new TypeError('No method named "'+a+'"');n[a]()}else i.interval&&i.ride&&(n.pause(),n.cycle())}))},t._dataApiClickHandler=function(e){var n=d.getSelectorFromElement(this);if(n){var i=o.default(n)[0];if(i&&o.default(i).hasClass("carousel")){var a=r({},o.default(i).data(),o.default(this).data()),s=this.getAttribute("data-slide-to");s&&(a.interval=!1),t._jQueryInterface.call(o.default(i),a),s&&o.default(i).data(w).to(s),e.preventDefault()}}},l(t,null,[{key:"VERSION",get:function(){return"4.6.2"}},{key:"Default",get:function(){return k}}]),t}();o.default(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",P._dataApiClickHandler),o.default(window).on("load.bs.carousel.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-ride="carousel"]')),e=0,n=t.length;e0&&(this._selector=s,this._triggerArray.push(a))}this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}var e=t.prototype;return e.toggle=function(){o.default(this._element).hasClass(q)?this.hide():this.show()},e.show=function(){var e,n,i=this;if(!(this._isTransitioning||o.default(this._element).hasClass(q)||(this._parent&&0===(e=[].slice.call(this._parent.querySelectorAll(".show, .collapsing")).filter((function(t){return"string"==typeof i._config.parent?t.getAttribute("data-parent")===i._config.parent:t.classList.contains(F)}))).length&&(e=null),e&&(n=o.default(e).not(this._selector).data(R))&&n._isTransitioning))){var a=o.default.Event("show.bs.collapse");if(o.default(this._element).trigger(a),!a.isDefaultPrevented()){e&&(t._jQueryInterface.call(o.default(e).not(this._selector),"hide"),n||o.default(e).data(R,null));var s=this._getDimension();o.default(this._element).removeClass(F).addClass(Q),this._element.style[s]=0,this._triggerArray.length&&o.default(this._triggerArray).removeClass(B).attr("aria-expanded",!0),this.setTransitioning(!0);var l="scroll"+(s[0].toUpperCase()+s.slice(1)),r=d.getTransitionDurationFromElement(this._element);o.default(this._element).one(d.TRANSITION_END,(function(){o.default(i._element).removeClass(Q).addClass("collapse show"),i._element.style[s]="",i.setTransitioning(!1),o.default(i._element).trigger("shown.bs.collapse")})).emulateTransitionEnd(r),this._element.style[s]=this._element[l]+"px"}}},e.hide=function(){var t=this;if(!this._isTransitioning&&o.default(this._element).hasClass(q)){var e=o.default.Event("hide.bs.collapse");if(o.default(this._element).trigger(e),!e.isDefaultPrevented()){var n=this._getDimension();this._element.style[n]=this._element.getBoundingClientRect()[n]+"px",d.reflow(this._element),o.default(this._element).addClass(Q).removeClass("collapse show");var i=this._triggerArray.length;if(i>0)for(var a=0;a0},e._getOffset=function(){var t=this,e={};return"function"==typeof this._config.offset?e.fn=function(e){return e.offsets=r({},e.offsets,t._config.offset(e.offsets,t._element)),e}:e.offset=this._config.offset,e},e._getPopperConfig=function(){var t={placement:this._getPlacement(),modifiers:{offset:this._getOffset(),flip:{enabled:this._config.flip},preventOverflow:{boundariesElement:this._config.boundary}}};return"static"===this._config.display&&(t.modifiers.applyStyle={enabled:!1}),r({},t,this._config.popperConfig)},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this).data(K);if(n||(n=new t(this,"object"==typeof e?e:null),o.default(this).data(K,n)),"string"==typeof e){if("undefined"==typeof n[e])throw new TypeError('No method named "'+e+'"');n[e]()}}))},t._clearMenus=function(e){if(!e||3!==e.which&&("keyup"!==e.type||9===e.which))for(var n=[].slice.call(document.querySelectorAll(it)),i=0,a=n.length;i0&&s--,40===e.which&&sdocument.documentElement.clientHeight;n||(this._element.style.overflowY="hidden"),this._element.classList.add(ht);var i=d.getTransitionDurationFromElement(this._dialog);o.default(this._element).off(d.TRANSITION_END),o.default(this._element).one(d.TRANSITION_END,(function(){t._element.classList.remove(ht),n||o.default(t._element).one(d.TRANSITION_END,(function(){t._element.style.overflowY=""})).emulateTransitionEnd(t._element,i)})).emulateTransitionEnd(i),this._element.focus()}},e._showElement=function(t){var e=this,n=o.default(this._element).hasClass(dt),i=this._dialog?this._dialog.querySelector(".modal-body"):null;this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||document.body.appendChild(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),o.default(this._dialog).hasClass("modal-dialog-scrollable")&&i?i.scrollTop=0:this._element.scrollTop=0,n&&d.reflow(this._element),o.default(this._element).addClass(ct),this._config.focus&&this._enforceFocus();var a=o.default.Event("shown.bs.modal",{relatedTarget:t}),s=function(){e._config.focus&&e._element.focus(),e._isTransitioning=!1,o.default(e._element).trigger(a)};if(n){var l=d.getTransitionDurationFromElement(this._dialog);o.default(this._dialog).one(d.TRANSITION_END,s).emulateTransitionEnd(l)}else s()},e._enforceFocus=function(){var t=this;o.default(document).off(pt).on(pt,(function(e){document!==e.target&&t._element!==e.target&&0===o.default(t._element).has(e.target).length&&t._element.focus()}))},e._setEscapeEvent=function(){var t=this;this._isShown?o.default(this._element).on(yt,(function(e){t._config.keyboard&&27===e.which?(e.preventDefault(),t.hide()):t._config.keyboard||27!==e.which||t._triggerBackdropTransition()})):this._isShown||o.default(this._element).off(yt)},e._setResizeEvent=function(){var t=this;this._isShown?o.default(window).on(_t,(function(e){return t.handleUpdate(e)})):o.default(window).off(_t)},e._hideModal=function(){var t=this;this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._showBackdrop((function(){o.default(document.body).removeClass(ft),t._resetAdjustments(),t._resetScrollbar(),o.default(t._element).trigger(gt)}))},e._removeBackdrop=function(){this._backdrop&&(o.default(this._backdrop).remove(),this._backdrop=null)},e._showBackdrop=function(t){var e=this,n=o.default(this._element).hasClass(dt)?dt:"";if(this._isShown&&this._config.backdrop){if(this._backdrop=document.createElement("div"),this._backdrop.className="modal-backdrop",n&&this._backdrop.classList.add(n),o.default(this._backdrop).appendTo(document.body),o.default(this._element).on(vt,(function(t){e._ignoreBackdropClick?e._ignoreBackdropClick=!1:t.target===t.currentTarget&&("static"===e._config.backdrop?e._triggerBackdropTransition():e.hide())})),n&&d.reflow(this._backdrop),o.default(this._backdrop).addClass(ct),!t)return;if(!n)return void t();var i=d.getTransitionDurationFromElement(this._backdrop);o.default(this._backdrop).one(d.TRANSITION_END,t).emulateTransitionEnd(i)}else if(!this._isShown&&this._backdrop){o.default(this._backdrop).removeClass(ct);var a=function(){e._removeBackdrop(),t&&t()};if(o.default(this._element).hasClass(dt)){var s=d.getTransitionDurationFromElement(this._backdrop);o.default(this._backdrop).one(d.TRANSITION_END,a).emulateTransitionEnd(s)}else a()}else t&&t()},e._adjustDialog=function(){var t=this._element.scrollHeight>document.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=Math.round(t.left+t.right)
    ',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",customClass:"",sanitize:!0,sanitizeFn:null,whiteList:{"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},popperConfig:null},Ut={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string|function)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)",customClass:"(string|function)",sanitize:"boolean",sanitizeFn:"(null|function)",whiteList:"object",popperConfig:"(null|object)"},Mt={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"},Wt=function(){function t(t,e){if("undefined"==typeof a.default)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var e=t.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=o.default(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),o.default(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(o.default(this.getTipElement()).hasClass(Rt))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),o.default.removeData(this.element,this.constructor.DATA_KEY),o.default(this.element).off(this.constructor.EVENT_KEY),o.default(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&o.default(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===o.default(this.element).css("display"))throw new Error("Please use show on visible elements");var e=o.default.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){o.default(this.element).trigger(e);var n=d.findShadowRoot(this.element),i=o.default.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!i)return;var s=this.getTipElement(),l=d.getUID(this.constructor.NAME);s.setAttribute("id",l),this.element.setAttribute("aria-describedby",l),this.setContent(),this.config.animation&&o.default(s).addClass(Lt);var r="function"==typeof this.config.placement?this.config.placement.call(this,s,this.element):this.config.placement,u=this._getAttachment(r);this.addAttachmentClass(u);var f=this._getContainer();o.default(s).data(this.constructor.DATA_KEY,this),o.default.contains(this.element.ownerDocument.documentElement,this.tip)||o.default(s).appendTo(f),o.default(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new a.default(this.element,s,this._getPopperConfig(u)),o.default(s).addClass(Rt),o.default(s).addClass(this.config.customClass),"ontouchstart"in document.documentElement&&o.default(document.body).children().on("mouseover",null,o.default.noop);var c=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,o.default(t.element).trigger(t.constructor.Event.SHOWN),e===qt&&t._leave(null,t)};if(o.default(this.tip).hasClass(Lt)){var h=d.getTransitionDurationFromElement(this.tip);o.default(this.tip).one(d.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},e.hide=function(t){var e=this,n=this.getTipElement(),i=o.default.Event(this.constructor.Event.HIDE),a=function(){e._hoverState!==xt&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),o.default(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(o.default(this.element).trigger(i),!i.isDefaultPrevented()){if(o.default(n).removeClass(Rt),"ontouchstart"in document.documentElement&&o.default(document.body).children().off("mouseover",null,o.default.noop),this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1,o.default(this.tip).hasClass(Lt)){var s=d.getTransitionDurationFromElement(n);o.default(n).one(d.TRANSITION_END,a).emulateTransitionEnd(s)}else a();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(t){o.default(this.getTipElement()).addClass("bs-tooltip-"+t)},e.getTipElement=function(){return this.tip=this.tip||o.default(this.config.template)[0],this.tip},e.setContent=function(){var t=this.getTipElement();this.setElementContent(o.default(t.querySelectorAll(".tooltip-inner")),this.getTitle()),o.default(t).removeClass("fade show")},e.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=At(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?o.default(e).parent().is(t)||t.empty().append(e):t.text(o.default(e).text())},e.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},e._getPopperConfig=function(t){var e=this;return r({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:".arrow"},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}},this.config.popperConfig)},e._getOffset=function(){var t=this,e={};return"function"==typeof this.config.offset?e.fn=function(e){return e.offsets=r({},e.offsets,t.config.offset(e.offsets,t.element)),e}:e.offset=this.config.offset,e},e._getContainer=function(){return!1===this.config.container?document.body:d.isElement(this.config.container)?o.default(this.config.container):o.default(document).find(this.config.container)},e._getAttachment=function(t){return Bt[t.toUpperCase()]},e._setListeners=function(){var t=this;this.config.trigger.split(" ").forEach((function(e){if("click"===e)o.default(t.element).on(t.constructor.Event.CLICK,t.config.selector,(function(e){return t.toggle(e)}));else if("manual"!==e){var n=e===Ft?t.constructor.Event.MOUSEENTER:t.constructor.Event.FOCUSIN,i=e===Ft?t.constructor.Event.MOUSELEAVE:t.constructor.Event.FOCUSOUT;o.default(t.element).on(n,t.config.selector,(function(e){return t._enter(e)})).on(i,t.config.selector,(function(e){return t._leave(e)}))}})),this._hideModalHandler=function(){t.element&&t.hide()},o.default(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=r({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||o.default(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),o.default(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Qt:Ft]=!0),o.default(e.getTipElement()).hasClass(Rt)||e._hoverState===xt?e._hoverState=xt:(clearTimeout(e._timeout),e._hoverState=xt,e.config.delay&&e.config.delay.show?e._timeout=setTimeout((function(){e._hoverState===xt&&e.show()}),e.config.delay.show):e.show())},e._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||o.default(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),o.default(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Qt:Ft]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=qt,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout((function(){e._hoverState===qt&&e.hide()}),e.config.delay.hide):e.hide())},e._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},e._getConfig=function(t){var e=o.default(this.element).data();return Object.keys(e).forEach((function(t){-1!==Pt.indexOf(t)&&delete e[t]})),"number"==typeof(t=r({},this.constructor.Default,e,"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),d.typeCheckConfig(It,t,this.constructor.DefaultType),t.sanitize&&(t.template=At(t.template,t.whiteList,t.sanitizeFn)),t},e._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},e._cleanTipClass=function(){var t=o.default(this.getTipElement()),e=t.attr("class").match(jt);null!==e&&e.length&&t.removeClass(e.join(""))},e._handlePopperPlacementChange=function(t){this.tip=t.instance.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},e._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(o.default(t).removeClass(Lt),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data(kt),a="object"==typeof e&&e;if((i||!/dispose|hide/.test(e))&&(i||(i=new t(this,a),n.data(kt,i)),"string"==typeof e)){if("undefined"==typeof i[e])throw new TypeError('No method named "'+e+'"');i[e]()}}))},l(t,null,[{key:"VERSION",get:function(){return"4.6.2"}},{key:"Default",get:function(){return Ht}},{key:"NAME",get:function(){return It}},{key:"DATA_KEY",get:function(){return kt}},{key:"Event",get:function(){return Mt}},{key:"EVENT_KEY",get:function(){return".bs.tooltip"}},{key:"DefaultType",get:function(){return Ut}}]),t}();o.default.fn.tooltip=Wt._jQueryInterface,o.default.fn.tooltip.Constructor=Wt,o.default.fn.tooltip.noConflict=function(){return o.default.fn.tooltip=Ot,Wt._jQueryInterface};var Vt="bs.popover",zt=o.default.fn.popover,Kt=new RegExp("(^|\\s)bs-popover\\S+","g"),Xt=r({},Wt.Default,{placement:"right",trigger:"click",content:"",template:''}),Yt=r({},Wt.DefaultType,{content:"(string|element|function)"}),$t={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"},Jt=function(t){var e,n;function i(){return t.apply(this,arguments)||this}n=t,(e=i).prototype=Object.create(n.prototype),e.prototype.constructor=e,u(e,n);var a=i.prototype;return a.isWithContent=function(){return this.getTitle()||this._getContent()},a.addAttachmentClass=function(t){o.default(this.getTipElement()).addClass("bs-popover-"+t)},a.getTipElement=function(){return this.tip=this.tip||o.default(this.config.template)[0],this.tip},a.setContent=function(){var t=o.default(this.getTipElement());this.setElementContent(t.find(".popover-header"),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(".popover-body"),e),t.removeClass("fade show")},a._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},a._cleanTipClass=function(){var t=o.default(this.getTipElement()),e=t.attr("class").match(Kt);null!==e&&e.length>0&&t.removeClass(e.join(""))},i._jQueryInterface=function(t){return this.each((function(){var e=o.default(this).data(Vt),n="object"==typeof t?t:null;if((e||!/dispose|hide/.test(t))&&(e||(e=new i(this,n),o.default(this).data(Vt,e)),"string"==typeof t)){if("undefined"==typeof e[t])throw new TypeError('No method named "'+t+'"');e[t]()}}))},l(i,null,[{key:"VERSION",get:function(){return"4.6.2"}},{key:"Default",get:function(){return Xt}},{key:"NAME",get:function(){return"popover"}},{key:"DATA_KEY",get:function(){return Vt}},{key:"Event",get:function(){return $t}},{key:"EVENT_KEY",get:function(){return".bs.popover"}},{key:"DefaultType",get:function(){return Yt}}]),i}(Wt);o.default.fn.popover=Jt._jQueryInterface,o.default.fn.popover.Constructor=Jt,o.default.fn.popover.noConflict=function(){return o.default.fn.popover=zt,Jt._jQueryInterface};var Gt="scrollspy",Zt="bs.scrollspy",te=o.default.fn[Gt],ee="active",ne="position",ie=".nav, .list-group",oe={offset:10,method:"auto",target:""},ae={offset:"number",method:"string",target:"(string|element)"},se=function(){function t(t,e){var n=this;this._element=t,this._scrollElement="BODY"===t.tagName?window:t,this._config=this._getConfig(e),this._selector=this._config.target+" .nav-link,"+this._config.target+" .list-group-item,"+this._config.target+" .dropdown-item",this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,o.default(this._scrollElement).on("scroll.bs.scrollspy",(function(t){return n._process(t)})),this.refresh(),this._process()}var e=t.prototype;return e.refresh=function(){var t=this,e=this._scrollElement===this._scrollElement.window?"offset":ne,n="auto"===this._config.method?e:this._config.method,i=n===ne?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),[].slice.call(document.querySelectorAll(this._selector)).map((function(t){var e,a=d.getSelectorFromElement(t);if(a&&(e=document.querySelector(a)),e){var s=e.getBoundingClientRect();if(s.width||s.height)return[o.default(e)[n]().top+i,a]}return null})).filter(Boolean).sort((function(t,e){return t[0]-e[0]})).forEach((function(e){t._offsets.push(e[0]),t._targets.push(e[1])}))},e.dispose=function(){o.default.removeData(this._element,Zt),o.default(this._scrollElement).off(".bs.scrollspy"),this._element=null,this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},e._getConfig=function(t){if("string"!=typeof(t=r({},oe,"object"==typeof t&&t?t:{})).target&&d.isElement(t.target)){var e=o.default(t.target).attr("id");e||(e=d.getUID(Gt),o.default(t.target).attr("id",e)),t.target="#"+e}return d.typeCheckConfig(Gt,t,ae),t},e._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},e._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},e._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height},e._process=function(){var t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),n=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=n){var i=this._targets[this._targets.length-1];this._activeTarget!==i&&this._activate(i)}else{if(this._activeTarget&&t0)return this._activeTarget=null,void this._clear();for(var o=this._offsets.length;o--;)this._activeTarget!==this._targets[o]&&t>=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t li > .active",ge=function(){function t(t){this._element=t}var e=t.prototype;return e.show=function(){var t=this;if(!(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&o.default(this._element).hasClass(ue)||o.default(this._element).hasClass("disabled")||this._element.hasAttribute("disabled"))){var e,n,i=o.default(this._element).closest(".nav, .list-group")[0],a=d.getSelectorFromElement(this._element);if(i){var s="UL"===i.nodeName||"OL"===i.nodeName?he:ce;n=(n=o.default.makeArray(o.default(i).find(s)))[n.length-1]}var l=o.default.Event("hide.bs.tab",{relatedTarget:this._element}),r=o.default.Event("show.bs.tab",{relatedTarget:n});if(n&&o.default(n).trigger(l),o.default(this._element).trigger(r),!r.isDefaultPrevented()&&!l.isDefaultPrevented()){a&&(e=document.querySelector(a)),this._activate(this._element,i);var u=function(){var e=o.default.Event("hidden.bs.tab",{relatedTarget:t._element}),i=o.default.Event("shown.bs.tab",{relatedTarget:n});o.default(n).trigger(e),o.default(t._element).trigger(i)};e?this._activate(e,e.parentNode,u):u()}}},e.dispose=function(){o.default.removeData(this._element,le),this._element=null},e._activate=function(t,e,n){var i=this,a=(!e||"UL"!==e.nodeName&&"OL"!==e.nodeName?o.default(e).children(ce):o.default(e).find(he))[0],s=n&&a&&o.default(a).hasClass(fe),l=function(){return i._transitionComplete(t,a,n)};if(a&&s){var r=d.getTransitionDurationFromElement(a);o.default(a).removeClass(de).one(d.TRANSITION_END,l).emulateTransitionEnd(r)}else l()},e._transitionComplete=function(t,e,n){if(e){o.default(e).removeClass(ue);var i=o.default(e.parentNode).find("> .dropdown-menu .active")[0];i&&o.default(i).removeClass(ue),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!1)}o.default(t).addClass(ue),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),d.reflow(t),t.classList.contains(fe)&&t.classList.add(de);var a=t.parentNode;if(a&&"LI"===a.nodeName&&(a=a.parentNode),a&&o.default(a).hasClass("dropdown-menu")){var s=o.default(t).closest(".dropdown")[0];if(s){var l=[].slice.call(s.querySelectorAll(".dropdown-toggle"));o.default(l).addClass(ue)}t.setAttribute("aria-expanded",!0)}n&&n()},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data(le);if(i||(i=new t(this),n.data(le,i)),"string"==typeof e){if("undefined"==typeof i[e])throw new TypeError('No method named "'+e+'"');i[e]()}}))},l(t,null,[{key:"VERSION",get:function(){return"4.6.2"}}]),t}();o.default(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',(function(t){t.preventDefault(),ge._jQueryInterface.call(o.default(this),"show")})),o.default.fn.tab=ge._jQueryInterface,o.default.fn.tab.Constructor=ge,o.default.fn.tab.noConflict=function(){return o.default.fn.tab=re,ge._jQueryInterface};var me="bs.toast",pe=o.default.fn.toast,_e="hide",ve="show",ye="showing",be="click.dismiss.bs.toast",Ee={animation:!0,autohide:!0,delay:500},Te={animation:"boolean",autohide:"boolean",delay:"number"},we=function(){function t(t,e){this._element=t,this._config=this._getConfig(e),this._timeout=null,this._setListeners()}var e=t.prototype;return e.show=function(){var t=this,e=o.default.Event("show.bs.toast");if(o.default(this._element).trigger(e),!e.isDefaultPrevented()){this._clearTimeout(),this._config.animation&&this._element.classList.add("fade");var n=function(){t._element.classList.remove(ye),t._element.classList.add(ve),o.default(t._element).trigger("shown.bs.toast"),t._config.autohide&&(t._timeout=setTimeout((function(){t.hide()}),t._config.delay))};if(this._element.classList.remove(_e),d.reflow(this._element),this._element.classList.add(ye),this._config.animation){var i=d.getTransitionDurationFromElement(this._element);o.default(this._element).one(d.TRANSITION_END,n).emulateTransitionEnd(i)}else n()}},e.hide=function(){if(this._element.classList.contains(ve)){var t=o.default.Event("hide.bs.toast");o.default(this._element).trigger(t),t.isDefaultPrevented()||this._close()}},e.dispose=function(){this._clearTimeout(),this._element.classList.contains(ve)&&this._element.classList.remove(ve),o.default(this._element).off(be),o.default.removeData(this._element,me),this._element=null,this._config=null},e._getConfig=function(t){return t=r({},Ee,o.default(this._element).data(),"object"==typeof t&&t?t:{}),d.typeCheckConfig("toast",t,this.constructor.DefaultType),t},e._setListeners=function(){var t=this;o.default(this._element).on(be,'[data-dismiss="toast"]',(function(){return t.hide()}))},e._close=function(){var t=this,e=function(){t._element.classList.add(_e),o.default(t._element).trigger("hidden.bs.toast")};if(this._element.classList.remove(ve),this._config.animation){var n=d.getTransitionDurationFromElement(this._element);o.default(this._element).one(d.TRANSITION_END,e).emulateTransitionEnd(n)}else e()},e._clearTimeout=function(){clearTimeout(this._timeout),this._timeout=null},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data(me);if(i||(i=new t(this,"object"==typeof e&&e),n.data(me,i)),"string"==typeof e){if("undefined"==typeof i[e])throw new TypeError('No method named "'+e+'"');i[e](this)}}))},l(t,null,[{key:"VERSION",get:function(){return"4.6.2"}},{key:"DefaultType",get:function(){return Te}},{key:"Default",get:function(){return Ee}}]),t}();o.default.fn.toast=we._jQueryInterface,o.default.fn.toast.Constructor=we,o.default.fn.toast.noConflict=function(){return o.default.fn.toast=pe,we._jQueryInterface},t.Alert=g,t.Button=E,t.Carousel=P,t.Collapse=V,t.Dropdown=lt,t.Modal=Ct,t.Popover=Jt,t.Scrollspy=se,t.Tab=ge,t.Toast=we,t.Tooltip=Wt,t.Util=d,Object.defineProperty(t,"__esModule",{value:!0})})); -//# sourceMappingURL=bootstrap.min.js.map \ No newline at end of file diff --git a/admin/static/js/common.js b/admin/static/js/common.js deleted file mode 100644 index 08eff2b1..00000000 --- a/admin/static/js/common.js +++ /dev/null @@ -1,355 +0,0 @@ -var alertNbLines = true; - -function treatFlagKey(flag) { - if (flag.values !== undefined) { - if (flag.separator) { - for (var i = flag.values.length - 1; i >= 0; i--) { - if (flag.nb_lines && (flag.values[i] == undefined || !flag.values[i].length)) { - if (alertNbLines) { - alertNbLines = false; - if (!confirm("Lorsque plusieurs flags sont attendus pour une même question, ceux-ci ne sont pas validés un par un. Ils ne sont validés qu'une fois tous les champs remplis correctement. (Sauf mention contraire, l'ordre n'importe pas)")) - console.log(flag.values[9999].length); // Launch exception here to avoid form validation - } - } - else if (!flag.values[i].length) { - flag.values.splice(i, 1); - } - } - - if (flag.ignore_order) - flag.value = flag.values.slice().sort().join(flag.separator) + flag.separator; - else - flag.value = flag.values.join(flag.separator) + flag.separator; - - if (flag.values.length == 0) - flag.values = [""]; - } - else - flag.value = flag.values[0]; - } - - if (flag.found == null && flag.soluce !== undefined) { - if (flag.value && flag.soluce) { - if (flag.ignore_case) - flag.value = flag.value.toLowerCase(); - if (flag.capture_regexp) { - var re = new RegExp(flag.capture_regexp, flag.ignore_case?'ui':'u'); - var match = re.exec(flag.value); - match.shift(); - flag.value = match.join("+"); - } - - if (flag.soluce == b2sum(flag.value)) - flag.found = new Date(); - } - } - return flag.found !== undefined && flag.found !== false; -} - -String.prototype.capitalize = function() { - return this - .toLowerCase() - .replace( - /(^|\s|-)([a-z])/g, - function(m,p1,p2) { return p1+p2.toUpperCase(); } - ); -} - -Array.prototype.inArray = function(v) { - return this.reduce(function(presence, current) { - return presence || current == v; - }, false); -} - -angular.module("FICApp") - .directive('autofocus', ['$timeout', function($timeout) { - return { - restrict: 'A', - link : function($scope, $element) { - $timeout(function() { - $element[0].focus(); - }); - } - } - }]) - .directive('autocarousel', ['$timeout', function($timeout) { - return { - restrict: 'A', - link : function($scope, $element) { - $timeout(function() { - $($element[0]).carousel(); - }); - } - } - }]) - .directive('fileModel', ['$parse', function ($parse) { - return { - restrict: 'A', - link: function($scope, element, attrs) { - var model = $parse(attrs.fileModel); - var modelSetter = model.assign; - - element.bind('change', function(){ - $scope.$apply(function(){ - modelSetter($scope, element[0].files[0]); - $scope.uploadFile(); - }); - }); - } - }; - }]); - -angular.module("FICApp") - .filter("escapeURL", function() { - return function(input) { - return encodeURIComponent(input); - } - }) - .filter("stripHTML", function() { - return function(input) { - if (!input) - return input; - return input.replace( - /(<([^>]+)>)/ig, - "" - ); - } - }) - .filter("capitalize", function() { - return function(input) { - return input.capitalize(); - } - }) - .filter("rankTitle", function() { - var itms = { - "rank": "Rang", - "name": "Équipe", - "score": "Score", - }; - return function(input) { - if (itms[input] != undefined) { - return itms[input]; - } else { - return input; - } - } - }) - .filter("time", function() { - return function(input) { - input = Math.floor(input); - if (input == undefined) { - return "--"; - } else if (input >= 10) { - return input; - } else { - return "0" + input; - } - } - }) - .filter("timer", function() { - return function(input) { - input = Math.floor(input / 1000); - var res = "" - - if (input >= 3600) { - res += Math.floor(input / 3600) + ":"; - input = input % 3600; - } - if (res || input >= 60) { - if (res && Math.floor(input / 60) <= 9) - res += "0"; - res += Math.floor(input / 60) + "'"; - input = input % 60; - } - - return res + (input>9?input:"0"+input) + '"'; - } - }) - .filter("since", function() { - return function(passed) { - if (passed < 120000) { - return "Il y a " + Math.floor(passed/1000) + " secondes"; - } else { - return "Il y a " + Math.floor(passed/60000) + " minutes"; - } - } - }) - .filter("size", function() { - var units = [ - "o", - "kio", - "Mio", - "Gio", - "Tio", - "Pio", - "Eio", - "Zio", - "Yio", - ] - return function(input) { - var res = input; - var unit = 0; - while (res > 1024) { - unit += 1; - res = res / 1024; - } - return (Math.round(res * 100) / 100) + " " + units[unit]; - } - }) - - .filter("coeff", function() { - return function(input) { - if (input > 1) { - return "+" + Math.floor((input - 1) * 100) + " %" - } else if (input < 1) { - return "-" + Math.floor((1 - input) * 100) + " %" - } else { - return ""; - } - } - }) - - .filter("objectLength", function() { - return function(input) { - if (input !== undefined) - return Object.keys(input).length; - else - return ""; - } - }) - - .filter("bto16", function() { - return function(input) { - const raw = atob(input); - let result = ''; - for (let i = 0; i < raw.length; i++) { - const hex = raw.charCodeAt(i).toString(16); - result += (hex.length === 2 ? hex : '0' + hex); - } - return result; - } - }); - -angular.module("FICApp") - .component('flagKey', { - bindings: { - kid: '=', - key: '=', - settings: '=', - wantchoices: '=', - }, - controller: function() { - this.additem = function(key) { - this.key.values.push(""); - }; - }, - template: ` -
    - - -
    - - - -
    - -
    -
    - -
    -
    - - -
    - ` - }); - -angular.module("FICApp") - .run(function($rootScope) { - $rootScope.recvTime = function(response) { - time = { - "cu": Math.floor(response.headers("x-fic-time") * 1000), - "he": (new Date()).getTime(), - }; - sessionStorage.time = angular.toJson(time); - return time; - } - }) - - .controller("CountdownController", function($scope, $rootScope, $interval) { - var time; - if (sessionStorage.time) - time = angular.fromJson(sessionStorage.time); - - $scope.time = {}; - - $rootScope.getSrvTime = function() { - if (time && time.cu && time.he) - return new Date(Date.now() + (time.cu - time.he)); - else - return undefined; - } - - function updTime() { - if (time && $rootScope.settings && $rootScope.settings.end) { - var srv_cur = new Date(Date.now() + (time.cu - time.he)); - - // Refresh on start/activate time reached - if (Math.floor($rootScope.settings.start / 1000) == Math.floor(srv_cur / 1000) ||Math.floor($rootScope.settings.activateTime / 1000) == Math.floor(srv_cur / 1000)) - $rootScope.refresh(true, true); - - var remain = 0; - if ($rootScope.settings.start === undefined || $rootScope.settings.start == 0) { - $scope.time = {}; - return - } else if ($rootScope.settings.start > srv_cur) { - $scope.startIn = Math.floor(($rootScope.settings.start - srv_cur) / 1000); - remain = $rootScope.settings.end - $rootScope.settings.start; - } else if ($rootScope.settings.end > srv_cur) { - $scope.startIn = 0; - remain = $rootScope.settings.end - srv_cur; - } - - $rootScope.timeProgression = 1 - remain / ($rootScope.settings.end - $rootScope.settings.start); - $rootScope.timeRemaining = remain; - - if ($rootScope.settings.activateTime) { - var now = new Date(); - var actTime = new Date($rootScope.settings.activateTime); - - if (actTime > now) - $rootScope.activateTimeCountDown = actTime - now; - else - $rootScope.activateTimeCountDown = null; - } else { - $rootScope.activateTimeCountDown = null; - } - - remain = remain / 1000; - - if (remain < 0) { - remain = 0; - $scope.time.end = true; - $scope.time.expired = true; - } else if (remain < 60) { - $scope.time.end = false; - $scope.time.expired = true; - } else { - $scope.time.end = false; - $scope.time.expired = false; - } - - $scope.time.remaining = remain; - $scope.time.hours = Math.floor(remain / 3600); - $scope.time.minutes = Math.floor((remain % 3600) / 60); - $scope.time.seconds = Math.floor(remain % 60); - } - } - updTime(); - $interval(updTime, 1000); - }) diff --git a/admin/static/js/d3.v3.min.js b/admin/static/js/d3.v3.min.js deleted file mode 100644 index 16648730..00000000 --- a/admin/static/js/d3.v3.min.js +++ /dev/null @@ -1,5 +0,0 @@ -!function(){function n(n){return n&&(n.ownerDocument||n.document||n).documentElement}function t(n){return n&&(n.ownerDocument&&n.ownerDocument.defaultView||n.document&&n||n.defaultView)}function e(n,t){return t>n?-1:n>t?1:n>=t?0:NaN}function r(n){return null===n?NaN:+n}function i(n){return!isNaN(n)}function u(n){return{left:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)<0?r=u+1:i=u}return r},right:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)>0?i=u:r=u+1}return r}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function l(n,t){for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}function c(){this._=Object.create(null)}function f(n){return(n+="")===bo||n[0]===_o?_o+n:n}function s(n){return(n+="")[0]===_o?n.slice(1):n}function h(n){return f(n)in this._}function p(n){return(n=f(n))in this._&&delete this._[n]}function g(){var n=[];for(var t in this._)n.push(s(t));return n}function v(){var n=0;for(var t in this._)++n;return n}function d(){for(var n in this._)return!1;return!0}function y(){this._=Object.create(null)}function m(n){return n}function M(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function x(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var e=0,r=wo.length;r>e;++e){var i=wo[e]+t;if(i in n)return i}}function b(){}function _(){}function w(n){function t(){for(var t,r=e,i=-1,u=r.length;++ie;e++)for(var i,u=n[e],o=0,a=u.length;a>o;o++)(i=u[o])&&t(i,o,e);return n}function Z(n){return ko(n,qo),n}function V(n){var t,e;return function(r,i,u){var o,a=n[u].update,l=a.length;for(u!=e&&(e=u,t=0),i>=t&&(t=i+1);!(o=a[t])&&++t0&&(n=n.slice(0,a));var c=To.get(n);return c&&(n=c,l=B),a?t?i:r:t?b:u}function $(n,t){return function(e){var r=ao.event;ao.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{ao.event=r}}}function B(n,t){var e=$(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function W(e){var r=".dragsuppress-"+ ++Do,i="click"+r,u=ao.select(t(e)).on("touchmove"+r,S).on("dragstart"+r,S).on("selectstart"+r,S);if(null==Ro&&(Ro="onselectstart"in e?!1:x(e.style,"userSelect")),Ro){var o=n(e).style,a=o[Ro];o[Ro]="none"}return function(n){if(u.on(r,null),Ro&&(o[Ro]=a),n){var t=function(){u.on(i,null)};u.on(i,function(){S(),t()},!0),setTimeout(t,0)}}}function J(n,e){e.changedTouches&&(e=e.changedTouches[0]);var r=n.ownerSVGElement||n;if(r.createSVGPoint){var i=r.createSVGPoint();if(0>Po){var u=t(n);if(u.scrollX||u.scrollY){r=ao.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=r[0][0].getScreenCTM();Po=!(o.f||o.e),r.remove()}}return Po?(i.x=e.pageX,i.y=e.pageY):(i.x=e.clientX,i.y=e.clientY),i=i.matrixTransform(n.getScreenCTM().inverse()),[i.x,i.y]}var a=n.getBoundingClientRect();return[e.clientX-a.left-n.clientLeft,e.clientY-a.top-n.clientTop]}function G(){return ao.event.changedTouches[0].identifier}function K(n){return n>0?1:0>n?-1:0}function Q(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function nn(n){return n>1?0:-1>n?Fo:Math.acos(n)}function tn(n){return n>1?Io:-1>n?-Io:Math.asin(n)}function en(n){return((n=Math.exp(n))-1/n)/2}function rn(n){return((n=Math.exp(n))+1/n)/2}function un(n){return((n=Math.exp(2*n))-1)/(n+1)}function on(n){return(n=Math.sin(n/2))*n}function an(){}function ln(n,t,e){return this instanceof ln?(this.h=+n,this.s=+t,void(this.l=+e)):arguments.length<2?n instanceof ln?new ln(n.h,n.s,n.l):_n(""+n,wn,ln):new ln(n,t,e)}function cn(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?u+(o-u)*n/60:180>n?o:240>n?u+(o-u)*(240-n)/60:u}function i(n){return Math.round(255*r(n))}var u,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,u=2*e-o,new mn(i(n+120),i(n),i(n-120))}function fn(n,t,e){return this instanceof fn?(this.h=+n,this.c=+t,void(this.l=+e)):arguments.length<2?n instanceof fn?new fn(n.h,n.c,n.l):n instanceof hn?gn(n.l,n.a,n.b):gn((n=Sn((n=ao.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new fn(n,t,e)}function sn(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new hn(e,Math.cos(n*=Yo)*t,Math.sin(n)*t)}function hn(n,t,e){return this instanceof hn?(this.l=+n,this.a=+t,void(this.b=+e)):arguments.length<2?n instanceof hn?new hn(n.l,n.a,n.b):n instanceof fn?sn(n.h,n.c,n.l):Sn((n=mn(n)).r,n.g,n.b):new hn(n,t,e)}function pn(n,t,e){var r=(n+16)/116,i=r+t/500,u=r-e/200;return i=vn(i)*na,r=vn(r)*ta,u=vn(u)*ea,new mn(yn(3.2404542*i-1.5371385*r-.4985314*u),yn(-.969266*i+1.8760108*r+.041556*u),yn(.0556434*i-.2040259*r+1.0572252*u))}function gn(n,t,e){return n>0?new fn(Math.atan2(e,t)*Zo,Math.sqrt(t*t+e*e),n):new fn(NaN,NaN,n)}function vn(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function dn(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function yn(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function mn(n,t,e){return this instanceof mn?(this.r=~~n,this.g=~~t,void(this.b=~~e)):arguments.length<2?n instanceof mn?new mn(n.r,n.g,n.b):_n(""+n,mn,cn):new mn(n,t,e)}function Mn(n){return new mn(n>>16,n>>8&255,255&n)}function xn(n){return Mn(n)+""}function bn(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function _n(n,t,e){var r,i,u,o=0,a=0,l=0;if(r=/([a-z]+)\((.*)\)/.exec(n=n.toLowerCase()))switch(i=r[2].split(","),r[1]){case"hsl":return e(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return t(Nn(i[0]),Nn(i[1]),Nn(i[2]))}return(u=ua.get(n))?t(u.r,u.g,u.b):(null==n||"#"!==n.charAt(0)||isNaN(u=parseInt(n.slice(1),16))||(4===n.length?(o=(3840&u)>>4,o=o>>4|o,a=240&u,a=a>>4|a,l=15&u,l=l<<4|l):7===n.length&&(o=(16711680&u)>>16,a=(65280&u)>>8,l=255&u)),t(o,a,l))}function wn(n,t,e){var r,i,u=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-u,l=(o+u)/2;return a?(i=.5>l?a/(o+u):a/(2-o-u),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=NaN,i=l>0&&1>l?0:r),new ln(r,i,l)}function Sn(n,t,e){n=kn(n),t=kn(t),e=kn(e);var r=dn((.4124564*n+.3575761*t+.1804375*e)/na),i=dn((.2126729*n+.7151522*t+.072175*e)/ta),u=dn((.0193339*n+.119192*t+.9503041*e)/ea);return hn(116*i-16,500*(r-i),200*(i-u))}function kn(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function Nn(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function En(n){return"function"==typeof n?n:function(){return n}}function An(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),Cn(t,e,n,r)}}function Cn(n,t,e,r){function i(){var n,t=l.status;if(!t&&Ln(l)||t>=200&&300>t||304===t){try{n=e.call(u,l)}catch(r){return void o.error.call(u,r)}o.load.call(u,n)}else o.error.call(u,l)}var u={},o=ao.dispatch("beforesend","progress","load","error"),a={},l=new XMLHttpRequest,c=null;return!this.XDomainRequest||"withCredentials"in l||!/^(http(s)?:)?\/\//.test(n)||(l=new XDomainRequest),"onload"in l?l.onload=l.onerror=i:l.onreadystatechange=function(){l.readyState>3&&i()},l.onprogress=function(n){var t=ao.event;ao.event=n;try{o.progress.call(u,l)}finally{ao.event=t}},u.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",u)},u.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",u):t},u.responseType=function(n){return arguments.length?(c=n,u):c},u.response=function(n){return e=n,u},["get","post"].forEach(function(n){u[n]=function(){return u.send.apply(u,[n].concat(co(arguments)))}}),u.send=function(e,r,i){if(2===arguments.length&&"function"==typeof r&&(i=r,r=null),l.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),l.setRequestHeader)for(var f in a)l.setRequestHeader(f,a[f]);return null!=t&&l.overrideMimeType&&l.overrideMimeType(t),null!=c&&(l.responseType=c),null!=i&&u.on("error",i).on("load",function(n){i(null,n)}),o.beforesend.call(u,l),l.send(null==r?null:r),u},u.abort=function(){return l.abort(),u},ao.rebind(u,o,"on"),null==r?u:u.get(zn(r))}function zn(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function Ln(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function qn(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var i=e+t,u={c:n,t:i,n:null};return aa?aa.n=u:oa=u,aa=u,la||(ca=clearTimeout(ca),la=1,fa(Tn)),u}function Tn(){var n=Rn(),t=Dn()-n;t>24?(isFinite(t)&&(clearTimeout(ca),ca=setTimeout(Tn,t)),la=0):(la=1,fa(Tn))}function Rn(){for(var n=Date.now(),t=oa;t;)n>=t.t&&t.c(n-t.t)&&(t.c=null),t=t.n;return n}function Dn(){for(var n,t=oa,e=1/0;t;)t.c?(t.t8?function(n){return n/e}:function(n){return n*e},symbol:n}}function jn(n){var t=n.decimal,e=n.thousands,r=n.grouping,i=n.currency,u=r&&e?function(n,t){for(var i=n.length,u=[],o=0,a=r[0],l=0;i>0&&a>0&&(l+a+1>t&&(a=Math.max(1,t-l)),u.push(n.substring(i-=a,i+a)),!((l+=a+1)>t));)a=r[o=(o+1)%r.length];return u.reverse().join(e)}:m;return function(n){var e=ha.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"-",l=e[4]||"",c=e[5],f=+e[6],s=e[7],h=e[8],p=e[9],g=1,v="",d="",y=!1,m=!0;switch(h&&(h=+h.substring(1)),(c||"0"===r&&"="===o)&&(c=r="0",o="="),p){case"n":s=!0,p="g";break;case"%":g=100,d="%",p="f";break;case"p":g=100,d="%",p="r";break;case"b":case"o":case"x":case"X":"#"===l&&(v="0"+p.toLowerCase());case"c":m=!1;case"d":y=!0,h=0;break;case"s":g=-1,p="r"}"$"===l&&(v=i[0],d=i[1]),"r"!=p||h||(p="g"),null!=h&&("g"==p?h=Math.max(1,Math.min(21,h)):"e"!=p&&"f"!=p||(h=Math.max(0,Math.min(20,h)))),p=pa.get(p)||Fn;var M=c&&s;return function(n){var e=d;if(y&&n%1)return"";var i=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>g){var l=ao.formatPrefix(n,h);n=l.scale(n),e=l.symbol+d}else n*=g;n=p(n,h);var x,b,_=n.lastIndexOf(".");if(0>_){var w=m?n.lastIndexOf("e"):-1;0>w?(x=n,b=""):(x=n.substring(0,w),b=n.substring(w))}else x=n.substring(0,_),b=t+n.substring(_+1);!c&&s&&(x=u(x,1/0));var S=v.length+x.length+b.length+(M?0:i.length),k=f>S?new Array(S=f-S+1).join(r):"";return M&&(x=u(k+x,k.length?f-b.length:1/0)),i+=v,n=x+b,("<"===o?i+n+k:">"===o?k+i+n:"^"===o?k.substring(0,S>>=1)+i+n+k.substring(S):i+(M?n:k+n))+e}}}function Fn(n){return n+""}function Hn(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function On(n,t,e){function r(t){var e=n(t),r=u(e,1);return r-t>t-e?e:r}function i(e){return t(e=n(new va(e-1)),1),e}function u(n,e){return t(n=new va(+n),e),n}function o(n,r,u){var o=i(n),a=[];if(u>1)for(;r>o;)e(o)%u||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{va=Hn;var r=new Hn;return r._=n,o(r,t,e)}finally{va=Date}}n.floor=n,n.round=r,n.ceil=i,n.offset=u,n.range=o;var l=n.utc=In(n);return l.floor=l,l.round=In(r),l.ceil=In(i),l.offset=In(u),l.range=a,n}function In(n){return function(t,e){try{va=Hn;var r=new Hn;return r._=t,n(r,e)._}finally{va=Date}}}function Yn(n){function t(n){function t(t){for(var e,i,u,o=[],a=-1,l=0;++aa;){if(r>=c)return-1;if(i=t.charCodeAt(a++),37===i){if(o=t.charAt(a++),u=C[o in ya?t.charAt(a++):o],!u||(r=u(n,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){_.lastIndex=0;var r=_.exec(t.slice(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){x.lastIndex=0;var r=x.exec(t.slice(e));return r?(n.w=b.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){N.lastIndex=0;var r=N.exec(t.slice(e));return r?(n.m=E.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.slice(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,A.c.toString(),t,r)}function l(n,t,r){return e(n,A.x.toString(),t,r)}function c(n,t,r){return e(n,A.X.toString(),t,r)}function f(n,t,e){var r=M.get(t.slice(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var s=n.dateTime,h=n.date,p=n.time,g=n.periods,v=n.days,d=n.shortDays,y=n.months,m=n.shortMonths;t.utc=function(n){function e(n){try{va=Hn;var t=new va;return t._=n,r(t)}finally{va=Date}}var r=t(n);return e.parse=function(n){try{va=Hn;var t=r.parse(n);return t&&t._}finally{va=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ct;var M=ao.map(),x=Vn(v),b=Xn(v),_=Vn(d),w=Xn(d),S=Vn(y),k=Xn(y),N=Vn(m),E=Xn(m);g.forEach(function(n,t){M.set(n.toLowerCase(),t)});var A={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return m[n.getMonth()]},B:function(n){return y[n.getMonth()]},c:t(s),d:function(n,t){return Zn(n.getDate(),t,2)},e:function(n,t){return Zn(n.getDate(),t,2)},H:function(n,t){return Zn(n.getHours(),t,2)},I:function(n,t){return Zn(n.getHours()%12||12,t,2)},j:function(n,t){return Zn(1+ga.dayOfYear(n),t,3)},L:function(n,t){return Zn(n.getMilliseconds(),t,3)},m:function(n,t){return Zn(n.getMonth()+1,t,2)},M:function(n,t){return Zn(n.getMinutes(),t,2)},p:function(n){return g[+(n.getHours()>=12)]},S:function(n,t){return Zn(n.getSeconds(),t,2)},U:function(n,t){return Zn(ga.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Zn(ga.mondayOfYear(n),t,2)},x:t(h),X:t(p),y:function(n,t){return Zn(n.getFullYear()%100,t,2)},Y:function(n,t){return Zn(n.getFullYear()%1e4,t,4)},Z:at,"%":function(){return"%"}},C={a:r,A:i,b:u,B:o,c:a,d:tt,e:tt,H:rt,I:rt,j:et,L:ot,m:nt,M:it,p:f,S:ut,U:Bn,w:$n,W:Wn,x:l,X:c,y:Gn,Y:Jn,Z:Kn,"%":lt};return t}function Zn(n,t,e){var r=0>n?"-":"",i=(r?-n:n)+"",u=i.length;return r+(e>u?new Array(e-u+1).join(t)+i:i)}function Vn(n){return new RegExp("^(?:"+n.map(ao.requote).join("|")+")","i")}function Xn(n){for(var t=new c,e=-1,r=n.length;++e68?1900:2e3)}function nt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function tt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function et(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function rt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function it(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function ut(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ot(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function at(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=xo(t)/60|0,i=xo(t)%60;return e+Zn(r,"0",2)+Zn(i,"0",2)}function lt(n,t,e){Ma.lastIndex=0;var r=Ma.exec(t.slice(e,e+1));return r?e+r[0].length:-1}function ct(n){for(var t=n.length,e=-1;++e=0?1:-1,a=o*e,l=Math.cos(t),c=Math.sin(t),f=u*c,s=i*l+f*Math.cos(a),h=f*o*Math.sin(a);ka.add(Math.atan2(h,s)),r=n,i=l,u=c}var t,e,r,i,u;Na.point=function(o,a){Na.point=n,r=(t=o)*Yo,i=Math.cos(a=(e=a)*Yo/2+Fo/4),u=Math.sin(a)},Na.lineEnd=function(){n(t,e)}}function dt(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function yt(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function mt(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function Mt(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function xt(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function bt(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function _t(n){return[Math.atan2(n[1],n[0]),tn(n[2])]}function wt(n,t){return xo(n[0]-t[0])a;++a)i.point((e=n[a])[0],e[1]);return void i.lineEnd()}var l=new Tt(e,n,null,!0),c=new Tt(e,null,l,!1);l.o=c,u.push(l),o.push(c),l=new Tt(r,n,null,!1),c=new Tt(r,null,l,!0),l.o=c,u.push(l),o.push(c)}}),o.sort(t),qt(u),qt(o),u.length){for(var a=0,l=e,c=o.length;c>a;++a)o[a].e=l=!l;for(var f,s,h=u[0];;){for(var p=h,g=!0;p.v;)if((p=p.n)===h)return;f=p.z,i.lineStart();do{if(p.v=p.o.v=!0,p.e){if(g)for(var a=0,c=f.length;c>a;++a)i.point((s=f[a])[0],s[1]);else r(p.x,p.n.x,1,i);p=p.n}else{if(g){f=p.p.z;for(var a=f.length-1;a>=0;--a)i.point((s=f[a])[0],s[1])}else r(p.x,p.p.x,-1,i);p=p.p}p=p.o,f=p.z,g=!g}while(!p.v);i.lineEnd()}}}function qt(n){if(t=n.length){for(var t,e,r=0,i=n[0];++r0){for(b||(u.polygonStart(),b=!0),u.lineStart();++o1&&2&t&&e.push(e.pop().concat(e.shift())),p.push(e.filter(Dt))}var p,g,v,d=t(u),y=i.invert(r[0],r[1]),m={point:o,lineStart:l,lineEnd:c,polygonStart:function(){m.point=f,m.lineStart=s,m.lineEnd=h,p=[],g=[]},polygonEnd:function(){m.point=o,m.lineStart=l,m.lineEnd=c,p=ao.merge(p);var n=Ot(y,g);p.length?(b||(u.polygonStart(),b=!0),Lt(p,Ut,n,e,u)):n&&(b||(u.polygonStart(),b=!0),u.lineStart(),e(null,null,1,u),u.lineEnd()),b&&(u.polygonEnd(),b=!1),p=g=null},sphere:function(){u.polygonStart(),u.lineStart(),e(null,null,1,u),u.lineEnd(),u.polygonEnd()}},M=Pt(),x=t(M),b=!1;return m}}function Dt(n){return n.length>1}function Pt(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:b,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Ut(n,t){return((n=n.x)[0]<0?n[1]-Io-Uo:Io-n[1])-((t=t.x)[0]<0?t[1]-Io-Uo:Io-t[1])}function jt(n){var t,e=NaN,r=NaN,i=NaN;return{lineStart:function(){n.lineStart(),t=1},point:function(u,o){var a=u>0?Fo:-Fo,l=xo(u-e);xo(l-Fo)0?Io:-Io),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(u,r),t=0):i!==a&&l>=Fo&&(xo(e-i)Uo?Math.atan((Math.sin(t)*(u=Math.cos(r))*Math.sin(e)-Math.sin(r)*(i=Math.cos(t))*Math.sin(n))/(i*u*o)):(t+r)/2}function Ht(n,t,e,r){var i;if(null==n)i=e*Io,r.point(-Fo,i),r.point(0,i),r.point(Fo,i),r.point(Fo,0),r.point(Fo,-i),r.point(0,-i),r.point(-Fo,-i),r.point(-Fo,0),r.point(-Fo,i);else if(xo(n[0]-t[0])>Uo){var u=n[0]a;++a){var c=t[a],f=c.length;if(f)for(var s=c[0],h=s[0],p=s[1]/2+Fo/4,g=Math.sin(p),v=Math.cos(p),d=1;;){d===f&&(d=0),n=c[d];var y=n[0],m=n[1]/2+Fo/4,M=Math.sin(m),x=Math.cos(m),b=y-h,_=b>=0?1:-1,w=_*b,S=w>Fo,k=g*M;if(ka.add(Math.atan2(k*_*Math.sin(w),v*x+k*Math.cos(w))),u+=S?b+_*Ho:b,S^h>=e^y>=e){var N=mt(dt(s),dt(n));bt(N);var E=mt(i,N);bt(E);var A=(S^b>=0?-1:1)*tn(E[2]);(r>A||r===A&&(N[0]||N[1]))&&(o+=S^b>=0?1:-1)}if(!d++)break;h=y,g=M,v=x,s=n}}return(-Uo>u||Uo>u&&-Uo>ka)^1&o}function It(n){function t(n,t){return Math.cos(n)*Math.cos(t)>u}function e(n){var e,u,l,c,f;return{lineStart:function(){c=l=!1,f=1},point:function(s,h){var p,g=[s,h],v=t(s,h),d=o?v?0:i(s,h):v?i(s+(0>s?Fo:-Fo),h):0;if(!e&&(c=l=v)&&n.lineStart(),v!==l&&(p=r(e,g),(wt(e,p)||wt(g,p))&&(g[0]+=Uo,g[1]+=Uo,v=t(g[0],g[1]))),v!==l)f=0,v?(n.lineStart(),p=r(g,e),n.point(p[0],p[1])):(p=r(e,g),n.point(p[0],p[1]),n.lineEnd()),e=p;else if(a&&e&&o^v){var y;d&u||!(y=r(g,e,!0))||(f=0,o?(n.lineStart(),n.point(y[0][0],y[0][1]),n.point(y[1][0],y[1][1]),n.lineEnd()):(n.point(y[1][0],y[1][1]),n.lineEnd(),n.lineStart(),n.point(y[0][0],y[0][1])))}!v||e&&wt(e,g)||n.point(g[0],g[1]),e=g,l=v,u=d},lineEnd:function(){l&&n.lineEnd(),e=null},clean:function(){return f|(c&&l)<<1}}}function r(n,t,e){var r=dt(n),i=dt(t),o=[1,0,0],a=mt(r,i),l=yt(a,a),c=a[0],f=l-c*c;if(!f)return!e&&n;var s=u*l/f,h=-u*c/f,p=mt(o,a),g=xt(o,s),v=xt(a,h);Mt(g,v);var d=p,y=yt(g,d),m=yt(d,d),M=y*y-m*(yt(g,g)-1);if(!(0>M)){var x=Math.sqrt(M),b=xt(d,(-y-x)/m);if(Mt(b,g),b=_t(b),!e)return b;var _,w=n[0],S=t[0],k=n[1],N=t[1];w>S&&(_=w,w=S,S=_);var E=S-w,A=xo(E-Fo)E;if(!A&&k>N&&(_=k,k=N,N=_),C?A?k+N>0^b[1]<(xo(b[0]-w)Fo^(w<=b[0]&&b[0]<=S)){var z=xt(d,(-y+x)/m);return Mt(z,g),[b,_t(z)]}}}function i(t,e){var r=o?n:Fo-n,i=0;return-r>t?i|=1:t>r&&(i|=2),-r>e?i|=4:e>r&&(i|=8),i}var u=Math.cos(n),o=u>0,a=xo(u)>Uo,l=ve(n,6*Yo);return Rt(t,e,l,o?[0,-n]:[-Fo,n-Fo])}function Yt(n,t,e,r){return function(i){var u,o=i.a,a=i.b,l=o.x,c=o.y,f=a.x,s=a.y,h=0,p=1,g=f-l,v=s-c;if(u=n-l,g||!(u>0)){if(u/=g,0>g){if(h>u)return;p>u&&(p=u)}else if(g>0){if(u>p)return;u>h&&(h=u)}if(u=e-l,g||!(0>u)){if(u/=g,0>g){if(u>p)return;u>h&&(h=u)}else if(g>0){if(h>u)return;p>u&&(p=u)}if(u=t-c,v||!(u>0)){if(u/=v,0>v){if(h>u)return;p>u&&(p=u)}else if(v>0){if(u>p)return;u>h&&(h=u)}if(u=r-c,v||!(0>u)){if(u/=v,0>v){if(u>p)return;u>h&&(h=u)}else if(v>0){if(h>u)return;p>u&&(p=u)}return h>0&&(i.a={x:l+h*g,y:c+h*v}),1>p&&(i.b={x:l+p*g,y:c+p*v}),i}}}}}}function Zt(n,t,e,r){function i(r,i){return xo(r[0]-n)0?0:3:xo(r[0]-e)0?2:1:xo(r[1]-t)0?1:0:i>0?3:2}function u(n,t){return o(n.x,t.x)}function o(n,t){var e=i(n,1),r=i(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function l(n){for(var t=0,e=d.length,r=n[1],i=0;e>i;++i)for(var u,o=1,a=d[i],l=a.length,c=a[0];l>o;++o)u=a[o],c[1]<=r?u[1]>r&&Q(c,u,n)>0&&++t:u[1]<=r&&Q(c,u,n)<0&&--t,c=u;return 0!==t}function c(u,a,l,c){var f=0,s=0;if(null==u||(f=i(u,l))!==(s=i(a,l))||o(u,a)<0^l>0){do c.point(0===f||3===f?n:e,f>1?r:t);while((f=(f+l+4)%4)!==s)}else c.point(a[0],a[1])}function f(i,u){return i>=n&&e>=i&&u>=t&&r>=u}function s(n,t){f(n,t)&&a.point(n,t)}function h(){C.point=g,d&&d.push(y=[]),S=!0,w=!1,b=_=NaN}function p(){v&&(g(m,M),x&&w&&E.rejoin(),v.push(E.buffer())),C.point=s,w&&a.lineEnd()}function g(n,t){n=Math.max(-Ha,Math.min(Ha,n)),t=Math.max(-Ha,Math.min(Ha,t));var e=f(n,t);if(d&&y.push([n,t]),S)m=n,M=t,x=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:b,y:_},b:{x:n,y:t}};A(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}b=n,_=t,w=e}var v,d,y,m,M,x,b,_,w,S,k,N=a,E=Pt(),A=Yt(n,t,e,r),C={point:s,lineStart:h,lineEnd:p,polygonStart:function(){a=E,v=[],d=[],k=!0},polygonEnd:function(){a=N,v=ao.merge(v);var t=l([n,r]),e=k&&t,i=v.length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),c(null,null,1,a),a.lineEnd()),i&&Lt(v,u,t,c,a),a.polygonEnd()),v=d=y=null}};return C}}function Vt(n){var t=0,e=Fo/3,r=ae(n),i=r(t,e);return i.parallels=function(n){return arguments.length?r(t=n[0]*Fo/180,e=n[1]*Fo/180):[t/Fo*180,e/Fo*180]},i}function Xt(n,t){function e(n,t){var e=Math.sqrt(u-2*i*Math.sin(t))/i;return[e*Math.sin(n*=i),o-e*Math.cos(n)]}var r=Math.sin(n),i=(r+Math.sin(t))/2,u=1+r*(2*i-r),o=Math.sqrt(u)/i;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/i,tn((u-(n*n+e*e)*i*i)/(2*i))]},e}function $t(){function n(n,t){Ia+=i*n-r*t,r=n,i=t}var t,e,r,i;$a.point=function(u,o){$a.point=n,t=r=u,e=i=o},$a.lineEnd=function(){n(t,e)}}function Bt(n,t){Ya>n&&(Ya=n),n>Va&&(Va=n),Za>t&&(Za=t),t>Xa&&(Xa=t)}function Wt(){function n(n,t){o.push("M",n,",",t,u)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function i(){o.push("Z")}var u=Jt(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return u=Jt(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Jt(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function Gt(n,t){Ca+=n,za+=t,++La}function Kt(){function n(n,r){var i=n-t,u=r-e,o=Math.sqrt(i*i+u*u);qa+=o*(t+n)/2,Ta+=o*(e+r)/2,Ra+=o,Gt(t=n,e=r)}var t,e;Wa.point=function(r,i){Wa.point=n,Gt(t=r,e=i)}}function Qt(){Wa.point=Gt}function ne(){function n(n,t){var e=n-r,u=t-i,o=Math.sqrt(e*e+u*u);qa+=o*(r+n)/2,Ta+=o*(i+t)/2,Ra+=o,o=i*n-r*t,Da+=o*(r+n),Pa+=o*(i+t),Ua+=3*o,Gt(r=n,i=t)}var t,e,r,i;Wa.point=function(u,o){Wa.point=n,Gt(t=r=u,e=i=o)},Wa.lineEnd=function(){n(t,e)}}function te(n){function t(t,e){n.moveTo(t+o,e),n.arc(t,e,o,0,Ho)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function i(){a.point=t}function u(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:i,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=i,a.point=t},pointRadius:function(n){return o=n,a},result:b};return a}function ee(n){function t(n){return(a?r:e)(n)}function e(t){return ue(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){M=NaN,S.point=u,t.lineStart()}function u(e,r){var u=dt([e,r]),o=n(e,r);i(M,x,m,b,_,w,M=o[0],x=o[1],m=e,b=u[0],_=u[1],w=u[2],a,t),t.point(M,x)}function o(){S.point=e,t.lineEnd()}function l(){ -r(),S.point=c,S.lineEnd=f}function c(n,t){u(s=n,h=t),p=M,g=x,v=b,d=_,y=w,S.point=u}function f(){i(M,x,m,b,_,w,p,g,s,v,d,y,a,t),S.lineEnd=o,o()}var s,h,p,g,v,d,y,m,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=l},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function i(t,e,r,a,l,c,f,s,h,p,g,v,d,y){var m=f-t,M=s-e,x=m*m+M*M;if(x>4*u&&d--){var b=a+p,_=l+g,w=c+v,S=Math.sqrt(b*b+_*_+w*w),k=Math.asin(w/=S),N=xo(xo(w)-1)u||xo((m*z+M*L)/x-.5)>.3||o>a*p+l*g+c*v)&&(i(t,e,r,a,l,c,A,C,N,b/=S,_/=S,w,d,y),y.point(A,C),i(A,C,N,b,_,w,f,s,h,p,g,v,d,y))}}var u=.5,o=Math.cos(30*Yo),a=16;return t.precision=function(n){return arguments.length?(a=(u=n*n)>0&&16,t):Math.sqrt(u)},t}function re(n){var t=ee(function(t,e){return n([t*Zo,e*Zo])});return function(n){return le(t(n))}}function ie(n){this.stream=n}function ue(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function oe(n){return ae(function(){return n})()}function ae(n){function t(n){return n=a(n[0]*Yo,n[1]*Yo),[n[0]*h+l,c-n[1]*h]}function e(n){return n=a.invert((n[0]-l)/h,(c-n[1])/h),n&&[n[0]*Zo,n[1]*Zo]}function r(){a=Ct(o=se(y,M,x),u);var n=u(v,d);return l=p-n[0]*h,c=g+n[1]*h,i()}function i(){return f&&(f.valid=!1,f=null),t}var u,o,a,l,c,f,s=ee(function(n,t){return n=u(n,t),[n[0]*h+l,c-n[1]*h]}),h=150,p=480,g=250,v=0,d=0,y=0,M=0,x=0,b=Fa,_=m,w=null,S=null;return t.stream=function(n){return f&&(f.valid=!1),f=le(b(o,s(_(n)))),f.valid=!0,f},t.clipAngle=function(n){return arguments.length?(b=null==n?(w=n,Fa):It((w=+n)*Yo),i()):w},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Zt(n[0][0],n[0][1],n[1][0],n[1][1]):m,i()):S},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(p=+n[0],g=+n[1],r()):[p,g]},t.center=function(n){return arguments.length?(v=n[0]%360*Yo,d=n[1]%360*Yo,r()):[v*Zo,d*Zo]},t.rotate=function(n){return arguments.length?(y=n[0]%360*Yo,M=n[1]%360*Yo,x=n.length>2?n[2]%360*Yo:0,r()):[y*Zo,M*Zo,x*Zo]},ao.rebind(t,s,"precision"),function(){return u=n.apply(this,arguments),t.invert=u.invert&&e,r()}}function le(n){return ue(n,function(t,e){n.point(t*Yo,e*Yo)})}function ce(n,t){return[n,t]}function fe(n,t){return[n>Fo?n-Ho:-Fo>n?n+Ho:n,t]}function se(n,t,e){return n?t||e?Ct(pe(n),ge(t,e)):pe(n):t||e?ge(t,e):fe}function he(n){return function(t,e){return t+=n,[t>Fo?t-Ho:-Fo>t?t+Ho:t,e]}}function pe(n){var t=he(n);return t.invert=he(-n),t}function ge(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*r+a*i;return[Math.atan2(l*u-f*o,a*r-c*i),tn(f*u+l*o)]}var r=Math.cos(n),i=Math.sin(n),u=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*u-l*o;return[Math.atan2(l*u+c*o,a*r+f*i),tn(f*r-a*i)]},e}function ve(n,t){var e=Math.cos(n),r=Math.sin(n);return function(i,u,o,a){var l=o*t;null!=i?(i=de(e,i),u=de(e,u),(o>0?u>i:i>u)&&(i+=o*Ho)):(i=n+o*Ho,u=n-.5*l);for(var c,f=i;o>0?f>u:u>f;f-=l)a.point((c=_t([e,-r*Math.cos(f),-r*Math.sin(f)]))[0],c[1])}}function de(n,t){var e=dt(t);e[0]-=n,bt(e);var r=nn(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Uo)%(2*Math.PI)}function ye(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function me(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function Me(n){return n.source}function xe(n){return n.target}function be(n,t,e,r){var i=Math.cos(t),u=Math.sin(t),o=Math.cos(r),a=Math.sin(r),l=i*Math.cos(n),c=i*Math.sin(n),f=o*Math.cos(e),s=o*Math.sin(e),h=2*Math.asin(Math.sqrt(on(r-t)+i*o*on(e-n))),p=1/Math.sin(h),g=h?function(n){var t=Math.sin(n*=h)*p,e=Math.sin(h-n)*p,r=e*l+t*f,i=e*c+t*s,o=e*u+t*a;return[Math.atan2(i,r)*Zo,Math.atan2(o,Math.sqrt(r*r+i*i))*Zo]}:function(){return[n*Zo,t*Zo]};return g.distance=h,g}function _e(){function n(n,i){var u=Math.sin(i*=Yo),o=Math.cos(i),a=xo((n*=Yo)-t),l=Math.cos(a);Ja+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*u-e*o*l)*a),e*u+r*o*l),t=n,e=u,r=o}var t,e,r;Ga.point=function(i,u){t=i*Yo,e=Math.sin(u*=Yo),r=Math.cos(u),Ga.point=n},Ga.lineEnd=function(){Ga.point=Ga.lineEnd=b}}function we(n,t){function e(t,e){var r=Math.cos(t),i=Math.cos(e),u=n(r*i);return[u*i*Math.sin(t),u*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),i=t(r),u=Math.sin(i),o=Math.cos(i);return[Math.atan2(n*u,r*o),Math.asin(r&&e*u/r)]},e}function Se(n,t){function e(n,t){o>0?-Io+Uo>t&&(t=-Io+Uo):t>Io-Uo&&(t=Io-Uo);var e=o/Math.pow(i(t),u);return[e*Math.sin(u*n),o-e*Math.cos(u*n)]}var r=Math.cos(n),i=function(n){return Math.tan(Fo/4+n/2)},u=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(i(t)/i(n)),o=r*Math.pow(i(n),u)/u;return u?(e.invert=function(n,t){var e=o-t,r=K(u)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/u,2*Math.atan(Math.pow(o/r,1/u))-Io]},e):Ne}function ke(n,t){function e(n,t){var e=u-t;return[e*Math.sin(i*n),u-e*Math.cos(i*n)]}var r=Math.cos(n),i=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),u=r/i+n;return xo(i)i;i++){for(;r>1&&Q(n[e[r-2]],n[e[r-1]],n[i])<=0;)--r;e[r++]=i}return e.slice(0,r)}function qe(n,t){return n[0]-t[0]||n[1]-t[1]}function Te(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Re(n,t,e,r){var i=n[0],u=e[0],o=t[0]-i,a=r[0]-u,l=n[1],c=e[1],f=t[1]-l,s=r[1]-c,h=(a*(l-c)-s*(i-u))/(s*o-a*f);return[i+h*o,l+h*f]}function De(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Pe(){rr(this),this.edge=this.site=this.circle=null}function Ue(n){var t=cl.pop()||new Pe;return t.site=n,t}function je(n){Be(n),ol.remove(n),cl.push(n),rr(n)}function Fe(n){var t=n.circle,e=t.x,r=t.cy,i={x:e,y:r},u=n.P,o=n.N,a=[n];je(n);for(var l=u;l.circle&&xo(e-l.circle.x)f;++f)c=a[f],l=a[f-1],nr(c.edge,l.site,c.site,i);l=a[0],c=a[s-1],c.edge=Ke(l.site,c.site,null,i),$e(l),$e(c)}function He(n){for(var t,e,r,i,u=n.x,o=n.y,a=ol._;a;)if(r=Oe(a,o)-u,r>Uo)a=a.L;else{if(i=u-Ie(a,o),!(i>Uo)){r>-Uo?(t=a.P,e=a):i>-Uo?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var l=Ue(n);if(ol.insert(t,l),t||e){if(t===e)return Be(t),e=Ue(t.site),ol.insert(l,e),l.edge=e.edge=Ke(t.site,l.site),$e(t),void $e(e);if(!e)return void(l.edge=Ke(t.site,l.site));Be(t),Be(e);var c=t.site,f=c.x,s=c.y,h=n.x-f,p=n.y-s,g=e.site,v=g.x-f,d=g.y-s,y=2*(h*d-p*v),m=h*h+p*p,M=v*v+d*d,x={x:(d*m-p*M)/y+f,y:(h*M-v*m)/y+s};nr(e.edge,c,g,x),l.edge=Ke(c,n,null,x),e.edge=Ke(n,g,null,x),$e(t),$e(e)}}function Oe(n,t){var e=n.site,r=e.x,i=e.y,u=i-t;if(!u)return r;var o=n.P;if(!o)return-(1/0);e=o.site;var a=e.x,l=e.y,c=l-t;if(!c)return a;var f=a-r,s=1/u-1/c,h=f/c;return s?(-h+Math.sqrt(h*h-2*s*(f*f/(-2*c)-l+c/2+i-u/2)))/s+r:(r+a)/2}function Ie(n,t){var e=n.N;if(e)return Oe(e,t);var r=n.site;return r.y===t?r.x:1/0}function Ye(n){this.site=n,this.edges=[]}function Ze(n){for(var t,e,r,i,u,o,a,l,c,f,s=n[0][0],h=n[1][0],p=n[0][1],g=n[1][1],v=ul,d=v.length;d--;)if(u=v[d],u&&u.prepare())for(a=u.edges,l=a.length,o=0;l>o;)f=a[o].end(),r=f.x,i=f.y,c=a[++o%l].start(),t=c.x,e=c.y,(xo(r-t)>Uo||xo(i-e)>Uo)&&(a.splice(o,0,new tr(Qe(u.site,f,xo(r-s)Uo?{x:s,y:xo(t-s)Uo?{x:xo(e-g)Uo?{x:h,y:xo(t-h)Uo?{x:xo(e-p)=-jo)){var p=l*l+c*c,g=f*f+s*s,v=(s*p-c*g)/h,d=(l*g-f*p)/h,s=d+a,y=fl.pop()||new Xe;y.arc=n,y.site=i,y.x=v+o,y.y=s+Math.sqrt(v*v+d*d),y.cy=s,n.circle=y;for(var m=null,M=ll._;M;)if(y.yd||d>=a)return;if(h>g){if(u){if(u.y>=c)return}else u={x:d,y:l};e={x:d,y:c}}else{if(u){if(u.yr||r>1)if(h>g){if(u){if(u.y>=c)return}else u={x:(l-i)/r,y:l};e={x:(c-i)/r,y:c}}else{if(u){if(u.yp){if(u){if(u.x>=a)return}else u={x:o,y:r*o+i};e={x:a,y:r*a+i}}else{if(u){if(u.xu||s>o||r>h||i>p)){if(g=n.point){var g,v=t-n.x,d=e-n.y,y=v*v+d*d;if(l>y){var m=Math.sqrt(l=y);r=t-m,i=e-m,u=t+m,o=e+m,a=g}}for(var M=n.nodes,x=.5*(f+h),b=.5*(s+p),_=t>=x,w=e>=b,S=w<<1|_,k=S+4;k>S;++S)if(n=M[3&S])switch(3&S){case 0:c(n,f,s,x,b);break;case 1:c(n,x,s,h,b);break;case 2:c(n,f,b,x,p);break;case 3:c(n,x,b,h,p)}}}(n,r,i,u,o),a}function vr(n,t){n=ao.rgb(n),t=ao.rgb(t);var e=n.r,r=n.g,i=n.b,u=t.r-e,o=t.g-r,a=t.b-i;return function(n){return"#"+bn(Math.round(e+u*n))+bn(Math.round(r+o*n))+bn(Math.round(i+a*n))}}function dr(n,t){var e,r={},i={};for(e in n)e in t?r[e]=Mr(n[e],t[e]):i[e]=n[e];for(e in t)e in n||(i[e]=t[e]);return function(n){for(e in r)i[e]=r[e](n);return i}}function yr(n,t){return n=+n,t=+t,function(e){return n*(1-e)+t*e}}function mr(n,t){var e,r,i,u=hl.lastIndex=pl.lastIndex=0,o=-1,a=[],l=[];for(n+="",t+="";(e=hl.exec(n))&&(r=pl.exec(t));)(i=r.index)>u&&(i=t.slice(u,i),a[o]?a[o]+=i:a[++o]=i),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,l.push({i:o,x:yr(e,r)})),u=pl.lastIndex;return ur;++r)a[(e=l[r]).i]=e.x(n);return a.join("")})}function Mr(n,t){for(var e,r=ao.interpolators.length;--r>=0&&!(e=ao.interpolators[r](n,t)););return e}function xr(n,t){var e,r=[],i=[],u=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(Mr(n[e],t[e]));for(;u>e;++e)i[e]=n[e];for(;o>e;++e)i[e]=t[e];return function(n){for(e=0;a>e;++e)i[e]=r[e](n);return i}}function br(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function _r(n){return function(t){return 1-n(1-t)}}function wr(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function Sr(n){return n*n}function kr(n){return n*n*n}function Nr(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function Er(n){return function(t){return Math.pow(t,n)}}function Ar(n){return 1-Math.cos(n*Io)}function Cr(n){return Math.pow(2,10*(n-1))}function zr(n){return 1-Math.sqrt(1-n*n)}function Lr(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/Ho*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*Ho/t)}}function qr(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function Tr(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Rr(n,t){n=ao.hcl(n),t=ao.hcl(t);var e=n.h,r=n.c,i=n.l,u=t.h-e,o=t.c-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return sn(e+u*n,r+o*n,i+a*n)+""}}function Dr(n,t){n=ao.hsl(n),t=ao.hsl(t);var e=n.h,r=n.s,i=n.l,u=t.h-e,o=t.s-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return cn(e+u*n,r+o*n,i+a*n)+""}}function Pr(n,t){n=ao.lab(n),t=ao.lab(t);var e=n.l,r=n.a,i=n.b,u=t.l-e,o=t.a-r,a=t.b-i;return function(n){return pn(e+u*n,r+o*n,i+a*n)+""}}function Ur(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function jr(n){var t=[n.a,n.b],e=[n.c,n.d],r=Hr(t),i=Fr(t,e),u=Hr(Or(e,t,-i))||0;t[0]*e[1]180?t+=360:t-n>180&&(n+=360),r.push({i:e.push(Ir(e)+"rotate(",null,")")-2,x:yr(n,t)})):t&&e.push(Ir(e)+"rotate("+t+")")}function Vr(n,t,e,r){n!==t?r.push({i:e.push(Ir(e)+"skewX(",null,")")-2,x:yr(n,t)}):t&&e.push(Ir(e)+"skewX("+t+")")}function Xr(n,t,e,r){if(n[0]!==t[0]||n[1]!==t[1]){var i=e.push(Ir(e)+"scale(",null,",",null,")");r.push({i:i-4,x:yr(n[0],t[0])},{i:i-2,x:yr(n[1],t[1])})}else 1===t[0]&&1===t[1]||e.push(Ir(e)+"scale("+t+")")}function $r(n,t){var e=[],r=[];return n=ao.transform(n),t=ao.transform(t),Yr(n.translate,t.translate,e,r),Zr(n.rotate,t.rotate,e,r),Vr(n.skew,t.skew,e,r),Xr(n.scale,t.scale,e,r),n=t=null,function(n){for(var t,i=-1,u=r.length;++i=0;)e.push(i[r])}function oi(n,t){for(var e=[n],r=[];null!=(n=e.pop());)if(r.push(n),(u=n.children)&&(i=u.length))for(var i,u,o=-1;++oe;++e)(t=n[e][1])>i&&(r=e,i=t);return r}function yi(n){return n.reduce(mi,0)}function mi(n,t){return n+t[1]}function Mi(n,t){return xi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function xi(n,t){for(var e=-1,r=+n[0],i=(n[1]-r)/t,u=[];++e<=t;)u[e]=i*e+r;return u}function bi(n){return[ao.min(n),ao.max(n)]}function _i(n,t){return n.value-t.value}function wi(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Si(n,t){n._pack_next=t,t._pack_prev=n}function ki(n,t){var e=t.x-n.x,r=t.y-n.y,i=n.r+t.r;return.999*i*i>e*e+r*r}function Ni(n){function t(n){f=Math.min(n.x-n.r,f),s=Math.max(n.x+n.r,s),h=Math.min(n.y-n.r,h),p=Math.max(n.y+n.r,p)}if((e=n.children)&&(c=e.length)){var e,r,i,u,o,a,l,c,f=1/0,s=-(1/0),h=1/0,p=-(1/0);if(e.forEach(Ei),r=e[0],r.x=-r.r,r.y=0,t(r),c>1&&(i=e[1],i.x=i.r,i.y=0,t(i),c>2))for(u=e[2],zi(r,i,u),t(u),wi(r,u),r._pack_prev=u,wi(u,i),i=r._pack_next,o=3;c>o;o++){zi(r,i,u=e[o]);var g=0,v=1,d=1;for(a=i._pack_next;a!==i;a=a._pack_next,v++)if(ki(a,u)){g=1;break}if(1==g)for(l=r._pack_prev;l!==a._pack_prev&&!ki(l,u);l=l._pack_prev,d++);g?(d>v||v==d&&i.ro;o++)u=e[o],u.x-=y,u.y-=m,M=Math.max(M,u.r+Math.sqrt(u.x*u.x+u.y*u.y));n.r=M,e.forEach(Ai)}}function Ei(n){n._pack_next=n._pack_prev=n}function Ai(n){delete n._pack_next,delete n._pack_prev}function Ci(n,t,e,r){var i=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,i)for(var u=-1,o=i.length;++u=0;)t=i[u],t.z+=e,t.m+=e,e+=t.s+(r+=t.c)}function Pi(n,t,e){return n.a.parent===t.parent?n.a:e}function Ui(n){return 1+ao.max(n,function(n){return n.y})}function ji(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Fi(n){var t=n.children;return t&&t.length?Fi(t[0]):n}function Hi(n){var t,e=n.children;return e&&(t=e.length)?Hi(e[t-1]):n}function Oi(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Ii(n,t){var e=n.x+t[3],r=n.y+t[0],i=n.dx-t[1]-t[3],u=n.dy-t[0]-t[2];return 0>i&&(e+=i/2,i=0),0>u&&(r+=u/2,u=0),{x:e,y:r,dx:i,dy:u}}function Yi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Zi(n){return n.rangeExtent?n.rangeExtent():Yi(n.range())}function Vi(n,t,e,r){var i=e(n[0],n[1]),u=r(t[0],t[1]);return function(n){return u(i(n))}}function Xi(n,t){var e,r=0,i=n.length-1,u=n[r],o=n[i];return u>o&&(e=r,r=i,i=e,e=u,u=o,o=e),n[r]=t.floor(u),n[i]=t.ceil(o),n}function $i(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:Sl}function Bi(n,t,e,r){var i=[],u=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]2?Bi:Vi,l=r?Wr:Br;return o=i(n,t,l,e),a=i(t,n,l,Mr),u}function u(n){return o(n)}var o,a;return u.invert=function(n){return a(n)},u.domain=function(t){return arguments.length?(n=t.map(Number),i()):n},u.range=function(n){return arguments.length?(t=n,i()):t},u.rangeRound=function(n){return u.range(n).interpolate(Ur)},u.clamp=function(n){return arguments.length?(r=n,i()):r},u.interpolate=function(n){return arguments.length?(e=n,i()):e},u.ticks=function(t){return Qi(n,t)},u.tickFormat=function(t,e){return nu(n,t,e)},u.nice=function(t){return Gi(n,t),i()},u.copy=function(){return Wi(n,t,e,r)},i()}function Ji(n,t){return ao.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Gi(n,t){return Xi(n,$i(Ki(n,t)[2])),Xi(n,$i(Ki(n,t)[2])),n}function Ki(n,t){null==t&&(t=10);var e=Yi(n),r=e[1]-e[0],i=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),u=t/r*i;return.15>=u?i*=10:.35>=u?i*=5:.75>=u&&(i*=2),e[0]=Math.ceil(e[0]/i)*i,e[1]=Math.floor(e[1]/i)*i+.5*i,e[2]=i,e}function Qi(n,t){return ao.range.apply(ao,Ki(n,t))}function nu(n,t,e){var r=Ki(n,t);if(e){var i=ha.exec(e);if(i.shift(),"s"===i[8]){var u=ao.formatPrefix(Math.max(xo(r[0]),xo(r[1])));return i[7]||(i[7]="."+tu(u.scale(r[2]))),i[8]="f",e=ao.format(i.join("")),function(n){return e(u.scale(n))+u.symbol}}i[7]||(i[7]="."+eu(i[8],r)),e=i.join("")}else e=",."+tu(r[2])+"f";return ao.format(e)}function tu(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function eu(n,t){var e=tu(t[2]);return n in kl?Math.abs(e-tu(Math.max(xo(t[0]),xo(t[1]))))+ +("e"!==n):e-2*("%"===n)}function ru(n,t,e,r){function i(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function u(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(i(t))}return o.invert=function(t){return u(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(i)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(i)),o):t},o.nice=function(){var t=Xi(r.map(i),e?Math:El);return n.domain(t),r=t.map(u),o},o.ticks=function(){var n=Yi(r),o=[],a=n[0],l=n[1],c=Math.floor(i(a)),f=Math.ceil(i(l)),s=t%1?2:t;if(isFinite(f-c)){if(e){for(;f>c;c++)for(var h=1;s>h;h++)o.push(u(c)*h);o.push(u(c))}else for(o.push(u(c));c++0;h--)o.push(u(c)*h);for(c=0;o[c]l;f--);o=o.slice(c,f)}return o},o.tickFormat=function(n,e){if(!arguments.length)return Nl;arguments.length<2?e=Nl:"function"!=typeof e&&(e=ao.format(e));var r=Math.max(1,t*n/o.ticks().length);return function(n){var o=n/u(Math.round(i(n)));return t-.5>o*t&&(o*=t),r>=o?e(n):""}},o.copy=function(){return ru(n.copy(),t,e,r)},Ji(o,n)}function iu(n,t,e){function r(t){return n(i(t))}var i=uu(t),u=uu(1/t);return r.invert=function(t){return u(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(i)),r):e},r.ticks=function(n){return Qi(e,n)},r.tickFormat=function(n,t){return nu(e,n,t)},r.nice=function(n){return r.domain(Gi(e,n))},r.exponent=function(o){return arguments.length?(i=uu(t=o),u=uu(1/t),n.domain(e.map(i)),r):t},r.copy=function(){return iu(n.copy(),t,e)},Ji(r,n)}function uu(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function ou(n,t){function e(e){return u[((i.get(e)||("range"===t.t?i.set(e,n.push(e)):NaN))-1)%u.length]}function r(t,e){return ao.range(n.length).map(function(n){return t+e*n})}var i,u,o;return e.domain=function(r){if(!arguments.length)return n;n=[],i=new c;for(var u,o=-1,a=r.length;++oe?[NaN,NaN]:[e>0?a[e-1]:n[0],et?NaN:t/u+n,[t,t+1/u]},r.copy=function(){return lu(n,t,e)},i()}function cu(n,t){function e(e){return e>=e?t[ao.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return cu(n,t)},e}function fu(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Qi(n,t)},t.tickFormat=function(t,e){return nu(n,t,e)},t.copy=function(){return fu(n)},t}function su(){return 0}function hu(n){return n.innerRadius}function pu(n){return n.outerRadius}function gu(n){return n.startAngle}function vu(n){return n.endAngle}function du(n){return n&&n.padAngle}function yu(n,t,e,r){return(n-e)*t-(t-r)*n>0?0:1}function mu(n,t,e,r,i){var u=n[0]-t[0],o=n[1]-t[1],a=(i?r:-r)/Math.sqrt(u*u+o*o),l=a*o,c=-a*u,f=n[0]+l,s=n[1]+c,h=t[0]+l,p=t[1]+c,g=(f+h)/2,v=(s+p)/2,d=h-f,y=p-s,m=d*d+y*y,M=e-r,x=f*p-h*s,b=(0>y?-1:1)*Math.sqrt(Math.max(0,M*M*m-x*x)),_=(x*y-d*b)/m,w=(-x*d-y*b)/m,S=(x*y+d*b)/m,k=(-x*d+y*b)/m,N=_-g,E=w-v,A=S-g,C=k-v;return N*N+E*E>A*A+C*C&&(_=S,w=k),[[_-l,w-c],[_*e/M,w*e/M]]}function Mu(n){function t(t){function o(){c.push("M",u(n(f),a))}for(var l,c=[],f=[],s=-1,h=t.length,p=En(e),g=En(r);++s1?n.join("L"):n+"Z"}function bu(n){return n.join("L")+"Z"}function _u(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t1&&i.push("H",r[0]),i.join("")}function wu(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t1){a=t[1],u=n[l],l++,r+="C"+(i[0]+o[0])+","+(i[1]+o[1])+","+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1];for(var c=2;c9&&(i=3*t/Math.sqrt(i),o[a]=i*e,o[a+1]=i*r));for(a=-1;++a<=l;)i=(n[Math.min(l,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),u.push([i||0,o[a]*i||0]);return u}function Fu(n){return n.length<3?xu(n):n[0]+Au(n,ju(n))}function Hu(n){for(var t,e,r,i=-1,u=n.length;++i=t?o(n-t):void(f.c=o)}function o(e){var i=g.active,u=g[i];u&&(u.timer.c=null,u.timer.t=NaN,--g.count,delete g[i],u.event&&u.event.interrupt.call(n,n.__data__,u.index));for(var o in g)if(r>+o){var c=g[o];c.timer.c=null,c.timer.t=NaN,--g.count,delete g[o]}f.c=a,qn(function(){return f.c&&a(e||1)&&(f.c=null,f.t=NaN),1},0,l),g.active=r,v.event&&v.event.start.call(n,n.__data__,t),p=[],v.tween.forEach(function(e,r){(r=r.call(n,n.__data__,t))&&p.push(r)}),h=v.ease,s=v.duration}function a(i){for(var u=i/s,o=h(u),a=p.length;a>0;)p[--a].call(n,o);return u>=1?(v.event&&v.event.end.call(n,n.__data__,t),--g.count?delete g[r]:delete n[e],1):void 0}var l,f,s,h,p,g=n[e]||(n[e]={active:0,count:0}),v=g[r];v||(l=i.time,f=qn(u,0,l),v=g[r]={tween:new c,time:l,timer:f,delay:i.delay,duration:i.duration,ease:i.ease,index:t},i=null,++g.count)}function no(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate("+(isFinite(r)?r:e(n))+",0)"})}function to(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate(0,"+(isFinite(r)?r:e(n))+")"})}function eo(n){return n.toISOString()}function ro(n,t,e){function r(t){return n(t)}function i(n,e){var r=n[1]-n[0],i=r/e,u=ao.bisect(Kl,i);return u==Kl.length?[t.year,Ki(n.map(function(n){return n/31536e6}),e)[2]]:u?t[i/Kl[u-1]1?{floor:function(t){for(;e(t=n.floor(t));)t=io(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=io(+t+1);return t}}:n))},r.ticks=function(n,t){var e=Yi(r.domain()),u=null==n?i(e,10):"number"==typeof n?i(e,n):!n.range&&[{range:n},t];return u&&(n=u[0],t=u[1]),n.range(e[0],io(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return ro(n.copy(),t,e)},Ji(r,n)}function io(n){return new Date(n)}function uo(n){return JSON.parse(n.responseText)}function oo(n){var t=fo.createRange();return t.selectNode(fo.body),t.createContextualFragment(n.responseText)}var ao={version:"3.5.17"},lo=[].slice,co=function(n){return lo.call(n)},fo=this.document;if(fo)try{co(fo.documentElement.childNodes)[0].nodeType}catch(so){co=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}if(Date.now||(Date.now=function(){return+new Date}),fo)try{fo.createElement("DIV").style.setProperty("opacity",0,"")}catch(ho){var po=this.Element.prototype,go=po.setAttribute,vo=po.setAttributeNS,yo=this.CSSStyleDeclaration.prototype,mo=yo.setProperty;po.setAttribute=function(n,t){go.call(this,n,t+"")},po.setAttributeNS=function(n,t,e){vo.call(this,n,t,e+"")},yo.setProperty=function(n,t,e){mo.call(this,n,t+"",e)}}ao.ascending=e,ao.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:NaN},ao.min=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i=r){e=r;break}for(;++ir&&(e=r)}else{for(;++i=r){e=r;break}for(;++ir&&(e=r)}return e},ao.max=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i=r){e=r;break}for(;++ie&&(e=r)}else{for(;++i=r){e=r;break}for(;++ie&&(e=r)}return e},ao.extent=function(n,t){var e,r,i,u=-1,o=n.length;if(1===arguments.length){for(;++u=r){e=i=r;break}for(;++ur&&(e=r),r>i&&(i=r))}else{for(;++u=r){e=i=r;break}for(;++ur&&(e=r),r>i&&(i=r))}return[e,i]},ao.sum=function(n,t){var e,r=0,u=n.length,o=-1;if(1===arguments.length)for(;++o1?l/(f-1):void 0},ao.deviation=function(){var n=ao.variance.apply(this,arguments);return n?Math.sqrt(n):n};var Mo=u(e);ao.bisectLeft=Mo.left,ao.bisect=ao.bisectRight=Mo.right,ao.bisector=function(n){return u(1===n.length?function(t,r){return e(n(t),r)}:n)},ao.shuffle=function(n,t,e){(u=arguments.length)<3&&(e=n.length,2>u&&(t=0));for(var r,i,u=e-t;u;)i=Math.random()*u--|0,r=n[u+t],n[u+t]=n[i+t],n[i+t]=r;return n},ao.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},ao.pairs=function(n){for(var t,e=0,r=n.length-1,i=n[0],u=new Array(0>r?0:r);r>e;)u[e]=[t=i,i=n[++e]];return u},ao.transpose=function(n){if(!(i=n.length))return[];for(var t=-1,e=ao.min(n,o),r=new Array(e);++t=0;)for(r=n[i],t=r.length;--t>=0;)e[--o]=r[t];return e};var xo=Math.abs;ao.range=function(n,t,e){if(arguments.length<3&&(e=1,arguments.length<2&&(t=n,n=0)),(t-n)/e===1/0)throw new Error("infinite range");var r,i=[],u=a(xo(e)),o=-1;if(n*=u,t*=u,e*=u,0>e)for(;(r=n+e*++o)>t;)i.push(r/u);else for(;(r=n+e*++o)=u.length)return r?r.call(i,o):e?o.sort(e):o;for(var l,f,s,h,p=-1,g=o.length,v=u[a++],d=new c;++p=u.length)return n;var r=[],i=o[e++];return n.forEach(function(n,i){r.push({key:n,values:t(i,e)})}),i?r.sort(function(n,t){return i(n.key,t.key)}):r}var e,r,i={},u=[],o=[];return i.map=function(t,e){return n(e,t,0)},i.entries=function(e){return t(n(ao.map,e,0),0)},i.key=function(n){return u.push(n),i},i.sortKeys=function(n){return o[u.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},ao.set=function(n){var t=new y;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},l(y,{has:h,add:function(n){return this._[f(n+="")]=!0,n},remove:p,values:g,size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t))}}),ao.behavior={},ao.rebind=function(n,t){for(var e,r=1,i=arguments.length;++r=0&&(r=n.slice(e+1),n=n.slice(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},ao.event=null,ao.requote=function(n){return n.replace(So,"\\$&")};var So=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ko={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},No=function(n,t){return t.querySelector(n)},Eo=function(n,t){return t.querySelectorAll(n)},Ao=function(n,t){var e=n.matches||n[x(n,"matchesSelector")];return(Ao=function(n,t){return e.call(n,t)})(n,t)};"function"==typeof Sizzle&&(No=function(n,t){return Sizzle(n,t)[0]||null},Eo=Sizzle,Ao=Sizzle.matchesSelector),ao.selection=function(){return ao.select(fo.documentElement)};var Co=ao.selection.prototype=[];Co.select=function(n){var t,e,r,i,u=[];n=A(n);for(var o=-1,a=this.length;++o=0&&"xmlns"!==(e=n.slice(0,t))&&(n=n.slice(t+1)),Lo.hasOwnProperty(e)?{space:Lo[e],local:n}:n}},Co.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=ao.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(z(t,n[t]));return this}return this.each(z(n,t))},Co.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=T(n)).length,i=-1;if(t=e.classList){for(;++ii){if("string"!=typeof n){2>i&&(e="");for(r in n)this.each(P(r,n[r],e));return this}if(2>i){var u=this.node();return t(u).getComputedStyle(u,null).getPropertyValue(n)}r=""}return this.each(P(n,e,r))},Co.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(U(t,n[t]));return this}return this.each(U(n,t))},Co.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},Co.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},Co.append=function(n){return n=j(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},Co.insert=function(n,t){return n=j(n),t=A(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},Co.remove=function(){return this.each(F)},Co.data=function(n,t){function e(n,e){var r,i,u,o=n.length,s=e.length,h=Math.min(o,s),p=new Array(s),g=new Array(s),v=new Array(o);if(t){var d,y=new c,m=new Array(o);for(r=-1;++rr;++r)g[r]=H(e[r]);for(;o>r;++r)v[r]=n[r]}g.update=p,g.parentNode=p.parentNode=v.parentNode=n.parentNode,a.push(g),l.push(p),f.push(v)}var r,i,u=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++uu;u++){i.push(t=[]),t.parentNode=(e=this[u]).parentNode;for(var a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return E(i)},Co.order=function(){for(var n=-1,t=this.length;++n=0;)(e=r[i])&&(u&&u!==e.nextSibling&&u.parentNode.insertBefore(e,u),u=e);return this},Co.sort=function(n){n=I.apply(this,arguments);for(var t=-1,e=this.length;++tn;n++)for(var e=this[n],r=0,i=e.length;i>r;r++){var u=e[r];if(u)return u}return null},Co.size=function(){var n=0;return Y(this,function(){++n}),n};var qo=[];ao.selection.enter=Z,ao.selection.enter.prototype=qo,qo.append=Co.append,qo.empty=Co.empty,qo.node=Co.node,qo.call=Co.call,qo.size=Co.size,qo.select=function(n){for(var t,e,r,i,u,o=[],a=-1,l=this.length;++ar){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(X(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(X(n,t,e))};var To=ao.map({mouseenter:"mouseover",mouseleave:"mouseout"});fo&&To.forEach(function(n){"on"+n in fo&&To.remove(n)});var Ro,Do=0;ao.mouse=function(n){return J(n,k())};var Po=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ao.touch=function(n,t,e){if(arguments.length<3&&(e=t,t=k().changedTouches),t)for(var r,i=0,u=t.length;u>i;++i)if((r=t[i]).identifier===e)return J(n,r)},ao.behavior.drag=function(){function n(){this.on("mousedown.drag",u).on("touchstart.drag",o)}function e(n,t,e,u,o){return function(){function a(){var n,e,r=t(h,v);r&&(n=r[0]-M[0],e=r[1]-M[1],g|=n|e,M=r,p({type:"drag",x:r[0]+c[0],y:r[1]+c[1],dx:n,dy:e}))}function l(){t(h,v)&&(y.on(u+d,null).on(o+d,null),m(g),p({type:"dragend"}))}var c,f=this,s=ao.event.target.correspondingElement||ao.event.target,h=f.parentNode,p=r.of(f,arguments),g=0,v=n(),d=".drag"+(null==v?"":"-"+v),y=ao.select(e(s)).on(u+d,a).on(o+d,l),m=W(s),M=t(h,v);i?(c=i.apply(f,arguments),c=[c.x-M[0],c.y-M[1]]):c=[0,0],p({type:"dragstart"})}}var r=N(n,"drag","dragstart","dragend"),i=null,u=e(b,ao.mouse,t,"mousemove","mouseup"),o=e(G,ao.touch,m,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},ao.rebind(n,r,"on")},ao.touches=function(n,t){return arguments.length<2&&(t=k().touches),t?co(t).map(function(t){var e=J(n,t);return e.identifier=t.identifier,e}):[]};var Uo=1e-6,jo=Uo*Uo,Fo=Math.PI,Ho=2*Fo,Oo=Ho-Uo,Io=Fo/2,Yo=Fo/180,Zo=180/Fo,Vo=Math.SQRT2,Xo=2,$o=4;ao.interpolateZoom=function(n,t){var e,r,i=n[0],u=n[1],o=n[2],a=t[0],l=t[1],c=t[2],f=a-i,s=l-u,h=f*f+s*s;if(jo>h)r=Math.log(c/o)/Vo,e=function(n){return[i+n*f,u+n*s,o*Math.exp(Vo*n*r)]};else{var p=Math.sqrt(h),g=(c*c-o*o+$o*h)/(2*o*Xo*p),v=(c*c-o*o-$o*h)/(2*c*Xo*p),d=Math.log(Math.sqrt(g*g+1)-g),y=Math.log(Math.sqrt(v*v+1)-v);r=(y-d)/Vo,e=function(n){var t=n*r,e=rn(d),a=o/(Xo*p)*(e*un(Vo*t+d)-en(d));return[i+a*f,u+a*s,o*e/rn(Vo*t+d)]}}return e.duration=1e3*r,e},ao.behavior.zoom=function(){function n(n){n.on(L,s).on(Wo+".zoom",p).on("dblclick.zoom",g).on(R,h)}function e(n){return[(n[0]-k.x)/k.k,(n[1]-k.y)/k.k]}function r(n){return[n[0]*k.k+k.x,n[1]*k.k+k.y]}function i(n){k.k=Math.max(A[0],Math.min(A[1],n))}function u(n,t){t=r(t),k.x+=n[0]-t[0],k.y+=n[1]-t[1]}function o(t,e,r,o){t.__chart__={x:k.x,y:k.y,k:k.k},i(Math.pow(2,o)),u(d=e,r),t=ao.select(t),C>0&&(t=t.transition().duration(C)),t.call(n.event)}function a(){b&&b.domain(x.range().map(function(n){return(n-k.x)/k.k}).map(x.invert)),w&&w.domain(_.range().map(function(n){return(n-k.y)/k.k}).map(_.invert))}function l(n){z++||n({type:"zoomstart"})}function c(n){a(),n({type:"zoom",scale:k.k,translate:[k.x,k.y]})}function f(n){--z||(n({type:"zoomend"}),d=null)}function s(){function n(){a=1,u(ao.mouse(i),h),c(o)}function r(){s.on(q,null).on(T,null),p(a),f(o)}var i=this,o=D.of(i,arguments),a=0,s=ao.select(t(i)).on(q,n).on(T,r),h=e(ao.mouse(i)),p=W(i);Il.call(i),l(o)}function h(){function n(){var n=ao.touches(g);return p=k.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=e(n))}),n}function t(){var t=ao.event.target;ao.select(t).on(x,r).on(b,a),_.push(t);for(var e=ao.event.changedTouches,i=0,u=e.length;u>i;++i)d[e[i].identifier]=null;var l=n(),c=Date.now();if(1===l.length){if(500>c-M){var f=l[0];o(g,f,d[f.identifier],Math.floor(Math.log(k.k)/Math.LN2)+1),S()}M=c}else if(l.length>1){var f=l[0],s=l[1],h=f[0]-s[0],p=f[1]-s[1];y=h*h+p*p}}function r(){var n,t,e,r,o=ao.touches(g);Il.call(g);for(var a=0,l=o.length;l>a;++a,r=null)if(e=o[a],r=d[e.identifier]){if(t)break;n=e,t=r}if(r){var f=(f=e[0]-n[0])*f+(f=e[1]-n[1])*f,s=y&&Math.sqrt(f/y);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+r[0])/2,(t[1]+r[1])/2],i(s*p)}M=null,u(n,t),c(v)}function a(){if(ao.event.touches.length){for(var t=ao.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var i in d)return void n()}ao.selectAll(_).on(m,null),w.on(L,s).on(R,h),N(),f(v)}var p,g=this,v=D.of(g,arguments),d={},y=0,m=".zoom-"+ao.event.changedTouches[0].identifier,x="touchmove"+m,b="touchend"+m,_=[],w=ao.select(g),N=W(g);t(),l(v),w.on(L,null).on(R,t)}function p(){var n=D.of(this,arguments);m?clearTimeout(m):(Il.call(this),v=e(d=y||ao.mouse(this)),l(n)),m=setTimeout(function(){m=null,f(n)},50),S(),i(Math.pow(2,.002*Bo())*k.k),u(d,v),c(n)}function g(){var n=ao.mouse(this),t=Math.log(k.k)/Math.LN2;o(this,n,e(n),ao.event.shiftKey?Math.ceil(t)-1:Math.floor(t)+1)}var v,d,y,m,M,x,b,_,w,k={x:0,y:0,k:1},E=[960,500],A=Jo,C=250,z=0,L="mousedown.zoom",q="mousemove.zoom",T="mouseup.zoom",R="touchstart.zoom",D=N(n,"zoomstart","zoom","zoomend");return Wo||(Wo="onwheel"in fo?(Bo=function(){return-ao.event.deltaY*(ao.event.deltaMode?120:1)},"wheel"):"onmousewheel"in fo?(Bo=function(){return ao.event.wheelDelta},"mousewheel"):(Bo=function(){return-ao.event.detail},"MozMousePixelScroll")),n.event=function(n){n.each(function(){var n=D.of(this,arguments),t=k;Hl?ao.select(this).transition().each("start.zoom",function(){k=this.__chart__||{x:0,y:0,k:1},l(n)}).tween("zoom:zoom",function(){var e=E[0],r=E[1],i=d?d[0]:e/2,u=d?d[1]:r/2,o=ao.interpolateZoom([(i-k.x)/k.k,(u-k.y)/k.k,e/k.k],[(i-t.x)/t.k,(u-t.y)/t.k,e/t.k]);return function(t){var r=o(t),a=e/r[2];this.__chart__=k={x:i-r[0]*a,y:u-r[1]*a,k:a},c(n)}}).each("interrupt.zoom",function(){f(n)}).each("end.zoom",function(){f(n)}):(this.__chart__=k,l(n),c(n),f(n))})},n.translate=function(t){return arguments.length?(k={x:+t[0],y:+t[1],k:k.k},a(),n):[k.x,k.y]},n.scale=function(t){return arguments.length?(k={x:k.x,y:k.y,k:null},i(+t),a(),n):k.k},n.scaleExtent=function(t){return arguments.length?(A=null==t?Jo:[+t[0],+t[1]],n):A},n.center=function(t){return arguments.length?(y=t&&[+t[0],+t[1]],n):y},n.size=function(t){return arguments.length?(E=t&&[+t[0],+t[1]],n):E},n.duration=function(t){return arguments.length?(C=+t,n):C},n.x=function(t){return arguments.length?(b=t,x=t.copy(),k={x:0,y:0,k:1},n):b},n.y=function(t){return arguments.length?(w=t,_=t.copy(),k={x:0,y:0,k:1},n):w},ao.rebind(n,D,"on")};var Bo,Wo,Jo=[0,1/0];ao.color=an,an.prototype.toString=function(){return this.rgb()+""},ao.hsl=ln;var Go=ln.prototype=new an;Go.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,this.l/n)},Go.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,n*this.l)},Go.rgb=function(){return cn(this.h,this.s,this.l)},ao.hcl=fn;var Ko=fn.prototype=new an;Ko.brighter=function(n){return new fn(this.h,this.c,Math.min(100,this.l+Qo*(arguments.length?n:1)))},Ko.darker=function(n){return new fn(this.h,this.c,Math.max(0,this.l-Qo*(arguments.length?n:1)))},Ko.rgb=function(){return sn(this.h,this.c,this.l).rgb()},ao.lab=hn;var Qo=18,na=.95047,ta=1,ea=1.08883,ra=hn.prototype=new an;ra.brighter=function(n){return new hn(Math.min(100,this.l+Qo*(arguments.length?n:1)),this.a,this.b)},ra.darker=function(n){return new hn(Math.max(0,this.l-Qo*(arguments.length?n:1)),this.a,this.b)},ra.rgb=function(){return pn(this.l,this.a,this.b)},ao.rgb=mn;var ia=mn.prototype=new an;ia.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,i=30;return t||e||r?(t&&i>t&&(t=i),e&&i>e&&(e=i),r&&i>r&&(r=i),new mn(Math.min(255,t/n),Math.min(255,e/n),Math.min(255,r/n))):new mn(i,i,i)},ia.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new mn(n*this.r,n*this.g,n*this.b)},ia.hsl=function(){return wn(this.r,this.g,this.b)},ia.toString=function(){return"#"+bn(this.r)+bn(this.g)+bn(this.b)};var ua=ao.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});ua.forEach(function(n,t){ua.set(n,Mn(t))}),ao.functor=En,ao.xhr=An(m),ao.dsv=function(n,t){function e(n,e,u){arguments.length<3&&(u=e,e=null);var o=Cn(n,t,null==e?r:i(e),u);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:i(n)):e},o}function r(n){return e.parse(n.responseText)}function i(n){return function(t){return e.parse(t.responseText,n)}}function u(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),l=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var i=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(i(n),e)}:i})},e.parseRows=function(n,t){function e(){if(f>=c)return o;if(i)return i=!1,u;var t=f;if(34===n.charCodeAt(t)){for(var e=t;e++f;){var r=n.charCodeAt(f++),a=1;if(10===r)i=!0;else if(13===r)i=!0,10===n.charCodeAt(f)&&(++f,++a);else if(r!==l)continue;return n.slice(t,f-a)}return n.slice(t)}for(var r,i,u={},o={},a=[],c=n.length,f=0,s=0;(r=e())!==o;){for(var h=[];r!==u&&r!==o;)h.push(r),r=e();t&&null==(h=t(h,s++))||a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new y,i=[];return t.forEach(function(n){for(var t in n)r.has(t)||i.push(r.add(t))}),[i.map(o).join(n)].concat(t.map(function(t){return i.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(u).join("\n")},e},ao.csv=ao.dsv(",","text/csv"),ao.tsv=ao.dsv(" ","text/tab-separated-values");var oa,aa,la,ca,fa=this[x(this,"requestAnimationFrame")]||function(n){setTimeout(n,17)};ao.timer=function(){qn.apply(this,arguments)},ao.timer.flush=function(){Rn(),Dn()},ao.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var sa=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Un);ao.formatPrefix=function(n,t){var e=0;return(n=+n)&&(0>n&&(n*=-1),t&&(n=ao.round(n,Pn(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((e-1)/3)))),sa[8+e/3]};var ha=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,pa=ao.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=ao.round(n,Pn(n,t))).toFixed(Math.max(0,Math.min(20,Pn(n*(1+1e-15),t))))}}),ga=ao.time={},va=Date;Hn.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){da.setUTCDate.apply(this._,arguments)},setDay:function(){da.setUTCDay.apply(this._,arguments)},setFullYear:function(){da.setUTCFullYear.apply(this._,arguments)},setHours:function(){da.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){da.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){da.setUTCMinutes.apply(this._,arguments)},setMonth:function(){da.setUTCMonth.apply(this._,arguments)},setSeconds:function(){da.setUTCSeconds.apply(this._,arguments)},setTime:function(){da.setTime.apply(this._,arguments)}};var da=Date.prototype;ga.year=On(function(n){return n=ga.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),ga.years=ga.year.range,ga.years.utc=ga.year.utc.range,ga.day=On(function(n){var t=new va(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),ga.days=ga.day.range,ga.days.utc=ga.day.utc.range,ga.dayOfYear=function(n){var t=ga.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=ga[n]=On(function(n){return(n=ga.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});ga[n+"s"]=e.range,ga[n+"s"].utc=e.utc.range,ga[n+"OfYear"]=function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)}}),ga.week=ga.sunday,ga.weeks=ga.sunday.range,ga.weeks.utc=ga.sunday.utc.range,ga.weekOfYear=ga.sundayOfYear;var ya={"-":"",_:" ",0:"0"},ma=/^\s*\d+/,Ma=/^%/;ao.locale=function(n){return{numberFormat:jn(n),timeFormat:Yn(n)}};var xa=ao.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"], -shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});ao.format=xa.numberFormat,ao.geo={},ft.prototype={s:0,t:0,add:function(n){st(n,this.t,ba),st(ba.s,this.s,this),this.s?this.t+=ba.t:this.s=ba.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var ba=new ft;ao.geo.stream=function(n,t){n&&_a.hasOwnProperty(n.type)?_a[n.type](n,t):ht(n,t)};var _a={Feature:function(n,t){ht(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,i=e.length;++rn?4*Fo+n:n,Na.lineStart=Na.lineEnd=Na.point=b}};ao.geo.bounds=function(){function n(n,t){M.push(x=[f=n,h=n]),s>t&&(s=t),t>p&&(p=t)}function t(t,e){var r=dt([t*Yo,e*Yo]);if(y){var i=mt(y,r),u=[i[1],-i[0],0],o=mt(u,i);bt(o),o=_t(o);var l=t-g,c=l>0?1:-1,v=o[0]*Zo*c,d=xo(l)>180;if(d^(v>c*g&&c*t>v)){var m=o[1]*Zo;m>p&&(p=m)}else if(v=(v+360)%360-180,d^(v>c*g&&c*t>v)){var m=-o[1]*Zo;s>m&&(s=m)}else s>e&&(s=e),e>p&&(p=e);d?g>t?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t):h>=f?(f>t&&(f=t),t>h&&(h=t)):t>g?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t)}else n(t,e);y=r,g=t}function e(){b.point=t}function r(){x[0]=f,x[1]=h,b.point=n,y=null}function i(n,e){if(y){var r=n-g;m+=xo(r)>180?r+(r>0?360:-360):r}else v=n,d=e;Na.point(n,e),t(n,e)}function u(){Na.lineStart()}function o(){i(v,d),Na.lineEnd(),xo(m)>Uo&&(f=-(h=180)),x[0]=f,x[1]=h,y=null}function a(n,t){return(t-=n)<0?t+360:t}function l(n,t){return n[0]-t[0]}function c(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nka?(f=-(h=180),s=-(p=90)):m>Uo?p=90:-Uo>m&&(s=-90),x[0]=f,x[1]=h}};return function(n){p=h=-(f=s=1/0),M=[],ao.geo.stream(n,b);var t=M.length;if(t){M.sort(l);for(var e,r=1,i=M[0],u=[i];t>r;++r)e=M[r],c(e[0],i)||c(e[1],i)?(a(i[0],e[1])>a(i[0],i[1])&&(i[1]=e[1]),a(e[0],i[1])>a(i[0],i[1])&&(i[0]=e[0])):u.push(i=e);for(var o,e,g=-(1/0),t=u.length-1,r=0,i=u[t];t>=r;i=e,++r)e=u[r],(o=a(i[1],e[0]))>g&&(g=o,f=e[0],h=i[1])}return M=x=null,f===1/0||s===1/0?[[NaN,NaN],[NaN,NaN]]:[[f,s],[h,p]]}}(),ao.geo.centroid=function(n){Ea=Aa=Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,ja);var t=Da,e=Pa,r=Ua,i=t*t+e*e+r*r;return jo>i&&(t=qa,e=Ta,r=Ra,Uo>Aa&&(t=Ca,e=za,r=La),i=t*t+e*e+r*r,jo>i)?[NaN,NaN]:[Math.atan2(e,t)*Zo,tn(r/Math.sqrt(i))*Zo]};var Ea,Aa,Ca,za,La,qa,Ta,Ra,Da,Pa,Ua,ja={sphere:b,point:St,lineStart:Nt,lineEnd:Et,polygonStart:function(){ja.lineStart=At},polygonEnd:function(){ja.lineStart=Nt}},Fa=Rt(zt,jt,Ht,[-Fo,-Fo/2]),Ha=1e9;ao.geo.clipExtent=function(){var n,t,e,r,i,u,o={stream:function(n){return i&&(i.valid=!1),i=u(n),i.valid=!0,i},extent:function(a){return arguments.length?(u=Zt(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),i&&(i.valid=!1,i=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(ao.geo.conicEqualArea=function(){return Vt(Xt)}).raw=Xt,ao.geo.albers=function(){return ao.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ao.geo.albersUsa=function(){function n(n){var u=n[0],o=n[1];return t=null,e(u,o),t||(r(u,o),t)||i(u,o),t}var t,e,r,i,u=ao.geo.albers(),o=ao.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=ao.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),l={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=u.scale(),e=u.translate(),r=(n[0]-e[0])/t,i=(n[1]-e[1])/t;return(i>=.12&&.234>i&&r>=-.425&&-.214>r?o:i>=.166&&.234>i&&r>=-.214&&-.115>r?a:u).invert(n)},n.stream=function(n){var t=u.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,i){t.point(n,i),e.point(n,i),r.point(n,i)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(u.precision(t),o.precision(t),a.precision(t),n):u.precision()},n.scale=function(t){return arguments.length?(u.scale(t),o.scale(.35*t),a.scale(t),n.translate(u.translate())):u.scale()},n.translate=function(t){if(!arguments.length)return u.translate();var c=u.scale(),f=+t[0],s=+t[1];return e=u.translate(t).clipExtent([[f-.455*c,s-.238*c],[f+.455*c,s+.238*c]]).stream(l).point,r=o.translate([f-.307*c,s+.201*c]).clipExtent([[f-.425*c+Uo,s+.12*c+Uo],[f-.214*c-Uo,s+.234*c-Uo]]).stream(l).point,i=a.translate([f-.205*c,s+.212*c]).clipExtent([[f-.214*c+Uo,s+.166*c+Uo],[f-.115*c-Uo,s+.234*c-Uo]]).stream(l).point,n},n.scale(1070)};var Oa,Ia,Ya,Za,Va,Xa,$a={point:b,lineStart:b,lineEnd:b,polygonStart:function(){Ia=0,$a.lineStart=$t},polygonEnd:function(){$a.lineStart=$a.lineEnd=$a.point=b,Oa+=xo(Ia/2)}},Ba={point:Bt,lineStart:b,lineEnd:b,polygonStart:b,polygonEnd:b},Wa={point:Gt,lineStart:Kt,lineEnd:Qt,polygonStart:function(){Wa.lineStart=ne},polygonEnd:function(){Wa.point=Gt,Wa.lineStart=Kt,Wa.lineEnd=Qt}};ao.geo.path=function(){function n(n){return n&&("function"==typeof a&&u.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=i(u)),ao.geo.stream(n,o)),u.result()}function t(){return o=null,n}var e,r,i,u,o,a=4.5;return n.area=function(n){return Oa=0,ao.geo.stream(n,i($a)),Oa},n.centroid=function(n){return Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,i(Wa)),Ua?[Da/Ua,Pa/Ua]:Ra?[qa/Ra,Ta/Ra]:La?[Ca/La,za/La]:[NaN,NaN]},n.bounds=function(n){return Va=Xa=-(Ya=Za=1/0),ao.geo.stream(n,i(Ba)),[[Ya,Za],[Va,Xa]]},n.projection=function(n){return arguments.length?(i=(e=n)?n.stream||re(n):m,t()):e},n.context=function(n){return arguments.length?(u=null==(r=n)?new Wt:new te(n),"function"!=typeof a&&u.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(u.pointRadius(+t),+t),n):a},n.projection(ao.geo.albersUsa()).context(null)},ao.geo.transform=function(n){return{stream:function(t){var e=new ie(t);for(var r in n)e[r]=n[r];return e}}},ie.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},ao.geo.projection=oe,ao.geo.projectionMutator=ae,(ao.geo.equirectangular=function(){return oe(ce)}).raw=ce.invert=ce,ao.geo.rotation=function(n){function t(t){return t=n(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t}return n=se(n[0]%360*Yo,n[1]*Yo,n.length>2?n[2]*Yo:0),t.invert=function(t){return t=n.invert(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t},t},fe.invert=ce,ao.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=se(-n[0]*Yo,-n[1]*Yo,0).invert,i=[];return e(null,null,1,{point:function(n,e){i.push(n=t(n,e)),n[0]*=Zo,n[1]*=Zo}}),{type:"Polygon",coordinates:[i]}}var t,e,r=[0,0],i=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=ve((t=+r)*Yo,i*Yo),n):t},n.precision=function(r){return arguments.length?(e=ve(t*Yo,(i=+r)*Yo),n):i},n.angle(90)},ao.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Yo,i=n[1]*Yo,u=t[1]*Yo,o=Math.sin(r),a=Math.cos(r),l=Math.sin(i),c=Math.cos(i),f=Math.sin(u),s=Math.cos(u);return Math.atan2(Math.sqrt((e=s*o)*e+(e=c*f-l*s*a)*e),l*f+c*s*a)},ao.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return ao.range(Math.ceil(u/d)*d,i,d).map(h).concat(ao.range(Math.ceil(c/y)*y,l,y).map(p)).concat(ao.range(Math.ceil(r/g)*g,e,g).filter(function(n){return xo(n%d)>Uo}).map(f)).concat(ao.range(Math.ceil(a/v)*v,o,v).filter(function(n){return xo(n%y)>Uo}).map(s))}var e,r,i,u,o,a,l,c,f,s,h,p,g=10,v=g,d=90,y=360,m=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(u).concat(p(l).slice(1),h(i).reverse().slice(1),p(c).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(u=+t[0][0],i=+t[1][0],c=+t[0][1],l=+t[1][1],u>i&&(t=u,u=i,i=t),c>l&&(t=c,c=l,l=t),n.precision(m)):[[u,c],[i,l]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(m)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],y=+t[1],n):[d,y]},n.minorStep=function(t){return arguments.length?(g=+t[0],v=+t[1],n):[g,v]},n.precision=function(t){return arguments.length?(m=+t,f=ye(a,o,90),s=me(r,e,m),h=ye(c,l,90),p=me(u,i,m),n):m},n.majorExtent([[-180,-90+Uo],[180,90-Uo]]).minorExtent([[-180,-80-Uo],[180,80+Uo]])},ao.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||i.apply(this,arguments)]}}var t,e,r=Me,i=xe;return n.distance=function(){return ao.geo.distance(t||r.apply(this,arguments),e||i.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(i=t,e="function"==typeof t?null:t,n):i},n.precision=function(){return arguments.length?n:0},n},ao.geo.interpolate=function(n,t){return be(n[0]*Yo,n[1]*Yo,t[0]*Yo,t[1]*Yo)},ao.geo.length=function(n){return Ja=0,ao.geo.stream(n,Ga),Ja};var Ja,Ga={sphere:b,point:b,lineStart:_e,lineEnd:b,polygonStart:b,polygonEnd:b},Ka=we(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(ao.geo.azimuthalEqualArea=function(){return oe(Ka)}).raw=Ka;var Qa=we(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},m);(ao.geo.azimuthalEquidistant=function(){return oe(Qa)}).raw=Qa,(ao.geo.conicConformal=function(){return Vt(Se)}).raw=Se,(ao.geo.conicEquidistant=function(){return Vt(ke)}).raw=ke;var nl=we(function(n){return 1/n},Math.atan);(ao.geo.gnomonic=function(){return oe(nl)}).raw=nl,Ne.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Io]},(ao.geo.mercator=function(){return Ee(Ne)}).raw=Ne;var tl=we(function(){return 1},Math.asin);(ao.geo.orthographic=function(){return oe(tl)}).raw=tl;var el=we(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(ao.geo.stereographic=function(){return oe(el)}).raw=el,Ae.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Io]},(ao.geo.transverseMercator=function(){var n=Ee(Ae),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[n[1],-n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},e([0,0,90])}).raw=Ae,ao.geom={},ao.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,i=En(e),u=En(r),o=n.length,a=[],l=[];for(t=0;o>t;t++)a.push([+i.call(this,n[t],t),+u.call(this,n[t],t),t]);for(a.sort(qe),t=0;o>t;t++)l.push([a[t][0],-a[t][1]]);var c=Le(a),f=Le(l),s=f[0]===c[0],h=f[f.length-1]===c[c.length-1],p=[];for(t=c.length-1;t>=0;--t)p.push(n[a[c[t]][2]]);for(t=+s;t=r&&c.x<=u&&c.y>=i&&c.y<=o?[[r,o],[u,o],[u,i],[r,i]]:[];f.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(u(n,t)/Uo)*Uo,y:Math.round(o(n,t)/Uo)*Uo,i:t}})}var r=Ce,i=ze,u=r,o=i,a=sl;return n?t(n):(t.links=function(n){return ar(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return ar(e(n)).cells.forEach(function(e,r){for(var i,u,o=e.site,a=e.edges.sort(Ve),l=-1,c=a.length,f=a[c-1].edge,s=f.l===o?f.r:f.l;++l=c,h=r>=f,p=h<<1|s;n.leaf=!1,n=n.nodes[p]||(n.nodes[p]=hr()),s?i=c:a=c,h?o=f:l=f,u(n,t,e,r,i,o,a,l)}var f,s,h,p,g,v,d,y,m,M=En(a),x=En(l);if(null!=t)v=t,d=e,y=r,m=i;else if(y=m=-(v=d=1/0),s=[],h=[],g=n.length,o)for(p=0;g>p;++p)f=n[p],f.xy&&(y=f.x),f.y>m&&(m=f.y),s.push(f.x),h.push(f.y);else for(p=0;g>p;++p){var b=+M(f=n[p],p),_=+x(f,p);v>b&&(v=b),d>_&&(d=_),b>y&&(y=b),_>m&&(m=_),s.push(b),h.push(_)}var w=y-v,S=m-d;w>S?m=d+w:y=v+S;var k=hr();if(k.add=function(n){u(k,n,+M(n,++p),+x(n,p),v,d,y,m)},k.visit=function(n){pr(n,k,v,d,y,m)},k.find=function(n){return gr(k,n[0],n[1],v,d,y,m)},p=-1,null==t){for(;++p=0?n.slice(0,t):n,r=t>=0?n.slice(t+1):"in";return e=vl.get(e)||gl,r=dl.get(r)||m,br(r(e.apply(null,lo.call(arguments,1))))},ao.interpolateHcl=Rr,ao.interpolateHsl=Dr,ao.interpolateLab=Pr,ao.interpolateRound=Ur,ao.transform=function(n){var t=fo.createElementNS(ao.ns.prefix.svg,"g");return(ao.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new jr(e?e.matrix:yl)})(n)},jr.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var yl={a:1,b:0,c:0,d:1,e:0,f:0};ao.interpolateTransform=$r,ao.layout={},ao.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++ea*a/y){if(v>l){var c=t.charge/l;n.px-=u*c,n.py-=o*c}return!0}if(t.point&&l&&v>l){var c=t.pointCharge/l;n.px-=u*c,n.py-=o*c}}return!t.charge}}function t(n){n.px=ao.event.x,n.py=ao.event.y,l.resume()}var e,r,i,u,o,a,l={},c=ao.dispatch("start","tick","end"),f=[1,1],s=.9,h=ml,p=Ml,g=-30,v=xl,d=.1,y=.64,M=[],x=[];return l.tick=function(){if((i*=.99)<.005)return e=null,c.end({type:"end",alpha:i=0}),!0;var t,r,l,h,p,v,y,m,b,_=M.length,w=x.length;for(r=0;w>r;++r)l=x[r],h=l.source,p=l.target,m=p.x-h.x,b=p.y-h.y,(v=m*m+b*b)&&(v=i*o[r]*((v=Math.sqrt(v))-u[r])/v,m*=v,b*=v,p.x-=m*(y=h.weight+p.weight?h.weight/(h.weight+p.weight):.5),p.y-=b*y,h.x+=m*(y=1-y),h.y+=b*y);if((y=i*d)&&(m=f[0]/2,b=f[1]/2,r=-1,y))for(;++r<_;)l=M[r],l.x+=(m-l.x)*y,l.y+=(b-l.y)*y;if(g)for(ri(t=ao.geom.quadtree(M),i,a),r=-1;++r<_;)(l=M[r]).fixed||t.visit(n(l));for(r=-1;++r<_;)l=M[r],l.fixed?(l.x=l.px,l.y=l.py):(l.x-=(l.px-(l.px=l.x))*s,l.y-=(l.py-(l.py=l.y))*s);c.tick({type:"tick",alpha:i})},l.nodes=function(n){return arguments.length?(M=n,l):M},l.links=function(n){return arguments.length?(x=n,l):x},l.size=function(n){return arguments.length?(f=n,l):f},l.linkDistance=function(n){return arguments.length?(h="function"==typeof n?n:+n,l):h},l.distance=l.linkDistance,l.linkStrength=function(n){return arguments.length?(p="function"==typeof n?n:+n,l):p},l.friction=function(n){return arguments.length?(s=+n,l):s},l.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,l):g},l.chargeDistance=function(n){return arguments.length?(v=n*n,l):Math.sqrt(v)},l.gravity=function(n){return arguments.length?(d=+n,l):d},l.theta=function(n){return arguments.length?(y=n*n,l):Math.sqrt(y)},l.alpha=function(n){return arguments.length?(n=+n,i?n>0?i=n:(e.c=null,e.t=NaN,e=null,c.end({type:"end",alpha:i=0})):n>0&&(c.start({type:"start",alpha:i=n}),e=qn(l.tick)),l):i},l.start=function(){function n(n,r){if(!e){for(e=new Array(i),l=0;i>l;++l)e[l]=[];for(l=0;c>l;++l){var u=x[l];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var o,a=e[t],l=-1,f=a.length;++lt;++t)(r=M[t]).index=t,r.weight=0;for(t=0;c>t;++t)r=x[t],"number"==typeof r.source&&(r.source=M[r.source]),"number"==typeof r.target&&(r.target=M[r.target]),++r.source.weight,++r.target.weight;for(t=0;i>t;++t)r=M[t],isNaN(r.x)&&(r.x=n("x",s)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof h)for(t=0;c>t;++t)u[t]=+h.call(this,x[t],t);else for(t=0;c>t;++t)u[t]=h;if(o=[],"function"==typeof p)for(t=0;c>t;++t)o[t]=+p.call(this,x[t],t);else for(t=0;c>t;++t)o[t]=p;if(a=[],"function"==typeof g)for(t=0;i>t;++t)a[t]=+g.call(this,M[t],t);else for(t=0;i>t;++t)a[t]=g;return l.resume()},l.resume=function(){return l.alpha(.1)},l.stop=function(){return l.alpha(0)},l.drag=function(){return r||(r=ao.behavior.drag().origin(m).on("dragstart.force",Qr).on("drag.force",t).on("dragend.force",ni)),arguments.length?void this.on("mouseover.force",ti).on("mouseout.force",ei).call(r):r},ao.rebind(l,c,"on")};var ml=20,Ml=1,xl=1/0;ao.layout.hierarchy=function(){function n(i){var u,o=[i],a=[];for(i.depth=0;null!=(u=o.pop());)if(a.push(u),(c=e.call(n,u,u.depth))&&(l=c.length)){for(var l,c,f;--l>=0;)o.push(f=c[l]),f.parent=u,f.depth=u.depth+1;r&&(u.value=0),u.children=c}else r&&(u.value=+r.call(n,u,u.depth)||0),delete u.children;return oi(i,function(n){var e,i;t&&(e=n.children)&&e.sort(t),r&&(i=n.parent)&&(i.value+=n.value)}),a}var t=ci,e=ai,r=li;return n.sort=function(e){return arguments.length?(t=e,n):t},n.children=function(t){return arguments.length?(e=t,n):e},n.value=function(t){return arguments.length?(r=t,n):r},n.revalue=function(t){return r&&(ui(t,function(n){n.children&&(n.value=0)}),oi(t,function(t){var e;t.children||(t.value=+r.call(n,t,t.depth)||0),(e=t.parent)&&(e.value+=t.value)})),t},n},ao.layout.partition=function(){function n(t,e,r,i){var u=t.children;if(t.x=e,t.y=t.depth*i,t.dx=r,t.dy=i,u&&(o=u.length)){var o,a,l,c=-1;for(r=t.value?r/t.value:0;++cs?-1:1),g=ao.sum(c),v=g?(s-l*p)/g:0,d=ao.range(l),y=[];return null!=e&&d.sort(e===bl?function(n,t){return c[t]-c[n]}:function(n,t){return e(o[n],o[t])}),d.forEach(function(n){y[n]={data:o[n],value:a=c[n],startAngle:f,endAngle:f+=a*v+p,padAngle:h}}),y}var t=Number,e=bl,r=0,i=Ho,u=0;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(i=t,n):i},n.padAngle=function(t){return arguments.length?(u=t,n):u},n};var bl={};ao.layout.stack=function(){function n(a,l){if(!(h=a.length))return a;var c=a.map(function(e,r){return t.call(n,e,r)}),f=c.map(function(t){return t.map(function(t,e){return[u.call(n,t,e),o.call(n,t,e)]})}),s=e.call(n,f,l);c=ao.permute(c,s),f=ao.permute(f,s);var h,p,g,v,d=r.call(n,f,l),y=c[0].length;for(g=0;y>g;++g)for(i.call(n,c[0][g],v=d[g],f[0][g][1]),p=1;h>p;++p)i.call(n,c[p][g],v+=f[p-1][g][1],f[p][g][1]);return a}var t=m,e=gi,r=vi,i=pi,u=si,o=hi;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:_l.get(t)||gi,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:wl.get(t)||vi,n):r},n.x=function(t){return arguments.length?(u=t,n):u},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(i=t,n):i},n};var _l=ao.map({"inside-out":function(n){var t,e,r=n.length,i=n.map(di),u=n.map(yi),o=ao.range(r).sort(function(n,t){return i[n]-i[t]}),a=0,l=0,c=[],f=[];for(t=0;r>t;++t)e=o[t],l>a?(a+=u[e],c.push(e)):(l+=u[e],f.push(e));return f.reverse().concat(c)},reverse:function(n){return ao.range(n.length).reverse()},"default":gi}),wl=ao.map({silhouette:function(n){var t,e,r,i=n.length,u=n[0].length,o=[],a=0,l=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;u>e;++e)l[e]=(a-o[e])/2;return l},wiggle:function(n){var t,e,r,i,u,o,a,l,c,f=n.length,s=n[0],h=s.length,p=[];for(p[0]=l=c=0,e=1;h>e;++e){for(t=0,i=0;f>t;++t)i+=n[t][e][1];for(t=0,u=0,a=s[e][0]-s[e-1][0];f>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;u+=o*n[t][e][1]}p[e]=l-=i?u/i*a:0,c>l&&(c=l)}for(e=0;h>e;++e)p[e]-=c;return p},expand:function(n){var t,e,r,i=n.length,u=n[0].length,o=1/i,a=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];if(r)for(t=0;i>t;t++)n[t][e][1]/=r;else for(t=0;i>t;t++)n[t][e][1]=o}for(e=0;u>e;++e)a[e]=0;return a},zero:vi});ao.layout.histogram=function(){function n(n,u){for(var o,a,l=[],c=n.map(e,this),f=r.call(this,c,u),s=i.call(this,f,c,u),u=-1,h=c.length,p=s.length-1,g=t?1:1/h;++u0)for(u=-1;++u=f[0]&&a<=f[1]&&(o=l[ao.bisect(s,a,1,p)-1],o.y+=g,o.push(n[u]));return l}var t=!0,e=Number,r=bi,i=Mi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=En(t),n):r},n.bins=function(t){return arguments.length?(i="number"==typeof t?function(n){return xi(n,t)}:En(t),n):i},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},ao.layout.pack=function(){function n(n,u){var o=e.call(this,n,u),a=o[0],l=i[0],c=i[1],f=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,oi(a,function(n){n.r=+f(n.value)}),oi(a,Ni),r){var s=r*(t?1:Math.max(2*a.r/l,2*a.r/c))/2;oi(a,function(n){n.r+=s}),oi(a,Ni),oi(a,function(n){n.r-=s})}return Ci(a,l/2,c/2,t?1:1/Math.max(2*a.r/l,2*a.r/c)),o}var t,e=ao.layout.hierarchy().sort(_i),r=0,i=[1,1];return n.size=function(t){return arguments.length?(i=t,n):i},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},ii(n,e)},ao.layout.tree=function(){function n(n,i){var f=o.call(this,n,i),s=f[0],h=t(s);if(oi(h,e),h.parent.m=-h.z,ui(h,r),c)ui(s,u);else{var p=s,g=s,v=s;ui(s,function(n){n.xg.x&&(g=n),n.depth>v.depth&&(v=n)});var d=a(p,g)/2-p.x,y=l[0]/(g.x+a(g,p)/2+d),m=l[1]/(v.depth||1);ui(s,function(n){n.x=(n.x+d)*y,n.y=n.depth*m})}return f}function t(n){for(var t,e={A:null,children:[n]},r=[e];null!=(t=r.pop());)for(var i,u=t.children,o=0,a=u.length;a>o;++o)r.push((u[o]=i={_:u[o],parent:t,children:(i=u[o].children)&&i.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:o}).a=i);return e.children[0]}function e(n){var t=n.children,e=n.parent.children,r=n.i?e[n.i-1]:null;if(t.length){Di(n);var u=(t[0].z+t[t.length-1].z)/2;r?(n.z=r.z+a(n._,r._),n.m=n.z-u):n.z=u}else r&&(n.z=r.z+a(n._,r._));n.parent.A=i(n,r,n.parent.A||e[0])}function r(n){n._.x=n.z+n.parent.m,n.m+=n.parent.m}function i(n,t,e){if(t){for(var r,i=n,u=n,o=t,l=i.parent.children[0],c=i.m,f=u.m,s=o.m,h=l.m;o=Ti(o),i=qi(i),o&&i;)l=qi(l),u=Ti(u),u.a=n,r=o.z+s-i.z-c+a(o._,i._),r>0&&(Ri(Pi(o,n,e),n,r),c+=r,f+=r),s+=o.m,c+=i.m,h+=l.m,f+=u.m;o&&!Ti(u)&&(u.t=o,u.m+=s-f),i&&!qi(l)&&(l.t=i,l.m+=c-h,e=n)}return e}function u(n){n.x*=l[0],n.y=n.depth*l[1]}var o=ao.layout.hierarchy().sort(null).value(null),a=Li,l=[1,1],c=null;return n.separation=function(t){return arguments.length?(a=t,n):a},n.size=function(t){return arguments.length?(c=null==(l=t)?u:null,n):c?null:l},n.nodeSize=function(t){return arguments.length?(c=null==(l=t)?null:u,n):c?l:null},ii(n,o)},ao.layout.cluster=function(){function n(n,u){var o,a=t.call(this,n,u),l=a[0],c=0;oi(l,function(n){var t=n.children;t&&t.length?(n.x=ji(t),n.y=Ui(t)):(n.x=o?c+=e(n,o):0,n.y=0,o=n)});var f=Fi(l),s=Hi(l),h=f.x-e(f,s)/2,p=s.x+e(s,f)/2;return oi(l,i?function(n){n.x=(n.x-l.x)*r[0],n.y=(l.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(p-h)*r[0],n.y=(1-(l.y?n.y/l.y:1))*r[1]}),a}var t=ao.layout.hierarchy().sort(null).value(null),e=Li,r=[1,1],i=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(i=null==(r=t),n):i?null:r},n.nodeSize=function(t){return arguments.length?(i=null!=(r=t),n):i?r:null},ii(n,t)},ao.layout.treemap=function(){function n(n,t){for(var e,r,i=-1,u=n.length;++it?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var u=e.children;if(u&&u.length){var o,a,l,c=s(e),f=[],h=u.slice(),g=1/0,v="slice"===p?c.dx:"dice"===p?c.dy:"slice-dice"===p?1&e.depth?c.dy:c.dx:Math.min(c.dx,c.dy);for(n(h,c.dx*c.dy/e.value),f.area=0;(l=h.length)>0;)f.push(o=h[l-1]),f.area+=o.area,"squarify"!==p||(a=r(f,v))<=g?(h.pop(),g=a):(f.area-=f.pop().area,i(f,v,c,!1),v=Math.min(c.dx,c.dy),f.length=f.area=0,g=1/0);f.length&&(i(f,v,c,!0),f.length=f.area=0),u.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var u,o=s(t),a=r.slice(),l=[];for(n(a,o.dx*o.dy/t.value),l.area=0;u=a.pop();)l.push(u),l.area+=u.area,null!=u.z&&(i(l,u.z?o.dx:o.dy,o,!a.length),l.length=l.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,i=0,u=1/0,o=-1,a=n.length;++oe&&(u=e),e>i&&(i=e));return r*=r,t*=t,r?Math.max(t*i*g/r,r/(t*u*g)):1/0}function i(n,t,e,r){var i,u=-1,o=n.length,a=e.x,c=e.y,f=t?l(n.area/t):0; -if(t==e.dx){for((r||f>e.dy)&&(f=e.dy);++ue.dx)&&(f=e.dx);++ue&&(t=1),1>e&&(n=0),function(){var e,r,i;do e=2*Math.random()-1,r=2*Math.random()-1,i=e*e+r*r;while(!i||i>1);return n+t*e*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(){var n=ao.random.normal.apply(ao,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=ao.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},ao.scale={};var Sl={floor:m,ceil:m};ao.scale.linear=function(){return Wi([0,1],[0,1],Mr,!1)};var kl={s:1,g:1,p:1,r:1,e:1};ao.scale.log=function(){return ru(ao.scale.linear().domain([0,1]),10,!0,[1,10])};var Nl=ao.format(".0e"),El={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ao.scale.pow=function(){return iu(ao.scale.linear(),1,[0,1])},ao.scale.sqrt=function(){return ao.scale.pow().exponent(.5)},ao.scale.ordinal=function(){return ou([],{t:"range",a:[[]]})},ao.scale.category10=function(){return ao.scale.ordinal().range(Al)},ao.scale.category20=function(){return ao.scale.ordinal().range(Cl)},ao.scale.category20b=function(){return ao.scale.ordinal().range(zl)},ao.scale.category20c=function(){return ao.scale.ordinal().range(Ll)};var Al=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(xn),Cl=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(xn),zl=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(xn),Ll=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(xn);ao.scale.quantile=function(){return au([],[])},ao.scale.quantize=function(){return lu(0,1,[0,1])},ao.scale.threshold=function(){return cu([.5],[0,1])},ao.scale.identity=function(){return fu([0,1])},ao.svg={},ao.svg.arc=function(){function n(){var n=Math.max(0,+e.apply(this,arguments)),c=Math.max(0,+r.apply(this,arguments)),f=o.apply(this,arguments)-Io,s=a.apply(this,arguments)-Io,h=Math.abs(s-f),p=f>s?0:1;if(n>c&&(g=c,c=n,n=g),h>=Oo)return t(c,p)+(n?t(n,1-p):"")+"Z";var g,v,d,y,m,M,x,b,_,w,S,k,N=0,E=0,A=[];if((y=(+l.apply(this,arguments)||0)/2)&&(d=u===ql?Math.sqrt(n*n+c*c):+u.apply(this,arguments),p||(E*=-1),c&&(E=tn(d/c*Math.sin(y))),n&&(N=tn(d/n*Math.sin(y)))),c){m=c*Math.cos(f+E),M=c*Math.sin(f+E),x=c*Math.cos(s-E),b=c*Math.sin(s-E);var C=Math.abs(s-f-2*E)<=Fo?0:1;if(E&&yu(m,M,x,b)===p^C){var z=(f+s)/2;m=c*Math.cos(z),M=c*Math.sin(z),x=b=null}}else m=M=0;if(n){_=n*Math.cos(s-N),w=n*Math.sin(s-N),S=n*Math.cos(f+N),k=n*Math.sin(f+N);var L=Math.abs(f-s+2*N)<=Fo?0:1;if(N&&yu(_,w,S,k)===1-p^L){var q=(f+s)/2;_=n*Math.cos(q),w=n*Math.sin(q),S=k=null}}else _=w=0;if(h>Uo&&(g=Math.min(Math.abs(c-n)/2,+i.apply(this,arguments)))>.001){v=c>n^p?0:1;var T=g,R=g;if(Fo>h){var D=null==S?[_,w]:null==x?[m,M]:Re([m,M],[S,k],[x,b],[_,w]),P=m-D[0],U=M-D[1],j=x-D[0],F=b-D[1],H=1/Math.sin(Math.acos((P*j+U*F)/(Math.sqrt(P*P+U*U)*Math.sqrt(j*j+F*F)))/2),O=Math.sqrt(D[0]*D[0]+D[1]*D[1]);R=Math.min(g,(n-O)/(H-1)),T=Math.min(g,(c-O)/(H+1))}if(null!=x){var I=mu(null==S?[_,w]:[S,k],[m,M],c,T,p),Y=mu([x,b],[_,w],c,T,p);g===T?A.push("M",I[0],"A",T,",",T," 0 0,",v," ",I[1],"A",c,",",c," 0 ",1-p^yu(I[1][0],I[1][1],Y[1][0],Y[1][1]),",",p," ",Y[1],"A",T,",",T," 0 0,",v," ",Y[0]):A.push("M",I[0],"A",T,",",T," 0 1,",v," ",Y[0])}else A.push("M",m,",",M);if(null!=S){var Z=mu([m,M],[S,k],n,-R,p),V=mu([_,w],null==x?[m,M]:[x,b],n,-R,p);g===R?A.push("L",V[0],"A",R,",",R," 0 0,",v," ",V[1],"A",n,",",n," 0 ",p^yu(V[1][0],V[1][1],Z[1][0],Z[1][1]),",",1-p," ",Z[1],"A",R,",",R," 0 0,",v," ",Z[0]):A.push("L",V[0],"A",R,",",R," 0 0,",v," ",Z[0])}else A.push("L",_,",",w)}else A.push("M",m,",",M),null!=x&&A.push("A",c,",",c," 0 ",C,",",p," ",x,",",b),A.push("L",_,",",w),null!=S&&A.push("A",n,",",n," 0 ",L,",",1-p," ",S,",",k);return A.push("Z"),A.join("")}function t(n,t){return"M0,"+n+"A"+n+","+n+" 0 1,"+t+" 0,"+-n+"A"+n+","+n+" 0 1,"+t+" 0,"+n}var e=hu,r=pu,i=su,u=ql,o=gu,a=vu,l=du;return n.innerRadius=function(t){return arguments.length?(e=En(t),n):e},n.outerRadius=function(t){return arguments.length?(r=En(t),n):r},n.cornerRadius=function(t){return arguments.length?(i=En(t),n):i},n.padRadius=function(t){return arguments.length?(u=t==ql?ql:En(t),n):u},n.startAngle=function(t){return arguments.length?(o=En(t),n):o},n.endAngle=function(t){return arguments.length?(a=En(t),n):a},n.padAngle=function(t){return arguments.length?(l=En(t),n):l},n.centroid=function(){var n=(+e.apply(this,arguments)+ +r.apply(this,arguments))/2,t=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-Io;return[Math.cos(t)*n,Math.sin(t)*n]},n};var ql="auto";ao.svg.line=function(){return Mu(m)};var Tl=ao.map({linear:xu,"linear-closed":bu,step:_u,"step-before":wu,"step-after":Su,basis:zu,"basis-open":Lu,"basis-closed":qu,bundle:Tu,cardinal:Eu,"cardinal-open":ku,"cardinal-closed":Nu,monotone:Fu});Tl.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Rl=[0,2/3,1/3,0],Dl=[0,1/3,2/3,0],Pl=[0,1/6,2/3,1/6];ao.svg.line.radial=function(){var n=Mu(Hu);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},wu.reverse=Su,Su.reverse=wu,ao.svg.area=function(){return Ou(m)},ao.svg.area.radial=function(){var n=Ou(Hu);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},ao.svg.chord=function(){function n(n,a){var l=t(this,u,n,a),c=t(this,o,n,a);return"M"+l.p0+r(l.r,l.p1,l.a1-l.a0)+(e(l,c)?i(l.r,l.p1,l.r,l.p0):i(l.r,l.p1,c.r,c.p0)+r(c.r,c.p1,c.a1-c.a0)+i(c.r,c.p1,l.r,l.p0))+"Z"}function t(n,t,e,r){var i=t.call(n,e,r),u=a.call(n,i,r),o=l.call(n,i,r)-Io,f=c.call(n,i,r)-Io;return{r:u,a0:o,a1:f,p0:[u*Math.cos(o),u*Math.sin(o)],p1:[u*Math.cos(f),u*Math.sin(f)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Fo)+",1 "+t}function i(n,t,e,r){return"Q 0,0 "+r}var u=Me,o=xe,a=Iu,l=gu,c=vu;return n.radius=function(t){return arguments.length?(a=En(t),n):a},n.source=function(t){return arguments.length?(u=En(t),n):u},n.target=function(t){return arguments.length?(o=En(t),n):o},n.startAngle=function(t){return arguments.length?(l=En(t),n):l},n.endAngle=function(t){return arguments.length?(c=En(t),n):c},n},ao.svg.diagonal=function(){function n(n,i){var u=t.call(this,n,i),o=e.call(this,n,i),a=(u.y+o.y)/2,l=[u,{x:u.x,y:a},{x:o.x,y:a},o];return l=l.map(r),"M"+l[0]+"C"+l[1]+" "+l[2]+" "+l[3]}var t=Me,e=xe,r=Yu;return n.source=function(e){return arguments.length?(t=En(e),n):t},n.target=function(t){return arguments.length?(e=En(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},ao.svg.diagonal.radial=function(){var n=ao.svg.diagonal(),t=Yu,e=n.projection;return n.projection=function(n){return arguments.length?e(Zu(t=n)):t},n},ao.svg.symbol=function(){function n(n,r){return(Ul.get(t.call(this,n,r))||$u)(e.call(this,n,r))}var t=Xu,e=Vu;return n.type=function(e){return arguments.length?(t=En(e),n):t},n.size=function(t){return arguments.length?(e=En(t),n):e},n};var Ul=ao.map({circle:$u,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Fl)),e=t*Fl;return"M0,"+-t+"L"+e+",0 0,"+t+" "+-e+",0Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});ao.svg.symbolTypes=Ul.keys();var jl=Math.sqrt(3),Fl=Math.tan(30*Yo);Co.transition=function(n){for(var t,e,r=Hl||++Zl,i=Ku(n),u=[],o=Ol||{time:Date.now(),ease:Nr,delay:0,duration:250},a=-1,l=this.length;++au;u++){i.push(t=[]);for(var e=this[u],a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return Wu(i,this.namespace,this.id)},Yl.tween=function(n,t){var e=this.id,r=this.namespace;return arguments.length<2?this.node()[r][e].tween.get(n):Y(this,null==t?function(t){t[r][e].tween.remove(n)}:function(i){i[r][e].tween.set(n,t)})},Yl.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function i(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function u(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?$r:Mr,a=ao.ns.qualify(n);return Ju(this,"attr."+n,t,a.local?u:i)},Yl.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(i));return r&&function(n){this.setAttribute(i,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(i.space,i.local));return r&&function(n){this.setAttributeNS(i.space,i.local,r(n))}}var i=ao.ns.qualify(n);return this.tween("attr."+n,i.local?r:e)},Yl.style=function(n,e,r){function i(){this.style.removeProperty(n)}function u(e){return null==e?i:(e+="",function(){var i,u=t(this).getComputedStyle(this,null).getPropertyValue(n);return u!==e&&(i=Mr(u,e),function(t){this.style.setProperty(n,i(t),r)})})}var o=arguments.length;if(3>o){if("string"!=typeof n){2>o&&(e="");for(r in n)this.style(r,n[r],e);return this}r=""}return Ju(this,"style."+n,e,u)},Yl.styleTween=function(n,e,r){function i(i,u){var o=e.call(this,i,u,t(this).getComputedStyle(this,null).getPropertyValue(n));return o&&function(t){this.style.setProperty(n,o(t),r)}}return arguments.length<3&&(r=""),this.tween("style."+n,i)},Yl.text=function(n){return Ju(this,"text",n,Gu)},Yl.remove=function(){var n=this.namespace;return this.each("end.transition",function(){var t;this[n].count<2&&(t=this.parentNode)&&t.removeChild(this)})},Yl.ease=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].ease:("function"!=typeof n&&(n=ao.ease.apply(ao,arguments)),Y(this,function(r){r[e][t].ease=n}))},Yl.delay=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].delay:Y(this,"function"==typeof n?function(r,i,u){r[e][t].delay=+n.call(r,r.__data__,i,u)}:(n=+n,function(r){r[e][t].delay=n}))},Yl.duration=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].duration:Y(this,"function"==typeof n?function(r,i,u){r[e][t].duration=Math.max(1,n.call(r,r.__data__,i,u))}:(n=Math.max(1,n),function(r){r[e][t].duration=n}))},Yl.each=function(n,t){var e=this.id,r=this.namespace;if(arguments.length<2){var i=Ol,u=Hl;try{Hl=e,Y(this,function(t,i,u){Ol=t[r][e],n.call(t,t.__data__,i,u)})}finally{Ol=i,Hl=u}}else Y(this,function(i){var u=i[r][e];(u.event||(u.event=ao.dispatch("start","end","interrupt"))).on(n,t)});return this},Yl.transition=function(){for(var n,t,e,r,i=this.id,u=++Zl,o=this.namespace,a=[],l=0,c=this.length;c>l;l++){a.push(n=[]);for(var t=this[l],f=0,s=t.length;s>f;f++)(e=t[f])&&(r=e[o][i],Qu(e,f,o,u,{time:r.time,ease:r.ease,delay:r.delay+r.duration,duration:r.duration})),n.push(e)}return Wu(a,o,u)},ao.svg.axis=function(){function n(n){n.each(function(){var n,c=ao.select(this),f=this.__chart__||e,s=this.__chart__=e.copy(),h=null==l?s.ticks?s.ticks.apply(s,a):s.domain():l,p=null==t?s.tickFormat?s.tickFormat.apply(s,a):m:t,g=c.selectAll(".tick").data(h,s),v=g.enter().insert("g",".domain").attr("class","tick").style("opacity",Uo),d=ao.transition(g.exit()).style("opacity",Uo).remove(),y=ao.transition(g.order()).style("opacity",1),M=Math.max(i,0)+o,x=Zi(s),b=c.selectAll(".domain").data([0]),_=(b.enter().append("path").attr("class","domain"),ao.transition(b));v.append("line"),v.append("text");var w,S,k,N,E=v.select("line"),A=y.select("line"),C=g.select("text").text(p),z=v.select("text"),L=y.select("text"),q="top"===r||"left"===r?-1:1;if("bottom"===r||"top"===r?(n=no,w="x",k="y",S="x2",N="y2",C.attr("dy",0>q?"0em":".71em").style("text-anchor","middle"),_.attr("d","M"+x[0]+","+q*u+"V0H"+x[1]+"V"+q*u)):(n=to,w="y",k="x",S="y2",N="x2",C.attr("dy",".32em").style("text-anchor",0>q?"end":"start"),_.attr("d","M"+q*u+","+x[0]+"H0V"+x[1]+"H"+q*u)),E.attr(N,q*i),z.attr(k,q*M),A.attr(S,0).attr(N,q*i),L.attr(w,0).attr(k,q*M),s.rangeBand){var T=s,R=T.rangeBand()/2;f=s=function(n){return T(n)+R}}else f.rangeBand?f=s:d.call(n,s,f);v.call(n,f,s),y.call(n,s,s)})}var t,e=ao.scale.linear(),r=Vl,i=6,u=6,o=3,a=[10],l=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Xl?t+"":Vl,n):r},n.ticks=function(){return arguments.length?(a=co(arguments),n):a},n.tickValues=function(t){return arguments.length?(l=t,n):l},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(i=+t,u=+arguments[e-1],n):i},n.innerTickSize=function(t){return arguments.length?(i=+t,n):i},n.outerTickSize=function(t){return arguments.length?(u=+t,n):u},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Vl="bottom",Xl={top:1,right:1,bottom:1,left:1};ao.svg.brush=function(){function n(t){t.each(function(){var t=ao.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=t.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),t.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=t.selectAll(".resize").data(v,m);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return $l[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,s=ao.transition(t),h=ao.transition(o);c&&(l=Zi(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),r(s)),f&&(l=Zi(f),h.attr("y",l[0]).attr("height",l[1]-l[0]),i(s)),e(s)})}function e(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+s[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function r(n){n.select(".extent").attr("x",s[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",s[1]-s[0])}function i(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function u(){function u(){32==ao.event.keyCode&&(C||(M=null,L[0]-=s[1],L[1]-=h[1],C=2),S())}function v(){32==ao.event.keyCode&&2==C&&(L[0]+=s[1],L[1]+=h[1],C=0,S())}function d(){var n=ao.mouse(b),t=!1;x&&(n[0]+=x[0],n[1]+=x[1]),C||(ao.event.altKey?(M||(M=[(s[0]+s[1])/2,(h[0]+h[1])/2]),L[0]=s[+(n[0]f?(i=r,r=f):i=f),v[0]!=r||v[1]!=i?(e?a=null:o=null,v[0]=r,v[1]=i,!0):void 0}function m(){d(),k.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ao.select("body").style("cursor",null),q.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),z(),w({type:"brushend"})}var M,x,b=this,_=ao.select(ao.event.target),w=l.of(b,arguments),k=ao.select(b),N=_.datum(),E=!/^(n|s)$/.test(N)&&c,A=!/^(e|w)$/.test(N)&&f,C=_.classed("extent"),z=W(b),L=ao.mouse(b),q=ao.select(t(b)).on("keydown.brush",u).on("keyup.brush",v);if(ao.event.changedTouches?q.on("touchmove.brush",d).on("touchend.brush",m):q.on("mousemove.brush",d).on("mouseup.brush",m),k.interrupt().selectAll("*").interrupt(),C)L[0]=s[0]-L[0],L[1]=h[0]-L[1];else if(N){var T=+/w$/.test(N),R=+/^n/.test(N);x=[s[1-T]-L[0],h[1-R]-L[1]],L[0]=s[T],L[1]=h[R]}else ao.event.altKey&&(M=L.slice());k.style("pointer-events","none").selectAll(".resize").style("display",null),ao.select("body").style("cursor",_.style("cursor")),w({type:"brushstart"}),d()}var o,a,l=N(n,"brushstart","brush","brushend"),c=null,f=null,s=[0,0],h=[0,0],p=!0,g=!0,v=Bl[0];return n.event=function(n){n.each(function(){var n=l.of(this,arguments),t={x:s,y:h,i:o,j:a},e=this.__chart__||t;this.__chart__=t,Hl?ao.select(this).transition().each("start.brush",function(){o=e.i,a=e.j,s=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=xr(s,t.x),r=xr(h,t.y);return o=a=null,function(i){s=t.x=e(i),h=t.y=r(i),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){o=t.i,a=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,v=Bl[!c<<1|!f],n):c},n.y=function(t){return arguments.length?(f=t,v=Bl[!c<<1|!f],n):f},n.clamp=function(t){return arguments.length?(c&&f?(p=!!t[0],g=!!t[1]):c?p=!!t:f&&(g=!!t),n):c&&f?[p,g]:c?p:f?g:null},n.extent=function(t){var e,r,i,u,l;return arguments.length?(c&&(e=t[0],r=t[1],f&&(e=e[0],r=r[0]),o=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(l=e,e=r,r=l),e==s[0]&&r==s[1]||(s=[e,r])),f&&(i=t[0],u=t[1],c&&(i=i[1],u=u[1]),a=[i,u],f.invert&&(i=f(i),u=f(u)),i>u&&(l=i,i=u,u=l),i==h[0]&&u==h[1]||(h=[i,u])),n):(c&&(o?(e=o[0],r=o[1]):(e=s[0],r=s[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(l=e,e=r,r=l))),f&&(a?(i=a[0],u=a[1]):(i=h[0],u=h[1],f.invert&&(i=f.invert(i),u=f.invert(u)),i>u&&(l=i,i=u,u=l))),c&&f?[[e,i],[r,u]]:c?[e,r]:f&&[i,u])},n.clear=function(){return n.empty()||(s=[0,0],h=[0,0],o=a=null),n},n.empty=function(){return!!c&&s[0]==s[1]||!!f&&h[0]==h[1]},ao.rebind(n,l,"on")};var $l={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Bl=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Wl=ga.format=xa.timeFormat,Jl=Wl.utc,Gl=Jl("%Y-%m-%dT%H:%M:%S.%LZ");Wl.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?eo:Gl,eo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},eo.toString=Gl.toString,ga.second=On(function(n){return new va(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ga.seconds=ga.second.range,ga.seconds.utc=ga.second.utc.range,ga.minute=On(function(n){return new va(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ga.minutes=ga.minute.range,ga.minutes.utc=ga.minute.utc.range,ga.hour=On(function(n){var t=n.getTimezoneOffset()/60;return new va(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ga.hours=ga.hour.range,ga.hours.utc=ga.hour.utc.range,ga.month=On(function(n){return n=ga.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ga.months=ga.month.range,ga.months.utc=ga.month.utc.range;var Kl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Ql=[[ga.second,1],[ga.second,5],[ga.second,15],[ga.second,30],[ga.minute,1],[ga.minute,5],[ga.minute,15],[ga.minute,30],[ga.hour,1],[ga.hour,3],[ga.hour,6],[ga.hour,12],[ga.day,1],[ga.day,2],[ga.week,1],[ga.month,1],[ga.month,3],[ga.year,1]],nc=Wl.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",zt]]),tc={range:function(n,t,e){return ao.range(Math.ceil(n/e)*e,+t,e).map(io)},floor:m,ceil:m};Ql.year=ga.year,ga.scale=function(){return ro(ao.scale.linear(),Ql,nc)};var ec=Ql.map(function(n){return[n[0].utc,n[1]]}),rc=Jl.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",zt]]);ec.year=ga.year.utc,ga.scale.utc=function(){return ro(ao.scale.linear(),ec,rc)},ao.text=An(function(n){return n.responseText}),ao.json=function(n,t){return Cn(n,"application/json",uo,t)},ao.html=function(n,t){return Cn(n,"text/html",oo,t)},ao.xml=An(function(n){return n.responseXML}),"function"==typeof define&&define.amd?(this.d3=ao,define(ao)):"object"==typeof module&&module.exports?module.exports=ao:this.d3=ao}(); \ No newline at end of file diff --git a/admin/static/js/i18n/angular-locale_fr-fr.js b/admin/static/js/i18n/angular-locale_fr-fr.js deleted file mode 100644 index 2e6dbbbb..00000000 --- a/admin/static/js/i18n/angular-locale_fr-fr.js +++ /dev/null @@ -1,125 +0,0 @@ -'use strict'; -angular.module("ngLocale", [], ["$provide", function($provide) { -var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"}; -$provide.value("$locale", { - "DATETIME_FORMATS": { - "AMPMS": [ - "AM", - "PM" - ], - "DAY": [ - "dimanche", - "lundi", - "mardi", - "mercredi", - "jeudi", - "vendredi", - "samedi" - ], - "ERANAMES": [ - "avant J\u00e9sus-Christ", - "apr\u00e8s J\u00e9sus-Christ" - ], - "ERAS": [ - "av. J.-C.", - "ap. J.-C." - ], - "FIRSTDAYOFWEEK": 0, - "MONTH": [ - "janvier", - "f\u00e9vrier", - "mars", - "avril", - "mai", - "juin", - "juillet", - "ao\u00fbt", - "septembre", - "octobre", - "novembre", - "d\u00e9cembre" - ], - "SHORTDAY": [ - "dim.", - "lun.", - "mar.", - "mer.", - "jeu.", - "ven.", - "sam." - ], - "SHORTMONTH": [ - "janv.", - "f\u00e9vr.", - "mars", - "avr.", - "mai", - "juin", - "juil.", - "ao\u00fbt", - "sept.", - "oct.", - "nov.", - "d\u00e9c." - ], - "STANDALONEMONTH": [ - "janvier", - "f\u00e9vrier", - "mars", - "avril", - "mai", - "juin", - "juillet", - "ao\u00fbt", - "septembre", - "octobre", - "novembre", - "d\u00e9cembre" - ], - "WEEKENDRANGE": [ - 5, - 6 - ], - "fullDate": "EEEE d MMMM y", - "longDate": "d MMMM y", - "medium": "d MMM y HH:mm:ss", - "mediumDate": "d MMM y", - "mediumTime": "HH:mm:ss", - "short": "dd/MM/y HH:mm", - "shortDate": "dd/MM/y", - "shortTime": "HH:mm" - }, - "NUMBER_FORMATS": { - "CURRENCY_SYM": "\u20ac", - "DECIMAL_SEP": ",", - "GROUP_SEP": "\u00a0", - "PATTERNS": [ - { - "gSize": 3, - "lgSize": 3, - "maxFrac": 3, - "minFrac": 0, - "minInt": 1, - "negPre": "-", - "negSuf": "", - "posPre": "", - "posSuf": "" - }, - { - "gSize": 3, - "lgSize": 3, - "maxFrac": 2, - "minFrac": 2, - "minInt": 1, - "negPre": "-", - "negSuf": "\u00a0\u00a4", - "posPre": "", - "posSuf": "\u00a0\u00a4" - } - ] - }, - "id": "fr-fr", - "localeID": "fr_FR", - "pluralCat": function(n, opt_precision) { var i = n | 0; if (i == 0 || i == 1) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;} -}); -}]); diff --git a/admin/static/js/jquery.min.js b/admin/static/js/jquery.min.js deleted file mode 100644 index 36b4e1a1..00000000 --- a/admin/static/js/jquery.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v3.5.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector | (c) JS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(g,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,v=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,y=n.hasOwnProperty,a=y.toString,l=a.call(Object),m={},b=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},w=g.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function C(e,t,n){var r,i,o=(n=n||w).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function T(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector",E=function(e,t){return new E.fn.init(e,t)};function d(e){var t=!!e&&"length"in e&&e.length,n=T(e);return!b(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+R+")"+R+"*"),U=new RegExp(R+"|>"),V=new RegExp(W),X=new RegExp("^"+B+"$"),Q={ID:new RegExp("^#("+B+")"),CLASS:new RegExp("^\\.("+B+")"),TAG:new RegExp("^("+B+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+R+"*(even|odd|(([+-]|)(\\d*)n|)"+R+"*(?:([+-]|)"+R+"*(\\d+)|))"+R+"*\\)|)","i"),bool:new RegExp("^(?:"+I+")$","i"),needsContext:new RegExp("^"+R+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+R+"*((?:-\\d)?\\d*)"+R+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,G=/^(?:input|select|textarea|button)$/i,K=/^h\d$/i,J=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+R+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){C()},ae=xe(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{O.apply(t=P.call(d.childNodes),d.childNodes),t[d.childNodes.length].nodeType}catch(e){O={apply:t.length?function(e,t){q.apply(e,P.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,d=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==d&&9!==d&&11!==d)return n;if(!r&&(C(e),e=e||T,E)){if(11!==d&&(u=Z.exec(t)))if(i=u[1]){if(9===d){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return O.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&p.getElementsByClassName&&e.getElementsByClassName)return O.apply(n,e.getElementsByClassName(i)),n}if(p.qsa&&!k[t+" "]&&(!v||!v.test(t))&&(1!==d||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===d&&(U.test(t)||_.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&p.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=A)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+be(l[o]);c=l.join(",")}try{return O.apply(n,f.querySelectorAll(c)),n}catch(e){k(t,!0)}finally{s===A&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>x.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[A]=!0,e}function ce(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)x.attrHandle[n[r]]=t}function de(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function pe(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in p=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},C=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:d;return r!=T&&9===r.nodeType&&r.documentElement&&(a=(T=r).documentElement,E=!i(T),d!=T&&(n=T.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),p.scope=ce(function(e){return a.appendChild(e).appendChild(T.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),p.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),p.getElementsByTagName=ce(function(e){return e.appendChild(T.createComment("")),!e.getElementsByTagName("*").length}),p.getElementsByClassName=J.test(T.getElementsByClassName),p.getById=ce(function(e){return a.appendChild(e).id=A,!T.getElementsByName||!T.getElementsByName(A).length}),p.getById?(x.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},x.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(x.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},x.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),x.find.TAG=p.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):p.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},x.find.CLASS=p.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(p.qsa=J.test(T.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+R+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+R+"*(?:value|"+I+")"),e.querySelectorAll("[id~="+A+"-]").length||v.push("~="),(t=T.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+R+"*name"+R+"*="+R+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+A+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=T.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+R+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(p.matchesSelector=J.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){p.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",W)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=J.test(a.compareDocumentPosition),y=t||J.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!p.sortDetached&&t.compareDocumentPosition(e)===n?e==T||e.ownerDocument==d&&y(d,e)?-1:t==T||t.ownerDocument==d&&y(d,t)?1:u?H(u,e)-H(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==T?-1:t==T?1:i?-1:o?1:u?H(u,e)-H(u,t):0;if(i===o)return de(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?de(a[r],s[r]):a[r]==d?-1:s[r]==d?1:0}),T},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(C(e),p.matchesSelector&&E&&!k[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||p.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){k(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&V.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+R+")"+e+"("+R+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return b(n)?E.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?E.grep(e,function(e){return e===n!==r}):"string"!=typeof n?E.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(E.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||L,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:j.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof E?t[0]:t,E.merge(this,E.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:w,!0)),k.test(r[1])&&E.isPlainObject(t))for(r in t)b(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=w.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):b(e)?void 0!==n.ready?n.ready(e):e(E):E.makeArray(e,this)}).prototype=E.fn,L=E(w);var q=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}E.fn.extend({has:function(e){var t=E(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,pe=/^$|^module$|\/(?:java|ecma)script/i;le=w.createDocumentFragment().appendChild(w.createElement("div")),(ce=w.createElement("input")).setAttribute("type","radio"),ce.setAttribute("checked","checked"),ce.setAttribute("name","t"),le.appendChild(ce),m.checkClone=le.cloneNode(!0).cloneNode(!0).lastChild.checked,le.innerHTML="",m.noCloneChecked=!!le.cloneNode(!0).lastChild.defaultValue,le.innerHTML="",m.option=!!le.lastChild;var he={thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};function ge(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&S(e,t)?E.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n",""]);var ye=/<|&#?\w+;/;function me(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),d=[],p=0,h=e.length;p\s*$/g;function Le(e,t){return S(e,"table")&&S(11!==t.nodeType?t:t.firstChild,"tr")&&E(e).children("tbody")[0]||e}function je(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n
    ",2===ft.childNodes.length),E.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(m.createHTMLDocument?((r=(t=w.implementation.createHTMLDocument("")).createElement("base")).href=w.location.href,t.head.appendChild(r)):t=w),o=!n&&[],(i=k.exec(e))?[t.createElement(i[1])]:(i=me([e],t,o),o&&o.length&&E(o).remove(),E.merge([],i.childNodes)));var r,i,o},E.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=E.css(e,"position"),c=E(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=E.css(e,"top"),u=E.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),b(t)&&(t=t.call(e,n,E.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},E.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){E.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===E.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===E.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=E(e).offset()).top+=E.css(e,"borderTopWidth",!0),i.left+=E.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-E.css(r,"marginTop",!0),left:t.left-i.left-E.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===E.css(e,"position"))e=e.offsetParent;return e||re})}}),E.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;E.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),E.each(["top","left"],function(e,n){E.cssHooks[n]=Fe(m.pixelPosition,function(e,t){if(t)return t=We(e,n),Ie.test(t)?E(e).position()[n]+"px":t})}),E.each({Height:"height",Width:"width"},function(a,s){E.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){E.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?E.css(e,t,i):E.style(e,t,n,i)},s,n?e:void 0,n)}})}),E.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),E.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){E.fn[n]=function(e,t){return 0=o.clientWidth&&n>=o.clientHeight}),l=0a[e]&&!t.escapeWithReference&&(n=Q(f[o],a[e]-('right'===e?f.width:f.height))),ae({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=le({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=Z,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!K(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-us[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,w=t(e.instance.popper),y=parseFloat(w['margin'+f]),E=parseFloat(w['border'+f+'Width']),v=b-e.offsets.popper[m]-y-E;return v=ee(Q(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},ae(n,m,$(v)),ae(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case ce.FLIP:p=[n,i];break;case ce.CLOCKWISE:p=G(n);break;case ce.COUNTERCLOCKWISE:p=G(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=Z,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)f(l.top)||'bottom'===n&&f(a.top)f(o.right),g=f(a.top)f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,w=-1!==['top','bottom'].indexOf(n),y=!!t.flipVariations&&(w&&'start'===r&&h||w&&'end'===r&&c||!w&&'start'===r&&g||!w&&'end'===r&&u),E=!!t.flipVariationsByContent&&(w&&'start'===r&&c||w&&'end'===r&&h||!w&&'start'===r&&u||!w&&'end'===r&&g),v=y||E;(m||b||v)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),v&&(r=z(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=le({},e.offsets.popper,C(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport',flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!K(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=D(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottomo.right||t.top>o.bottom||t.rightwindow.devicePixelRatio||!fe),c='bottom'===o?'top':'bottom',g='right'===n?'left':'right',b=B('transform');if(d='bottom'==c?'HTML'===l.nodeName?-l.clientHeight+h.bottom:-f.height+h.bottom:h.top,s='right'==g?'HTML'===l.nodeName?-l.clientWidth+h.right:-f.width+h.right:h.left,a&&b)m[b]='translate3d('+s+'px, '+d+'px, 0)',m[c]=0,m[g]=0,m.willChange='transform';else{var w='bottom'==c?-1:1,y='right'==g?-1:1;m[c]=d*w,m[g]=s*y,m.willChange=c+', '+g}var E={"x-placement":e.placement};return e.attributes=le({},E,e.attributes),e.styles=le({},m,e.styles),e.arrowStyles=le({},e.offsets.arrow,e.arrowStyles),e},gpuAcceleration:!0,x:'bottom',y:'right'},applyStyle:{order:900,enabled:!0,fn:function(e){return V(e.instance.popper,e.styles),j(e.instance.popper,e.attributes),e.arrowElement&&Object.keys(e.arrowStyles).length&&V(e.arrowElement,e.arrowStyles),e},onLoad:function(e,t,o,n,i){var r=L(i,t,e,o.positionFixed),p=O(o.placement,r,t,e,o.modifiers.flip.boundariesElement,o.modifiers.flip.padding);return t.setAttribute('x-placement',p),V(t,{position:o.positionFixed?'fixed':'absolute'}),o},gpuAcceleration:void 0}}},ge}); -//# sourceMappingURL=popper.min.js.map diff --git a/admin/static/views/auth.html b/admin/static/views/auth.html deleted file mode 100644 index 0c8e3238..00000000 --- a/admin/static/views/auth.html +++ /dev/null @@ -1,86 +0,0 @@ -
    -

    - Authentification -

    -
    -
    - - -
    -
    -
    - -
    -
    -

    - OAuth 2 - Actif - Non configuré -

    -
    - -
    -
    -
    - -
    - -
    -
    -

    - Autorité de certification - Générée - Introuvable -

    - -
    - -
    - Aucune CA n'a été générée pour le moment. -
    - -
    - -
    {{ k }}
    -
    /CN={{ v.CommonName }}/OU={{ v.OrganizationalUnit }}/O={{ v.Organization }}/L={{ v.Locality }}/P={{ v.Province }}/C={{ v.Country }}/
    -
    {{ v }}
    -
    -
    -
    - -
    - -
    -
    -

    - Association utilisateurs et équipes -

    -
    -
    -
    - - - - - - - - - - - - -
    UtilisateurÉquipe
    {{ association.association }} - - {{ team.name }} - -
    -
    diff --git a/admin/static/views/claim-list.html b/admin/static/views/claim-list.html deleted file mode 100644 index 140feb65..00000000 --- a/admin/static/views/claim-list.html +++ /dev/null @@ -1,87 +0,0 @@ -

    - Tâches et réclammations ({{ claims.length }}) - - -
    - - - - -
    -
    -

    - -

    - - - - - - - - - - - -
    - {{ field }} -
    - - {{ claim[field] }} - - - - {{ assignee.name }} - - - - - {{ last_update }} - - - - {{ teams[claim.id_team].name }} - -
    - -
    - -
    -

    - Assignables à - -

    - - - - - - - - - - - - - - - -
    IDNom
    - {{ a.id }} - - {{ a.name }} - - - - - -
    -
    diff --git a/admin/static/views/claim.html b/admin/static/views/claim.html deleted file mode 100644 index 8b2ffdb5..00000000 --- a/admin/static/views/claim.html +++ /dev/null @@ -1,75 +0,0 @@ -
    - - - - - - - -
    - -

    Tâche

    - -
    -
    - -
    - -
    - - - - -
    - - -
    - -
    - - -
    -
    - -
    - -
    -
    -
    -
    -
    - - -
    -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    -
    - -
    - Par {{ assignee.name }} le {{ description.date | date:"mediumTime" }} : - {{ description.content }} -
    - -
    -
    diff --git a/admin/static/views/event-list.html b/admin/static/views/event-list.html deleted file mode 100644 index aa91a755..00000000 --- a/admin/static/views/event-list.html +++ /dev/null @@ -1,23 +0,0 @@ -

    - Événements - - -

    - -

    - - - - - - - - - - - -
    - {{ field }} -
    - {{ event[field] }} -
    diff --git a/admin/static/views/event.html b/admin/static/views/event.html deleted file mode 100644 index 5543fedd..00000000 --- a/admin/static/views/event.html +++ /dev/null @@ -1,23 +0,0 @@ -

    - Événement - Ajouter un événement -

    - -
    -
    - -
    - - - -
    -
    -
    - - -
    -
    - -
    -
    diff --git a/admin/static/views/exercice-flags.html b/admin/static/views/exercice-flags.html deleted file mode 100644 index 8de3f0ee..00000000 --- a/admin/static/views/exercice-flags.html +++ /dev/null @@ -1,262 +0,0 @@ -
    - -

    - {{exercice.title}} Flags -
    - - -
    -

    - -
    -
    - -

    Drapeaux

    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - - -
    -
    -
    - -
    - - -
    -
    - - -
    -
    - -
    - -
    - -
    -
    - -
    -
    - - -
    -
    -
    - - -
    -
    - - -
    -
    - -
    -
    - -
    -
    - -
    -
    -
    -
    -
    -
    - Dépendances : -
      - -
    - sans -
    -
    -
    - Statistiques -
      -
    • ID : {{ flag.id }}
    • -
    • Validés : {{ stats["completed"] }}
    • -
    • - Tentés : {{ stats["tries"] }} - -
    • -
    • - Équipes : - aucune - -
    • -
    -
    -
    -
    - -
    -
    - -
    - -
    -
    - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    - Aucun choix -
    -
    -
    -
    - -
    -
    - -
    -
    -
    -
    -
    - -
    -
    - -

    Quizz

    -
    -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    - -
    -
    - -
    -
    -
    -
    - -
    -
    - -
    -
    -
    - Dépendances : -
      - -
    - sans -
    -
    - Statistiques -
      -
    • ID : {{ q.id }}
    • -
    • Validés : {{ stats["completed"] }}
    • -
    • - Tentés : {{ stats["tries"] }} - -
    • -
    • - Équipes : - aucune - -
    • -
    -
    -
    -
    -
    -
    -
    - - - - diff --git a/admin/static/views/exercice-list.html b/admin/static/views/exercice-list.html deleted file mode 100644 index 6046a9c5..00000000 --- a/admin/static/views/exercice-list.html +++ /dev/null @@ -1,95 +0,0 @@ -
    -

    Exercices

    -
    - - Tags - - - Voir les fichiers - -
    - - - -
    - -
    -
    - -
    -
    -
    - -
    -

    - - - - - - - - - - - - - - - -
    - - - {{ field }} - - Thème -
    - - - {{ exercice[field] | stripHTML }} - - {{ themes[exercice.id_theme].name }} -
    -
    - -
    -
    - Édition de masse -

    - Les propriétés en gras seront écrasées. -

    -
    - - -
    - - - - - - -
    -
    -
    -
    diff --git a/admin/static/views/exercice-resolution.html b/admin/static/views/exercice-resolution.html deleted file mode 100644 index 88d00e92..00000000 --- a/admin/static/views/exercice-resolution.html +++ /dev/null @@ -1,15 +0,0 @@ -

    - {{exercice.title}} Résolution -
    - - -
    -

    - -
    -
    - -
    -
    -
    -
    diff --git a/admin/static/views/exercice.html b/admin/static/views/exercice.html deleted file mode 100644 index dc796197..00000000 --- a/admin/static/views/exercice.html +++ /dev/null @@ -1,475 +0,0 @@ -
    -

    - {{exercice.title}} - {{themes[exercice.id_theme].name}} -

    -
    - - -
    -
    - Vidéo - Flags -
    - - -
    - Voir sur la forge -
    -
    - -
    -

    Différences par rapport au dépôt

    -
    - {{ diffline.field }} -
    -
    -{{ diffline.be }}
    -
    +{{ diffline.af }}
    -
    -
    -
    - -
    - -
    - -
    -
    - -
    - - - - - - - - -
    -
    -
    - - -
    -
    - -
    -
    - -
    - -
    - - - - - - - - - -
    -
    - -
    -
    -

    Statistiques

    -
    -
    -
    -
    Points actuels
    -
    - -
    Défi tenté par
    -
    - -
    Nombre de tentatives
    -
    - -
    Défi validé par
    -
    - -
    Drapeaux validés
    -
    {{ stats.flag_solved.length }}
    -
    aucun
    - -
    QCM validés
    -
    {{ stats.mcq_solved.length }}
    -
    aucun
    -
    -
    -
    - -
    -
    -

    Téléchargements

    -
    -
    -
    - -
    -
    - - -
    - -
    -
    - Taille : {{ file.size | size }} ‐ - BLAKE2b : {{ file.checksum | cksum }} -
    -
    - - -
    -
    - Dépend de la validation de : - aucun flag -
      - -
    -
    -
    -
    -
    -
    - -
    -
    -

    Indices

    -
    -
    -
    - -
    - - -

    - Fichier : {{ hint.file }}
    - Hash : {{ hint.content }}
    -

    -
    -
    -
    - Coût -
    - -
    -
    - - -
    -
    -
    - Dépendances : -
      - -
    - sans -
    -
    -
    -
    -
    - -
    -
    -

    Drapeaux

    -
    -
    -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    -
    - -
    - -
    -
    - -
    - - -
    -
    - - -
    -
    -
    - -
    -
    -
    -
    - -
    - -
    -
    -
    - Dépendances : -
      - -
    - sans -
    -
    -
    -
    -
    -
    - -
    -
    -

    Quizz

    -
    -
    -
    - -
    -
    - -
    - -
    -
    -
    -
    - -
    -
    - -
    -
    -
    -
    - -
    -
    - -
    -
    -
    - Dépendances : -
      - -
    - sans -
    -
    -
    -
    -
    - -
    -
    -

    Tags

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

    Historique

    -
    - - -
    -
    - - - - - - - - -
    - {{ row.time | date:"mediumTime" }}
    {{ row.kind }} x{{ row.coefficient }} -
    - - {{ row.team_name }} - - - : - {{ row.secondary_title }} - {{ row.secondary_title }} - {{ row.secondary_title }} - - : {{ row.secondary }} -
    , {{ line.kind }}#{{ line.related }}
    -
    - - -
    - - - -
    - -
    - -
    - - - - - - diff --git a/admin/static/views/exercices-forgelink.html b/admin/static/views/exercices-forgelink.html deleted file mode 100644 index 2588ebc4..00000000 --- a/admin/static/views/exercices-forgelink.html +++ /dev/null @@ -1,12 +0,0 @@ -

    Accès rapide aux exercices

    - -
    -

    - {{ theme.name }} : {{ theme.path }} -

    - -
    diff --git a/admin/static/views/file-list.html b/admin/static/views/file-list.html deleted file mode 100644 index 83a3242c..00000000 --- a/admin/static/views/file-list.html +++ /dev/null @@ -1,43 +0,0 @@ -

    - Fichiers - - - - - -

    - -

    - - - - - - - - - - - - - - - -
    - {{ field }} - checksum
    - - - - {{ file[field] }} - - -
    {{ file.checksum | bto16 }}
    -
    {{ file.checksum_shown | bto16 }}
    -
    diff --git a/admin/static/views/home.html b/admin/static/views/home.html deleted file mode 100644 index 720f46af..00000000 --- a/admin/static/views/home.html +++ /dev/null @@ -1,111 +0,0 @@ - - -
    - -
    -
    -
    -

    Classement

    -
    - - - - - - - - -
    {{ team.rank }}{{ team.name }}{{ team.score | number:0 }}
    -
    -
    -
    diff --git a/admin/static/views/pki.html b/admin/static/views/pki.html deleted file mode 100644 index 30f22686..00000000 --- a/admin/static/views/pki.html +++ /dev/null @@ -1,158 +0,0 @@ -

    - Certificats clients - -
    - - -
    -

    - -

    - - - - - - - - - - - - - - - - - - - - -
    SerialDate de créationÉquipeRévoqué ?Action
    {{ certificate.id }}{{ certificate.creation }} - - {{ team.name }} - - - - {{ certificate.revoked }} - Télécharger - -
    - - - - - - - - - -
    - -

    - Autorité de certification - Générée - Introuvable - - -

    - -
    - Aucune CA n'a été générée pour le moment. - -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    {{ k }}
    -
    /CN={{ v.CommonName }}/OU={{ v.OrganizationalUnit }}/O={{ v.Organization }}/L={{ v.Locality }}/P={{ v.Province }}/C={{ v.Country }}/
    -
    {{ v }}
    -
    -
    diff --git a/admin/static/views/public.html b/admin/static/views/public.html deleted file mode 100644 index ab4e5dac..00000000 --- a/admin/static/views/public.html +++ /dev/null @@ -1,407 +0,0 @@ -
    -

    - Interface publique - - - - -
    - - - -
    -

    - -
    -
    -
    - Aucun contenu n'est actuellement affiché. - Aucun contenu à afficher. -
    - -
    -
    - -
    - -
    - -
    -
    -
    - -
    - - - -
    -
    - -
    - -
    - - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    -
    - - -
    -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    -
    -
    -
    - -
    -
    -
    - - -
    - -
    - -
    -
    - -
    -
    -
    -
    - -
    -
    -
    - -
    - -
    - -
    -
    - -
    -
    -
    -
    - -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - -
    - -
    - -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - - -
    -
    -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    -
    -
    diff --git a/admin/static/views/repositories.html b/admin/static/views/repositories.html deleted file mode 100644 index daca48fa..00000000 --- a/admin/static/views/repositories.html +++ /dev/null @@ -1,29 +0,0 @@ -
    -
    -

    - Repositories -

    -
    - - - - - - - - - - - - - - - - -
    CheminBrancheCommit Plus récent
    {{ repository.path }}{{ repository.branch }} - {{ repository.hash }}
    - -
    - -
    -
    diff --git a/admin/static/views/settings.html b/admin/static/views/settings.html deleted file mode 100644 index 203f2b2f..00000000 --- a/admin/static/views/settings.html +++ /dev/null @@ -1,531 +0,0 @@ -
    -
    -
    -
    - -
    - - - Propagation dans : {{ activateTimeCountDown | timer }}. - Il restera : {{ timeRemaining - activateTimeCountDown | timer }} - -
    -

    - Paramètres -

    -
    -
    -
    - -
    -
    - -
    - -
    -
    -
    -
    - -
    - -
    - -
    -
    - -
    -
    -
    - -
    - min -
    -
    -
    -
    - -
    - -
    -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    -
    - -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    -
    -
    -
    -
    -
    - -
    - -
    -
    -
    - -
    -
    - -
    - -
    -
    - -
    - -
    - -
    -
    -
    -
    -
    -
    -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    -
    - -
    -
    -

    Managers QA

    -
    -
    -
      -
    • - {{ m }} - -
    • -
    - -
    -
    - -
    -
    - - - - -
    -
    -
    -
    - -
    -
    - - -

    Infos challenge

    -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    -
    - -
    - - -
    -
    -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    -
    - - - -
    - - -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    - - - Télécharger l'archive pour publication -
    -
    -

    Changements anticipés

    -
    -
    -

    - {{ ns.date }} -

    -
    - - -
    -
    -
      -
    • - {{ k }} → {{ v }} -
    • -
    -
    -
    - -
    -
    -
    - Synchronisation et suppressions de masse -
    -
    -
    -
    - -
    -
    -
    - -
    -
    - Paramètres de synchronisation - - - - - -
    -
    - -
    -
    diff --git a/admin/static/views/sync.html b/admin/static/views/sync.html deleted file mode 100644 index 06d8e84f..00000000 --- a/admin/static/views/sync.html +++ /dev/null @@ -1,159 +0,0 @@ -
    -
    -

    - Synchronisation -

    -
    -
    -
    - -
    -
    -
    - -
    -
    -

    - Import des thèmes -

    -
    - Dernier import : {{ syncReport._updated[syncReport._updated.length-1] | date:"medium" }} -
    - - Voir les dépôts - -
    -
    -
    -
    -
    -
    -
    Type
    -
    -
    Synchronisation
    -
    -
    ID
    -
    - {{ syncStatus['sync-id'] }} - -
    -
    Statut
    -
    - {{ syncPercent }} % - Pull - Synchronisation -
    -
    - -
    {{ syncStatus.lastError }}
    - -
    -
    - - - -
    - - - -
    -
    -
    - -
    -
    - - - Lien public - - -

    - Dernier rapport de synchronisation -

    -
    -
    -
    -

    - Génération -

    -
    -
      -
    • {{ item }}
    • -
    -
    -
    -
    -

    - Général -

    -
    -
      -
    • {{ item }}
    • -
    -
    -
    -
    -

    - {{ th }} -

    -
    - - - - -
    -
    -
      -
    • {{ item }}
    • -
    -
    -
    - -
    -
    - -

    - Différences avec le dépôts -

    -
    -
    -
    Lancez la génération du rapport pour lister les différences.
    -
    -
    -
    -

    - {{ th }} -

    -
    - - - - -
    -
    - -
    -
    diff --git a/admin/static/views/tags.html b/admin/static/views/tags.html deleted file mode 100644 index a24dbf27..00000000 --- a/admin/static/views/tags.html +++ /dev/null @@ -1,34 +0,0 @@ -
    -

    Tags

    -
    - - - - - - - - - - - - - - - - -
    - Tag - - Nb - - Exercices -
    - {{ tag }} - - {{ exercices.length }} - - - {{ e.title }} - -
    diff --git a/admin/static/views/team-edit.html b/admin/static/views/team-edit.html deleted file mode 100644 index 9d215896..00000000 --- a/admin/static/views/team-edit.html +++ /dev/null @@ -1,224 +0,0 @@ -

    - {{ team.name }} -
    - - -
    - - -

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

    Membres

    -
    -
    - This team has no member! -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    - -
    -
    - - Certificates - Generated - Not found -
    -
    -
    -
    - Numéro de série - -
    -
    - {{ cert.id }} - Révoqué -
    -
    - Date de création - Télécharger -
    -
    {{ cert.creation }}
    -
    Mot de passe
    -
    {{ cert.password }}
    -
    Date de révocation
    -
    {{ cert.revoked }}
    -
    -
    -
    - -
    -
    - - Utilisateurs associés -
    -
    -
      -
    • - {{ a }} - -
    • -
    • -
      - - - - -
      -
    • -
    -
    -
    - -
    - -
    - -
    - - - - - - - - -
    - {{ row.time | date:"mediumTime" }}
    {{ row.kind }} x{{ row.coefficient }} -
    - - {{ row.primary_title }} - - {{ row.primary }} - - : - {{ row.secondary_title }} - {{ row.secondary_title }} - {{ row.secondary_title }} - - (coeff x{{ row.secondary }}) - : {{ row.secondary }} - - - -
    - - - -
    -
    - - diff --git a/admin/static/views/team-export.html b/admin/static/views/team-export.html deleted file mode 100644 index 713c448c..00000000 --- a/admin/static/views/team-export.html +++ /dev/null @@ -1,22 +0,0 @@ -
    -
    -
    -

    {{ team.name }} {{ teams[my.team_id].rank }}/{{ nb_teams }} –

    -
    -
    -

    -
      -
    • - - {{ exercice.title }} - ({{ my.exercices[eid].solved_rank }}e{{ my.exercices[eid].hints | countHints }} indices) - -
    • -
    -
    -
    - -
    -
    -
    -
    diff --git a/admin/static/views/team-list.html b/admin/static/views/team-list.html deleted file mode 100644 index 5b175d2f..00000000 --- a/admin/static/views/team-list.html +++ /dev/null @@ -1,40 +0,0 @@ -
    -

    - Équipes -

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

    - - - - - - - - - - - - - -
    - {{ field }} - - color -
    - {{ team[field] }} - - {{ team['color'] | toColor }} -
    diff --git a/admin/static/views/team-print.html b/admin/static/views/team-print.html deleted file mode 100644 index c082db2e..00000000 --- a/admin/static/views/team-print.html +++ /dev/null @@ -1,38 +0,0 @@ -

    {{ config.title }} – Équipes

    - -

    - - - - - - - - - - - - - - - - -
    - ID - - Nom d'équipe - - members -
    - {{ team.id }} - - {{ team.name }} - - - - - -
    - {{ member[field] }} -
    -
    diff --git a/admin/static/views/team-score.html b/admin/static/views/team-score.html deleted file mode 100644 index e0f34b5c..00000000 --- a/admin/static/views/team-score.html +++ /dev/null @@ -1,42 +0,0 @@ -

    - {{ team.name }} - Détails des scores -

    - -

    - - - - - - - - - - - - - - - - - - - - - - - -
    ExerciceRaisonPointsCalculDate
    - {{ exercice.title }} - - {{ row.reason }} - - {{ row.points * row.coeff }} - - {{ row.points }} * {{ row.coeff }} - - {{ row.points }} * {{ settings.questionGainRatio }} / {{ settings.questionGainRatio / row.coeff }} - - {{ row.time | date:"mediumTime" }} -
    {{ my.score100 / 100 }}
    diff --git a/admin/static/views/team-stats.html b/admin/static/views/team-stats.html deleted file mode 100644 index 4bb75eb1..00000000 --- a/admin/static/views/team-stats.html +++ /dev/null @@ -1,57 +0,0 @@ - - -

    - {{ team.name }} - - et , {{ member.firstname | capitalize }} {{ member.nickname }} {{ member.lastname | capitalize }} - -

    - -
    - -
    -
    Points
    -
    {{ my.score }}
    -
    Classement
    -
    {{ teams[my.team_id].rank }}/{{ nb_teams }} ({{ nb_reg_teams }} registered teams)
    -
    - -

    Présence

    - -
    -
    - -

    Exercices résolus : {{ solved_exercices }}/{{ exercices.length }} {{ solved_exercices * 100 / exercices.length | number:0 }}%

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

    Tentatives par niveaux

    -
    -
    -

    Tentatives par thèmes

    -
    -
    - -
    diff --git a/admin/static/views/theme-list.html b/admin/static/views/theme-list.html deleted file mode 100644 index c5a292f7..00000000 --- a/admin/static/views/theme-list.html +++ /dev/null @@ -1,24 +0,0 @@ -

    - Thèmes - - - Liens d'accès à la forge -

    - -

    - - - - - - - - - - - -
    - {{ field }} -
    - {{ theme[field] | stripHTML }} -
    diff --git a/admin/static/views/theme.html b/admin/static/views/theme.html deleted file mode 100644 index 5c17fbb6..00000000 --- a/admin/static/views/theme.html +++ /dev/null @@ -1,69 +0,0 @@ -
    -

    - {{theme.name}} {{theme.authors | stripHTML}} -

    - -
    - -
    -

    Différences par rapport au dépôt

    -
    - {{ diffline.field }} -
    -
    -{{ diffline.be }}
    -
    +{{ diffline.af }}
    -
    -
    -
    - -
    - -
    -
    -
    - - - - - -
    -
    - - -
    -
    - -
    -
    - -
    -

    - Exercices ({{ exercices.length }}) - -
    - - -
    -

    - -

    - - - - - - - - - - - -
    - {{ field }} -
    - {{ exercice[field] | stripHTML }} -
    -
    -
    diff --git a/admin/sync/README.md b/admin/sync/README.md deleted file mode 100644 index 70b2d017..00000000 --- a/admin/sync/README.md +++ /dev/null @@ -1,235 +0,0 @@ -Détails de l'aborescence attendue ---------------------------------- - -Tous les textes doivent utiliser l'encodage UTF8. - -- Un dossier par thème `IDTEAM-Nom du thème` (`IDTEAM` peut être un mot, sans tiret `-` ; le nom du thème est celui qui sera affiché dans l'interface, soyez créatifs !), contenant : - * `AUTHORS.txt` avec vos noms, tels qu'ils apparraîtront sur le site (voir exemple [plus bas](#exemple-authorstxt)) - * `overview.txt` une présentation rapide du scenario (~2-3 phrases d'accroche pour lecture rapide), compréhensible par un décideur, petit schéma à l'appui - * `heading.jpg` une photographie libre de tout droit (également celui de référence) représentant le thème - * Un dossier par challenge : `CHID-Titre du challenge` (avec `CHID` l'identifiant numérique du challenge, typiquement son numéro d'ordre), contenant : - + `overview.txt` une présentation rapide du challenge (~1-2 phrases d'accroche), compréhensible par un décideur, petit schéma à l'appui si besoin - + `statement.txt` contenant le scénario du challenge, tel qu'il sera affiché sur le site, à destination des participants - + `finished.txt` (facultatif) contenant un texte affiché au participant ayant validé l'exercice : par exemple pour donner plus d'informations sur les vulnérabilités rencontrées - + `challenge.txt` définitions des paramètres de votre challenge (au format [toml](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md)) : - - `gain = 42` : nombre de points que rapporte cet exercice ; - - `tags = ["Android", "RAT", "ROM"]` : mots-clefs de l'exercice ; - - `[[depend]]` : dépendance à un autre exercice : - * `id = CHID` : identifiant du challenge ; - * `theme = "NomDuTheme"` : (facultatif) nom du thème dans lequel aller chercher l'identifiant (par défaut, on prend le thème courant) ; - - `[[flag]]` : drapeau classique à valider pour résoudre le challenge : - * `id = 42` : (facultatif) identifiant du flag au sein de l'exercice, pour définir des dépendances ; - * `label = "Intitulé"` : (facultatif, par défaut : `Flag`) intitulé du drapeau ; - * `raw = 'MieH2athxuPhai6u'` ou `raw = ['part1', 'part2']` : drapeau exact à trouver ; sous forme de tableau, le participant n'aura pas connaissaance du nombre d'éléments ; - * `validator_regexp = "^(?:sudo +)?(.*)$"` : (facultatif) expression rationnelle dont les groupes capturés serviront comme chaîne à valider (notez que `?:` au début d'un groupe ne le capturera pas) ; - * `ordered = false` : (facultatif, par défaut : `false`) ignore l'ordre dans lequels les éléments du tableau sont passés ; - * `ignorecase = true` : (facultatif, par défaut : `false`) ignore la case de ce drapeau ; - * `placeholder = "Indication"` : (facultatif) chaîne de caractères placée sous le champ du formulaire, idéale pour donner une indication de format ; - * `[[flag.unlock_file]]` : bloque l'accès à un fichier tant que le flag n'est pas obtenu : - + `filename = "toto.txt"` : nom du fichier tel qu'il apparaît dans le dossier `files` ; - * `[[flag.need_flag]]` : liste des flags devant être validés avant de débloquer celui-ci : - + `id = 23` : identifiant du flag tel qu'il a été défini plus tôt dans le fichier ; - - `[[flag_mcq]]` : drapeau sous forme de question à choix multiple (cases à cocher) : - * `label = "Intitulé du groupe"` : (facultatif) intitulé du groupe de choix ; - * `[[flag_mcq.choice]]` : représente un choix, répétez autant de fois qu'il y a de choix : - + `label = "Intitulé de la réponse"`, - + `value = true` : (facultatif, par défaut `false`) valeur attendue pour ce choix ; pour un QCM justifié, utilisez une chaîne de caractères (notez qu'il n'est pas possible de combiner des réponses vraies justifiées et justifiées), - + `placeholder = "Flag correspondant"` : (facultatif) indication affichée dans le champ de texte des QCM justifiés ; - - `[[flag_ucq]]` : drapeau sous forme de question à choix unique : - * `id = 42` : (facultatif) identifiant du flag au sein de l'exercice, pour définir des dépendances ; - * `label = "Intitulé du groupe"` : (facultatif) intitulé du groupe de choix ; - * `raw = 'MieH2athxuPhai6u'` : drapeau attendu parmi les propositions ; - * `validator_regexp = "^(?:sudo +)?(.*)$"` : (facultatif) expression rationnelle dont les groupes capturés serviront comme chaîne à valider (notez que `?:` au début d'un groupe ne le capturera pas) ; - * `placeholder = "Indication"` : (facultatif, uniquement si `displayAs = select`) chaîne de caractères placée sous le champ du formulaire ; - * `displayAs = "select|radio"` : (facultatif, par défaut `radio`) manière dont est affichée le choix : `select` pour une liste de choix, `radio` pour des boutons radios ; - * `choices_cost = 20` : (facultatif, par défaut `0`) coût pour afficher les choix, avant l'affichage, se comporte comme un `flag` classique (à 0, les choix sont affichés directement) ; - * `[[flag_ucq.choice]]` : représente un choix, répétez autant de fois qu'il y a de choix : - + `value = "response"` : valeur qui sera retournée pour comparaison avec la valeur `raw` du ucq, - + `label = "Intitulé de la réponse"` : (facultatif, par défaut identique à `value`) ; - * `[[flag_ucq.unlock_file]]` : bloque l'accès à un fichier tant que le flag n'est pas obtenu : - + `filename = "toto.txt"` : nom du fichier tel qu'il apparaît dans le dossier `files` ; - * `[[flag_ucq.need_flag]]` : liste des flags devant être validés avant de débloquer celui-ci : - + `id = 23` : identifiant du flag tel qu'il a été défini plus tôt dans le fichier ; - - `[[hint]]` : paramètres pour un indice : - * `filename = "toto.txt"` : (mutuellement exclusif avec `content`) nom du fichier tel qu'il apparaît dans le dossier `hints` ; - * `content = "Contenu de l'indice"` : (mutuellement exclusif avec `filename`) contenu de l'indice affiché, en markdown ; - * `cost = 10` : (facultatif, par défaut 1/4 des gains du challenge) coût de l'indice ; - * `title = "Foo Bar"` : (facultatif, par défaut "Astuce $id") titre de l'astuce dans l'interface ; - + `links.txt` : webographie publiée avec les solutions - - un lien par ligne - - format d'une ligne : `https://lien Description` - le premier ' ' est utilisé comme séparateur entre le lien et sa description - - liens vers les CVE concernées, metasploit/exploitDB, article qui vous a aidé, extrait/dépôt de code, ... - + `hints/` : dossier contenant des indices pour orienter le participant (qu'il débloquera en échange d'un certain nombre de points) - - `DIGESTS.txt` : contenant les condensats des fichiers : `$(b2sum * > DIGESTS.txt)` à générer avant l'upload ! - - les fichiers textes de moins de 25 lignes sont affichés directement, les autres (autres types ou textes plus longs) sont proposés au téléchargement. - + `files/` : fichiers à distribuer aux participants - - `DIGESTS.txt` : contenant les condensats des fichiers : `$(b2sum * > DIGESTS.txt)` à générer avant l'upload ! - - Pas plus 4GB à télécharger **par challenge** (ie. tous les fichiers de ce challenge) - - Archives `.tar.bz2`, `.tar.gz`, `.tar.xz` ou `.zip` lorsque nécessaire. **PAS** de `.rar`, ... - - Compresser (sans tarball, juste via **gzip**) les fichiers lorsque c'est utile (memory dump, images BMP, disques, fichiers textes, ...) - - Utiliser `$(split -b 240M -d BIG_FILE BIG_FILE.)` pour uploader les gros fichiers sur owncloud. - Ces fichiers seront concaténés au moment de leur import sur l'interface. - Seul le hash du fichier entier est requis dans le fichier `DIGESTS.txt`. - + `resolution.mp4` : la vidéo de résolution, montée : - - format MP4 (H.264 + AAC + 3GPP Timed Text) - - utiliser les sous-titres pour commenter les étapes ; pas de commentaires audio - - environ 2' par vidéo : maxi 1'30" pour les challenges simples, 3-4' maxi - - `ffmpeg -video_size 1920x1080 -framerate 25 -f x11grab -i :0.0 -f alsa -ac 2 -i hw:0 -strict experimental resolution.mp4` - - [recordMyDesktop](http://recordmydesktop.sourceforge.net/) sous Linux - - [Screencast Capture Lite](http://cesarsouza.github.io/screencast-capture/) pour Windows (pas de logiciel de « démonstration » => il faut payer la licence pour publier une vidéo) - - [Aegisub](http://www.aegisub.org/), [Gnome Subtitle](http://gnomesubtitles.org/), emacs/vim, ... pour les sous-titres - + `ressources/` : - - ressources et scripts que vous avez réalisés pour le challenge : pour sa construction ou sa résolution - - schéma du SI au premier challenge concerné - - éventuellement un `README.txt` avec les liens des outils externes - - -Exemple d'arborescence ----------------------- - - SATURN-Active Directory/ - ... - SATURN-Virtualisation légère/ - ... - JUPITER-PDF/ - AUTHORS.txt - overview.txt - 1-Cible cachée/ - ... - 2-Black&White/ - ... - 3-Ligne rouge/ - files/ - DIGESTS.txt - clue.pdf - big_clue.pdf.00 - big_clue.pdf.01 - big_clue.pdf.02 - ressources/ - generator.pl - PDFextractor.exe - solver.py - pdf_index_schema.svg - challenge.txt - links.txt - overview.txt - resolution.mp4 - scenario.txt - ... - - -Exemple `AUTHORS.txt` {#exemple-authorstxt} ---------------------- - -``` -Courtois J. -Bombal S. -Mercier P-O. -``` - -Vous pouvez indiquer entre chevrons, un lien qui sera associé à votre nom. - -Vous pouvez utiliser un pseudo si vous n'êtes pas fier de vos réalisations. - - -Exemple `challenge.txt` {#exemple-challengetxt} ------------------------ - -``` -gain = 42 - -[[depend]] -id = 2 - -[[flag]] -label = "Date d'exfiltration" -placeholder = "Format : yyyy-mm" -raw = '2015-12' - -[[flag]] -label = "IPv6 d'exfiltration" -raw = 'fe80::319c:1002:7c60:68fa' -ignorecase = true - -[[flag_ucq]] -label = "Conditions générales de validation de challenge" -raw = 'conscent' - - [[flag_ucq.choice]] - label = "J'accepte les conditions" - value = 'conscent' - -[[flag_ucq]] -label = "Quelle est la couleur du cheval blanc d'Henri IV ?" -raw = 'blanc' -ignorecase = true -displayAs = "select" - - [[flag_ucq.choice]] - value = 'Noir' - - [[flag_ucq.choice]] - label = 'Roux' - value = 'Alezan' - - [[flag_ucq.choice]] - label = 'Brun' - value = 'Alezan' - - [[flag_ucq.choice]] - label = "Crème" - value = 'Blanc' - -[[flag_mcq]] -label = "Quels sont les films réalisés par C. Nolan ?" - - [[flag_mcq.choice]] - label = "Memento" - value = true - - [[flag_mcq.choice]] - label = "Inception" - value = true - - [[flag_mcq.choice]] - label = "Transcendance" - -[[hint]] -filename = 'enocean-specs.pdf' -title = "Spécifications du protocole utilisé" - -[[hint]] -content = """ -Le TOML c'est magique. -Je peux avoir des chaînes de caractères sur plusieurs lignes ! -""" -title = "L'astuce du siècle" -cost = 30 -``` - - -Exemple `links.txt` {#exemple-linkstxt} -------------------- - -``` -https://media.ccc.de/... Vidéo d'inspiration -https://metasplo.it/ Exploit utilisé -https://nist.gov/ CVE-2016-4242 -``` - - -Exemple `DIGESTS.txt` {#exemple-digeststxt} ---------------------- - -``` -3222734c6c8782682a9c36135a3518e8f4d1facabf76e702cf50da0037a4ed0a425e51266c2914fb83828573e397f96c2a95d419bd85919055479d028f51dba5 fic2016.jpg -023939b0c52b0dfce66954318ab82f7a8c10af4c79c8d5781612b58c74f3ace056067d7b15967e612b176a186b46d3d900c4db8881ba47202521eec33e5bb87b fic.org -7c91450239cf9b0717642c55c3429dd7326db26e87d4ca198758053333f0640ee89d2dd9b2f1919598f89644b06aa8fc2085648e3d1e542a6db324c9b16a0bdf header.tex -``` - ---- -title: Format des répertoires pour la synchronisation -author: FIC team 2019 -date: "Dernière mise à jour du document : 1 décembre 2018" ---- diff --git a/admin/sync/challengeinfo.go b/admin/sync/challengeinfo.go deleted file mode 100644 index 6e3b4a73..00000000 --- a/admin/sync/challengeinfo.go +++ /dev/null @@ -1,46 +0,0 @@ -package sync - -import ( - "path" - "strings" - - "srs.epita.fr/fic-server/libfic" - "srs.epita.fr/fic-server/settings" -) - -// ImportChallengeInfo imports images defined in the challengeinfo. -func ImportChallengeInfo(ci *settings.ChallengeInfo, dashboardDir string) (err error) { - if len(ci.MainLogo) > 0 { - for i, logo := range ci.MainLogo { - dest := path.Join(fic.FilesDir, "logo", path.Base(logo)) - err = importFile(GlobalImporter, logo, dest) - if err != nil { - return - } - - ci.MainLogo[i] = path.Join("$FILES$", strings.TrimPrefix(dest, fic.FilesDir)) - } - } - - if len(ci.DashboardBackground) > 0 { - dest := path.Join(dashboardDir, path.Base(ci.DashboardBackground)) - err = importFile(GlobalImporter, ci.DashboardBackground, dest) - if err != nil { - return - } - } - - if len(ci.Partners) > 0 { - for i, partner := range ci.Partners { - dest := path.Join(fic.FilesDir, "partner", path.Base(partner.Src)) - err = importFile(GlobalImporter, partner.Src, dest) - if err != nil { - return - } - - ci.Partners[i].Src = path.Join("$FILES$", strings.TrimPrefix(dest, fic.FilesDir)) - } - } - - return nil -} diff --git a/admin/sync/doc.go b/admin/sync/doc.go deleted file mode 100644 index aba17388..00000000 --- a/admin/sync/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package sync provides helpers to import the challenge from various locations. -package sync diff --git a/admin/sync/errors.go b/admin/sync/errors.go deleted file mode 100644 index b0e2a015..00000000 --- a/admin/sync/errors.go +++ /dev/null @@ -1,148 +0,0 @@ -package sync - -import ( - "fmt" - "log" - "path" - - "srs.epita.fr/fic-server/libfic" -) - -var ( - ErrResolutionNotFound = fmt.Errorf("no resolution video or text file found") -) - -type ThemeError struct { - error - ThemeId int64 - ThemePath string - ThemeName string -} - -func NewThemeError(theme *fic.Theme, err error) *ThemeError { - if theme == nil { - return &ThemeError{ - error: err, - ThemePath: fic.StandaloneExercicesDirectory, - } - } - - return &ThemeError{ - error: err, - ThemeId: theme.Id, - ThemePath: path.Base(theme.Path), - ThemeName: theme.Name, - } -} - -func (e *ThemeError) Error() string { - return fmt.Sprintf("%s: %s", e.ThemePath, e.error.Error()) -} - -func (e *ThemeError) GetError() error { - return e.error -} - -type ExerciceError struct { - *ThemeError - ExerciceId int64 - ExercicePath string - ExerciceName string -} - -func NewExerciceError(exercice *fic.Exercice, err error, theme ...*fic.Theme) *ExerciceError { - ltheme := len(theme) - if ltheme > 1 { - log.Fatal("Only 1 variadic arg is accepted in NewExerciceError") - return nil - } else if ltheme == 1 { - return &ExerciceError{ - ThemeError: NewThemeError(theme[0], err), - ExerciceId: exercice.Id, - ExercicePath: path.Base(exercice.Path), - ExerciceName: exercice.Title, - } - } else { - return &ExerciceError{ - ThemeError: &ThemeError{error: err}, - ExerciceId: exercice.Id, - ExercicePath: path.Base(exercice.Path), - ExerciceName: exercice.Title, - } - } -} - -func (e *ExerciceError) Error() string { - return fmt.Sprintf("%s: %s", e.ExercicePath, e.ThemeError.error.Error()) -} - -type FileError struct { - *ExerciceError - Filename string -} - -func NewFileError(exercice *fic.Exercice, filename string, err error, theme ...*fic.Theme) *FileError { - return &FileError{ - ExerciceError: NewExerciceError(exercice, err, theme...), - Filename: filename, - } -} - -func (e *FileError) Error() string { - return fmt.Sprintf("%s: file %q: %s", e.ExercicePath, e.Filename, e.ThemeError.error.Error()) -} - -type ChallengeTxtError struct { - *ExerciceError - ChallengeTxtLine uint -} - -func NewChallengeTxtError(exercice *fic.Exercice, line uint, err error, theme ...*fic.Theme) *ChallengeTxtError { - return &ChallengeTxtError{ - ExerciceError: NewExerciceError(exercice, err, theme...), - ChallengeTxtLine: line, - } -} - -func (e *ChallengeTxtError) Error() string { - if e.ChallengeTxtLine != 0 { - return fmt.Sprintf("%s:%d: %s", path.Join(e.ExercicePath, "challenge.toml"), e.ChallengeTxtLine, e.ThemeError.error.Error()) - } else { - return fmt.Sprintf("%s: %s", path.Join(e.ExercicePath, "challenge.toml"), e.ThemeError.error.Error()) - } -} - -type HintError struct { - *ChallengeTxtError - HintId int - HintTitle string -} - -func NewHintError(exercice *fic.Exercice, hint *fic.EHint, line int, err error, theme ...*fic.Theme) *HintError { - return &HintError{ - ChallengeTxtError: NewChallengeTxtError(exercice, 0, err, theme...), - HintId: line + 1, - HintTitle: hint.Title, - } -} - -func (e *HintError) Error() string { - return fmt.Sprintf("%s: hint#%d (%s): %s", path.Join(e.ExercicePath, "challenge.toml"), e.HintId, e.HintTitle, e.ThemeError.error.Error()) -} - -type FlagError struct { - *ChallengeTxtError - FlagId int - FlagTitle string -} - -func NewFlagError(exercice *fic.Exercice, flag *ExerciceFlag, line int, err error, theme ...*fic.Theme) *FlagError { - return &FlagError{ - ChallengeTxtError: NewChallengeTxtError(exercice, 0, err, theme...), - FlagId: line, - } -} - -func (e *FlagError) Error() string { - return fmt.Sprintf("%s: flag#%d: %s", path.Join(e.ExercicePath, "challenge.toml"), e.FlagId, e.ThemeError.error.Error()) -} diff --git a/admin/sync/exceptions.go b/admin/sync/exceptions.go deleted file mode 100644 index 6bc0a01f..00000000 --- a/admin/sync/exceptions.go +++ /dev/null @@ -1,126 +0,0 @@ -package sync - -import ( - "fmt" - "path/filepath" - "strings" - - "srs.epita.fr/fic-server/libfic" -) - -type CheckExceptions map[string]string - -func (c *CheckExceptions) GetExerciceExceptions(e *fic.Exercice) *CheckExceptions { - return c.GetFileExceptions(filepath.Base(e.Path)) -} - -func (c *CheckExceptions) GetFileExceptions(paths ...string) *CheckExceptions { - ret := CheckExceptions{} - - if c != nil { - for k, v := range *c { - cols := strings.SplitN(k, ":", 2) - if len(cols) < 2 { - continue - } - - for _, path := range paths { - if strings.HasPrefix(cols[0], path) { - k = strings.TrimPrefix(k, path) - - if strings.HasPrefix(k, "/") { - k = strings.TrimPrefix(k, "/") - } - - ret[k] = v - break - } else if eval, err := filepath.Match(cols[0], path); err == nil && eval { - ret[k] = v - break - } - } - } - } - - // Ignore redondances in resolution.md - if len(paths) > 0 && paths[0] == "resolution.md" { - ret["resolution.md:*:redondances_paragraphe"] = "automatic" - } - - return &ret -} - -func (c *CheckExceptions) Filter2ndCol(str string) *CheckExceptions { - if c == nil { - return nil - } - - ret := CheckExceptions{} - - for k, v := range *c { - cols := strings.SplitN(k, ":", 3) - if len(cols) < 2 { - continue - } - - if cols[1] == "spelling" { - ret[k] = v - } else if eval, err := filepath.Match(cols[1], str); err == nil && eval { - ret[k] = v - } - } - - return &ret -} - -func (c *CheckExceptions) HasException(ref string) bool { - if c == nil { - return false - } - - for k, _ := range *c { - if strings.HasSuffix(k, ref) { - return true - } - } - - return false -} - -func ParseExceptionString(fexcept string, exceptions *CheckExceptions) *CheckExceptions { - if exceptions == nil { - exceptions = &CheckExceptions{} - } - - for n, line := range strings.Split(fexcept, "\n") { - (*exceptions)[strings.TrimSpace(line)] = fmt.Sprintf("repochecker-ack.txt:%d", n+1) - } - - return exceptions -} - -func LoadThemeException(i Importer, th *fic.Theme) (exceptions *CheckExceptions) { - if th == nil { - return - } - - if fexcept, err := GetFileContent(i, filepath.Join(th.Path, "repochecker-ack.txt")); err == nil { - return ParseExceptionString(fexcept, nil) - } - - return -} - -func LoadExerciceException(i Importer, th *fic.Theme, e *fic.Exercice, th_exceptions *CheckExceptions) (exceptions *CheckExceptions) { - if th_exceptions == nil { - th_exceptions = LoadThemeException(i, th) - } - - exceptions = th_exceptions.GetExerciceExceptions(e) - - if fexcept, err := GetFileContent(i, filepath.Join(e.Path, "repochecker-ack.txt")); err == nil { - return ParseExceptionString(fexcept, exceptions) - } - - return -} diff --git a/admin/sync/exceptions_test.go b/admin/sync/exceptions_test.go deleted file mode 100644 index 5d0c0a37..00000000 --- a/admin/sync/exceptions_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package sync - -import ( - "testing" -) - -const sampleFile = `0-exercice-1/overview.md:spelling:Sterik -0-exercice-1/overview.md:spelling:RSSI -0-exercice-1/statement.md:spelling:Sterik -0-exercice-1/statement.md:spelling:RSSI -0-exercice-1/finished.md:spelling:GoPhish -0-exercice-1/resolution.md:not-forbidden-string:51.38.152.16 -0-exercice-1/resolution.md:not-forbidden-string:109.57.42.65 -0-exercice-1/resolution.md:not-forbidden-string:server_update.sh -0-exercice-1/resolution.md:spelling:Flag -0-exercice-1/resolution.md:spelling:MHA -0-exercice-1/resolution.md:spelling:hops -0-exercice-1/resolution.md:4:g3__gn_les_2m__b1_a4_1 -0-exercice-1/resolution.md:10:g3__gn_les_2m__b1_a4_1 -0-exercice-1/resolution.md:spelling:echo -0-exercice-1/resolution.md:spelling:nbash -0-exercice-1/resolution.md:spelling:bash -0-exercice-1/resolution.md:spelling:update -0-exercice-1/resolution.md:spelling:sh -0-exercice-1/resolution.md:spelling:Success -0-exercice-1/resolution.md:11:typo_guillemets_typographiques_doubles_fermants -0-exercice-1/resolution.md:spelling:cronjob -0-exercice-1/resolution.md:spelling:Level -challenge.toml:spelling:time -challenge.toml:spelling:ago -0-exercice-1/resolution.md:spelling:SCL -challenge.toml:spelling:SCL` - -func TestLoadExceptions(t *testing.T) { - exceptions := ParseExceptionString(sampleFile, nil) - - if len(*exceptions) != 26 { - t.Fatalf("Expected 26 exceptions, got %d", len(*exceptions)) - } -} - -func TestFilterExceptions(t *testing.T) { - exceptions := ParseExceptionString(sampleFile, nil) - - filteredExceptions := exceptions.GetFileExceptions("resolution.md") - if len(*filteredExceptions) != 1 { - t.Fatalf("Expected 1 exceptions, got %d", len(*filteredExceptions)) - } - - filteredExceptions = exceptions.GetFileExceptions("challenge.toml") - if len(*filteredExceptions) != 3 { - t.Fatalf("Expected 3 exceptions, got %d", len(*filteredExceptions)) - } - - filteredExceptions = exceptions.GetFileExceptions("0-exercice-1") - if len(*filteredExceptions) != 23 { - t.Fatalf("Expected 23 exceptions, got %d", len(*filteredExceptions)) - } - - filteredExceptions = exceptions.Filter2ndCol("spelling") - if len(*filteredExceptions) != 20 { - t.Fatalf("Expected 20 exceptions, got %d", len(*filteredExceptions)) - } -} diff --git a/admin/sync/exercice_defines.go b/admin/sync/exercice_defines.go deleted file mode 100644 index 23b119a1..00000000 --- a/admin/sync/exercice_defines.go +++ /dev/null @@ -1,226 +0,0 @@ -package sync - -import ( - "fmt" - "path" - "strconv" - - "github.com/BurntSushi/toml" - "go.uber.org/multierr" - - "srs.epita.fr/fic-server/libfic" -) - -// ExerciceHintParams holds EHint definition infomation. -type ExerciceHintParams struct { - Filename string - Content string - Cost *int64 - Title string - NeedFlag []ExerciceDependency `toml:"need_flag,omitempty"` -} - -// ExerciceDependency holds dependency definitions information. -type ExerciceDependency struct { - Id int64 - Theme string `toml:",omitempty"` -} - -// ExerciceUnlockFile holds parameters related to a locked file. -type ExerciceUnlockFile struct { - Filename string `toml:",omitempty"` -} - -// ExerciceFile defines attributes on files. -type ExerciceFile struct { - Filename string `toml:",omitempty"` - URL string `toml:",omitempty"` - Hidden bool `toml:",omitempty"` - Disclaimer string `toml:",omitempty"` -} - -// ExerciceFlag holds informations about one flag. -type ExerciceFlag struct { - Id int64 - Label string `toml:",omitempty"` - Type string `toml:",omitempty"` - Raw interface{} - Separator string `toml:",omitempty"` - ShowLines bool `toml:",omitempty"` - Ordered bool `toml:",omitempty"` - CaseSensitive bool `toml:",omitempty"` - NoTrim bool `toml:",omitempty"` - CaptureRe string `toml:"capture_regexp,omitempty"` - SortReGroups bool `toml:"sort_capture_regexp_groups,omitempty"` - Placeholder string `toml:",omitempty"` - Help string `toml:",omitempty"` - BonusGain int32 `toml:"bonus_gain,omitempty"` - ChoicesCost int32 `toml:"choices_cost,omitempty"` - Choice []ExerciceFlagChoice - LockedFile []ExerciceUnlockFile `toml:"unlock_file,omitempty"` - NeedFlag []ExerciceDependency `toml:"need_flag,omitempty"` - NeedFlags []int64 `toml:"need_flags,omitempty"` - NoShuffle bool - Unit string `toml:"unit,omitempty"` - Variant string `toml:"variant,omitempty"` - NumberMin interface{} `toml:"min,omitempty"` - NumberMax interface{} `toml:"max,omitempty"` - NumberStep interface{} `toml:"step,omitempty"` -} - -func (f ExerciceFlag) RawString() []string { - switch f.Raw.(type) { - case string: - return []string{f.Raw.(string)} - case []string: - return f.Raw.([]string) - default: - return nil - } -} - -func (f ExerciceFlag) RawNumber() ([]float64, error) { - switch f.Raw.(type) { - case float64: - return []float64{f.Raw.(float64)}, nil - case []float64: - return f.Raw.([]float64), nil - case int64: - return []float64{float64(f.Raw.(int64))}, nil - case []int64: - var res []float64 - for _, raw := range f.Raw.([]int64) { - res = append(res, float64(raw)) - } - return res, nil - case string: - if v, err := strconv.ParseFloat(f.Raw.(string), 64); err == nil { - return []float64{v}, nil - } else { - return nil, err - } - case []string: - var res []float64 - for _, raw := range f.Raw.([]string) { - if v, err := strconv.ParseFloat(raw, 64); err == nil { - res = append(res, v) - } else { - return nil, err - } - } - return res, nil - default: - return nil, fmt.Errorf("invalid raw type: %T", f.Raw) - } -} - -// ExerciceFlagChoice holds informations about a choice (for MCQ and UCQ). -type ExerciceFlagChoice struct { - ExerciceFlag - Value interface{} `toml:",omitempty"` -} - -// ExerciceParams contains values parsed from defines.txt. -type ExerciceParams struct { - WIP bool `toml:"wip"` - Gain int64 - Tags []string - Files []ExerciceFile `toml:"file"` - Hints []ExerciceHintParams `toml:"hint"` - Dependencies []ExerciceDependency `toml:"depend"` - Flags []ExerciceFlag `toml:"flag"` - FlagsMCQ []ExerciceFlag `toml:"flag_mcq"` - FlagsUCQ []ExerciceFlag `toml:"flag_ucq"` -} - -func (p ExerciceParams) GetRawFlags() (ret []string) { - for _, f := range append(p.Flags, p.FlagsUCQ...) { - raw := f.RawString() - if len(raw) > 0 { - ret = append(ret, raw...) - } - } - for _, f := range p.FlagsMCQ { - for _, c := range f.Choice { - ret = append(ret, c.Label) - } - } - return -} - -// parseExerciceParams reads challenge definitions from defines.txt and extract usefull data to set up the challenge. -func parseExerciceParams(i Importer, exPath string) (p ExerciceParams, md toml.MetaData, err error) { - var defs string - - if i.Exists(path.Join(exPath, "challenge.toml")) { - defs, err = GetFileContent(i, path.Join(exPath, "challenge.toml")) - } else { - defs, err = GetFileContent(i, path.Join(exPath, "challenge.txt")) - } - - if err != nil { - return - } - - md, err = toml.Decode(defs, &p) - - return -} - -// getExerciceParams returns normalized -func getExerciceParams(i Importer, exercice *fic.Exercice) (params ExerciceParams, errs error) { - var err error - if params, _, err = parseExerciceParams(i, exercice.Path); err != nil { - errs = multierr.Append(errs, NewChallengeTxtError(exercice, 0, err)) - } else if len(params.Flags) == 0 && len(params.FlagsUCQ) == 0 && len(params.FlagsMCQ) == 0 { - if !params.WIP { - errs = multierr.Append(errs, NewChallengeTxtError(exercice, 0, fmt.Errorf("has no flag"))) - } - } else { - // Treat legacy UCQ flags as ExerciceFlag - for _, flag := range params.FlagsUCQ { - params.Flags = append(params.Flags, ExerciceFlag{ - Id: flag.Id, - Label: flag.Label, - Type: "ucq", - Raw: flag.Raw, - CaptureRe: flag.CaptureRe, - Placeholder: flag.Placeholder, - ChoicesCost: flag.ChoicesCost, - Choice: flag.Choice, - LockedFile: flag.LockedFile, - NeedFlag: flag.NeedFlag, - }) - } - params.FlagsUCQ = []ExerciceFlag{} - - // Treat legacy MCQ flags as ExerciceFlag - for _, flag := range params.FlagsMCQ { - params.Flags = append(params.Flags, ExerciceFlag{ - Id: flag.Id, - Label: flag.Label, - Type: "mcq", - ChoicesCost: flag.ChoicesCost, - Choice: flag.Choice, - LockedFile: flag.LockedFile, - NeedFlag: flag.NeedFlag, - }) - } - params.FlagsMCQ = []ExerciceFlag{} - } - return -} - -func GetExerciceFilesParams(i Importer, exercice *fic.Exercice) (map[string]ExerciceFile, error) { - params, _, err := parseExerciceParams(i, exercice.Path) - if err != nil { - return nil, err - } - - paramsFiles := map[string]ExerciceFile{} - for _, f := range params.Files { - paramsFiles[f.Filename] = f - } - - return paramsFiles, nil -} diff --git a/admin/sync/exercice_files.go b/admin/sync/exercice_files.go deleted file mode 100644 index 3ce45c80..00000000 --- a/admin/sync/exercice_files.go +++ /dev/null @@ -1,512 +0,0 @@ -package sync - -import ( - "compress/gzip" - "encoding/hex" - "fmt" - "io" - "log" - "net/http" - "net/url" - "os" - "path" - "strings" - "unicode" - - "github.com/gin-gonic/gin" - "go.uber.org/multierr" - - "srs.epita.fr/fic-server/libfic" -) - -type remoteFileDomainWhitelist []string - -func (l *remoteFileDomainWhitelist) String() string { - return fmt.Sprintf("%v", *l) -} - -func (l *remoteFileDomainWhitelist) Set(value string) error { - *l = append(*l, value) - return nil -} - -var RemoteFileDomainWhitelist remoteFileDomainWhitelist - -func isURLAllowed(in string) bool { - if len(RemoteFileDomainWhitelist) == 0 { - return true - } - - u, err := url.Parse(in) - if err != nil { - return false - } - - for _, t := range RemoteFileDomainWhitelist { - if t == u.Host { - return true - } - } - - return false -} - -func BuildFilesListInto(i Importer, exercice *fic.Exercice, into string) (files []string, digests map[string][]byte, errs error) { - // If no files directory, don't display error - if !i.Exists(path.Join(exercice.Path, into)) { - return - } - - // Parse DIGESTS.txt - if digs, err := GetFileContent(i, path.Join(exercice.Path, into, "DIGESTS.txt")); err != nil { - errs = multierr.Append(errs, NewExerciceError(exercice, fmt.Errorf("unable to read %s: %w", path.Join(into, "DIGESTS.txt"), err))) - } else if len(digs) > 0 { - digests = map[string][]byte{} - for nline, d := range strings.Split(digs, "\n") { - if dsplt := strings.SplitN(d, " ", 2); len(dsplt) < 2 { - errs = multierr.Append(errs, NewExerciceError(exercice, fmt.Errorf("unable to parse %s line %d: invalid format", path.Join(into, "DIGESTS.txt"), nline+1))) - continue - } else if hash, err := hex.DecodeString(dsplt[0]); err != nil { - errs = multierr.Append(errs, NewExerciceError(exercice, fmt.Errorf("unable to parse %s line %d: %w", path.Join(into, "DIGESTS.txt"), nline+1, err))) - continue - } else { - digests[strings.TrimFunc(dsplt[1], unicode.IsSpace)] = hash - } - } - } - - // Read file list - if flist, err := i.ListDir(path.Join(exercice.Path, into)); err != nil { - errs = multierr.Append(errs, NewExerciceError(exercice, err)) - } else { - for _, fname := range flist { - if fname == "DIGESTS.txt" || fname == ".gitattributes" { - continue - } - - if matched, _ := path.Match("*.[0-9][0-9]", fname); matched { - fname = fname[:len(fname)-3] - } else if matched, _ := path.Match("*[0-9][0-9]", fname); matched { - fname = fname[:len(fname)-2] - } else if matched, _ := path.Match("*_MERGED", fname); matched { - continue - } - - fileFound := false - for _, f := range files { - if fname == f { - fileFound = true - break - } - } - - if !fileFound { - files = append(files, fname) - } - } - } - - // Complete with remote file names - if paramsFiles, err := GetExerciceFilesParams(i, exercice); err == nil { - for _, pf := range paramsFiles { - if pf.URL != "" { - found := false - for _, file := range files { - if file == pf.Filename { - found = true - break - } - } - - if !found { - files = append(files, pf.Filename) - } - } - } - } - - return -} - -// CheckExerciceFilesPresence limits remote checks to presence, don't get it to check digest. -func CheckExerciceFilesPresence(i Importer, exercice *fic.Exercice) (files []string, errs error) { - flist, digests, berrs := BuildFilesListInto(i, exercice, "files") - errs = multierr.Append(errs, berrs) - - paramsFiles, _ := GetExerciceFilesParams(i, exercice) - - for _, fname := range flist { - if !i.Exists(path.Join(exercice.Path, "files", fname)) && !i.Exists(path.Join(exercice.Path, "files", fname+".00")) { - // File not found locally, is this a remote file? - if pf, exists := paramsFiles[fname]; !exists || pf.URL == "" { - errs = multierr.Append(errs, NewFileError(exercice, fname, fmt.Errorf("No such file or directory"))) - continue - } else if !isURLAllowed(pf.URL) { - errs = multierr.Append(errs, NewFileError(exercice, fname, fmt.Errorf("URL hostname is not whitelisted"))) - continue - } else { - resp, err := http.Head(pf.URL) - if err != nil { - errs = multierr.Append(errs, NewFileError(exercice, fname, err)) - continue - } - defer resp.Body.Close() - - if resp.StatusCode >= 300 { - errs = multierr.Append(errs, NewFileError(exercice, fname, fmt.Errorf("Unexpected status code for the HTTP response: %d %s", resp.StatusCode, resp.Status))) - continue - } - } - } - - if _, ok := digests[fname]; !ok { - errs = multierr.Append(errs, NewFileError(exercice, fname, fmt.Errorf("unable to import file: No digest given"))) - } else { - files = append(files, fname) - } - } - - for fname := range digests { - if !i.Exists(path.Join(exercice.Path, "files", fname)) && !i.Exists(path.Join(exercice.Path, "files", fname+".gz")) && !i.Exists(path.Join(exercice.Path, "files", fname+".00")) && !i.Exists(path.Join(exercice.Path, "files", fname+".gz.00")) { - if pf, exists := paramsFiles[fname]; !exists || pf.URL == "" { - if pf, exists := paramsFiles[fname+".gz"]; !exists || pf.URL == "" { - errs = multierr.Append(errs, NewFileError(exercice, fname, fmt.Errorf("unable to read file: No such file or directory. Check your DIGESTS.txt for legacy entries."))) - } - } - } - } - - return -} - -// CheckExerciceFiles checks that remote files have the right digest. -func CheckExerciceFiles(i Importer, exercice *fic.Exercice, exceptions *CheckExceptions) (files []string, errs error) { - flist, digests, berrs := BuildFilesListInto(i, exercice, "files") - errs = multierr.Append(errs, berrs) - - paramsFiles, err := GetExerciceFilesParams(i, exercice) - if err != nil { - errs = multierr.Append(errs, NewChallengeTxtError(exercice, 0, err)) - } - - for _, fname := range flist { - dest := path.Join(exercice.Path, "files", fname) - - if pf, exists := paramsFiles[fname]; exists && pf.URL != "" { - if li, ok := i.(LocalImporter); ok { - errs = multierr.Append(errs, DownloadExerciceFile(paramsFiles[fname], li.GetLocalPath(dest), exercice, false)) - } else { - errs = multierr.Append(errs, DownloadExerciceFile(paramsFiles[fname], dest, exercice, false)) - } - } - - if fd, closer, err := GetFile(i, dest); err != nil { - errs = multierr.Append(errs, NewFileError(exercice, fname, fmt.Errorf("unable to read file: %w", err))) - continue - } else { - defer closer() - - hash160, hash512 := fic.CreateHashBuffers(fd) - - if _, err := fic.CheckBufferHash(hash160, hash512, digests[fname]); err != nil { - errs = multierr.Append(errs, NewFileError(exercice, fname, err)) - } else if size, err := GetFileSize(i, path.Join(exercice.Path, "files", fname)); err != nil { - errs = multierr.Append(errs, NewFileError(exercice, fname, err)) - } else { - var digest_shown []byte - if strings.HasSuffix(fname, ".gz") { - if d, exists := digests[strings.TrimSuffix(fname, ".gz")]; exists { - digest_shown = d - - // Check that gunzipped file digest is correct - if fd, closer, err := GetFile(i, path.Join(exercice.Path, "files", fname)); err != nil { - errs = multierr.Append(errs, NewFileError(exercice, fname, fmt.Errorf("unable to read file: %w", err))) - continue - } else if gunzipfd, err := gzip.NewReader(fd); err != nil { - closer() - errs = multierr.Append(errs, NewFileError(exercice, fname, fmt.Errorf("unable to gunzip file: %w", err))) - continue - } else { - defer gunzipfd.Close() - defer closer() - - hash160_inflate, hash512_inflate := fic.CreateHashBuffers(gunzipfd) - - if _, err := fic.CheckBufferHash(hash160_inflate, hash512_inflate, digest_shown); err != nil { - errs = multierr.Append(errs, NewFileError(exercice, strings.TrimSuffix(fname, ".gz"), err)) - } - } - } - } - - disclaimer := "" - if f, exists := paramsFiles[fname]; exists { - // Call checks hooks - for _, hk := range hooks.mdTextHooks { - for _, err := range multierr.Errors(hk(f.Disclaimer, exercice.Language, exceptions)) { - errs = multierr.Append(errs, NewFileError(exercice, fname, err)) - } - } - - if disclaimer, err = ProcessMarkdown(i, fixnbsp(f.Disclaimer), exercice.Path); err != nil { - errs = multierr.Append(errs, NewFileError(exercice, fname, fmt.Errorf("error during markdown formating of disclaimer: %w", err))) - } - } - - file := exercice.NewDummyFile(path.Join(exercice.Path, "files", fname), GetDestinationFilePath(path.Join(exercice.Path, "files", fname), nil), (*hash512).Sum(nil), digest_shown, disclaimer, size) - - // Call checks hooks - for _, h := range hooks.fileHooks { - for _, e := range multierr.Errors(h(file, exercice, exceptions)) { - errs = multierr.Append(errs, NewFileError(exercice, fname, e)) - } - } - } - } - - files = append(files, fname) - } - return -} - -// DownloadExerciceFile is responsible to fetch remote files. -func DownloadExerciceFile(pf ExerciceFile, dest string, exercice *fic.Exercice, force bool) (errs error) { - if st, err := os.Stat(dest); !force && !os.IsNotExist(err) { - resp, err := http.Head(pf.URL) - if err == nil && resp.ContentLength == st.Size() { - return - } - } - - if !isURLAllowed(pf.URL) { - errs = multierr.Append(errs, NewFileError(exercice, path.Base(dest), fmt.Errorf("URL hostname is not whitelisted"))) - return - } - - log.Println("Download exercice file: ", pf.URL) - - resp, err := http.Get(pf.URL) - if err != nil { - errs = multierr.Append(errs, NewFileError(exercice, path.Base(dest), err)) - return - } - defer resp.Body.Close() - - if err = os.MkdirAll(path.Dir(dest), 0751); err != nil { - errs = multierr.Append(errs, NewFileError(exercice, path.Base(dest), err)) - return - } - - // Write file - var fdto *os.File - if fdto, err = os.Create(dest); err != nil { - errs = multierr.Append(errs, NewFileError(exercice, path.Base(dest), err)) - return - } else { - defer fdto.Close() - - _, err = io.Copy(fdto, resp.Body) - if err != nil { - errs = multierr.Append(errs, NewFileError(exercice, path.Base(dest), err)) - return - } - } - - return -} - -type importedFile struct { - file interface{} - Name string -} - -func SyncExerciceFiles(i Importer, exercice *fic.Exercice, paramsFiles map[string]ExerciceFile, actionAfterImport func(fname string, digests map[string][]byte, filePath, origin string) (interface{}, error)) (ret []*importedFile, errs error) { - files, digests, berrs := BuildFilesListInto(i, exercice, "files") - errs = multierr.Append(errs, berrs) - - // Import standard files - for _, fname := range files { - var f interface{} - var err error - - if pf, exists := paramsFiles[fname]; exists && pf.URL != "" && !i.Exists(path.Join(exercice.Path, "files", fname)) { - dest := GetDestinationFilePath(pf.URL, &pf.Filename) - - if _, err := os.Stat(dest); !os.IsNotExist(err) { - if d, err := actionAfterImport(fname, digests, dest, pf.URL); err == nil { - f = d - } - } - - if f == nil { - errs = multierr.Append(errs, DownloadExerciceFile(paramsFiles[fname], dest, exercice, false)) - - f, err = actionAfterImport(fname, digests, dest, pf.URL) - } - } else { - f, err = i.importFile(path.Join(exercice.Path, "files", fname), func(filePath, origin string) (interface{}, error) { - return actionAfterImport(fname, digests, filePath, origin) - }) - } - - if err != nil { - errs = multierr.Append(errs, NewFileError(exercice, fname, err)) - continue - } - - ret = append(ret, &importedFile{ - f, - fname, - }) - } - - return -} - -// ImportExerciceFiles reads the content of files/ directory and import it as EFile for the given challenge. -// It takes care of DIGESTS.txt and ensure imported files match. -func ImportExerciceFiles(i Importer, exercice *fic.Exercice, exceptions *CheckExceptions) (errs error) { - if _, err := exercice.WipeFiles(); err != nil { - errs = multierr.Append(errs, err) - } - - paramsFiles, err := GetExerciceFilesParams(i, exercice) - if err != nil { - errs = multierr.Append(errs, NewChallengeTxtError(exercice, 0, err)) - return - } - - actionAfterImport := func(fname string, digests map[string][]byte, filePath, origin string) (interface{}, error) { - var digest_shown []byte - if strings.HasSuffix(fname, ".gz") { - if d, exists := digests[strings.TrimSuffix(fname, ".gz")]; exists { - digest_shown = d - } - } - - published := true - disclaimer := "" - if f, exists := paramsFiles[fname]; exists { - published = !f.Hidden - - // Call checks hooks - for _, hk := range hooks.mdTextHooks { - for _, err := range multierr.Errors(hk(f.Disclaimer, exercice.Language, exceptions)) { - errs = multierr.Append(errs, NewFileError(exercice, fname, err)) - } - } - - if disclaimer, err = ProcessMarkdown(i, fixnbsp(f.Disclaimer), exercice.Path); err != nil { - errs = multierr.Append(errs, NewFileError(exercice, fname, fmt.Errorf("error during markdown formating of disclaimer: %w", err))) - } - } - - return exercice.ImportFile(filePath, origin, digests[fname], digest_shown, disclaimer, published) - } - - files, berrs := SyncExerciceFiles(i, exercice, paramsFiles, actionAfterImport) - errs = multierr.Append(errs, berrs) - - // Import files in db - for _, file := range files { - fname := file.Name - f := file.file - - if f.(*fic.EFile).Size == 0 { - errs = multierr.Append(errs, NewFileError(exercice, fname, fmt.Errorf("imported file is empty!"))) - } else { - file := f.(*fic.EFile) - - // Call checks hooks - for _, h := range hooks.fileHooks { - for _, e := range multierr.Errors(h(file, exercice, exceptions)) { - errs = multierr.Append(errs, NewFileError(exercice, fname, e)) - } - } - - // Create empty non-gziped file for nginx gzip-static module - if len(file.ChecksumShown) > 0 && strings.HasSuffix(file.Name, ".gz") { - file.Name = strings.TrimSuffix(file.Name, ".gz") - file.Path = strings.TrimSuffix(file.Path, ".gz") - - fd, err := os.Create(path.Join(fic.FilesDir, file.Path)) - if err == nil { - fd.Close() - - _, err = file.Update() - if err != nil { - log.Println("Unable to update file after .gz removal:", err.Error()) - } - } else { - log.Printf("Unable to create %q: %s", file.Path, err) - } - } - } - } - return -} - -func GetRemoteExerciceFiles(thid, exid string) ([]*fic.EFile, error) { - theme, exceptions, errs := BuildTheme(GlobalImporter, thid) - if theme == nil { - return nil, errs - } - - exercice, _, _, _, _, errs := BuildExercice(GlobalImporter, theme, path.Join(theme.Path, exid), nil, exceptions) - if exercice == nil { - return nil, errs - } - - files, digests, errs := BuildFilesListInto(GlobalImporter, exercice, "files") - if files == nil { - return nil, errs - } - - var ret []*fic.EFile - for _, fname := range files { - fPath := path.Join(exercice.Path, "files", fname) - fSize, _ := GetFileSize(GlobalImporter, fPath) - - file := fic.EFile{ - Path: fPath, - Name: fname, - Checksum: digests[fname], - Size: fSize, - Published: true, - } - - if d, exists := digests[strings.TrimSuffix(file.Name, ".gz")]; exists { - file.Name = strings.TrimSuffix(file.Name, ".gz") - file.Path = strings.TrimSuffix(file.Path, ".gz") - file.ChecksumShown = d - } - - ret = append(ret, &file) - } - - // Complete with attributes - if paramsFiles, err := GetExerciceFilesParams(GlobalImporter, exercice); err == nil { - for _, file := range ret { - if f, ok := paramsFiles[file.Name]; ok { - file.Published = !f.Hidden - - if disclaimer, err := ProcessMarkdown(GlobalImporter, fixnbsp(f.Disclaimer), exercice.Path); err == nil { - file.Disclaimer = disclaimer - } - } - } - } - - return ret, nil -} - -// ApiGetRemoteExerciceFiles is an accessor to remote exercice files list. -func ApiGetRemoteExerciceFiles(c *gin.Context) { - files, err := GetRemoteExerciceFiles(c.Params.ByName("thid"), c.Params.ByName("exid")) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, files) -} diff --git a/admin/sync/exercice_hints.go b/admin/sync/exercice_hints.go deleted file mode 100644 index acb40bd6..00000000 --- a/admin/sync/exercice_hints.go +++ /dev/null @@ -1,200 +0,0 @@ -package sync - -import ( - "bufio" - "crypto" - "encoding/hex" - "fmt" - "io" - "net/http" - "os" - "path" - "strings" - - "github.com/gin-gonic/gin" - "go.uber.org/multierr" - _ "golang.org/x/crypto/blake2b" - - "srs.epita.fr/fic-server/libfic" -) - -type importHint struct { - Line int - Hint *fic.EHint - FlagsDeps []int64 -} - -func buildExerciceHints(i Importer, exercice *fic.Exercice, exceptions *CheckExceptions) (hints []importHint, errs error) { - params, _, err := parseExerciceParams(i, exercice.Path) - if err != nil { - errs = multierr.Append(errs, NewChallengeTxtError(exercice, 0, err)) - return - } - - for n, hint := range params.Hints { - h := &fic.EHint{} - if hint.Title == "" { - h.Title = fmt.Sprintf("Astuce #%d", n+1) - } else { - h.Title = fixnbsp(hint.Title) - } - if hint.Cost == nil { - h.Cost = exercice.Gain / 4 - } else { - h.Cost = *hint.Cost - } - - if hint.Filename != "" { - if hint.Content != "" { - errs = multierr.Append(errs, NewHintError(exercice, h, n, fmt.Errorf("content and filename can't be filled at the same time"))) - continue - } else if !i.Exists(path.Join(exercice.Path, "files", hint.Filename)) { - errs = multierr.Append(errs, NewHintError(exercice, h, n, fmt.Errorf("%q: File not found", hint.Filename))) - continue - } else { - // Handle files as downloadable content - if res, err := i.importFile(path.Join(exercice.Path, "files", hint.Filename), func(filePath string, origin string) (interface{}, error) { - // Calculate hash - hash512 := crypto.BLAKE2b_512.New() - if fd, err := os.Open(filePath); err != nil { - return nil, err - } else { - defer fd.Close() - - reader := bufio.NewReader(fd) - if _, err := io.Copy(hash512, reader); err != nil { - return nil, err - } - } - result512 := hash512.Sum(nil) - - // Special format for downloadable hints: $FILES + hexhash + path from FILES/ - return "$FILES" + hex.EncodeToString(result512) + strings.TrimPrefix(filePath, fic.FilesDir), nil - }); err != nil { - errs = multierr.Append(errs, NewHintError(exercice, h, n, fmt.Errorf("%q: unable to import hint file: %w", hint.Filename, err))) - continue - } else if s, ok := res.(string); !ok { - errs = multierr.Append(errs, NewHintError(exercice, h, n, fmt.Errorf("%q: unable to import hint file: invalid string returned as filename", hint.Filename))) - continue - } else { - h.Content = s - } - } - } else if hint.Content == "" { - errs = multierr.Append(errs, NewHintError(exercice, h, n, fmt.Errorf("content and filename can't be empty at the same time"))) - continue - } else { - // Call checks hooks - for _, hk := range hooks.mdTextHooks { - for _, err := range multierr.Errors(hk(h.Content, exercice.Language, exceptions)) { - errs = multierr.Append(errs, NewHintError(exercice, h, n, err)) - } - } - - if h.Content, err = ProcessMarkdown(i, fixnbsp(hint.Content), exercice.Path); err != nil { - errs = multierr.Append(errs, NewHintError(exercice, h, n, fmt.Errorf("error during markdown formating: %w", err))) - } - } - - // Call checks hooks - for _, hook := range hooks.hintHooks { - for _, e := range multierr.Errors(hook(h, exercice, exceptions)) { - errs = multierr.Append(errs, NewHintError(exercice, h, n, e)) - } - } - - newHint := importHint{ - Line: n + 1, - Hint: h, - } - - // Read dependency to flag - for _, nf := range hint.NeedFlag { - newHint.FlagsDeps = append(newHint.FlagsDeps, nf.Id) - } - - hints = append(hints, newHint) - } - - return -} - -// CheckExerciceHints checks if all hints are corrects.. -func CheckExerciceHints(i Importer, exercice *fic.Exercice, exceptions *CheckExceptions) ([]importHint, error) { - exceptions = exceptions.GetFileExceptions("challenge.toml", "challenge.txt") - - hints, errs := buildExerciceHints(i, exercice, exceptions) - - for _, hint := range hints { - if hint.Hint.Cost >= exercice.Gain { - errs = multierr.Append(errs, NewHintError(exercice, hint.Hint, hint.Line, fmt.Errorf("hint's cost is higher than exercice gain"))) - } - } - - return hints, errs -} - -// SyncExerciceHints reads the content of files/ directories and import it as EHint for the given challenge. -func SyncExerciceHints(i Importer, exercice *fic.Exercice, flagsBindings map[int64]fic.Flag, exceptions *CheckExceptions) (hintsBindings map[int]*fic.EHint, errs error) { - if _, err := exercice.WipeHints(); err != nil { - errs = multierr.Append(errs, err) - } else { - exceptions = exceptions.GetFileExceptions("challenge.toml", "challenge.txt") - - hints, berrs := buildExerciceHints(i, exercice, exceptions) - errs = multierr.Append(errs, berrs) - - hintsBindings = map[int]*fic.EHint{} - - for _, hint := range hints { - // Import hint - if h, err := exercice.AddHint(hint.Hint.Title, hint.Hint.Content, hint.Hint.Cost); err != nil { - errs = multierr.Append(errs, NewHintError(exercice, hint.Hint, hint.Line, err)) - } else { - hintsBindings[hint.Line] = h - - // Handle hints dependencies on flags - for _, nf := range hint.FlagsDeps { - if f, ok := flagsBindings[nf]; ok { - if herr := h.AddDepend(f); herr != nil { - errs = multierr.Append(errs, NewHintError(exercice, hint.Hint, hint.Line, fmt.Errorf("error hint dependency to flag #%d: %w", nf, herr))) - } - } else { - errs = multierr.Append(errs, NewHintError(exercice, hint.Hint, hint.Line, fmt.Errorf("error hint dependency to flag #%d: Unexistant flag", nf))) - } - } - } - } - } - return -} - -func GetRemoteExerciceHints(thid, exid string) ([]importHint, error) { - theme, exceptions, errs := BuildTheme(GlobalImporter, thid) - if theme == nil { - return nil, errs - } - - exercice, _, _, eexceptions, _, errs := BuildExercice(GlobalImporter, theme, path.Join(theme.Path, exid), nil, exceptions) - if exercice == nil { - return nil, errs - } - - hints, errs := CheckExerciceHints(GlobalImporter, exercice, eexceptions) - if hints == nil { - return nil, errs - } - - return hints, nil -} - -// ApiListRemoteExerciceHints is an accessor letting foreign packages to access remote exercice hints. -func ApiGetRemoteExerciceHints(c *gin.Context) { - hints, errs := GetRemoteExerciceHints(c.Params.ByName("thid"), c.Params.ByName("exid")) - if hints != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, fmt.Errorf("%q", errs)) - return - } - - c.JSON(http.StatusOK, hints) -} diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go deleted file mode 100644 index e62dcf34..00000000 --- a/admin/sync/exercice_keys.go +++ /dev/null @@ -1,730 +0,0 @@ -package sync - -import ( - "fmt" - "math" - "math/rand" - "net/http" - "path" - "sort" - "strconv" - "strings" - "unicode" - - "github.com/gin-gonic/gin" - "go.uber.org/multierr" - - "srs.epita.fr/fic-server/libfic" -) - -// isFullGraphic detects if some rune are not graphic one. -// This function is usefull to display warning when importing key ending with \r. -func isFullGraphic(s string) bool { - for _, c := range s { - if !unicode.IsGraphic(c) { - return false - } - } - return true -} - -func validatorRegexp(vre string) (validator_regexp *string) { - if len(vre) > 0 { - validator_regexp = &vre - } else { - validator_regexp = nil - } - return -} - -func getRawKey(input interface{}, validatorRe string, ordered bool, showLines bool, separator string) (raw string, prep string, errs error) { - // Concatenate array - if f, ok := input.([]interface{}); ok { - if len(validatorRe) > 0 { - errs = multierr.Append(errs, fmt.Errorf("ValidatorRe cannot be defined for this kind of flag.")) - validatorRe = "" - } - - if len(separator) == 0 { - separator = "," - } else if len(separator) > 1 { - separator = string(separator[0]) - errs = multierr.Append(errs, fmt.Errorf("separator truncated to %q", separator)) - } - - var fitems []string - for i, v := range f { - if g, ok := v.(string); ok { - if strings.Index(g, separator) != -1 { - errs = multierr.Append(errs, fmt.Errorf("flag items cannot contain %q character as it is used as separator. Change the separator attribute for this flag.", separator)) - return - } else { - fitems = append(fitems, g) - } - } else { - errs = multierr.Append(errs, fmt.Errorf("item %d has an invalid type: can only be string, is %T.", i, g)) - return - } - } - - ignord := "f" - if !ordered { - // Sort the list without taking the case in count. - sort.Slice(fitems, func(i, j int) bool { - return strings.ToLower(fitems[i]) < strings.ToLower(fitems[j]) - }) - ignord = "t" - } - - nbLines := 0 - if showLines { - if len(fitems) > 9 { - errs = multierr.Append(errs, fmt.Errorf("too much items in vector to use ShowLines features, max 9.")) - } else { - nbLines = len(fitems) - } - } - - raw = strings.Join(fitems, separator) + separator - - prep = fmt.Sprintf("`%s%s%d", separator, ignord, nbLines) - } else if f, ok := input.(int64); ok { - raw = fmt.Sprintf("%d", f) - } else if f, ok := input.(float64); ok { - raw = strconv.FormatFloat(f, 'f', -1, 64) - } else if f, ok := input.(string); !ok { - errs = multierr.Append(errs, fmt.Errorf("has an invalid type: can only be []string or string, not %T", input)) - return - } else { - raw = f - } - return -} - -func buildLabelFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, exceptions *CheckExceptions) (f *fic.FlagLabel, errs error) { - if len(flag.Label) == 0 { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, flagline, fmt.Errorf("Label cannot be empty."))) - return - } - - // Call checks hooks - for _, h := range hooks.mdTextHooks { - for _, err := range multierr.Errors(h(flag.Label, exercice.Language, exceptions.Filter2ndCol(strconv.Itoa(flagline)))) { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, flagline, err)) - } - } - - if mdlabel, err := ProcessMarkdown(GlobalImporter, flag.Label, exercice.Path); err != nil { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, flagline, fmt.Errorf("unable to parse property label as Markdown: %w", err))) - } else { - if strings.Count(flag.Label, "\n\n") == 0 { - flag.Label = mdlabel[3 : len(mdlabel)-4] - } else { - flag.Label = mdlabel - } - } - - if flag.Raw != nil { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, flagline, fmt.Errorf("raw cannot be defined."))) - } - - if len(flag.Choice) != 0 { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, flagline, fmt.Errorf("choices cannot be defined."))) - } - - f = &fic.FlagLabel{ - Order: int8(flagline), - Label: flag.Label, - Variant: flag.Variant, - } - - // Call checks hooks - for _, h := range hooks.flagLabelHooks { - for _, e := range multierr.Errors(h(f, exercice, exceptions.Filter2ndCol(strconv.Itoa(flagline)))) { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, flagline, e)) - } - } - return -} - -func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defaultLabel string, exceptions *CheckExceptions) (f *fic.Flag, choices []*fic.FlagChoice, errs error) { - if len(flag.Label) == 0 { - flag.Label = defaultLabel - } - - if len(flag.Variant) != 0 { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, flagline, fmt.Errorf("variant is not defined for this kind of flag."))) - } - - if flag.Label[0] == '`' { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, flagline, fmt.Errorf("Label should not begin with `."))) - flag.Label = flag.Label[1:] - } - - raw, prep, terrs := getRawKey(flag.Raw, flag.CaptureRe, flag.Ordered, flag.ShowLines, flag.Separator) - - errors := multierr.Errors(terrs) - if len(errors) > 0 { - for _, terr := range errors { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, flagline, terr)) - } - f = nil - return - } - flag.Label = prep + flag.Label - - if len(flag.Label) > 255 && flag.Type != "label" { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, flagline, fmt.Errorf("label is too long (max 255 chars per label)."))) - } - - if (flag.Type == "text" && !isFullGraphic(strings.Replace(raw, "\n", "", -1))) || (flag.Type != "text" && !isFullGraphic(raw)) { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, flagline, fmt.Errorf("WARNING non-printable characters in flag, is this really expected?"))) - } - - hashedFlag, err := fic.ComputeHashedFlag([]byte(raw), !flag.CaseSensitive, flag.NoTrim, validatorRegexp(flag.CaptureRe), flag.SortReGroups) - if err != nil { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, flagline, err)) - return - } - fk := &fic.FlagKey{ - Type: flag.Type, - IdExercice: exercice.Id, - Order: int8(flagline), - Label: flag.Label, - Placeholder: flag.Placeholder, - Help: flag.Help, - Unit: flag.Unit, - IgnoreCase: !flag.CaseSensitive, - Multiline: flag.Type == "text", - CaptureRegexp: validatorRegexp(flag.CaptureRe), - SortReGroups: flag.SortReGroups, - Checksum: hashedFlag[:], - ChoicesCost: flag.ChoicesCost, - BonusGain: flag.BonusGain, - } - - // Call checks hooks - for _, h := range hooks.flagKeyHooks { - for _, e := range multierr.Errors(h(fk, raw, exercice, exceptions.Filter2ndCol(strconv.Itoa(flagline)))) { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, flagline, e)) - } - } - - fl := fic.Flag(fk) - f = &fl - - if len(flag.Choice) > 0 || (flag.Type == "ucq" || flag.Type == "radio") { - // Import choices - hasOne := false - - if !flag.NoShuffle { - rand.Shuffle(len(flag.Choice), func(i, j int) { - flag.Choice[i], flag.Choice[j] = flag.Choice[j], flag.Choice[i] - }) - } - - for _, choice := range flag.Choice { - val, prep, terrs := getRawKey(choice.Value, "", false, false, "") - errors := multierr.Errors(terrs) - if len(errors) > 0 { - for _, terr := range errors { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, flagline, terr)) - } - continue - } - - if len(choice.Label) == 0 { - choice.Label = val - } - choice.Label = prep + choice.Label - - if !flag.CaseSensitive { - val = strings.ToLower(val) - } - - fc := &fic.FlagChoice{ - Label: choice.Label, - Value: val, - } - - // Call checks hooks - for _, h := range hooks.flagChoiceHooks { - for _, e := range multierr.Errors(h(fc, exercice, exceptions.Filter2ndCol(strconv.Itoa(flagline)))) { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, flagline, e)) - } - } - - choices = append(choices, fc) - - if val == "true" || val == "false" { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, flagline, fmt.Errorf("value can't be %q, this is not a MCQ, the value has to be meaningful. The value is shown to players as response identifier.", val))) - } - - if val == raw || (!flag.CaseSensitive && val == strings.ToLower(raw)) { - hasOne = true - } - } - if !hasOne { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, flagline, fmt.Errorf("no valid answer defined."))) - } - - // Call checks hooks - for _, h := range hooks.flagKeyWithChoicesHooks { - for _, e := range multierr.Errors(h(fk, raw, choices, exercice, exceptions.Filter2ndCol(strconv.Itoa(flagline)))) { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, flagline, e)) - } - } - } - return -} - -type importFlag struct { - origin ExerciceFlag - Line int - Flag fic.Flag - JustifyOf *fic.MCQ_entry - Choices []*fic.FlagChoice - FilesDeps []string - FlagsDeps []int64 -} - -func iface2Number(input interface{}, output *string) (norm float64, err error) { - if input != nil { - if v, ok := input.(int64); ok { - *output = fmt.Sprintf("%d", v) - norm = float64(v) - } else if v, ok := input.(float64); ok { - *output = strconv.FormatFloat(v, 'f', -1, 64) - norm = v - } else { - err = fmt.Errorf("has an invalid type: expected int or float, got %T", input) - } - } - return -} - -// buildExerciceFlag read challenge.txt and extract all flags. -func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nline int, exceptions *CheckExceptions) (ret []importFlag, errs error) { - switch strings.ToLower(flag.Type) { - case "": - flag.Type = "key" - case "label": - flag.Type = "label" - case "key": - flag.Type = "key" - case "number": - var smin, smax, sstep string - fstep, err := iface2Number(flag.NumberStep, &sstep) - if err != nil { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("step %w", err))) - } - - _, err = iface2Number(flag.NumberMin, &smin) - if err != nil { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("min %w", err))) - } - - _, err = iface2Number(flag.NumberMax, &smax) - if err != nil { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("max %w", err))) - } - - // Ensure step permit validating the flag - if rns, err := flag.RawNumber(); err != nil { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("raw %w", err))) - } else { - if fstep == 0 { - fstep = 1.0 - } - - for _, rn := range rns { - v := math.Abs(rn) / fstep - if float64(int(v)) != v { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("choosen step=%f doesn't include response=%f", fstep, rn))) - } - } - } - - flag.Type = fmt.Sprintf("number,%s,%s,%s", smin, smax, sstep) - case "text": - flag.Type = "text" - case "vector": - flag.Type = "vector" - case "ucq": - flag.Type = "ucq" - case "radio": - flag.Type = "radio" - case "mcq": - flag.Type = "mcq" - case "justified": - flag.Type = "justified" - default: - errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("invalid type of flag: should be 'key', 'number', 'text', 'mcq', 'justified', 'ucq', 'radio' or 'vector'"))) - return - } - - if !strings.HasPrefix(flag.Type, "number") { - if flag.NumberMin != nil { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("property min undefined for this kind of flag: should the type be 'number'"))) - } else if flag.NumberMax != nil { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("property max undefined for this kind of flag: should the type be 'number'"))) - } else if flag.NumberStep != nil { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("property step undefined for this kind of flag: should the type be 'number'"))) - } - } - - if len(flag.Help) > 0 { - // Call checks hooks - for _, hk := range hooks.mdTextHooks { - for _, err := range multierr.Errors(hk(flag.Help, exercice.Language, exceptions)) { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, err)) - } - } - - if mdhelp, err := ProcessMarkdown(i, flag.Help, exercice.Path); err != nil { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("unable to parse property help as Markdown: %w", err))) - } else { - flag.Help = mdhelp[3 : len(mdhelp)-4] - } - } - - if flag.Type == "label" { - addedFlag, berrs := buildLabelFlag(exercice, flag, nline+1, exceptions) - errs = multierr.Append(errs, berrs) - if addedFlag != nil { - ret = append(ret, importFlag{ - origin: flag, - Line: nline + 1, - Flag: addedFlag, - }) - } - } else if flag.Type == "key" || strings.HasPrefix(flag.Type, "number") || flag.Type == "text" || flag.Type == "ucq" || flag.Type == "radio" || flag.Type == "vector" { - addedFlag, choices, berrs := buildKeyFlag(exercice, flag, nline+1, "Flag", exceptions) - errs = multierr.Append(errs, berrs) - if addedFlag != nil { - ret = append(ret, importFlag{ - origin: flag, - Line: nline + 1, - Flag: *addedFlag, - Choices: choices, - }) - } - } else if flag.Type == "mcq" || flag.Type == "justified" { - addedFlag := fic.MCQ{ - IdExercice: exercice.Id, - Order: int8(nline + 1), - Title: flag.Label, - Entries: []*fic.MCQ_entry{}, - } - - hasOne := false - isJustified := flag.Type == "justified" - - if len(flag.Variant) != 0 { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("variant is not defined for this kind of flag"))) - } - - if !flag.NoShuffle { - rand.Shuffle(len(flag.Choice), func(i, j int) { - flag.Choice[i], flag.Choice[j] = flag.Choice[j], flag.Choice[i] - }) - } - - for cid, choice := range flag.Choice { - var val bool - - if choice.Raw != nil { - if hasOne && !isJustified { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("all true items has to be justified in this MCQ"))) - continue - } - - val = true - isJustified = true - } else if p, ok := choice.Value.(bool); ok { - val = p - if isJustified { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("all true items has to be justified in this MCQ"))) - continue - } - } else if choice.Value == nil { - val = false - } else { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("choice %d: incorrect type for value: %T is not boolean.", cid, choice.Value))) - continue - } - - entry := &fic.MCQ_entry{ - Label: choice.Label, - Response: val, - } - addedFlag.Entries = append(addedFlag.Entries, entry) - - if isJustified && choice.Raw != nil { - addedFlag, choices, berrs := buildKeyFlag(exercice, choice.ExerciceFlag, nline+1, "Flag correspondant", exceptions) - errs = multierr.Append(errs, berrs) - if addedFlag != nil { - ret = append(ret, importFlag{ - origin: flag, - Line: nline + 1, - Flag: *addedFlag, - JustifyOf: entry, - Choices: choices, - }) - } - } - } - - // Call checks hooks - for _, h := range hooks.flagMCQHooks { - for _, e := range multierr.Errors(h(&addedFlag, addedFlag.Entries, exercice, exceptions)) { - errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, e)) - } - } - - ret = append([]importFlag{importFlag{ - origin: flag, - Line: nline + 1, - Flag: &addedFlag, - }}, ret...) - } - return -} - -// buildExerciceFlags read challenge.txt and extract all flags. -func buildExerciceFlags(i Importer, exercice *fic.Exercice, exceptions *CheckExceptions) (flags map[int64]importFlag, flagids []int64, errs error) { - params, gerrs := getExerciceParams(i, exercice) - if len(multierr.Errors(gerrs)) > 0 { - return flags, flagids, gerrs - } - - flags = map[int64]importFlag{} - - for nline, flag := range params.Flags { - if flag.Id == 0 { - // TODO: should be more smart than that. Perhaps search to increment if possible. - flag.Id = rand.Int63() - } - - // Ensure flag ID is unique - for _, ok := flags[flag.Id]; ok; _, ok = flags[flag.Id] { - errs = multierr.Append(errs, NewFlagError(exercice, ¶ms.Flags[nline], nline+1, fmt.Errorf("identifier already used (%d), using a random one.", flag.Id))) - flag.Id = rand.Int63() - } - - newFlags, ferrs := buildExerciceFlag(i, exercice, flag, nline, exceptions) - errs = multierr.Append(errs, ferrs) - if len(newFlags) > 0 { - for _, newFlag := range newFlags { - fId := flag.Id - for _, ok := flags[fId]; ok; _, ok = flags[fId] { - fId = rand.Int63() - } - - // Read dependency to flag - for _, nf := range flag.NeedFlag { - if len(nf.Theme) > 0 { - errs = multierr.Append(errs, NewFlagError(exercice, ¶ms.Flags[nline], nline+1, fmt.Errorf("dependancy on another scenario is not implemented yet."))) - } - newFlag.FlagsDeps = append(newFlag.FlagsDeps, nf.Id) - } - for _, nf := range flag.NeedFlags { - newFlag.FlagsDeps = append(newFlag.FlagsDeps, nf) - } - - // Read dependency to file - for _, lf := range flag.LockedFile { - newFlag.FilesDeps = append(newFlag.FilesDeps, lf.Filename) - } - - flags[fId] = newFlag - flagids = append(flagids, fId) - } - } - } - - return -} - -// CheckExerciceFlags checks if all flags for the given challenge are correct. -func CheckExerciceFlags(i Importer, exercice *fic.Exercice, files []string, exceptions *CheckExceptions) (rf []fic.Flag, errs error) { - exceptions = exceptions.GetFileExceptions("challenge.toml", "challenge.txt") - - flags, flagsids, berrs := buildExerciceFlags(i, exercice, exceptions) - errs = multierr.Append(errs, berrs) - - for _, flagid := range flagsids { - if flag, ok := flags[flagid]; ok { - // Check dependency to flag - for _, nf := range flag.FlagsDeps { - if _, ok := flags[nf]; !ok { - errs = multierr.Append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("flag depend on flag id=%d: id not defined", nf))) - } - } - - if fk, ok := flag.Flag.(*fic.FlagKey); ok { - // Check dependency to flag optional flag - if fk.BonusGain == 0 { - for _, nf := range flag.FlagsDeps { - if fk2, ok := flags[nf].Flag.(*fic.FlagKey); ok && fk2.BonusGain != 0 { - errs = multierr.Append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("flag is not optional but depend on flag id=%d which is optional", nf))) - } - } - } - - if int64(fk.ChoicesCost) >= exercice.Gain { - errs = multierr.Append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("flag's choice_cost is higher than exercice gain"))) - } - - if raw, ok := flag.origin.Raw.(string); ok && raw == fk.Placeholder { - errs = multierr.Append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("flag's placeholder and raw are identical"))) - } - } - - // Check dependency loop - deps := flag.FlagsDeps - for i := 0; i < len(deps); i++ { - if deps[i] == flagid { - errs = multierr.Append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("flag dependency loop detected: flag id=%d: depends on itself", flagid))) - break - } - - deploppadd: - for _, d := range flags[deps[i]].FlagsDeps { - for _, dd := range deps { - if dd == d { - continue deploppadd - } - } - deps = append(deps, d) - } - } - - // Check dependency to file - for _, lf := range flag.FilesDeps { - found := false - for _, f := range files { - if f == lf { - found = true - break - } - } - if !found { - errs = multierr.Append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("flag depend on %s: No such file", lf))) - } - } - - rf = append(rf, flag.Flag) - } - } - - return -} - -// ExerciceFlagsMap builds the flags bindings between challenge.txt and DB. -func ExerciceFlagsMap(i Importer, exercice *fic.Exercice) (kmap map[int64]fic.Flag) { - flags, flagids, _ := buildExerciceFlags(i, exercice, nil) - - kmap = map[int64]fic.Flag{} - - for _, flagid := range flagids { - if flag, ok := flags[flagid]; ok { - if addedFlag, err := flag.Flag.RecoverId(); err == nil { - kmap[flagid] = addedFlag - } - } - } - - return -} - -// SyncExerciceFlags imports all kind of flags for the given challenge. -func SyncExerciceFlags(i Importer, exercice *fic.Exercice, exceptions *CheckExceptions) (kmap map[int64]fic.Flag, errs error) { - if _, err := exercice.WipeFlags(); err != nil { - errs = multierr.Append(errs, err) - } else if _, err := exercice.WipeMCQs(); err != nil { - errs = multierr.Append(errs, err) - } else { - exceptions = exceptions.GetFileExceptions("challenge.toml", "challenge.txt") - - flags, flagids, berrs := buildExerciceFlags(i, exercice, exceptions) - errs = multierr.Append(errs, berrs) - - kmap = map[int64]fic.Flag{} - - // Import flags - for _, flagid := range flagids { - if flag, ok := flags[flagid]; ok { - if flag.JustifyOf != nil { - if f, ok := flag.Flag.(*fic.FlagKey); ok { - f.Label = fmt.Sprintf("%%%d%%%s", flag.JustifyOf.Id, f.Label) - } - } - - if addedFlag, err := exercice.AddFlag(flag.Flag); err != nil { - errs = multierr.Append(errs, NewFlagError(exercice, nil, flag.Line, err)) - } else { - if f, ok := addedFlag.(*fic.FlagKey); ok { - for _, choice := range flag.Choices { - if _, err := f.AddChoice(choice); err != nil { - errs = multierr.Append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("choice #FIXME: %w", err))) - } - } - } - - kmap[flagid] = addedFlag - - // Import dependency to flag - for _, nf := range flag.FlagsDeps { - if rf, ok := kmap[nf]; !ok { - errs = multierr.Append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("dependency to flag id=%d: id not defined, perhaps not available at time of processing", nf))) - } else if err := addedFlag.AddDepend(rf); err != nil { - errs = multierr.Append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("dependency to id=%d: %w", nf, err))) - } - } - - // Import dependency to file - for _, lf := range flag.FilesDeps { - if rf, err := exercice.GetFileByFilename(lf); err != nil { - errs = multierr.Append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("dependency to %s: %w", lf, err))) - } else if err := rf.AddDepend(addedFlag); err != nil { - errs = multierr.Append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("dependency to %s: %w", lf, err))) - } - } - } - } - } - } - - return -} - -func GetRemoteExerciceFlags(thid, exid string) ([]fic.Flag, error) { - theme, exceptions, errs := BuildTheme(GlobalImporter, thid) - if theme == nil { - return nil, errs - } - - exercice, _, _, eexceptions, _, errs := BuildExercice(GlobalImporter, theme, path.Join(theme.Path, exid), nil, exceptions) - if exercice == nil { - return nil, errs - } - - flags, errs := CheckExerciceFlags(GlobalImporter, exercice, []string{}, eexceptions) - if flags == nil { - return nil, errs - } - - return flags, nil -} - -// ApiListRemoteExerciceFlags is an accessor letting foreign packages to access remote exercice flags. -func ApiGetRemoteExerciceFlags(c *gin.Context) { - flags, err := GetRemoteExerciceFlags(c.Params.ByName("thid"), c.Params.ByName("exid")) - if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, flags) -} diff --git a/admin/sync/exercices.go b/admin/sync/exercices.go deleted file mode 100644 index b452d4b6..00000000 --- a/admin/sync/exercices.go +++ /dev/null @@ -1,531 +0,0 @@ -package sync - -import ( - "bytes" - "fmt" - "hash/adler32" - "image" - "net/http" - "net/url" - "path" - "strconv" - "strings" - - "github.com/BurntSushi/toml" - "github.com/gin-gonic/gin" - "github.com/yuin/goldmark" - "go.uber.org/multierr" - - "srs.epita.fr/fic-server/libfic" -) - -// Set AllowWIPExercice if WIP exercices are accepted. -var AllowWIPExercice bool = false - -func fixnbsp(s string) string { - return strings.Replace(strings.Replace(strings.Replace(s, " ?", " ?", -1), " !", " !", -1), " :", " :", -1) -} - -// GetExercices returns all exercice directories existing in a given theme, considering the given Importer. -func GetExercices(i Importer, theme *fic.Theme) ([]string, error) { - var exercices []string - - if len(theme.Path) == 0 { - return []string{}, nil - } else if dirs, err := i.ListDir(theme.Path); err != nil { - return []string{}, err - } else { - for _, dir := range dirs { - if _, err := i.ListDir(path.Join(theme.Path, dir)); err == nil { - if dir[0] != '.' && strings.Contains(dir, "-") { - exercices = append(exercices, dir) - } - } - } - } - - return exercices, nil -} - -func buildDependancyMap(i Importer, theme *fic.Theme) (dmap map[int64]*fic.Exercice, err error) { - var exercices []string - if exercices, err = GetExercices(i, theme); err != nil { - return - } else { - dmap = map[int64]*fic.Exercice{} - - for _, edir := range exercices { - var eid int - var ename string - eid, ename, err = parseExerciceDirname(edir) - if err != nil { - err = nil - continue - } - - // ename can be overrride by title.txt - if i.Exists(path.Join(theme.Path, edir, "title.txt")) { - if myTitle, err := GetFileContent(i, path.Join(theme.Path, edir, "title.txt")); err == nil { - ename = strings.TrimSpace(myTitle) - } - } - - var e *fic.Exercice - e, err = theme.GetExerciceByTitle(ename) - if err != nil { - return dmap, fmt.Errorf("unable to GetExerciceByTitle(ename=%q, tid=%d): %w", ename, theme.Id, err) - } - - dmap[int64(eid)] = e - } - - return - } -} - -func parseExerciceDirname(edir string) (eid int, ename string, err error) { - edir_splt := strings.SplitN(edir, "-", 2) - if len(edir_splt) != 2 { - err = fmt.Errorf("%q is not a valid exercice directory: missing id prefix", edir) - return - } - - eid, err = strconv.Atoi(edir_splt[0]) - if err != nil { - err = fmt.Errorf("%q: invalid exercice identifier: %s", edir, err) - return - } - - // ID 0: peak a deterministic-random-ordered ID instead - if eid == 0 { - eid = int(adler32.Checksum([]byte(edir_splt[1]))) - } - - ename = edir_splt[1] - - return -} - -// BuildExercice creates an Exercice from a given importer. -func BuildExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]*fic.Exercice, exceptions_in *CheckExceptions) (e *fic.Exercice, p ExerciceParams, eid int, exceptions *CheckExceptions, edir string, errs error) { - e = &fic.Exercice{} - - e.Path = epath - edir = path.Base(epath) - - var err error - eid, e.Title, err = parseExerciceDirname(edir) - if err != nil { - // Ignore eid if we are certain this is an exercice directory, eid will be 0 - if !i.Exists(path.Join(epath, "title.txt")) { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("unable to parse exercice directory: %w", err), theme)) - return nil, p, eid, exceptions_in, edir, errs - } - } - - // Get exceptions - exceptions = LoadExerciceException(i, theme, e, exceptions_in) - //log.Printf("Kept repochecker exceptions for this exercice: %v", exceptions) - - if theme != nil { - e.Language = theme.Language - } - // Overwrite language if language.txt exists - if language, err := GetFileContent(i, path.Join(epath, "language.txt")); err == nil { - language = strings.TrimSpace(language) - if strings.Contains(language, "\n") { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("language.txt: Language can't contain new lines"), theme)) - } else { - e.Language = language - } - } - - // Overwrite title if title.txt exists - if myTitle, err := GetFileContent(i, path.Join(epath, "title.txt")); err == nil { - myTitle = strings.TrimSpace(myTitle) - if strings.Contains(myTitle, "\n") { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("title.txt: Title can't contain new lines"), theme)) - } else { - e.Title = myTitle - } - } - - // Character reserved for WIP exercices - if len(e.Title) > 0 && e.Title[0] == '%' { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("title can't contain start by '%%'"), theme)) - } - - e.URLId = fic.ToURLid(e.Title) - e.Title = fixnbsp(e.Title) - - if i.Exists(path.Join(epath, "AUTHORS.txt")) { - if authors, err := getAuthors(i, epath); err != nil { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("unable to get AUTHORS.txt: %w", err))) - } else { - // Format authors - e.Authors = strings.Join(authors, ", ") - } - } - - // Process headline - if i.Exists(path.Join(epath, "headline.txt")) { - e.Headline, err = GetFileContent(i, path.Join(epath, "headline.txt")) - } else if i.Exists(path.Join(epath, "headline.md")) { - e.Headline, err = GetFileContent(i, path.Join(epath, "headline.md")) - } - if err != nil { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("unable to get exercice's headline: %w", err))) - } - if e.Headline != "" { - // Call checks hooks - for _, h := range hooks.mdTextHooks { - for _, err := range multierr.Errors(h(e.Headline, e.Language, exceptions.GetFileExceptions("headline.md", "headline.txt"))) { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("headline.md: %w", err))) - } - } - } - - // Texts to format using Markdown - if i.Exists(path.Join(epath, "overview.txt")) { - e.Overview, err = GetFileContent(i, path.Join(epath, "overview.txt")) - } else if i.Exists(path.Join(epath, "overview.md")) { - e.Overview, err = GetFileContent(i, path.Join(epath, "overview.md")) - } else { - err = fmt.Errorf("Unable to find overview.txt nor overview.md") - } - if err != nil { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("overview.txt: %s", err), theme)) - } else { - e.Overview = fixnbsp(e.Overview) - - // Call checks hooks - for _, h := range hooks.mdTextHooks { - for _, err := range multierr.Errors(h(e.Overview, e.Language, exceptions.GetFileExceptions("overview.md", "overview.txt"))) { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("overview.md: %w", err))) - } - } - - var buf bytes.Buffer - if e.Headline == "" { - err := goldmark.Convert([]byte(strings.Split(e.Overview, "\n")[0]), &buf) - if err != nil { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("overview.md: an error occurs during markdown formating of the headline: %w", err), theme)) - } else { - e.Headline = string(buf.Bytes()) - } - } - - if e.Overview, err = ProcessMarkdown(i, e.Overview, epath); err != nil { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("overview.md: an error occurs during markdown formating: %w", err), theme)) - } - } - - if i.Exists(path.Join(epath, "statement.txt")) { - e.Statement, err = GetFileContent(i, path.Join(epath, "statement.txt")) - } else if i.Exists(path.Join(epath, "statement.md")) { - e.Statement, err = GetFileContent(i, path.Join(epath, "statement.md")) - } else { - err = fmt.Errorf("Unable to find statement.txt nor statement.md") - } - if err != nil { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("statement.md: %w", err), theme)) - } else { - // Call checks hooks - for _, h := range hooks.mdTextHooks { - for _, err := range multierr.Errors(h(e.Statement, e.Language, exceptions.GetFileExceptions("statement.md", "statement.txt"))) { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("statement.md: %w", err))) - } - } - - if e.Statement, err = ProcessMarkdown(i, fixnbsp(e.Statement), epath); err != nil { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("statement.md: an error occurs during markdown formating: %w", err), theme)) - } - } - - if i.Exists(path.Join(epath, "finished.txt")) { - e.Finished, err = GetFileContent(i, path.Join(epath, "finished.txt")) - } else if i.Exists(path.Join(epath, "finished.md")) { - e.Finished, err = GetFileContent(i, path.Join(epath, "finished.md")) - } - if err != nil { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("finished.md: %w", err), theme)) - } else if len(e.Finished) > 0 { - // Call checks hooks - for _, h := range hooks.mdTextHooks { - for _, err := range multierr.Errors(h(e.Finished, e.Language, exceptions.GetFileExceptions("finished.md", "finished.txt"))) { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("finished.md: %w", err))) - } - } - - if e.Finished, err = ProcessMarkdown(i, e.Finished, epath); err != nil { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("finished.md: an error occurs during markdown formating: %w", err), theme)) - } - } - - if i.Exists(path.Join(epath, "heading.jpg")) { - e.Image = path.Join(epath, "heading.jpg") - } else if i.Exists(path.Join(epath, "heading.png")) { - e.Image = path.Join(epath, "heading.png") - } else if theme == nil || theme.Image == "" { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("heading.jpg: No such file"))) - } - - // Parse challenge.txt - var md toml.MetaData - p, md, err = parseExerciceParams(i, epath) - if err != nil { - errs = multierr.Append(errs, NewChallengeTxtError(e, 0, err, theme)) - return - } - - // Alert about unknown keys in challenge.txt - if len(md.Undecoded()) > 0 { - for _, k := range md.Undecoded() { - errs = multierr.Append(errs, NewChallengeTxtError(e, 0, fmt.Errorf("unknown key %q found, check https://fic.srs.epita.fr/doc/files/challenge/", k), theme)) - } - } - - e.WIP = p.WIP - if p.WIP && !AllowWIPExercice { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("exercice declared Work In Progress in challenge.toml"), theme)) - } - - if p.Gain == 0 { - errs = multierr.Append(errs, NewChallengeTxtError(e, 0, fmt.Errorf("Undefined gain for challenge"), theme)) - } else { - e.Gain = p.Gain - } - - // Handle dependency - if len(p.Dependencies) > 0 { - if len(p.Dependencies[0].Theme) > 0 && (theme == nil || p.Dependencies[0].Theme != theme.Name) { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("unable to treat dependency to another theme (%q): not implemented.", p.Dependencies[0].Theme), theme)) - } else { - if dmap == nil { - if dmap2, err := buildDependancyMap(i, theme); err != nil { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("unable to build dependency map: %w", err), theme)) - } else { - dmap = &dmap2 - } - } - - if dmap != nil { - for edk, ed := range *dmap { - if edk == p.Dependencies[0].Id { - e.Depend = &ed.Id - break - } - } - if e.Depend == nil { - dmap_keys := []string{} - for k, _ := range *dmap { - dmap_keys = append(dmap_keys, fmt.Sprintf("%d", k)) - } - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("Unable to find required exercice dependancy %d (available at time of processing: %s)", p.Dependencies[0].Id, strings.Join(dmap_keys, ",")), theme)) - } - } - } - } - - // Handle resolutions - resolutionFound := false - - e.VideoURI = path.Join(epath, "resolution.mp4") - if !i.Exists(e.VideoURI) { - e.VideoURI = "" - } else if size, err := GetFileSize(i, e.VideoURI); err != nil { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("resolution.mp4: %w", err), theme)) - e.VideoURI = "" - } else if size == 0 { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("resolution.mp4: The file is empty!"), theme)) - e.VideoURI = "" - } else { - e.VideoURI = strings.Replace(url.PathEscape(path.Join("$RFILES$", e.VideoURI)), "%2F", "/", -1) - resolutionFound = true - } - - writeup := path.Join(epath, "resolution.md") - if !i.Exists(writeup) { - writeup = path.Join(epath, "resolution.txt") - } - - if i.Exists(writeup) { - if size, err := GetFileSize(i, writeup); err != nil { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("resolution.md: %w", err), theme)) - } else if size == 0 { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("resolution.md: The file is empty!"), theme)) - } else if e.Resolution, err = GetFileContent(i, writeup); err != nil { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("resolution.md: %w", err), theme)) - } else { - // Call checks hooks - for _, h := range hooks.mdTextHooks { - for _, err := range multierr.Errors(h(e.Resolution, e.Language, exceptions.GetFileExceptions("resolution.md"), p.GetRawFlags()...)) { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("resolution.md: %w", err))) - } - } - - if e.Resolution, err = ProcessMarkdown(i, e.Resolution, epath); err != nil { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("resolution.md: error during markdown processing: %w", err), theme)) - } else { - resolutionFound = true - } - } - } - - if !resolutionFound { - errs = multierr.Append(errs, NewExerciceError(e, ErrResolutionNotFound, theme)) - } - - // Call checks hooks - for _, h := range hooks.exerciceHooks { - for _, err := range multierr.Errors(h(e, exceptions)) { - errs = multierr.Append(errs, NewExerciceError(e, err)) - } - } - return -} - -// SyncExercice imports new or updates existing given exercice. -func SyncExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]*fic.Exercice, exceptions_in *CheckExceptions) (e *fic.Exercice, eid int, exceptions *CheckExceptions, errs error) { - var err error - var p ExerciceParams - var berrors error - - e, p, eid, exceptions, _, berrors = BuildExercice(i, theme, epath, dmap, exceptions_in) - errs = multierr.Append(errs, berrors) - - if e != nil { - if len(e.Image) > 0 { - if _, err := i.importFile(e.Image, - func(filePath string, origin string) (interface{}, error) { - if err := resizePicture(i, origin, filePath, image.Rect(0, 0, 500, 300)); err != nil { - return nil, err - } - - e.Image = strings.TrimPrefix(filePath, fic.FilesDir) - - e.BackgroundColor, _ = getBackgroundColor(filePath) - - // If the theme has no image yet, use the first exercice's image found - if theme != nil && theme.Image == "" { - theme.Image = e.Image - _, err := theme.Update() - if err != nil { - return nil, err - } - } - - return nil, nil - }); err != nil { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("unable to import heading image: %w", err))) - } - } - - // Create or update the exercice - err = theme.SaveNamedExercice(e) - if err != nil { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("error on exercice save: %w", err), theme)) - return - } - - // Import eercice tags - if _, err := e.WipeTags(); err != nil { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("unable to wipe tags: %w", err), theme)) - } - for _, tag := range p.Tags { - if _, err := e.AddTag(tag); err != nil { - errs = multierr.Append(errs, NewExerciceError(e, fmt.Errorf("unable to add tag: %w", err), theme)) - return - } - } - } - - return -} - -// SyncExercices imports new or updates existing exercices, in a given theme. -func SyncExercices(i Importer, theme *fic.Theme, exceptions *CheckExceptions) (exceptions_out map[int]*CheckExceptions, errs error) { - if exercices, err := GetExercices(i, theme); err != nil { - errs = multierr.Append(errs, err) - } else { - exceptions_out = make(map[int]*CheckExceptions) - emap := map[string]int{} - - dmap, _ := buildDependancyMap(i, theme) - - for _, edir := range exercices { - e, eid, ex_exceptions, cur_errs := SyncExercice(i, theme, path.Join(theme.Path, edir), &dmap, exceptions) - if e != nil { - emap[e.Title] = eid - dmap[int64(eid)] = e - exceptions_out[eid] = ex_exceptions - errs = multierr.Append(errs, cur_errs) - } - } - - // Remove old exercices - if exercices, err := theme.GetExercices(); err == nil { - for _, ex := range exercices { - if _, ok := emap[ex.Title]; !ok { - ex.DeleteCascade() - } - } - } - } - return -} - -func ListRemoteExercices(thid string) ([]string, error) { - if thid == "_" { - return GetExercices(GlobalImporter, &fic.StandaloneExercicesTheme) - } - - theme, _, errs := BuildTheme(GlobalImporter, thid) - if theme == nil { - return nil, errs - } - - return GetExercices(GlobalImporter, theme) -} - -// ApiListRemoteExercices is an accessor letting foreign packages to access remote exercices list. -func ApiListRemoteExercices(c *gin.Context) { - exercices, err := ListRemoteExercices(c.Params.ByName("thid")) - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, exercices) -} - -func GetRemoteExercice(thid, exid string, inTheme *fic.Theme) (*fic.Exercice, error) { - if thid == fic.StandaloneExercicesDirectory || thid == "_" { - exercice, _, _, _, _, errs := BuildExercice(GlobalImporter, nil, path.Join(fic.StandaloneExercicesDirectory, exid), nil, nil) - return exercice, errs - } - - theme, exceptions, errs := BuildTheme(GlobalImporter, thid) - if theme == nil { - return nil, fmt.Errorf("Theme not found") - } - - if inTheme == nil { - inTheme = theme - } - - exercice, _, _, _, _, errs := BuildExercice(GlobalImporter, inTheme, path.Join(theme.Path, exid), nil, exceptions) - return exercice, errs -} - -// ApiGetRemoteExercice is an accessor letting foreign packages to access remote exercice attributes. -func ApiGetRemoteExercice(c *gin.Context) { - exercice, err := GetRemoteExercice(c.Params.ByName("thid"), c.Params.ByName("exid"), nil) - if exercice != nil { - c.JSON(http.StatusOK, exercice) - return - } else { - c.JSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } -} diff --git a/admin/sync/file.go b/admin/sync/file.go deleted file mode 100644 index 7efd47fa..00000000 --- a/admin/sync/file.go +++ /dev/null @@ -1,267 +0,0 @@ -package sync - -import ( - "bufio" - "bytes" - "encoding/base32" - "fmt" - "io" - "net/url" - "os" - "path" - "strings" - - "srs.epita.fr/fic-server/libfic" - - "golang.org/x/crypto/blake2b" -) - -// Importer are abstract methods required to import challenges. -type Importer interface { - // Kind returns information about the Importer, for human interrest. - Kind() string - // Id returns information about the current state (commit id, ...). - Id() *string - // init performs the importer initialization. - Init() error - // sync tries to pull the latest modification of the underlying storage. - Sync() error - // Exists checks if the given location exists from the Importer point of view. - Exists(filename string) bool - // toURL gets the full path/URL to the given file, the Importer will look internaly (used for debuging purpose). - toURL(filename string) string - // importFile imports the file at the given URI, inside the global FILES/ directory. - // Then calls back the next function, with the downloaded location and the original URI. - // Callback return is forwarded. - importFile(URI string, next func(string, string) (interface{}, error)) (interface{}, error) - // getFileReader returns a reader to the requested file. - GetFile(filename string) (io.Reader, error) - // listDir returns a list of the files and subdirectories contained inside the directory at the given location. - ListDir(filename string) ([]string, error) - // stat returns many information about the given file: such as last modification date, size, ... - Stat(filename string) (os.FileInfo, error) -} - -// DirectAccessImporter abstracts importer that support direct file access through a local path -type DirectAccessImporter interface { - GetLocalPath(p ...string) string -} - -// ForgeLinkedImporter abstracts importer that are linked to a forge -type ForgeLinkedImporter interface { - GetThemeLink(th *fic.Theme) (*url.URL, error) - GetExerciceLink(e *fic.Exercice) (*url.URL, error) -} - -// WritableImporter abstracts importer that we can also write on -type WritableImporter interface { - // writeFile write the given buffer to the file at the given location. - writeFile(filename string, reader io.Reader) error -} - -// GlobalImporter stores the main importer instance to use for global imports. -var GlobalImporter Importer - -// GetFileSize returns the size. -func GetFileSize(i Importer, URI string) (size int64, err error) { - if i.Exists(URI) { - if fi, err := i.Stat(URI); err != nil { - return 0, err - } else { - return fi.Size(), nil - } - } - - dirname := path.Dir(URI) - if i.Exists(dirname) { - filename := path.Base(URI) - if files, err := i.ListDir(dirname); err != nil { - return size, err - } else { - for _, fname := range []string{filename, filename + "."} { - found := false - for _, file := range files { - if matched, _ := path.Match(fname+"[0-9][0-9]", file); matched { - found = true - if fi, err := i.Stat(path.Join(dirname, file)); err != nil { - return size, err - } else { - size += fi.Size() - } - } - } - - if found { - return size, nil - } - } - } - } - - return size, fmt.Errorf("%q: no such file or directory", URI) -} - -// GetFile helps to manage huge file transfert by concatenating splitted (with split(1)) files. -func GetFile(i Importer, URI string) (io.Reader, func(), error) { - // Import file if it exists - if i.Exists(URI) { - fd, err := i.GetFile(URI) - return fd, func() { - if fdc, ok := fd.(io.ReadCloser); ok { - fdc.Close() - } - }, err - } - - // Try to find file parts - dirname := path.Dir(URI) - if i.Exists(dirname) { - filename := path.Base(URI) - if files, err := i.ListDir(dirname); err != nil { - return nil, nil, err - } else { - var readers []io.Reader - - for _, fname := range []string{filename, filename + "."} { - for _, file := range files { - if matched, _ := path.Match(fname+"[0-9][0-9]", file); matched { - fd, err := i.GetFile(path.Join(dirname, file)) - if err != nil { - // Close already opened files to avoid leaks - for _, rd := range readers { - if rdc, ok := rd.(io.ReadCloser); ok { - rdc.Close() - } - } - - return nil, nil, err - } - - readers = append(readers, fd) - } - } - - if len(readers) > 0 { - return io.MultiReader(readers...), func() { - for _, rd := range readers { - if rdc, ok := rd.(io.ReadCloser); ok { - rdc.Close() - } - } - }, nil - } - } - } - } - - return nil, nil, fmt.Errorf("%q: no such file or directory", URI) -} - -// GetFileContent retrieves the content of the given text file. -func GetFileContent(i Importer, URI string) (string, error) { - if fd, closer, err := GetFile(i, URI); err != nil { - return "", err - } else { - defer closer() - - buffd := bufio.NewReader(fd) - - // Ensure we read UTF-8 content. - buf := make([]rune, 0) - for b, _, err := buffd.ReadRune(); err == nil; b, _, err = buffd.ReadRune() { - buf = append(buf, b) - } - - return strings.TrimSpace(string(buf)), nil - } -} - -// GetDestinationFilePath generates the destination path, from the URI. -// This function permits to obfusce to player the original URI. -// Theoricaly, changing the import method doesn't change destination URI. -func GetDestinationFilePath(URI string, filename *string) string { - if filename == nil { - tmp := path.Base(URI) - filename = &tmp - } - hash := blake2b.Sum512([]byte(URI)) - return path.Join(fic.FilesDir, strings.ToLower(base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(hash[:])), *filename) -} - -var fileWriter = fileWriterToFS - -func SetWriteFileFunc(writerFunc func(dest string) (io.WriteCloser, error)) { - fileWriter = writerFunc -} - -func fileWriterToFS(dest string) (io.WriteCloser, error) { - if err := os.MkdirAll(path.Dir(dest), 0751); err != nil { - return nil, err - } - - return os.Create(dest) -} - -func importFile(i Importer, URI string, dest string) error { - if fdfrom, closer, err := GetFile(i, URI); err != nil { - os.Remove(dest) - return err - } else { - defer closer() - - fdto, err := fileWriter(dest) - if err != nil { - return err - } - defer fdto.Close() - - _, err = io.Copy(fdto, fdfrom) - return err - } -} - -// ImportFile imports the file at the given URI, using helpers of the given Importer. -// After import, next is called with relative path where the file has been saved and the original URI. -func ImportFile(i Importer, URI string, next func(string, string) (interface{}, error)) (interface{}, error) { - dest := GetDestinationFilePath(URI, nil) - - // If the present file is still valide, don't erase it - if _, err := os.Stat(dest); !os.IsNotExist(err) { - if r, err := next(dest, URI); err == nil { - return r, err - } - } - - if err := importFile(i, URI, dest); err != nil { - return nil, err - } - - return next(dest, URI) -} - -// WriteFileContent save the given content to the given text file. -func WriteFileContent(i Importer, URI string, content []byte) error { - if wi, ok := i.(WritableImporter); ok { - return wi.writeFile(URI, bytes.NewReader(content)) - } else { - return fmt.Errorf("%t is not capable of writing", i) - } -} - -func OpenOrGetFile(i Importer, URI string) (fd io.Reader, closer func() error, err error) { - if strings.HasPrefix(URI, "$FILES$") { - var fdc io.ReadCloser - fdc, err = os.Open(path.Join(fic.FilesDir, strings.TrimPrefix(URI, "$FILES$/"))) - fd = fdc - closer = fdc.Close - } else { - fd, err = GlobalImporter.GetFile(URI) - if fdcloser, ok := fd.(io.ReadCloser); ok { - closer = fdcloser.Close - } else { - closer = func() error { return nil } - } - } - - return -} diff --git a/admin/sync/full.go b/admin/sync/full.go deleted file mode 100644 index d112fd2e..00000000 --- a/admin/sync/full.go +++ /dev/null @@ -1,253 +0,0 @@ -package sync - -import ( - "encoding/json" - "io" - "log" - "os" - "sync" - "time" - - "go.uber.org/multierr" - - "srs.epita.fr/fic-server/admin/generation" - "srs.epita.fr/fic-server/libfic" -) - -// DeepReportPath stores the path to the report generated during full recursive import. -var DeepReportPath = "full_import_report.json" - -// oneDeepSync ensure there is no more than one running deep sync. -var oneDeepSync sync.Mutex -var oneThemeDeepSync sync.Mutex - -// DeepSyncProgress expose the progression of the depp synchronization (0 = 0%, 255 = 100%). -var DeepSyncProgress uint8 - -func OneDeepSyncStatus() bool { - if oneDeepSync.TryLock() { - oneDeepSync.Unlock() - return true - } - return false -} - -func OneThemeDeepSyncStatus() bool { - if oneThemeDeepSync.TryLock() { - oneThemeDeepSync.Unlock() - return true - } - return false -} - -type SyncReport struct { - DateStart time.Time `json:"_started"` - DateEnd time.Time `json:"_ended"` - DateUpdated []time.Time `json:"_updated"` - Regeneration []string `json:"_regeneration"` - SyncId string `json:"_id,omitempty"` - ThemesSync []string `json:"_themes,omitempty"` - Themes map[string][]string `json:"themes"` - Exercices []string `json:"exercices,omitempty"` -} - -// SpeedySyncDeep performs a recursive synchronisation without importing files. -func SpeedySyncDeep(i Importer) (errs SyncReport) { - oneDeepSync.Lock() - defer func() { - oneDeepSync.Unlock() - if DeepSyncProgress != 255 { - log.Printf("Speedy synchronization terminated at step %d/255", DeepSyncProgress) - } - }() - DeepSyncProgress = 1 - - errs.Themes = map[string][]string{} - - startTime := time.Now() - - errs.DateStart = startTime - exceptions, sterrs := SyncThemes(i) - for _, sterr := range multierr.Errors(sterrs) { - errs.ThemesSync = append(errs.ThemesSync, sterr.Error()) - } - - if themes, err := fic.GetThemesExtended(); err == nil { - DeepSyncProgress = 2 - - var themeStep uint8 = uint8(250) / uint8(len(themes)) - - for tid, theme := range themes { - DeepSyncProgress = 3 + uint8(tid)*themeStep - ex_exceptions, seerrs := SyncExercices(i, theme, exceptions[theme.Path]) - for _, seerr := range multierr.Errors(seerrs) { - errs.Themes[theme.Name] = append(errs.Themes[theme.Name], seerr.Error()) - } - - if exercices, err := theme.GetExercices(); err == nil { - if len(exercices) == 0 { - continue - } - var exerciceStep uint8 = themeStep / uint8(len(exercices)) - for eid, exercice := range exercices { - log.Printf("Speedy synchronization in progress: %d/255 - doing Theme %q, Exercice %q: %q\n", DeepSyncProgress, theme.Name, exercice.Title, exercice.Path) - - DeepSyncProgress = 3 + uint8(tid)*themeStep + uint8(eid)*exerciceStep - flagsBindings, ferrs := SyncExerciceFlags(i, exercice, ex_exceptions[eid]) - for _, ferr := range multierr.Errors(ferrs) { - errs.Themes[theme.Name] = append(errs.Themes[theme.Name], ferr.Error()) - } - - DeepSyncProgress += exerciceStep / 2 - _, herrs := SyncExerciceHints(i, exercice, flagsBindings, ex_exceptions[eid]) - for _, herr := range multierr.Errors(herrs) { - errs.Themes[theme.Name] = append(errs.Themes[theme.Name], herr.Error()) - } - } - } - } - } - - DeepSyncProgress = 254 - errs.DateEnd = time.Now() - - DeepSyncProgress = 255 - log.Println("Speedy synchronization done in", time.Since(startTime)) - return -} - -// SyncDeep performs a recursive synchronisation: from themes to challenge items. -func SyncDeep(i Importer) (errs SyncReport) { - oneDeepSync.Lock() - defer func() { - oneDeepSync.Unlock() - if DeepSyncProgress != 255 { - log.Printf("Full synchronization terminated at step %d/255", DeepSyncProgress) - } - }() - DeepSyncProgress = 1 - - errs.Themes = map[string][]string{} - - startTime := time.Now() - - // Import all themes - errs.DateStart = startTime - exceptions, sterrs := SyncThemes(i) - for _, sterr := range multierr.Errors(sterrs) { - errs.ThemesSync = append(errs.ThemesSync, sterr.Error()) - } - - // Synchronize themes - if themes, err := fic.GetThemesExtended(); err == nil { - DeepSyncProgress = 2 - - var themeStep uint8 = uint8(250) / uint8(len(themes)) - - for tid, theme := range themes { - stderrs := SyncThemeDeep(i, theme, tid, themeStep, exceptions[theme.Path]) - for _, stderr := range multierr.Errors(stderrs) { - errs.Themes[theme.Name] = append(errs.Themes[theme.Name], stderr.Error()) - } - } - } - - DeepSyncProgress = 254 - - EditDeepReport(&errs, true) - - resp, err := generation.FullGeneration() - if err != nil { - errs.Regeneration = append(errs.Regeneration, err.Error()) - } else { - defer resp.Body.Close() - - v, _ := io.ReadAll(resp.Body) - errs.Regeneration = append(errs.Regeneration, string(v)) - } - - DeepSyncProgress = 255 - log.Println("Full synchronization done in", time.Since(startTime)) - return -} - -func readDeepReport() (ret *SyncReport, err error) { - if fdfrom, err := os.Open(DeepReportPath); err == nil { - defer fdfrom.Close() - - jdec := json.NewDecoder(fdfrom) - - if err := jdec.Decode(&ret); err != nil { - return nil, err - } - } else { - return nil, err - } - - return -} - -func EditDeepReport(errs *SyncReport, erase bool) { - errs.Regeneration = []string{} - - if !erase { - if in, err := readDeepReport(); err != nil { - errs.Regeneration = append(errs.Regeneration, err.Error()) - log.Println(err) - } else { - for k, v := range errs.Themes { - in.Themes[k] = v - } - - errs = in - } - } - - errs.DateUpdated = append(errs.DateUpdated, time.Now()) - - if fdto, err := os.Create(DeepReportPath); err == nil { - defer fdto.Close() - - if out, err := json.Marshal(errs); err == nil { - fdto.Write(out) - } else { - errs.Regeneration = append(errs.Regeneration, err.Error()) - log.Println(err) - } - } else { - errs.Regeneration = append(errs.Regeneration, err.Error()) - log.Println(err) - } - -} - -// SyncThemeDeep performs a recursive synchronisation: from challenges to challenge items. -func SyncThemeDeep(i Importer, theme *fic.Theme, tid int, themeStep uint8, exceptions *CheckExceptions) (errs error) { - var ex_exceptions map[int]*CheckExceptions - - oneThemeDeepSync.Lock() - defer oneThemeDeepSync.Unlock() - - DeepSyncProgress = 3 + uint8(tid)*themeStep - ex_exceptions, errs = SyncExercices(i, theme, exceptions) - - if exercices, err := theme.GetExercices(); err == nil && len(exercices) > 0 { - var exerciceStep uint8 = themeStep / uint8(len(exercices)) - for eid, exercice := range exercices { - log.Printf("Deep synchronization in progress: %d/255 - doing Theme %q, Exercice %q: %q\n", DeepSyncProgress, theme.Name, exercice.Title, exercice.Path) - - DeepSyncProgress = 3 + uint8(tid)*themeStep + uint8(eid)*exerciceStep - errs = multierr.Append(errs, ImportExerciceFiles(i, exercice, ex_exceptions[eid])) - - DeepSyncProgress += exerciceStep / 3 - flagsBindings, ferrs := SyncExerciceFlags(i, exercice, ex_exceptions[eid]) - errs = multierr.Append(errs, ferrs) - - DeepSyncProgress += exerciceStep / 3 - _, herrs := SyncExerciceHints(i, exercice, flagsBindings, ex_exceptions[eid]) - errs = multierr.Append(errs, herrs) - } - } - - return -} diff --git a/admin/sync/hooks.go b/admin/sync/hooks.go deleted file mode 100644 index f789762c..00000000 --- a/admin/sync/hooks.go +++ /dev/null @@ -1,108 +0,0 @@ -package sync - -import ( - "fmt" - "plugin" - - "srs.epita.fr/fic-server/libfic" -) - -var hooks = &CheckHooks{customHooks: map[string]CustomCheckHook{}} - -type CheckFlagChoiceHook func(*fic.FlagChoice, *fic.Exercice, *CheckExceptions) error -type CheckFlagKeyHook func(*fic.FlagKey, string, *fic.Exercice, *CheckExceptions) error -type CheckFlagKeyWithChoicesHook func(*fic.FlagKey, string, []*fic.FlagChoice, *fic.Exercice, *CheckExceptions) error -type CheckFlagLabelHook func(*fic.FlagLabel, *fic.Exercice, *CheckExceptions) error -type CheckFlagMCQHook func(*fic.MCQ, []*fic.MCQ_entry, *fic.Exercice, *CheckExceptions) error -type CheckFileHook func(*fic.EFile, *fic.Exercice, *CheckExceptions) error -type CheckHintHook func(*fic.EHint, *fic.Exercice, *CheckExceptions) error -type CheckMDTextHook func(string, string, *CheckExceptions, ...string) error -type CheckExerciceHook func(*fic.Exercice, *CheckExceptions) error -type CustomCheckHook func(interface{}, *CheckExceptions) error - -type CheckHooks struct { - flagChoiceHooks []CheckFlagChoiceHook - flagKeyHooks []CheckFlagKeyHook - flagKeyWithChoicesHooks []CheckFlagKeyWithChoicesHook - flagLabelHooks []CheckFlagLabelHook - flagMCQHooks []CheckFlagMCQHook - fileHooks []CheckFileHook - hintHooks []CheckHintHook - mdTextHooks []CheckMDTextHook - exerciceHooks []CheckExerciceHook - customHooks map[string]CustomCheckHook -} - -func (h *CheckHooks) RegisterFlagChoiceHook(f CheckFlagChoiceHook) { - h.flagChoiceHooks = append(h.flagChoiceHooks, f) -} - -func (h *CheckHooks) RegisterFlagKeyHook(f CheckFlagKeyHook) { - h.flagKeyHooks = append(h.flagKeyHooks, f) -} - -func (h *CheckHooks) RegisterFlagKeyWithChoicesHook(f CheckFlagKeyWithChoicesHook) { - h.flagKeyWithChoicesHooks = append(h.flagKeyWithChoicesHooks, f) -} - -func (h *CheckHooks) RegisterFlagLabelHook(f CheckFlagLabelHook) { - h.flagLabelHooks = append(h.flagLabelHooks, f) -} - -func (h *CheckHooks) RegisterFlagMCQHook(f CheckFlagMCQHook) { - h.flagMCQHooks = append(h.flagMCQHooks, f) -} - -func (h *CheckHooks) RegisterFileHook(f CheckFileHook) { - h.fileHooks = append(h.fileHooks, f) -} - -func (h *CheckHooks) RegisterHintHook(f CheckHintHook) { - h.hintHooks = append(h.hintHooks, f) -} - -func (h *CheckHooks) RegisterMDTextHook(f CheckMDTextHook) { - h.mdTextHooks = append(h.mdTextHooks, f) -} - -func (h *CheckHooks) RegisterExerciceHook(f CheckExerciceHook) { - h.exerciceHooks = append(h.exerciceHooks, f) -} - -func (h *CheckHooks) RegisterCustomHook(hookname string, f CustomCheckHook) { - h.customHooks[hookname] = f -} - -func (h *CheckHooks) CallCustomHook(hookname string, data interface{}, exceptions *CheckExceptions) error { - if v, ok := h.customHooks[hookname]; ok { - return v(data, exceptions) - } - return nil -} - -func LoadChecksPlugin(fname string) error { - p, err := plugin.Open(fname) - if err != nil { - return err - } - - register, err := p.Lookup("RegisterChecksHooks") - if err != nil { - return err - } - - register.(func(*CheckHooks))(hooks) - - return nil -} - -type CheckPluginList []string - -func (l *CheckPluginList) String() string { - return fmt.Sprintf("%v", *l) -} - -func (l *CheckPluginList) Set(value string) error { - *l = append(*l, value) - return nil -} diff --git a/admin/sync/importer_cloud.go b/admin/sync/importer_cloud.go deleted file mode 100644 index 9085f4fe..00000000 --- a/admin/sync/importer_cloud.go +++ /dev/null @@ -1,141 +0,0 @@ -package sync - -import ( - "errors" - "io" - "net/http" - "net/url" - "os" - "path" - "strings" - - "github.com/studio-b12/gowebdav" -) - -// CloudImporter implements an Importer, where files to imports are located -// remotely, under a WebDAV server (such as sabre/dav, owncloud, ...). -type CloudImporter struct { - // baseDAV is the URL (most probably http or https one) to the root directory. - // It should contains all themes, in separated directories. - baseDAV url.URL - // username is the username used to perform authentication through BasicAuth. - username string - // password is the password used to perform authentication through BasicAuth. - password string -} - -// NewCloudImporter registers a new object CloudImporter, as the URL conversion -// can returns errors. -func NewCloudImporter(baseDAV string, username string, password string) (*CloudImporter, error) { - if r, err := url.Parse(baseDAV); err != nil { - return nil, err - } else { - return &CloudImporter{*r, username, password}, nil - } -} - -func (i CloudImporter) Kind() string { - return "cloud file importer: " + i.baseDAV.String() -} - -func (i CloudImporter) Id() *string { - return nil -} - -func (i CloudImporter) Init() error { - return nil -} - -func (i CloudImporter) Sync() error { - return nil -} - -func (i CloudImporter) Exists(filename string) bool { - fullURL := i.baseDAV - fullURL.Path = path.Join(fullURL.Path, filename) - - client := http.Client{} - if req, err := http.NewRequest("HEAD", fullURL.String(), nil); err == nil { - req.SetBasicAuth(i.username, i.password) - - if resp, err := client.Do(req); err == nil { - defer resp.Body.Close() - - return resp.StatusCode == http.StatusOK - } - } - return false -} - -func (i CloudImporter) toURL(filename string) string { - fullURL := i.baseDAV - fullURL.Path = path.Join(fullURL.Path, filename) - return fullURL.String() -} - -func (i CloudImporter) importFile(URI string, next func(string, string) (interface{}, error)) (interface{}, error) { - return ImportFile(i, URI, next) -} - -func (i CloudImporter) GetFile(filename string) (io.Reader, error) { - fullURL := i.baseDAV - fullURL.Path = path.Join(fullURL.Path, filename) - - client := http.Client{} - if req, err := http.NewRequest("GET", fullURL.String(), nil); err != nil { - return nil, err - } else { - req.SetBasicAuth(i.username, i.password) - if resp, err := client.Do(req); err != nil { - return nil, err - } else { - if resp.StatusCode != http.StatusOK { - resp.Body.Close() - return nil, errors.New(resp.Status) - } else { - return resp.Body, nil - } - } - } -} - -func (i CloudImporter) writeFile(filename string, reader io.Reader) error { - fullURL := i.baseDAV - fullURL.Path = path.Join(fullURL.Path, filename) - - client := http.Client{} - if req, err := http.NewRequest("PUT", fullURL.String(), reader); err != nil { - return err - } else { - req.SetBasicAuth(i.username, i.password) - if resp, err := client.Do(req); err != nil { - return err - } else { - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return errors.New(resp.Status) - } else { - return nil - } - } - } -} - -func (i CloudImporter) ListDir(filename string) ([]string, error) { - client := gowebdav.NewClient(i.baseDAV.String(), i.username, i.password) - - if files, err := client.ReadDir(strings.Replace(url.PathEscape(filename), "%2F", "/", -1)); err != nil { - return nil, err - } else { - res := make([]string, 0) - for _, file := range files { - res = append(res, file.Name()) - } - return res, nil - } -} - -func (i CloudImporter) Stat(filename string) (os.FileInfo, error) { - return gowebdav.NewClient(i.baseDAV.String(), i.username, i.password).Stat(strings.Replace(url.PathEscape(filename), "%2F", "/", -1)) -} diff --git a/admin/sync/importer_git.go b/admin/sync/importer_git.go deleted file mode 100644 index cda699d5..00000000 --- a/admin/sync/importer_git.go +++ /dev/null @@ -1,214 +0,0 @@ -//go:build gitgo -// +build gitgo - -package sync - -import ( - "io/ioutil" - "log" - "os" - "path" - "strings" - - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/config" - "github.com/go-git/go-git/v5/plumbing" - "github.com/go-git/go-git/v5/plumbing/transport/ssh" -) - -// GitImporter implements an Importer, where files to imports are located -// inside a local directory from your filesystem, backed by git. -type GitImporter struct { - li LocalImporter - Remote string - Branch string - Auth ssh.AuthMethod -} - -func NewGitImporter(li LocalImporter, remote string, branch string) GitImporter { - var auth ssh.AuthMethod - - // If there is no ssh-agent setup, try to use a default ssh key - if _, exists := os.LookupEnv("SSH_AUTH_SOCK"); !exists { - if home, exists := os.LookupEnv("HOME"); exists { - for _, d := range []string{"id_fic", "id_ed25519", "id_rsa"} { - pemBytes, err := ioutil.ReadFile(path.Join(home, ".ssh", d)) - if err == nil { - log.Println("[GitImporter] Using", path.Join(home, ".ssh", d), "as ssh key to sync repository") - auth, err = ssh.NewPublicKeys("git", pemBytes, "") - if ccfg, err := auth.ClientConfig(); err != nil { - if hkc, err := ssh.NewKnownHostsCallback(); err != nil { - ccfg.HostKeyCallback = hkc - } - } - break - } - } - } - } - - return GitImporter{ - li: li, - Remote: remote, - Branch: branch, - Auth: auth, - } -} - -func (i GitImporter) Id() *string { - var gitinfo string - r, err := git.PlainOpen(i.li.Base) - if err == nil { - ref, err := r.Head() - if err == nil { - gitinfo = ref.Hash().String() - } - } - - return &gitinfo -} - -func (i GitImporter) Init() error { - // Check if the directory exists, create it if needed - if err := i.li.Init(); err != nil { - return err - } - - // If the directory is empty, clone it - if n, err := countFileInDir(i.li.Base); err != nil { - return err - } else if n == 0 { - _, err = git.PlainClone(i.li.Base, false, &git.CloneOptions{ - URL: i.Remote, - ReferenceName: plumbing.ReferenceName(i.Branch), - RecurseSubmodules: git.DefaultSubmoduleRecursionDepth, - Auth: i.Auth, - }) - if err != nil { - return err - } - } - - // Check if the .git directory exists, change the origin remote to our - r, err := git.PlainOpen(i.li.Base) - if err != nil { - return err - } - - r.DeleteRemote("origin") - _, err = r.CreateRemote(&config.RemoteConfig{ - Name: "origin", - URLs: []string{i.Remote}, - }) - if err != nil { - return err - } - - return nil -} - -func (i GitImporter) Sync() error { - oneGitPull.Lock() - defer oneGitPull.Unlock() - - r, err := git.PlainOpen(i.li.Base) - if err != nil { - return err - } - - w, err := r.Worktree() - if err != nil { - return err - } - - // Perform a git pull --rebase origin/master - err = w.Pull(&git.PullOptions{ - RemoteName: "origin", - ReferenceName: plumbing.ReferenceName(i.Branch), - Depth: 1, - RecurseSubmodules: git.DefaultSubmoduleRecursionDepth, - Force: true, - Auth: i.Auth, - }) - return err -} - -func (i GitImporter) GetSubmodules() ([]GitSubmoduleStatus, error) { - oneGitPull.Lock() - defer oneGitPull.Unlock() - - r, err := git.PlainOpen(i.li.Base) - if err != nil { - return nil, err - } - - w, err := r.Worktree() - if err != nil { - return nil, err - } - - modules, err := w.Submodules() - - var ret []GitSubmoduleStatus - for _, mod := range modules { - st, err := mod.Status() - if err == nil { - ret = append(ret, GitSubmoduleStatus{ - Hash: st.Expected.String(), - Path: st.Path, - Branch: strings.TrimSuffix(strings.TrimPrefix(strings.TrimPrefix(strings.TrimPrefix(st.Branch.String(), "("), "refs/"), "heads/"), ")"), - }) - } - } - - return ret, err -} - -func (i GitImporter) GetSubmodule(repopath string) (*GitSubmoduleStatus, error) { - oneGitPull.Lock() - defer oneGitPull.Unlock() - - r, err := git.PlainOpen(path.Join(i.li.Base, repopath)) - if err != nil { - return nil, err - } - - st, err := r.Head() - if err != nil { - return nil, err - } - - return &GitSubmoduleStatus{ - Hash: st.Hash().String(), - Path: repopath, - Branch: st.Name().Short(), - }, nil -} - -func (i GitImporter) IsRepositoryUptodate(repopath string) (*GitSubmoduleStatus, error) { - oneGitPull.Lock() - defer oneGitPull.Unlock() - - r, err := git.PlainOpen(path.Join(i.li.Base, repopath)) - if err != nil { - return nil, err - } - - // Perform a git pull --rebase origin/master - err = r.Fetch(&git.FetchOptions{ - RemoteName: "origin", - RefSpecs: []config.RefSpec{config.RefSpec("+refs/heads/" + i.Branch + ":refs/remotes/origin/" + i.Branch)}, - Auth: i.Auth, - }) - - st, err := r.Reference(plumbing.ReferenceName("origin/"+i.Branch), true) - if err != nil { - return nil, err - } - - return &GitSubmoduleStatus{ - Hash: st.Hash().String(), - Path: repopath, - Branch: st.Name().Short(), - }, nil -} diff --git a/admin/sync/importer_git_common.go b/admin/sync/importer_git_common.go deleted file mode 100644 index 2ca9f75c..00000000 --- a/admin/sync/importer_git_common.go +++ /dev/null @@ -1,85 +0,0 @@ -package sync - -import ( - "io" - "net/url" - "os" - "regexp" - "sync" -) - -var gitRemoteRe = regexp.MustCompile(`^(?:(?:git@|https://)([\w.@]+)(?:/|:))((?:[\w-_]+)/(?:[\w-_/]+))(?:.git){0,1}(?:(?:/){0,1})$`) - -var oneGitPull sync.Mutex - -func OneGitPullStatus() bool { - if oneGitPull.TryLock() { - oneGitPull.Unlock() - return true - } - return false -} - -func countFileInDir(dirname string) (int, error) { - files, err := os.ReadDir(dirname) - if err != nil { - return 0, err - } - - return len(files), nil -} - -func (i GitImporter) Exists(filename string) bool { - return i.li.Exists(filename) -} - -func (i GitImporter) toURL(filename string) string { - return i.li.toURL(filename) -} - -func (i GitImporter) GetLocalPath(filename ...string) string { - return i.li.GetLocalPath(filename...) -} - -func (i GitImporter) importFile(URI string, next func(string, string) (interface{}, error)) (interface{}, error) { - return i.li.importFile(URI, next) -} - -func (i GitImporter) GetFile(filename string) (io.Reader, error) { - return i.li.GetFile(filename) -} - -func (i GitImporter) writeFile(filename string, reader io.Reader) error { - return i.li.writeFile(filename, reader) -} - -func (i GitImporter) ListDir(filename string) ([]string, error) { - return i.li.ListDir(filename) -} - -func (i GitImporter) Stat(filename string) (os.FileInfo, error) { - return i.li.Stat(filename) -} - -func (i GitImporter) Kind() string { - return "git originated from " + i.Remote + " on " + i.li.Kind() -} - -func (i GitImporter) DeleteDir(filename string) error { - return i.li.DeleteDir(filename) -} - -func getForgeBaseLink(remote string) (u *url.URL, err error) { - res := gitRemoteRe.FindStringSubmatch(remote) - u, err = url.Parse(res[2]) - u.Scheme = "https" - u.Host = res[1] - return -} - -type GitSubmoduleStatus struct { - Hash string `json:"hash"` - Text string `json:"text,omitempty"` - Path string `json:"path"` - Branch string `json:"branch"` -} diff --git a/admin/sync/importer_gitbin.go b/admin/sync/importer_gitbin.go deleted file mode 100644 index 7d511265..00000000 --- a/admin/sync/importer_gitbin.go +++ /dev/null @@ -1,349 +0,0 @@ -//go:build !gitgo -// +build !gitgo - -package sync - -import ( - "bytes" - "errors" - "fmt" - "log" - "net/url" - "os" - "os/exec" - "path" - "strings" - - "srs.epita.fr/fic-server/libfic" -) - -// GitImporter implements an Importer, where files to imports are located -// inside a local directory from your filesystem, backed by git (binary). -type GitImporter struct { - li LocalImporter - Remote string - Branch string -} - -func NewGitImporter(li LocalImporter, remote string, branch string) GitImporter { - if len(branch) == 0 { - branch = "master" - } - - return GitImporter{ - li: li, - Remote: remote, - Branch: branch, - } -} - -func (i GitImporter) Id() *string { - cmdshow := exec.Command("git", "-C", i.li.Base, "show") - var outshow bytes.Buffer - cmdshow.Stdout = &outshow - err := cmdshow.Run() - - var commit string - if err != nil { - commit = fmt.Sprintf("error (%s)", err.Error()) - } else { - commit, err = outshow.ReadString('\n') - if err == nil { - commit = strings.TrimPrefix(commit, "commit ") - } else { - commit = fmt.Sprintf("error (%s)", err.Error()) - } - } - - return &commit -} - -func (i GitImporter) Init() error { - // Check if the directory exists, create it if needed - if err := i.li.Init(); err != nil { - return err - } - - // If the directory is empty, clone it - if n, err := countFileInDir(i.li.Base); err != nil { - return err - } else if n == 0 { - args := []string{"clone", "--recursive", "--depth", "1"} - if i.Branch != "" { - args = append(args, "-b", i.Branch) - } - args = append(args, "--shallow-submodules", i.Remote, i.li.Base) - - log.Println("Please wait while creating the local git repository...") - cmdclone := exec.Command("git", args...) - stdout, err := cmdclone.CombinedOutput() - if err != nil { - return fmt.Errorf("%w:\n%s", err, stdout) - } - log.Println("Local git repository successfully cloned") - } else if _, err := os.Stat(path.Join(i.li.Base, ".git")); errors.Is(err, os.ErrNotExist) { - log.Println("[ERR] ", i.li.Base, " is not a valid git repository and it cannot be initialized because it's not empty.") - return nil - } - - // Check if the .git directory exists, change the origin remote to our - cmdremote := exec.Command("git", "-C", i.li.Base, "remote", "set-url", "origin", i.Remote) - stdout, err := cmdremote.CombinedOutput() - if err != nil { - return fmt.Errorf("%w:\n%s", err, stdout) - } - - return nil -} - -func (i GitImporter) Sync() error { - oneGitPull.Lock() - defer oneGitPull.Unlock() - - log.Println("Synchronizing local git repository...") - cmdfetch := exec.Command("git", "-C", i.li.Base, "fetch", "origin") - stdout, err := cmdfetch.CombinedOutput() - if err != nil { - log.Printf("Git repository fetch failed: %s\n%s", err, stdout) - return fmt.Errorf("%w:\n%s", err, stdout) - } - - cmdclean := exec.Command("git", "-C", i.li.Base, "clean", "-xfde", "*_MERGED") - stdout, err = cmdclean.CombinedOutput() - if err != nil { - log.Printf("Local git repository clean failed: %s\n%s", err, stdout) - return fmt.Errorf("%w:\n%s", err, stdout) - } - - if _, err := os.Stat(path.Join(i.li.Base, ".gitmodules")); !errors.Is(err, os.ErrNotExist) { - // We have submodules, clean it - cmdsubclean := exec.Command("git", "-C", i.li.Base, "submodule", "foreach", "--recursive", "git", "clean", "-xfde", "*_MERGED") - stdout, err = cmdsubclean.CombinedOutput() - if err != nil { - log.Printf("Local git repository submodules clean failed: %s\n%s", err, stdout) - return fmt.Errorf("%w:\n%s", err, stdout) - } - - // Start by a light hard reset (without submodules, in order to init new ones) - cmdreset := exec.Command("git", "-C", i.li.Base, "reset", "--hard", "origin/"+i.Branch) - stdout, err = cmdreset.CombinedOutput() - if err != nil { - log.Printf("Local git repository reset failed: %s\n%s", err, stdout) - return fmt.Errorf("%w:\n%s", err, stdout) - } - - cmdsubinit := exec.Command("git", "-C", i.li.Base, "submodule", "init") - stdout, err = cmdsubinit.CombinedOutput() - if err != nil { - log.Printf("Local git repository submodule init failed: %s\n%s", err, stdout) - return fmt.Errorf("%w:\n%s", err, stdout) - } - - cmdsubupdate := exec.Command("git", "-C", i.li.Base, "submodule", "update") - stdout, err = cmdsubupdate.CombinedOutput() - if err != nil { - log.Printf("Local git repository submodule update failed: %s\n%s", err, stdout) - } - } - - cmdreset := exec.Command("git", "-C", i.li.Base, "reset", "--hard", "--recurse-submodule", "origin/"+i.Branch) - stdout, err = cmdreset.CombinedOutput() - if err != nil { - log.Printf("Local git repository reset failed: %s\n%s", err, stdout) - return fmt.Errorf("%w:\n%s", err, stdout) - } - - if _, err := os.Stat(path.Join(i.li.Base, ".gitmodules")); !errors.Is(err, os.ErrNotExist) { - // Treat submodules - cmdsublfs := exec.Command("git", "-C", i.li.Base, "submodule", "foreach", "--recursive", "git", "lfs", "pull") - stdout, err = cmdsublfs.CombinedOutput() - if err != nil { - log.Printf("Local LFS synchronization failed: %s\n%s", err, stdout) - return fmt.Errorf("%w:\n%s", err, stdout) - } - } else { - cmdlfs := exec.Command("git", "-C", i.li.Base, "lfs", "pull") - stdout, err = cmdlfs.CombinedOutput() - if err != nil { - log.Printf("Local LFS synchronization failed: %s\n%s", err, stdout) - return fmt.Errorf("%w:\n%s", err, stdout) - } - } - - log.Println("Local git repository synchronized successfully") - return nil -} - -func (i GitImporter) GetThemeLink(th *fic.Theme) (u *url.URL, err error) { - prefix := "" - - if _, err = os.Stat(path.Join(i.li.Base, ".gitmodules")); !errors.Is(err, os.ErrNotExist) { - thdir := path.Join(i.li.Base, th.Path) - cmdremote := exec.Command("git", "-C", thdir, "remote", "get-url", "origin") - var stdout []byte - stdout, err = cmdremote.CombinedOutput() - if err != nil { - return - } - - u, err = getForgeBaseLink(string(bytes.TrimSpace(stdout))) - - // Search .git directory - for { - if _, err = os.Stat(path.Join(thdir, ".git")); !errors.Is(err, os.ErrNotExist) { - break - } - - thdir, _ = path.Split(thdir) - } - prefix = strings.TrimPrefix(thdir, i.li.Base) - } else { - u, err = getForgeBaseLink(i.Remote) - } - - if err != nil { - return - } - - u.Path = path.Join(u.Path, "-", "tree", i.Branch, strings.TrimPrefix("/"+th.Path, prefix)) - - return -} - -func (i GitImporter) GetExerciceLink(e *fic.Exercice) (u *url.URL, err error) { - prefix := "" - - if _, err = os.Stat(path.Join(i.li.Base, ".gitmodules")); !errors.Is(err, os.ErrNotExist) { - exdir := path.Join(i.li.Base, e.Path) - cmdremote := exec.Command("git", "-C", exdir, "remote", "get-url", "origin") - var stdout []byte - stdout, err = cmdremote.CombinedOutput() - if err != nil { - return - } - - u, err = getForgeBaseLink(string(bytes.TrimSpace(stdout))) - - // Search .git directory - for { - if _, err = os.Stat(path.Join(exdir, ".git")); !errors.Is(err, os.ErrNotExist) { - break - } - - exdir, _ = path.Split(exdir) - } - prefix = strings.TrimPrefix(exdir, i.li.Base) - } else { - u, err = getForgeBaseLink(i.Remote) - } - - if err != nil { - return - } - - u.Path = path.Join(u.Path, "-", "tree", i.Branch, strings.TrimPrefix("/"+e.Path, prefix)) - - return -} - -func (i GitImporter) GetSubmodules() ([]GitSubmoduleStatus, error) { - oneGitPull.Lock() - defer oneGitPull.Unlock() - - cmdsubmodule := exec.Command("git", "-C", i.li.Base, "submodule", "status") - stdout, err := cmdsubmodule.CombinedOutput() - if err != nil { - log.Printf("Git repository submodule failed: %s\n%s", err, stdout) - return nil, fmt.Errorf("%w:\n%s", err, stdout) - } - - var ret []GitSubmoduleStatus - for _, line := range strings.Split(string(stdout), "\n") { - flds := strings.Fields(line) - if len(flds) == 3 { - ret = append(ret, GitSubmoduleStatus{ - Hash: flds[0], - Path: flds[1], - Branch: strings.TrimSuffix(strings.TrimPrefix(strings.TrimPrefix(strings.TrimPrefix(strings.TrimPrefix(strings.TrimPrefix(flds[2], "("), "refs/"), "remotes/"), "heads/"), "origin/"), ")"), - }) - } - } - - return ret, err -} - -func (i GitImporter) GetSubmodule(repopath string) (*GitSubmoduleStatus, error) { - oneGitPull.Lock() - defer oneGitPull.Unlock() - - if repopath == "" { - cmdsubmodule := exec.Command("git", "-C", i.li.Base, "show", "-q", "--oneline") - stdout, err := cmdsubmodule.CombinedOutput() - if err != nil { - log.Printf("Git repository show failed: %s\n%s", err, stdout) - return nil, fmt.Errorf("%w:\n%s", err, stdout) - } - - flds := strings.SplitN(string(stdout), " ", 2) - return &GitSubmoduleStatus{ - Hash: flds[0], - Text: strings.TrimSpace(flds[1]), - Path: "", - Branch: i.Branch, - }, nil - } else { - cmdsubmodule := exec.Command("git", "-C", i.li.Base, "submodule", "status", repopath) - stdout, err := cmdsubmodule.CombinedOutput() - if err != nil { - log.Printf("Git repository submodule failed: %s\n%s", err, stdout) - return nil, fmt.Errorf("%w:\n%s", err, stdout) - } - - flds := strings.Fields(strings.TrimSpace(string(stdout))) - if len(flds) == 3 { - return &GitSubmoduleStatus{ - Hash: flds[0], - Path: flds[1], - Branch: strings.TrimSuffix(strings.TrimPrefix(strings.TrimPrefix(strings.TrimPrefix(strings.TrimPrefix(strings.TrimPrefix(flds[2], "("), "refs/"), "remotes/"), "heads/"), "origin/"), ")"), - }, nil - } - } - - return nil, fmt.Errorf("Unable to parse") -} - -func (i GitImporter) IsRepositoryUptodate(repopath string) (*GitSubmoduleStatus, error) { - oneGitPull.Lock() - defer oneGitPull.Unlock() - - cmdsubmodule := exec.Command("git", "-C", path.Join(i.li.Base, repopath), "fetch", "origin", i.Branch) - stdout, err := cmdsubmodule.CombinedOutput() - if err != nil { - log.Printf("Git repository submodule fetch failed: %s\n%s", err, stdout) - return nil, fmt.Errorf("%w:\n%s", err, stdout) - } - - cmdsubmodule = exec.Command("git", "-C", path.Join(i.li.Base, repopath), "show", "-q", "--oneline", "origin/"+i.Branch) - stdout, err = cmdsubmodule.CombinedOutput() - if err != nil { - cmdconfig := exec.Command("git", "-C", path.Join(i.li.Base, repopath), "config", "remote.origin.fetch") - if cfg, err2 := cmdconfig.CombinedOutput(); err2 == nil && !strings.Contains(string(cfg), "+refs/heads/*:refs/remotes/origin/*") { - cmdsubmodule := exec.Command("git", "-C", path.Join(i.li.Base, repopath), "config", "remote.origin.fetch", "+refs/heads/*:refs/remotes/origin/*") - if stdout, err = cmdsubmodule.CombinedOutput(); err != nil { - log.Printf("Git repository submodule config failed: %s\n%s", err, stdout) - return nil, fmt.Errorf("%w:\n%s", err, stdout) - } - } else { - log.Printf("Git repository submodule status failed: %s\n%s", err, stdout) - return nil, fmt.Errorf("%w:\n%s", err, stdout) - } - } - - flds := strings.SplitN(string(stdout), " ", 2) - return &GitSubmoduleStatus{ - Hash: flds[0], - Text: strings.TrimSpace(flds[1]), - Path: repopath, - Branch: i.Branch, - }, nil -} diff --git a/admin/sync/importer_localfs.go b/admin/sync/importer_localfs.go deleted file mode 100644 index 5f8301ae..00000000 --- a/admin/sync/importer_localfs.go +++ /dev/null @@ -1,123 +0,0 @@ -package sync - -import ( - "fmt" - "io" - "io/ioutil" - "os" - "path" -) - -// LocalImporter implements an Importer, where files to imports are located -// inside a local directory from your filesystem. -type LocalImporter struct { - // Base is the root directory used by the LocalImporter. It should contains all themes. - Base string - // Symlink changes the normal file copy/concatenate behaviour to symlink/concatenate. - // If enable, your base directory must be accessible by the frontend processus as it will follow the symlink. - Symlink bool -} - -func (i LocalImporter) Kind() string { - if i.Symlink { - return "local file importer (through symlink): " + i.Base - } else { - return "local file importer: " + i.Base - } -} - -func (i LocalImporter) Id() *string { - return nil -} - -func (i LocalImporter) Init() error { - if f, err := os.Stat(i.Base); os.IsNotExist(err) { - if err = os.Mkdir(i.Base, 0751); err != nil { - return err - } - } else if err != nil { - return err - } else if !f.IsDir() { - return fmt.Errorf("%q exists and is not a directory", i.Base) - } - return nil -} - -func (i LocalImporter) Sync() error { - return nil -} - -func (i LocalImporter) Exists(filename string) bool { - _, err := os.Stat(i.toURL(filename)) - return !os.IsNotExist(err) -} - -func (i LocalImporter) toURL(filename string) string { - return path.Join(i.Base, filename) -} - -func (i LocalImporter) GetLocalPath(p ...string) string { - return i.toURL(path.Join(p...)) -} - -func (i LocalImporter) importFile(URI string, next func(string, string) (interface{}, error)) (interface{}, error) { - if i.Symlink { - dest := GetDestinationFilePath(URI, nil) - - if err := os.MkdirAll(path.Dir(dest), 0751); err != nil { - return nil, err - } - - if i.Exists(URI) { - os.Symlink(i.toURL(URI), dest) - return next(dest, URI) - } else { - os.Symlink(i.toURL(URI)+"_MERGED", dest) - return ImportFile(i, URI, next) - } - } else { - return ImportFile(i, URI, next) - } -} - -func (i LocalImporter) GetFile(filename string) (io.Reader, error) { - if fd, err := os.Open(path.Join(i.Base, filename)); err != nil { - return nil, err - } else { - return fd, nil - } -} - -func (i LocalImporter) writeFile(filename string, reader io.Reader) error { - if fd, err := os.Create(path.Join(i.Base, filename)); err != nil { - return err - } else { - defer fd.Close() - io.Copy(fd, reader) - return nil - } -} - -func (i LocalImporter) ListDir(filename string) ([]string, error) { - if files, err := ioutil.ReadDir(path.Join(i.Base, filename)); err != nil { - return nil, err - } else { - res := make([]string, 0) - for _, file := range files { - res = append(res, file.Name()) - } - return res, nil - } -} - -func (i LocalImporter) Stat(filename string) (os.FileInfo, error) { - return os.Stat(path.Join(i.Base, filename)) -} - -type DeletableImporter interface { - DeleteDir(filename string) error -} - -func (i LocalImporter) DeleteDir(filename string) error { - return os.RemoveAll(path.Join(i.Base, filename)) -} diff --git a/admin/sync/markdown.go b/admin/sync/markdown.go deleted file mode 100644 index 5175b212..00000000 --- a/admin/sync/markdown.go +++ /dev/null @@ -1,98 +0,0 @@ -package sync - -import ( - "bytes" - "encoding/base32" - "net/url" - "path" - "strings" - - "srs.epita.fr/fic-server/libfic" - - "github.com/yuin/goldmark" - "github.com/yuin/goldmark/ast" - "github.com/yuin/goldmark/extension" - "github.com/yuin/goldmark/parser" - "github.com/yuin/goldmark/renderer/html" - "github.com/yuin/goldmark/text" - "github.com/yuin/goldmark/util" - "golang.org/x/crypto/blake2b" -) - -func ProcessMarkdown(i Importer, input string, rootDir string) (output string, err error) { - // Define the path where save linked files - hash := blake2b.Sum512([]byte(rootDir)) - - imgImporter := NewImageImporterTransformer(i, rootDir, hash) - - // Process md - markdown := goldmark.New( - goldmark.WithExtensions(extension.DefinitionList), - goldmark.WithExtensions(extension.Linkify), - goldmark.WithExtensions(extension.Strikethrough), - goldmark.WithExtensions(extension.Table), - goldmark.WithExtensions(extension.Typographer), - goldmark.WithParserOptions( - parser.WithASTTransformers( - util.Prioritized(imgImporter, 200), - ), - ), - goldmark.WithRendererOptions( - html.WithHardWraps(), - ), - ) - - var buf bytes.Buffer - context := parser.NewContext() - if err = markdown.Convert([]byte(input), &buf, parser.WithContext(context)); err != nil { - return - } - - output = string(buf.Bytes()) - - // Trim output - output = strings.TrimSpace(output) - - return output, imgImporter.(*imageImporterTransformer).err -} - -type imageImporterTransformer struct { - importer Importer - rootDir string - hash [blake2b.Size]byte - absPath string - err error -} - -func NewImageImporterTransformer(i Importer, rootDir string, hash [blake2b.Size]byte) parser.ASTTransformer { - absPath := "$FILES$/" + strings.ToLower(base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(hash[:])) - return &imageImporterTransformer{i, rootDir, hash, absPath, nil} -} - -func (t *imageImporterTransformer) Transform(doc *ast.Document, reader text.Reader, pc parser.Context) { - t.err = ast.Walk(doc, func(node ast.Node, enter bool) (ast.WalkStatus, error) { - if !enter { - return ast.WalkContinue, nil - } - - switch child := node.(type) { - case *ast.Image: - iPath := string(child.Destination) - - // Unescape string if needed (mostly %20 to space) - if ip, err := url.QueryUnescape(iPath); err == nil { - iPath = ip - } - - dPath := path.Join(fic.FilesDir, strings.ToLower(base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(t.hash[:])), iPath) - child.Destination = []byte(path.Join(t.absPath, string(child.Destination))) - - err := importFile(t.importer, path.Join(t.rootDir, iPath), dPath) - if err != nil { - return ast.WalkStop, err - } - } - - return ast.WalkContinue, nil - }) -} diff --git a/admin/sync/themes.go b/admin/sync/themes.go deleted file mode 100644 index 09a4440b..00000000 --- a/admin/sync/themes.go +++ /dev/null @@ -1,417 +0,0 @@ -package sync - -import ( - "bytes" - "fmt" - "image" - "image/jpeg" - "io" - "math/rand" - "net/http" - "os" - "path" - "regexp" - "strings" - "unicode" - - "github.com/cenkalti/dominantcolor" - "github.com/gin-gonic/gin" - "github.com/yuin/goldmark" - "go.uber.org/multierr" - "golang.org/x/image/draw" - - "srs.epita.fr/fic-server/libfic" -) - -// GetThemes returns all theme directories in the base directory. -func GetThemes(i Importer) (themes []string, err error) { - if dirs, err := i.ListDir("/"); err != nil { - return nil, err - } else { - for _, dir := range dirs { - if !strings.HasPrefix(dir, ".") && !strings.HasPrefix(dir, "_") && dir != fic.StandaloneExercicesDirectory { - if _, err := i.ListDir(dir); err == nil { - themes = append(themes, dir) - } - } - } - } - - return themes, nil -} - -// GetThemesExtended returns all theme directories, including standalone exercices. -func GetThemesExtended(i Importer) (themes []string, err error) { - themes, err = GetThemes(i) - if err != nil { - return - } - - if i.Exists(fic.StandaloneExercicesDirectory) { - themes = append(themes, fic.StandaloneExercicesDirectory) - } - - return -} - -// resizePicture makes the given image just fill the given rectangle. -func resizePicture(i Importer, imgPath string, importedPath string, rect image.Rectangle) error { - if fl, err := i.GetFile(imgPath); err != nil { - return err - } else { - if src, _, err := image.Decode(fl); err != nil { - if flc, ok := fl.(io.ReadCloser); ok { - flc.Close() - } - return err - } else if src.Bounds().Max.X > rect.Max.X && src.Bounds().Max.Y > rect.Max.Y { - if flc, ok := fl.(io.ReadCloser); ok { - flc.Close() - } - - mWidth := rect.Max.Y * src.Bounds().Max.X / src.Bounds().Max.Y - mHeight := rect.Max.X * src.Bounds().Max.Y / src.Bounds().Max.X - - if mWidth > rect.Max.X { - rect.Max.X = mWidth - } else { - rect.Max.Y = mHeight - } - dst := image.NewRGBA(rect) - draw.CatmullRom.Scale(dst, rect, src, src.Bounds(), draw.Over, nil) - - dstFile, err := fileWriter(strings.TrimSuffix(importedPath, ".jpg") + ".thumb.jpg") - if err != nil { - return err - } - defer dstFile.Close() - - if err = jpeg.Encode(dstFile, dst, &jpeg.Options{Quality: 100}); err != nil { - return err - } - } else { - dstFile, err := fileWriter(strings.TrimSuffix(importedPath, ".jpg") + ".thumb.jpg") - if err != nil { - return err - } - defer dstFile.Close() - - if err = jpeg.Encode(dstFile, src, &jpeg.Options{Quality: 100}); err != nil { - return err - } - } - } - return nil -} - -type SubImager interface { - SubImage(r image.Rectangle) image.Image -} - -// getBackgroundColor retrieves the most dominant color in the bottom of the image. -func getBackgroundColor(importedPath string) (uint32, error) { - fl, err := os.Open(strings.TrimSuffix(importedPath, ".jpg") + ".thumb.jpg") - if err != nil { - return 0, err - } - - src, _, err := image.Decode(fl) - if err != nil { - fl.Close() - return 0, err - } - - bounds := src.Bounds() - - // Test if the right and left corner have the same color - bottomLeft := src.(SubImager).SubImage(image.Rect(0, bounds.Dy()-10, 40, bounds.Dy())) - bottomRight := src.(SubImager).SubImage(image.Rect(bounds.Dx()-40, bounds.Dy()-10, bounds.Dx(), bounds.Dy())) - - colorLeft := dominantcolor.Find(bottomLeft) - colorRight := dominantcolor.Find(bottomRight) - if uint32(colorLeft.R>>5)<<16+uint32(colorLeft.G>>5)<<8+uint32(colorLeft.B>>5) == uint32(colorRight.R>>5)<<16+uint32(colorRight.G>>5)<<8+uint32(colorRight.B>>5) { - return uint32(colorLeft.R)<<16 + uint32(colorLeft.G)<<8 + uint32(colorLeft.B), nil - } - - // Only keep the darkest color of the bottom of the image - bottomFull := src.(SubImager).SubImage(image.Rect(0, bounds.Dy()-5, bounds.Dx(), bounds.Dy())) - colors := dominantcolor.FindN(bottomFull, 4) - - color := colors[0] - for _, c := range colors { - if uint32(color.R<<2)+uint32(color.G<<2)+uint32(color.B<<2) > uint32(c.R<<2)+uint32(c.G<<2)+uint32(c.B<<2) { - color = c - } - } - - return uint32(color.R)<<16 + uint32(color.G)<<8 + uint32(color.B), nil -} - -// getAuthors parses the AUTHORS file. -func getAuthors(i Importer, tname string) ([]string, error) { - if authors, err := GetFileContent(i, path.Join(tname, "AUTHORS.txt")); err != nil { - return nil, err - } else { - var ret []string - re := regexp.MustCompile("^([^<]+)(?: +<(.*)>)?$") - for _, a := range strings.Split(authors, "\n") { - a = strings.TrimFunc(a, unicode.IsSpace) - grp := re.FindStringSubmatch(a) - if len(grp) < 2 || grp[2] == "" { - ret = append(ret, a) - } else { - ret = append(ret, fmt.Sprintf("%s", grp[2], grp[1])) - } - } - return ret, nil - } -} - -// BuildTheme creates a Theme from a given importer. -func BuildTheme(i Importer, tdir string) (th *fic.Theme, exceptions *CheckExceptions, errs error) { - th = &fic.Theme{} - - th.Path = tdir - - // Get exceptions - exceptions = LoadThemeException(i, th) - - // Overwrite language - if language, err := GetFileContent(i, path.Join(tdir, "language.txt")); err == nil { - language = strings.TrimSpace(language) - if strings.Contains(language, "\n") { - errs = multierr.Append(errs, NewThemeError(th, fmt.Errorf("language.txt: Language can't contain new lines"))) - } else { - th.Language = language - } - } - - // Extract theme's label - if tname, err := GetFileContent(i, path.Join(tdir, "title.txt")); err == nil { - th.Name = fixnbsp(tname) - } else if f := strings.Index(tdir, "-"); f >= 0 { - th.Name = fixnbsp(tdir[f+1:]) - } else { - th.Name = fixnbsp(tdir) - } - th.URLId = fic.ToURLid(th.Name) - - if authors, err := getAuthors(i, tdir); err != nil { - if tdir != fic.StandaloneExercicesDirectory { - errs = multierr.Append(errs, NewThemeError(th, fmt.Errorf("unable to get AUTHORS.txt: %w", err))) - return nil, nil, errs - } - } else { - // Format authors - th.Authors = strings.Join(authors, ", ") - } - - var err error - if i.Exists(path.Join(tdir, "headline.txt")) { - th.Headline, err = GetFileContent(i, path.Join(tdir, "headline.txt")) - } else if i.Exists(path.Join(tdir, "headline.md")) { - th.Headline, err = GetFileContent(i, path.Join(tdir, "headline.md")) - } - if err != nil { - errs = multierr.Append(errs, NewThemeError(th, fmt.Errorf("unable to get theme's headline: %w", err))) - } - if th.Headline != "" { - // Call checks hooks - for _, h := range hooks.mdTextHooks { - for _, err := range multierr.Errors(h(th.Headline, th.Language, exceptions.GetFileExceptions("headline.md", "headline.txt"))) { - errs = multierr.Append(errs, NewThemeError(th, fmt.Errorf("headline.md: %w", err))) - } - } - } - - var intro string - if i.Exists(path.Join(tdir, "overview.txt")) { - intro, err = GetFileContent(i, path.Join(tdir, "overview.txt")) - } else if i.Exists(path.Join(tdir, "overview.md")) { - intro, err = GetFileContent(i, path.Join(tdir, "overview.md")) - } else { - err = fmt.Errorf("unable to find overview.txt nor overview.md") - } - if err != nil { - errs = multierr.Append(errs, NewThemeError(th, fmt.Errorf("unable to get theme's overview: %w", err))) - } else { - // Call checks hooks - for _, h := range hooks.mdTextHooks { - for _, err := range multierr.Errors(h(intro, th.Language, exceptions.GetFileExceptions("overview.md", "overview.txt"))) { - errs = multierr.Append(errs, NewThemeError(th, fmt.Errorf("overview.md: %w", err))) - } - } - - // Split headline from intro - if th.Headline == "" { - ovrvw := strings.Split(fixnbsp(intro), "\n") - th.Headline = ovrvw[0] - if len(ovrvw) > 1 { - intro = strings.Join(ovrvw[1:], "\n") - } - } - - // Format overview (markdown) - th.Intro, err = ProcessMarkdown(i, intro, tdir) - if err != nil { - errs = multierr.Append(errs, NewThemeError(th, fmt.Errorf("overview.txt: an error occurs during markdown formating: %w", err))) - } - var buf bytes.Buffer - err := goldmark.Convert([]byte(th.Headline), &buf) - if err != nil { - errs = multierr.Append(errs, NewThemeError(th, fmt.Errorf("overview.txt: an error occurs during markdown formating of the headline: %w", err))) - } else { - th.Headline = string(buf.Bytes()) - } - } - - if i.Exists(path.Join(tdir, "heading.jpg")) { - th.Image = path.Join(tdir, "heading.jpg") - } else if i.Exists(path.Join(tdir, "heading.png")) { - th.Image = path.Join(tdir, "heading.png") - } else { - errs = multierr.Append(errs, NewThemeError(th, fmt.Errorf("heading.jpg: No such file"))) - } - - if i.Exists(path.Join(tdir, "partner.jpg")) { - th.PartnerImage = path.Join(tdir, "partner.jpg") - } else if i.Exists(path.Join(tdir, "partner.png")) { - th.PartnerImage = path.Join(tdir, "partner.png") - } - - if i.Exists(path.Join(tdir, "partner.txt")) { - if txt, err := GetFileContent(i, path.Join(tdir, "partner.txt")); err != nil { - errs = multierr.Append(errs, NewThemeError(th, fmt.Errorf("unable to get partner's text: %w", err))) - } else { - th.PartnerText, err = ProcessMarkdown(i, txt, tdir) - if err != nil { - errs = multierr.Append(errs, NewThemeError(th, fmt.Errorf("partner.txt: an error occurs during markdown formating: %w", err))) - } - } - } - return -} - -// SyncThemeFiles import all theme's related files -func SyncThemeFiles(i Importer, btheme *fic.Theme) (errs error) { - if len(btheme.Image) > 0 { - if _, err := i.importFile(btheme.Image, - func(filePath string, origin string) (interface{}, error) { - if err := resizePicture(i, origin, filePath, image.Rect(0, 0, 500, 300)); err != nil { - return nil, err - } - - btheme.Image = strings.TrimPrefix(filePath, fic.FilesDir) - btheme.BackgroundColor, _ = getBackgroundColor(filePath) - return nil, nil - }); err != nil { - errs = multierr.Append(errs, NewThemeError(btheme, fmt.Errorf("unable to import heading image: %w", err))) - } - } - - if len(btheme.PartnerImage) > 0 { - if _, err := i.importFile(btheme.PartnerImage, - func(filePath string, origin string) (interface{}, error) { - btheme.PartnerImage = strings.TrimPrefix(filePath, fic.FilesDir) - return nil, nil - }); err != nil { - errs = multierr.Append(errs, NewThemeError(btheme, fmt.Errorf("unable to import partner image: %w", err))) - } - } - - return -} - -// SyncThemes imports new or updates existing themes. -func SyncThemes(i Importer) (exceptions map[string]*CheckExceptions, errs error) { - if themes, err := GetThemes(i); err != nil { - errs = multierr.Append(errs, fmt.Errorf("Unable to list themes: %w", err)) - } else { - rand.Shuffle(len(themes), func(i, j int) { - themes[i], themes[j] = themes[j], themes[i] - }) - - exceptions = map[string]*CheckExceptions{} - - for _, tdir := range themes { - btheme, excepts, berrs := BuildTheme(i, tdir) - errs = multierr.Append(errs, berrs) - - if btheme == nil { - continue - } - - exceptions[tdir] = excepts - - err = SyncThemeFiles(i, btheme) - if err != nil { - errs = multierr.Append(errs, NewThemeError(btheme, fmt.Errorf("unable to import heading image: %w", err))) - } - - var theme *fic.Theme - if theme, err = fic.GetThemeByPath(btheme.Path); err != nil { - if _, err := fic.CreateTheme(btheme); err != nil { - errs = multierr.Append(errs, NewThemeError(btheme, fmt.Errorf("an error occurs during add: %w", err))) - continue - } - } - - if !fic.CmpTheme(theme, btheme) { - btheme.Id = theme.Id - if _, err := btheme.Update(); err != nil { - errs = multierr.Append(errs, NewThemeError(btheme, fmt.Errorf("an error occurs during update: %w", err))) - continue - } - } - } - } - - return -} - -func LoadThemeExceptions(i Importer, theme *fic.Theme) (*CheckExceptions, error) { - return nil, nil -} - -// ApiListRemoteThemes is an accessor letting foreign packages to access remote themes list. -func ApiListRemoteThemes(c *gin.Context) { - themes, err := GetThemes(GlobalImporter) - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, themes) -} - -func GetRemoteTheme(thid string) (*fic.Theme, error) { - if thid == fic.StandaloneExercicesTheme.URLId || thid == fic.StandaloneExercicesDirectory { - return &fic.StandaloneExercicesTheme, nil - } - - theme, _, errs := BuildTheme(GlobalImporter, thid) - if theme == nil { - return nil, errs - } - - return theme, nil -} - -// ApiListRemoteTheme is an accessor letting foreign packages to access remote main theme attributes. -func ApiGetRemoteTheme(c *gin.Context) { - var theme *fic.Theme - var err error - - if c.Params.ByName("thid") == fic.StandaloneExercicesTheme.URLId { - theme, err = GetRemoteTheme(fic.StandaloneExercicesDirectory) - } else { - theme, err = GetRemoteTheme(c.Params.ByName("thid")) - } - - if err != nil { - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) - return - } - - c.JSON(http.StatusOK, theme) -} diff --git a/admin/team-names.txt b/admin/team-names.txt deleted file mode 100644 index 61e128b3..00000000 --- a/admin/team-names.txt +++ /dev/null @@ -1,204 +0,0 @@ -{{ListeCoul L|[[Aigue-marine (couleur)|Aigue-marine]]|79|F8|F8|121|248|248|51|0|0|3|180|51|97}} -{{ListeCoul L|[[Acajou (couleur)|Acajou]]|88|42|1D|136|66|29|0|51|79|47|21|79|53}} -{{ListeCoul L|[[Albâtre (couleur)|Albâtre]]|FE|FE|FE|254|254|254|0|0|0|0|0|0|100}} -{{ListeCoul L|[[Alezan]] (chevaux)|A7|67|26|167|103|38|0|38|77|35|30|77|65}} -{{ListeCoul L|[[Amarante (couleur)|Amarante]]|91|28|3B|145|40|59|0|72|59|43|349|72|57}} -{{ListeCoul L|[[Améthyste (couleur)|Améthyste]]|88|4D|A7|136|77|167|19|54|0|35|279|54|65}} -{{ListeCoul L|[[Anthracite (couleur)|Anthracite]]|30|30|30|48|48|48|0|0|0|81|0|0|19}} -{{ListeCoul L|[[Aquilain (couleur)|Aquilain]] (chevaux)|AD|4F|09|173|79|9|0|54|95|32|26|95|68}} -{{ListeCoul L|[[Ambre (couleur)|Ambre]] jaune|F0|C3|00|240|195|0|0|19|100|6|49|100|94}} -{{ListeCoul L|[[Auburn (couleur)|Auburn]] (cheveux)|9D|3E|0C|157|62|12|0|61|92|38|21|92|62}} -{{ListeCoul L|[[Aurore (couleur)|Aurore]]|FF|CB|60|255|203|96|0|20|62|0|40|62|100}} -{{ListeCoul L|[[Azur (couleur)|Azurin]]|A9|EA|FE|169|234|254|33|8|0|0|194|33|99}} -{{ListeCoul L|[[Beige]]|C8|AD|7F|200|173|127|0|14|37|22|38|40|64}} -{{ListeCoul L|[[Bisque (couleur)|Bisque]]|FF|E4|C4|255|228|196|0|11|23|0|33|100|88}} -{{ListeCoul L|[[Bistre (couleur)|Bistre]]|85|6D|4D|133|109|77|0|18|42|48|34|27|41}} -{{ListeCoul L|[[Acier]]|3A|8E|BA|58|142|186|69|24|0|27|201|52|48}} -{{ListeCoul L|[[Barbeau]] ou bleuet|54|72|AE|84|114|174|52|34|0|32|220|36|51}} -{{ListeCoul L|[[Ciel]]|77|B5|FE|119|181|254|53|29|0|0|212|99|73}} -{{ListeCoul L|[[Cobalt]] (pigment)|22|42|7C|34|66|124|73|47|0|51|219|57|31}} -{{ListeCoul L|[[Minuit]]|00|33|66|0|51|102|100|50|0|60|210|100|20}} -{{ListeCoul L|[[Hussard]] (pigment)|24|44|5C|36|68|92|61|26|0|64|206|44|25}} -{{ListeCoul L|[[Mers du sud]]|00|CC|CB|0|204|203|100|0|0|20|180|100|40}} -{{ListeCoul L|[[Dragée]]|DF|F2|FF|223|242|255|13|5|0|0|204|100|94}} -{{ListeCoul L|[[Guède]] (pigment)|56|73|9A|86|115|154|44|25|0|40|214|28|47}} -{{ListeCoul L|[[Klein]]|00|2F|A7|00|47|167|100|72|0|35|223|100|33}} -{{ListeCoul L|[[Majorelle]] |60|50|DC|96|80|220|56|64|0|14|247|67|59}} -{{ListeCoul L|[[Paon]]|06|77|90|6|119|144|96|17|0|44|191|92|29}} -{{ListeCoul L|[[Pétrole]]|1D|48|51|29|72|81|64|11|0|68|190|47|22}} -{{ListeCoul L|[[Brique (couleur)|Brique]]|84|2E|1B|132|46|27|0|65|80|48|11|66|31}} -{{ListeCoul L|[[Noix]]|3F|22|04|63|34|4|0|46|94|75|31|88|13}} -{{ListeCoul L|[[Cacao (couleur)|Cacao]]|61|4B|3A|97|75|58|0|23|40|62|26|25|30}} -{{ListeCoul L|[[Cannelle (couleur)|Cannelle]]|7E|58|35|126|88|53|0|30|58|51|29|41|35}} -{{ListeCoul L|[[Capucine (couleur)|Capucine]]|FF|5E|4D|255|94|77|0|63|70|0|6|100|65}} -{{ListeCoul L|[[Caramel (couleur)|Caramel]] (pigments)|7E|33|00|126|51|0|0|60|100|51|24|100|25}} -{{ListeCoul L|[[Carmin]] (pigment)|96|00|18|150|0|24|0|100|84|41|350|100|29}} -{{ListeCoul L|[[Céruse]] (pigment)|FE|FE|FE|254|254|254|0|0|0|0|0|0|100}} -{{ListeCoul L|[[Chamois (couleur)|Chamois]]|D0|C0|7A|208|192|122|0|8|41|18|49|48|65}} -{{ListeCoul L|[[Chartreuse (couleur)|Chartreuse]]|7F|FF|00|127|255|0|50|0|100|0|90|100|50}} -{{ListeCoul L|[[Châtaigne (couleur)|Châtaigne]]|80|6D|5A|128|109|90|0|15|30|50|30|17|43}} -{{ListeCoul L|[[Cinabre (couleur)|Cinabre]] (pigment)|DB|17|02|219|23|2|0|89|99|14|6|98|43}} -{{ListeCoul L|[[Colombin (couleur)|Colombin]] (vieilli)|6A|45|5D|106|69|93|0|35|12|58|321|21|34}} -{{ListeCoul L|[[Coquelicot (couleur)|Coquelicot]]|C6|08|00|198|8|0|0|96|100|22|2|100|39}} -{{ListeCoul L|[[Corail (couleur)|Corail]]|E7|3E|01|231|62|1|0|73|100|9|16|99|45}} -{{ListeCoul L|[[Cuivre (couleur)|Cuivre]]|B3|67|00|179|103|0|0|42|100|30|35|100|35}} -{{ListeCoul L|[[Cyan]]|2B|FA|FA|43|250|250|83|0|0|2|180|95|57}} -{{ListeCoul L|[[Denim (couleur)|Denim]]|15|60|BD|21|96|189|89|49|0|26|213|80|41}} -{{ListeCoul L|[[Ébène (couleur)|Ébène]]|00|00|00|0|0|0|0|0|0|100|0|0|0}} -{{ListeCoul L|[[Écarlate]]|ED|00|00|237|0|0|0|100|100|7|0|100|46}} -{{ListeCoul L|[[Écru]]|FE|FE|E0|254|254|224|0|0|12|0|60|94|94}} -{{ListeCoul L|[[Émeraude (couleur)|Émeraude]]|01|D7|58|1|215|88|100|0|59|16|144|99|42}} -{{ListeCoul L|[[Étain]]|ED|ED|ED|237|237|237|0|0|0|7|0|0|93}} -{{ListeCoul L|[[Fauve (couleur)|Fauve]]|AD|4F|09|173|79|9|0|54|95|32|26|90|36}} -{{ListeCoul L|[[Feu (couleur)|Feu]]|FF|49|01|255|73|1|0|71|100|0|17|100|50}} -{{ListeCoul L|[[Flave]]|E6|E6|97|230|230|151|0|0|34|10|60|61|75}} -{{ListeCoul L|[[Fuchsia (couleur)|Fuchsia]]|FD|3F|92|253|63|146|0|75|42|1|334|75|62}} -{{ListeCoul L|[[Garance (couleur)|Garance]] (pigment)|EE|10|10|238|16|16|0|93|93|7|0|87|50}} -{{ListeCoul L|[[Glycine (couleur)|Glycine]]|C9|A0|DC|201|160|220|9|27|0|14|281|46|75}} -{{ListeCoul L|[[Grège]]|BB|AE|98|187|174|152|0|7|19|27|38|20|66}} -{{ListeCoul L|[[Grenat (couleur)|Grenat]]|6E|0B|14|110|11|20|0|90|82|57|355|82|24}} -{{ListeCoul L|[[Lin]]|D2|CA|EC|210|202|236|11|14|0|7|254|47|86}} -{{ListeCoul L|[[Payne]] (mélange de pigments)|67|71|79|103|113|121|15|7|0|53|207|8|44}} -{{ListeCoul L|[[Fer]]|7F|7F|7F|127|127|127|0|0|0|50|0|0|50}} -{{ListeCoul L|[[Perle]]|CE|CE|CE|206|206|206|0|0|0|19|0|0|81}} -{{ListeCoul L|[[Héliotrope (couleur)|Héliotrope]]|DF|73|FF|223|115|255|13|55|0|0|286|100|73}} -{{ListeCoul L|[[Incarnadin]]|FE|96|A0|254|150|160|0|41|37|0|354|98|79}} -{{ListeCoul L|[[Incarnat]]|FF|6F|7D|255|111|125|0|56|51|0|354|100|72}} -{{ListeCoul L|[[Indigo]] |2E|00|6C|46|0|108|57|100|0|58|266|100|21}} -{{ListeCoul L|[[Isabelle (cheval)|Isabelle]] (chevaux)|78|5E|2F|120|94|47|0|22|61|53|39|44|33}} -{{ListeCoul L|[[Ivoire (couleur)|Ivoire]]|FF|FF|D4|255|255|212|0|0|17|0|60|100|92}} -{{ListeCoul L|[[Jais]]|00|00|00|0|0|0|0|0|0|100|0|0|0}} -{{ListeCoul L|[[Auréolin]]|FD|EE|00|253|238|0|0|6|100|0|56|90|99}} -{{ListeCoul L|[[Canari]]|E7|F0|0D|231|240|13|4|0|95|6|62|90|50}} -{{ListeCoul L|[[Mars]] (pigment)|EE|D1|53|238|209|83|0|12|65|7|49|82|63}} -{{ListeCoul L|[[Naples]] (pigment)|FF|F0|BC|255|240|188|0|6|26|0|47|100|87}} -{{ListeCoul L|[[Mimosa]]|FE|F8|6C|254|248|108|0|2|57|0|58|99|71}} -{{ListeCoul L|[[Lapis-lazuli (couleur)|Lapis-lazuli]]|26|61|9C|38|97|156|76|38|0|39|210|61|38}} -{{ListeCoul L|[[Lavallière (couleur)|Lavallière]] (reliure)|8F|59|22|143|89|34|0|38|76|44|30|62|35}} -{{ListeCoul L|[[Lavande (couleur)|Lavande]]|96|83|EC|150|131|236|36|44|0|7|251|73|72}} -{{ListeCoul L|[[Lilas (couleur)|Lilas]]|B6|66|D2|182|102|210|13|51|0|18|284|55|61}} -{{ListeCoul L|[[Magenta (couleur)|Magenta]]|FF|00|FF|255|0|255|0|100|0|0|300|100|50}} -{{ListeCoul L|[[Maïs (couleur)|Maïs]]|FF|DE|75|255|222|117|0|13|54|0|46|100|73}} -{{ListeCoul L|[[Malachite (couleur)|Malachite]]|1F|A0|55|31|160|85|81|0|47|37|145|68|37}} -{{ListeCoul L|[[Mandarine (couleur)|Mandarine]]|FE|A3|47|254|163|71|0|36|72|0|30|99|64}} -{{ListeCoul L|[[Mauve (couleur)]]|D4|73|D4|150|85|120|0|43|20|41|328|28|46}} -{{ListeCoul L|[[Menthe (couleur)|Menthe]]|16|B8|4E|22|184|78|88|0|58|28|141|79|40}} -{{ListeCoul L|[[Mordoré]]|87|59|1A|135|89|26|0|34|81|47|35|68|32}} -{{ListeCoul L|[[Moutarde (couleur)|Moutarde]]|C7|CF|00|199|207|0|4|0|100|19|62|100|41}} -{{ListeCoul L|[[Nacarat]]|FC|5D|5D|252|93|93|0|63|63|1|0|63|99}} -{{ListeCoul L|[[Nankin (couleur)|Nankin]]|F7|E2|69|247|226|105|0|9|57|3|51|90|69}} -{{ListeCoul L|[[Aniline]] (pigment)|12|0D|16|18|13|22|18|41|0|91|273|26|7}} -{{ListeCoul L|[[Ocre]] (pigment) |DF|AF|2C|223|175|44|0|22|80|13|44|74|52}} -{{ListeCoul L|[[Olive (couleur)|Olive]]|70|8D|23|112|141|35|21|0|75|45|76|60|35}} -{{ListeCoul L|[[Opale]]|66|CC|CC|102|204|204|50|0|0|20|180|50|60}} -{{ListeCoul L|[[Orange (couleur)|Orange]]|FF|7F|00|255|127|0|0|50|100|0|30|100|50}} -{{ListeCoul L|[[Orchidée (couleur)|Orchidée]]|DA|70|D6|218|112|214|0|49|2|15|302|59|65}} -{{ListeCoul L|[[Orpiment (couleur)|Orpiment]] (pigment)|FC|D2|1C|252|210|28|0|17|89|1|49|97|55}} -{{ListeCoul L|[[Paille (couleur)|Paille]]|FE|E3|47|254|227|71|0|11|72|0|51|99|64}} -{{ListeCoul L|[[Parme (couleur)|Parme]]|CF|A0|E9|207|160|233|11|31|0|9|279|62|77}} -{{ListeCoul L|[[Pastel (couleur)|Pastel]] (pigment)|56|73|9A|86|115|154|44|25|0|40|214|28|47}} -{{ListeCoul L|[[Pistache (couleur)|Pistache]]|BE|F5|74|190|245|116|22|0|53|4|86|87|71}} -{{ListeCoul L|[[Ponceau (couleur)|Ponceau]]|C6|08|00|198|8|0|0|96|100|22|2|100|39}} -{{ListeCoul L|[[Pourpre]] (héraldique)|9E|0E|40|158|14|64|0|91|59|38|339|84|34}} -{{ListeCoul L|[[Prasin]]|4C|A6|6B|76|166|107|54|0|36|35|141|37|47}} -{{ListeCoul L|[[Mountbatten]]|99|7A|8D|153|122|141|0|20|8|40|323|13|54}} -{{ListeCoul L|[[Cardinal]]|B8|20|10|184|32|16|0|83|91|28|6|84|39}} -{{ListeCoul L|[[Alizarine]]|E3|26|36|227|38|54|0|85|70|1|355|83|89}} -{{ListeCoul L|[[Andrinople]] (pigment)|A9|11|01|169|17|1|0|90|99|34|6|99|33}} -{{ListeCoul L|[[Falun]] (pigment)|80|18|18|128|24|24|0|81|81|50|0|68|30}} -{{ListeCoul L|[[Tomette]]|AE|4A|34|174|74|52|0|57|70|32|11|54|44}} -{{ListeCoul L|[[Rubis (couleur)|Rubis]]|E0|11|5F|224|17|95|0|92|58|12|337|92|88}} -{{ListeCoul L|[[Sable (couleur)|Sable]]|E0|CD|A9|224|205|169|0|8|25|12|39|47|77}} -{{ListeCoul L|[[Safran (épice)#Coloration et parfumerie|Safran]]|F3|D6|17|243|214|23|0|12|91|5|52|90|52}} -{{ListeCoul L|[[Saphir (couleur)|Saphir]]|01|31|B4|1|49|180|99|73|0|29|224|99|35}} -{{ListeCoul L|[[Sarcelle (couleur)|Sarcelle]]|00|8E|8E|0|142|142|100|0|0|44|180|100|28}} -{{ListeCoul L|[[Saumon (couleur)|Saumon]]|F8|8E|55|248|142|85|0|43|66|3|21|92|65}} -{{ListeCoul L|[[Sépia]] (pigment)|AE|89|64|174|137|100|0|21|43|32|30|31|54}} -{{ListeCoul L|[[Sinople]] (héraldique)|14|94|14|20|148|20|86|0|86|42|120|76|33}} -{{ListeCoul L|[[Smalt (couleur)|Smalt]] (pigment)|00|33|99|0|51|153|100|67|0|40|220|100|30}} -{{ListeCoul L|[[Soufre (couleur)|Soufre]]|FF|FF|6B|255|255|107|0|0|58|0|60|100|71}} -{{ListeCoul L|[[Tangerine]] (couleur)|FF|7F|00|255|127|0|0|50|100|0|30|100|50}} -{{ListeCoul L|[[Taupe (couleur)|Taupe]]|46|3F|32|70|63|50|0|10|29|73|39|17|24}} -{{ListeCoul L|[[Ombre]] (pigment)|92|6D|27|146|109|39|0|25|73|43|39|58|36}} -{{ListeCoul L|[[Tomate (couleur)|Tomate]]|DE|29|16|222|41|22|0|82|90|13|6|82|48}} -{{ListeCoul L|[[Topaze (couleur)|Topaze]]|FA|EA|73|250|234|115|0|6|54|2|53|93|72}} -{{ListeCoul L|[[Turquoise (couleur)|Turquoise]]|25|FD|E9|37|253|233|85|0|8|1|174|98|57}} -{{ListeCoul L|[[Vermeil (couleur)|Vermeil]]|FF|09|21|255|9|33|0|96|87|0|354|100|52}} -{{ListeCoul L|[[Vermillon]]|DB|17|02|219|23|2|0|89|99|14|6|98|43}} -{{ListeCoul L|[[Céladon]]|83|A6|97|131|166|151|21|0|9|35|154|16|58}} -{{ListeCoul L|[[Hooker]] (mélange de pigments)|1B|4F|08|27|79|8|66|0|90|69|104|82|17}} -{{ListeCoul L|[[Lime]]|9E|FD|38|158|253|56|38|0|78|1|89|98|61}} -{{ListeCoul L|[[Mélèze]]|38|6F|48|56|111|72|50|0|35|56|137|33|33}} -{{ListeCoul L|[[Opaline]]|97|DF|C6|151|223|198|32|0|11|13|159|53|73}} -{{ListeCoul L|[[Pin]]|01|79|6F|1|121|111|99|0|8|53|175|98|24}} -{{ListeCoul L|[[Prairie]]|57|D5|3B|87|213|59|59|0|72|16|109|65|53}} -{{ListeCoul L|[[Printemps]]|00|FF|7F|0|255|127|100|0|50|0|150|100|50}} -{{ListeCoul L|[[Sapin]]|09|52|28|9|82|40|89|0|51|68|145|80|18}} -{{ListeCoul L|[[Sauge]]|68|9D|71|104|157|113|34|0|28|38|130|21|51}} -{{ListeCoul L|[[Tilleul]]|A5|D1|52|165|209|82|21|0|61|18|81|58|57}} -{{ListeCoul L|[[Véronèse]] (pigment)|5A|65|21|90|101|33|11|0|67|60|70|51|26}} -{{ListeCoul L|[[Violine]]|A1|06|84|161|6|132|0|96|18|37|311|93|33}} -{{ListeCoul L|[[Viride]] (pigment)|40|82|6D|64|130|109|51|0|16|49|161|34|38}} -{{ListeCoul L|[[Zinzolin]]|6C|02|77|108|2|119|9|98|0|53|294|97|24}} -{{ListeCoul L|[[Abricot (couleur)|Abricot]]|E6|7E|30|230|126|48|5|60|87|0|26|79|90}} -{{ListeCoul L|[[Absinthe (couleur)|Absinthe]]|7F|DD|4C|127|221|76|43|0|66|13|99|66|87}} -{{ListeCoul L|[[Corbeau]] (cheveux)|00|00|00|0|0|0|0|0|0|100|0|0|0}} -{{ListeCoul L|[[Amande (couleur)|Amande]]|82|C4|6C|130|196|108|34|0|45|23|105|45|77}} -{{ListeCoul L|[[Ardoise (couleur)|Ardoise]]|5A|5E|6B|90|94|107|16|12|0|58|226|16|42}} -{{ListeCoul L|[[Argile (couleur)|Argile]]|EF|EF|EF|239|239|239|0|0|0|6|0|0|94}} -{{ListeCoul L|[[Asperge (couleur)|Asperge]]|7B|A0|5B|123|160|91|23|0|43|37|92|43|63}} -{{ListeCoul L|[[Azur (couleur)|Azur]]|00|7F|FF|0|127|255|100|50|0|0|210|100|100}} -{{ListeCoul L|[[Baillet (couleur)|Baillet]] (chevaux vieilli)|AE|64|2D|174|100|45|0|43|74|32|26|59|43}} -{{ListeCoul L|[[Banane (couleur)|Banane]]|D1|B6|06|209|182|6|0|13|97|18|52|94|42}} -{{ListeCoul L|[[Blé (couleur)|Blé]]|E8|D6|30|232|214|48|0|8|79|9|54|80|55}} -{{ListeCoul L|[[Canard]]|04|8B|9A|4|139|154|97|10|0|40|186|95|31}} -{{ListeCoul L|[[Céleste]]|26|C4|EC|38|196|236|84|17|0|7|192|84|54}} -{{ListeCoul L|[[Charrette]]|8E|A2|C6|142|162|198|28|18|0|22|219|33|67}} -{{ListeCoul L|[[Outremer]]|1B|01|9B|27|1|155|83|99|0|39|250|99|31}} -{{ListeCoul L|[[Persan]]|66|00|FF|102|0|255|60|100|0|0|264|100|50}} -{{ListeCoul L|[[Turquin]]|42|5B|8A|66|91|138|52|34|0|46|219|35|40}} -{{ListeCoul L|[[Vénitien]] (cheveux)|E7|A8|54|231|168|84|0|27|64|9|34|75|62}} -{{ListeCoul L|[[Bureau (couleur)|Bureau]]|6B|57|31|107|87|49|0|19|54|58|39|37|31}} -{{ListeCoul L|[[Cachou (couleur)|Cachou]] (pigments)|2F|1B|0C|47|27|12|0|43|74|82|26|59|12}} -{{ListeCoul L|[[Cæruléum]]|35|7A|B7|53|122|183|71|33|0|28|208|55|46}} -{{ListeCoul L|[[Café (couleur)|Café]]|46|2E|01|70|46|1|0|34|99|73|39|97|14}} -{{ListeCoul L|[[Carotte (couleur)|Carotte]]|F4|66|1B|244|102|27|0|58|89|4|21|91|53}} -{{ListeCoul L|[[Cerise (couleur)|Cerise]]|DE|31|63|222|49|99|0|78|55|13|343|72|53}} -{{ListeCoul L|[[Cerise (couleur)|Hollywood]]|F4|00|A1|244|0|161|0|100|34|4|320|100|48}} -{{ListeCoul L|[[Chair (couleur)|Chair]]|FE|C3|AC|254|195|172|0|23|32|0|17|98|84}} -{{ListeCoul L|[[Chaudron (couleur)|Chaudron]]|85|53|0F|133|83|15|0|38|89|48|35|80|29}} -{{ListeCoul L|[[Chocolat (couleur)|Chocolat]]|5A|3A|22|90|58|34|0|36|62|65|26|45|24}} -{{ListeCoul L|[[Citron (couleur)|Citron]]|F7|FF|3C|247|255|60|3|0|76|0|62|100|62}} -{{ListeCoul L|[[Citrouille (couleur)|Citrouille]]|DF|6D|14|223|109|20|0|51|91|13|26|84|48}} -{{ListeCoul L|[[Fraise (couleur)|Fraise]]|BF|30|30|191|48|48|0|75|75|25|0|60|47}} -{{ListeCoul L|[[Framboise (couleur)|Framboise]]|C7|2C|48|199|44|72|0|78|64|22|349|64|48}} -{{ListeCoul L|[[Grenadine (couleur)|Grenadine]]|E9|38|3F|233|56|63|0|76|73|9|358|80|57}} -{{ListeCoul L|[[Gris]]|60|60|60|96|96|96|0|0|0|62|0|0|38}} -{{ListeCoul L|[[Groseille (couleur)|Groseille]]|CF|0A|1D|207|10|29|0|95|86|19|354|91|43}} -{{ListeCoul L|[[Kaki (couleur)|Kaki]]|94|81|2B|148|129|43|0|13|71|42|49|55|37}} -{{ListeCoul L|[[Marine (couleur)|Marine]]|03|22|4C|3|34|76|96|55|0|70|215|92|15}} -{{ListeCoul L|[[Marron (couleur)|Marron]]|58|29|00|88|41|0|0|53|100|65|28|100|17}} -{{ListeCoul L|[[Melon]]|DE|98|16|222|152|22|0|32|90|13|39|82|48}} -{{ListeCoul L|[[Neige (couleur)|Neige]]|FE|FE|FE|254|254|254|0|0|0|0|0|0|100}} -{{ListeCoul L|[[Encre]]|00|00|00|0|0|0|0|0|0|100|0|0|0}} -{{ListeCoul L|[[Carbone]] (pigment)|13|0E|0A|19|14|10|0|26|47|93|27|31|6}} -{{ListeCoul L|[[Noisette (couleur)|Noisette]]|95|56|28|149|86|40|0|42|73|42|25|58|37}} -{{ListeCoul L|[[Prune (couleur)|Prune]]|81|14|53|129|20|83|0|84|36|49|325|73|29}} -{{ListeCoul L|[[Puce (couleur)|Puce]]|4E|16|09|78|22|9|0|72|88|69|11|79|17}} -{{ListeCoul L|[[Rose bonbon (couleur)|Bonbon]]|F9|42|9E|249|66|158|0|74|37|2|330|74|98}} -{{ListeCoul L|[[Souris (couleur)|Souris]]|9E|9E|9E|158|158|158|0|0|0|38|0|0|62}} -{{ListeCoul L|[[Sienne]] (pigment)|8A|33|24|138|51|36|0|63|74|46|9|59|34}} -{{ListeCoul L|[[Tourterelle (couleur)|Tourterelle]] |BB|AC|AC|187|172|172|0|8|8|27|0|10|70}} -{{ListeCoul L|[[Vanille (couleur)|Vanille]]|E1|CE|9A|225|206|154|0|8|32|12|44|54|74}} -{{ListeCoul L|[[Vert]]|00|80|20|0|128|32|100|50|100|0|135|100|50}} -{{ListeCoul L|[[Anis]]|9F|E8|55|159|232|85|31|0|63|9|90|76|62}} -{{ListeCoul L|[[Bouteille]]|09|6A|09|9|106|9|92|0|92|58|120|84|23}} -{{ListeCoul L|[[Épinard]]|17|57|32|23|87|50|74|0|43|66|145|58|22}} -{{ListeCoul L|[[Gazon]] ou herbe|3A|9D|23|58|157|35|63|0|78|38|109|64|38}} -{{ListeCoul L|[[Lichen]]|85|C1|7E|133|193|126|31|0|35|24|114|35|63}} -{{ListeCoul L|[[Mousse]]|67|9F|5A|103|159|90|35|0|43|38|109|28|49}} -{{ListeCoul L|[[Perroquet]]|3A|F2|4B|58|242|75|76|0|69|5|126|88|59}} -{{ListeCoul L|[[Poireau]]|4C|A6|6B|76|166|107|54|0|36|35|141|37|47}} -{{ListeCoul L|[[Pomme]]|34|C9|24|52|201|36|74|0|82|21|114|70|46}} -{{ListeCoul L|[[Violet]]|7F|00|FF|127|0|255|50|100|0|40|270|100|50}} -{{ListeCoul L|[[Évêque]]|72|3E|64|114|62|100|0|46|12|55|316|30|35}} diff --git a/backup.sh b/backup.sh new file mode 100755 index 00000000..84852fca --- /dev/null +++ b/backup.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +cd `dirname "$0"` +TO_BCKP="/mnt/backup" + +chown synchro "$TO_BCKP" + +if [ "$UID" = "0" ] +then + SCRIPT=`pwd`/`basename "$0"` + su -c "sh $SCRIPT $@" synchro + exit $? +fi + +if mount | grep "$TO_BCKP" > /dev/null +then + + mysqldump -u backup --password="Riuy6of sae^W0Sh" fic2014 > "$TO_BCKP"/db/`date +%Y%m%d-%H%M`.sql + + rsync -avL misc "$TO_BCKP" + rsync -avL .git "$TO_BCKP" + rsync -avL logs "$TO_BCKP" + rsync -avL /var/log "$TO_BCKP" + rsync -avL true_files "$TO_BCKP" + +else + echo No volume mount on $TO_BCKP +fi diff --git a/check.pl b/check.pl new file mode 100755 index 00000000..92fd49ab --- /dev/null +++ b/check.pl @@ -0,0 +1,240 @@ +#!/usr/bin/env perl + +use v5.10.1; +use strict; +use warnings; +use DBI; +use File::Basename; +use Digest; +use Digest::MD5 qw(md5); +use Digest::SHA qw(sha1 sha224 sha256 sha384 sha512); +use Mcrypt qw(:ALGORITHMS :MODES :FUNCS); + +sub encrypt +{ + my ($algo, $key, $data, $mode) = @_; + + my $td = mcrypt_load( $algo, "", $mode, '' ); + mcrypt_init($td, $key, ""); + + my $encrypted = mcrypt_encrypt($td, $data); + + mcrypt_end($td); + + return $encrypted; +} + +sub my_crypt +{ + my ($key, $content) = @_; + + my $kfirst = pack('H*', substr($key, 0, 64)); + $content = encrypt(SERPENT, $kfirst, $content, ECB); + + if (length $key > 64) + { + my $ksec = pack('H*', substr($key, 64, 64)); + $content = encrypt(SERPENT, $ksec, $content, ECB); + } + + return unpack('H*', $content); +} + +#Return number of good solutions +my $exit = 0; + +my $root = dirname(__FILE__); + +chdir($root); + +# First, read PHP configuration to extract some settings +my $profile; +my $submission_dir; + +open my $conf, "<", "$root/onyx/config/root.xml"; +for my $p (<$conf>) +{ + if ($p =~ /<(?:option|var) name="(.*)">(.*)<\/(?:option|var)>/) + { + $profile = $2 if ($1 eq "profile"); + $submission_dir = $2 if ($1 eq "submission_dir"); + } +} +close $conf; + +my $end_time = 1999999999; +if (-f "$root/misc/challenge_started") +{ + open my $conf, "<", "$root/misc/challenge_started"; + $end_time = <$conf>; + close $conf; + chomp($end_time); + $end_time += 14400; +} + +die("TIME expired!") if time() > $end_time; +die("No DB profile found") if ! $profile; +die("submission_dir is not a directory") if ! $submission_dir || ! -d $submission_dir; + +# Read db settings +my %db_settings; +open my $dbprof, "<", "$root/onyx/db/$profile.profile.php"; +while (<$dbprof>) +{ + if (/\$___profile\[['"](.+)['"]\] = ['"](.+)['"]/) + { + $db_settings{$1} = $2; + } +} +close $dbprof; + +my $dbh; +# List all files to treat +opendir(my $dh, $submission_dir) || die "Can't opendir submission_dir: $!"; +for my $f (readdir $dh) +{ + if ($f =~ /^([0-9]+)-([0-9]+)-([a-zA-Z0-9_]+)$/) + { + my $good = -1; + + my $team = $1; + my $theme = $2; + my $exercice = $3; + + open my $fh, "<", "$submission_dir/$f"; + my %solution; + $solution{md5} = <$fh>; chomp( $solution{md5} ); + $solution{sha1} = <$fh>; chomp( $solution{sha1} ); + $solution{sha256} = <$fh>; chomp( $solution{sha256} ); + $solution{sha384} = <$fh>; chomp( $solution{sha384} ); + $solution{sha512} = <$fh>; chomp( $solution{sha512} ); + $solution{whirlpool} = <$fh>; chomp( $solution{whirlpool} ); + close $fh; + + use Data::Dumper; + print STDERR Dumper(\%solution); + + $dbh = DBI->connect("DBI:mysql:database=$db_settings{db};host=$db_settings{host};port=3306", + $db_settings{user}, $db_settings{pass}, + {'RaiseError' => 1, 'PrintError' => 1}) + or die $DBI::errstr if !$dbh; + + query($dbh, "INSERT INTO exercice_tries (id_exercice, id_team, time) VALUES ('$exercice', $team, CURRENT_TIMESTAMP);"); + + my $sth = query($dbh, "SELECT format, value FROM exercice_keys WHERE id_exercice = ".$dbh->quote($exercice)); + + # Check solutions + while (my $row = get_row($sth)) + { + $good = 1 if ($good == -1); + + my $type = @$row[0]; + my $sol = @$row[1]; + my $filh; my $tmp_solution; + + if ($type eq "md5") { + $filh = md5($f); + } + elsif ($type eq "sha1") { + $filh = sha1($f); + } + elsif ($type eq "sha224") { + $filh = sha224($f); + } + elsif ($type eq "sha256") { + $filh = sha256($f); + } + elsif ($type eq "sha384") { + $filh = sha384($f); + } + elsif ($type eq "sha512") { + $filh = sha512($f); + } + elsif ($type eq "whirlpool") { + my $hash = Digest->new( 'Whirlpool' ); + $hash->add( $f ); + $filh = $hash->digest; + } + else { + warn "$type not implemented"; + } + + $tmp_solution = my_crypt($sol, $filh) if ($filh); + + say STDERR "check $type: $solution{$type} vs $tmp_solution"; + + if ($solution{$type} ne $tmp_solution) + { + $good = 0; + last; + } + } + + # Register solve + if ($good == -1) { + say STDERR localtime().": Exercice $exercice doesn't exist ; given by team $team in theme $theme."; + } + elsif ($good == 1) + { + # Check if the exercice exists + $sth = query($dbh, "SELECT `require` FROM exercices E WHERE E.id_theme = $theme AND E.id = ".$dbh->quote($exercice)); + if ($sth->rows) + { + my $row = get_row($sth); + + # Check if the team has solved dependancies + $sth = query($dbh, "SELECT S.id FROM solved S WHERE S.id_exercice = ".$dbh->quote(@$row[0])) if @$row[0]; + if (! @$row[0] || $sth->rows) + { + # Check if the team has not already solved this exercice + $sth = query($dbh, "SELECT S.id FROM solved S WHERE S.id_exercice = ".$dbh->quote($exercice)); + if (! $sth->rows) + { + say "resetresetreset:TEAM$team,$theme:SYNCSYN:TEAM$team:SYNC:HOME:SYNC:all:DS"; + + say STDERR localtime().": Team $team solve exercice $exercice in $theme"; + query($dbh, "INSERT INTO solved (id_team, id_exercice, time) VALUES ($team, ".$dbh->quote($exercice).", CURRENT_TIMESTAMP);"); + $exit++; + } + else { + warn localtime().": Team $team try to solve exercice $exercice in $theme but ALREADY solved it"; + } + } + else { + warn localtime().": Team $team try to solve exercice $exercice in $theme but has NOT SOLVED required exercice ".@$row[0]; + } + } + else { + warn localtime().": Team $team try to solve exercice $exercice in $theme but does not exist"; + } + } + else { + say "resetresetreset:TEAM$team,$theme:SYNCSYN"; + + say STDERR localtime().": Team $team didn't give the correct answer for exercice $exercice."; + } + + # Remove the file + unlink("$submission_dir/$f"); + } +} +closedir $dh; + +$dbh->disconnect() if $dbh; + +exit( $exit > 126 ? 126 : $exit ); + +sub query +{ + my $sth = $_[0]->prepare($_[1]); + $sth->execute(); + + die($_[0]->errstr) if (!$sth); + + return $sth; +} + +sub get_row +{ + return $_[0]->fetchrow_arrayref(); +} diff --git a/checker/.gitignore b/checker/.gitignore deleted file mode 100644 index 41646b19..00000000 --- a/checker/.gitignore +++ /dev/null @@ -1 +0,0 @@ -checker \ No newline at end of file diff --git a/checker/choices.go b/checker/choices.go deleted file mode 100644 index 8687610d..00000000 --- a/checker/choices.go +++ /dev/null @@ -1,97 +0,0 @@ -package main - -import ( - "encoding/base64" - "encoding/binary" - "encoding/json" - "io/ioutil" - "log" - "math/rand" - "os" - - "srs.epita.fr/fic-server/libfic" -) - -type wantChoices struct { - FlagId int `json:"id"` -} - -func treatWantChoices(pathname string, team *fic.Team) { - // Generate a unique identifier to follow the request in logs - bid := make([]byte, 5) - binary.LittleEndian.PutUint32(bid, rand.Uint32()) - id := "[" + base64.StdEncoding.EncodeToString(bid) + "]" - log.Println(id, "New wantChoices receive", pathname) - - var ask wantChoices - - cnt_raw, err := ioutil.ReadFile(pathname) - if err != nil { - log.Printf("%s [ERR] %s\n", id, err) - return - } - - err = json.Unmarshal(cnt_raw, &ask) - if err != nil { - log.Printf("%s [ERR] %s\n", id, err) - return - } - - if ask.FlagId == 0 { - log.Printf("%s [WRN] Invalid content in wantChoices file: %s\n", id, pathname) - os.Remove(pathname) - return - } - - flag, err := fic.GetFlagKey(ask.FlagId) - if err != nil { - log.Printf("%s [ERR] %s\n", id, err) - return - } - - if !team.CanSeeFlag(flag) { - log.Printf("%s [!!!] The team asks to display choices whereas it doesn't have access to the flag\n", id) - return - } - - exercice, err := flag.GetExercice() - if err != nil { - log.Printf("%s [ERR] Unable to retrieve the flag's underlying exercice: %s\n", id, err) - return - } - - if !team.HasAccess(exercice) { - log.Printf("%s [!!!] The team asks to display choices whereas it doesn't have access to the exercice\n", id) - return - } - - if exercice.Disabled { - log.Println("[!!!] The team submits something for a disabled exercice") - return - } - - if exercice.IdTheme != nil { - theme, err := fic.GetTheme(*exercice.IdTheme) - if err != nil { - log.Printf("%s [ERR] Unable to retrieve theme for exercice %d: %s\n", id, exercice.Id, err) - return - } - - // Theme should not be locked - if theme.Locked { - log.Printf("%s [!!!] Want choice received for locked theme %d (fid=%d): %s\n", id, exercice.IdTheme, ask.FlagId, theme.Name) - return - } - } - - err = team.DisplayChoices(flag) - if err != nil { - log.Printf("%s [ERR] %s\n", id, err) - return - } - - appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeam, TeamId: team.Id}) - if err = os.Remove(pathname); err != nil { - log.Printf("%s [ERR] %s\n", id, err) - } -} diff --git a/checker/generation.go b/checker/generation.go deleted file mode 100644 index 7964e111..00000000 --- a/checker/generation.go +++ /dev/null @@ -1,52 +0,0 @@ -package main - -import ( - "bytes" - "encoding/json" - "fmt" - "log" - "net" - "net/http" - "strings" - "time" - - "srs.epita.fr/fic-server/libfic" -) - -var generatorSocket string - -func appendGenQueue(gs fic.GenStruct) error { - buf, err := json.Marshal(gs) - if err != nil { - return fmt.Errorf("Something is wrong with JSON encoder: %w", err) - } - - sockType := "unix" - if strings.Contains(generatorSocket, ":") { - sockType = "tcp" - } - - socket, err := net.Dial(sockType, generatorSocket) - if err != nil { - log.Printf("Unable to contact generator at: %s, retring in 1 second", generatorSocket) - time.Sleep(time.Second) - return appendGenQueue(gs) - } - defer socket.Close() - - httpClient := &http.Client{ - Transport: &http.Transport{ - Dial: func(network, addr string) (net.Conn, error) { - return socket, nil - }, - }, - } - - resp, err := httpClient.Post("http://localhost/enqueue", "application/json", bytes.NewReader(buf)) - if err != nil { - return fmt.Errorf("Unable to enqueue new generation event: %w", err) - } - resp.Body.Close() - - return nil -} diff --git a/checker/hint.go b/checker/hint.go deleted file mode 100644 index 07d651b4..00000000 --- a/checker/hint.go +++ /dev/null @@ -1,114 +0,0 @@ -package main - -import ( - "encoding/base64" - "encoding/binary" - "encoding/json" - "fmt" - "html" - "io/ioutil" - "log" - "math/rand" - "os" - - "srs.epita.fr/fic-server/libfic" -) - -type askOpenHint struct { - HintId int64 `json:"id"` -} - -func treatOpeningHint(pathname string, team *fic.Team) { - // Generate a unique identifier to follow the request in logs - bid := make([]byte, 5) - binary.LittleEndian.PutUint32(bid, rand.Uint32()) - id := "[" + base64.StdEncoding.EncodeToString(bid) + "]" - log.Println(id, "New openingHint receive", pathname) - - var ask askOpenHint - - cnt_raw, err := ioutil.ReadFile(pathname) - if err != nil { - log.Printf("%s [ERR] %s\n", id, err) - return - } - - err = json.Unmarshal(cnt_raw, &ask) - if err != nil { - log.Printf("%s [ERR] %s\n", id, err) - return - } - - if ask.HintId == 0 { - log.Printf("%s [WRN] Invalid content in hint file: %s\n", id, pathname) - os.Remove(pathname) - return - } - - hint, err := fic.GetHint(ask.HintId) - if err != nil { - log.Printf("%s [ERR] Unable to retrieve the given hint: %s\n", id, err) - return - } - - exercice, err := hint.GetExercice() - if err != nil { - log.Printf("%s [ERR] Unable to retrieve the hint's underlying exercice: %s\n", id, err) - return - } - - if exercice.Disabled { - log.Println("[!!!] The team submits something for a disabled exercice") - return - } - - if !team.HasAccess(exercice) { - log.Printf("%s [!!!] The team asks to open an hint whereas it doesn't have access to the exercice\n", id) - return - } - - if !team.CanSeeHint(hint) { - log.Printf("%s [!!!] The team asks to open an hint whereas it doesn't have access to it due to hint dependencies\n", id) - return - } - - // Find the corresponding theme - var theme *fic.Theme - if exercice.IdTheme != nil { - theme, err = fic.GetTheme(*exercice.IdTheme) - if err != nil { - log.Printf("%s [ERR] Unable to retrieve theme for exercice %d: %s\n", id, exercice.Id, err) - return - } - - // Theme should not be locked - if theme.Locked { - log.Printf("%s [!!!] Open hint received for locked theme %d (hid=%d): %s\n", id, exercice.IdTheme, ask.HintId, theme.Name) - return - } - } - - if err = team.OpenHint(hint); err != nil && !fic.DBIsDuplicateKeyError(err) { // Skip DUPLICATE KEY errors - log.Printf("%s [ERR] Unable to open hint: %s\n", id, err) - return - } - - if theme == nil { - if _, err = fic.NewEvent(fmt.Sprintf("L'équipe %s a dévoilé un indice pour le défi %s !", html.EscapeString(team.Name), exercice.Title), "info"); err != nil { - log.Printf("%s [WRN] Unable to create event: %s\n", id, err) - } - } else { - // Write event - if lvl, err := exercice.GetLevel(); err != nil { - log.Printf("%s [WRN] %s\n", id, err) - } else if _, err = fic.NewEvent(fmt.Sprintf("L'équipe %s a dévoilé un indice pour le %de défi %s !", html.EscapeString(team.Name), lvl, theme.Name), "info"); err != nil { - log.Printf("%s [WRN] Unable to create event: %s\n", id, err) - } - } - - appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeam, TeamId: team.Id}) - appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenEvents}) - if err = os.Remove(pathname); err != nil { - log.Printf("%s [ERR] %s\n", id, err) - } -} diff --git a/checker/issue.go b/checker/issue.go deleted file mode 100644 index 28684f19..00000000 --- a/checker/issue.go +++ /dev/null @@ -1,90 +0,0 @@ -package main - -import ( - "encoding/base64" - "encoding/binary" - "encoding/json" - "io/ioutil" - "log" - "math/rand" - "os" - - "srs.epita.fr/fic-server/libfic" -) - -type IssueUpload struct { - Id int64 `json:"id"` - IdExercice int64 `json:"id_exercice"` - Subject string `json:"subject"` - Description string `json:"description"` -} - -func treatIssue(pathname string, team *fic.Team) { - // Generate a unique identifier to follow the request in logs - bid := make([]byte, 5) - binary.LittleEndian.PutUint32(bid, rand.Uint32()) - id := "[" + base64.StdEncoding.EncodeToString(bid) + "]" - log.Println(id, "New issue receive", pathname) - - var issue IssueUpload - - if cnt_raw, err := ioutil.ReadFile(pathname); err != nil { - log.Printf("%s [ERR] %s\n", id, err) - } else if err := json.Unmarshal(cnt_raw, &issue); err != nil { - log.Printf("%s [ERR] %s\n", id, err) - } else if len(issue.Subject) == 0 && len(issue.Description) == 0 { - if err = os.Remove(pathname); err != nil { - log.Printf("%s [ERR] %s\n", id, err) - } - log.Printf("%s Empty issue: not treated.\n", id) - } else if len(issue.Subject) == 0 { - if issue.Id <= 0 { - if err = os.Remove(pathname); err != nil { - log.Printf("%s [ERR] %s\n", id, err) - } - log.Printf("%s Issue with no subject: not treated.\n", id) - } else if claim, err := team.GetClaim(issue.Id); err != nil { - log.Printf("%s [ERR] Team id=%d,name=%q tries to access issue id=%d, but not granted: %s.\n", id, team.Id, team.Name, issue.Id, err) - } else if len(issue.Description) == 0 { - if err = os.Remove(pathname); err != nil { - log.Printf("%s [ERR] %s\n", id, err) - } - log.Printf("%s Empty issue: not treated.\n", id) - } else if desc, err := claim.AddDescription(issue.Description, &fic.ClaimAssignee{Id: 0}, true); err != nil { - log.Printf("%s [WRN] Unable to add description to issue: %s\n", id, err) - } else { - claim.State = "new" - claim.Update() - - log.Printf("%s [OOK] New comment added to issue id=%d: id_description=%d\n", id, claim.Id, desc.Id) - if err = os.Remove(pathname); err != nil { - log.Printf("%s [ERR] %s\n", id, err) - } - appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeamIssues, TeamId: team.Id}) - } - } else { - var exercice *fic.Exercice = nil - if e, err := fic.GetExercice(issue.IdExercice); err == nil { - exercice = e - } - - if claim, err := fic.NewClaim(issue.Subject, team, exercice, nil, "medium"); err != nil { - log.Printf("%s [ERR] Unable to create new issue: %s\n", id, err) - } else if len(issue.Description) > 0 { - if _, err := claim.AddDescription(issue.Description, &fic.ClaimAssignee{Id: 0}, true); err != nil { - log.Printf("%s [WRN] Unable to add description to issue: %s\n", id, err) - } else { - log.Printf("%s [OOK] New issue created: id=%d\n", id, claim.Id) - if err = os.Remove(pathname); err != nil { - log.Printf("%s [ERR] %s\n", id, err) - } - } - } else { - log.Printf("%s [OOK] New issue created: id=%d\n", id, claim.Id) - if err = os.Remove(pathname); err != nil { - log.Printf("%s [ERR] %s\n", id, err) - } - } - appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeamIssues, TeamId: team.Id}) - } -} diff --git a/checker/locked.go b/checker/locked.go deleted file mode 100644 index 697754d8..00000000 --- a/checker/locked.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "encoding/json" - "log" - "os" - - "srs.epita.fr/fic-server/libfic" -) - -var TeamLockedExercices = map[int64]map[string]bool{} - -func treatLocked(pathname string, team *fic.Team) { - fd, err := os.Open(pathname) - if err != nil { - log.Printf("[ERR] Unable to open %q: %s", pathname, err) - return - } - - var locked map[string]bool - - jdec := json.NewDecoder(fd) - if err := jdec.Decode(&locked); err != nil { - log.Printf("[ERR] Unable to parse JSON %q: %s", pathname, err) - return - } - - TeamLockedExercices[team.Id] = locked - log.Printf("Team %q (tid=%d) has locked %d exercices", team.Name, team.Id, len(locked)) -} diff --git a/checker/main.go b/checker/main.go deleted file mode 100644 index 1e16b1a7..00000000 --- a/checker/main.go +++ /dev/null @@ -1,229 +0,0 @@ -package main - -import ( - "flag" - "io/ioutil" - "log" - "math/rand" - "os" - "os/signal" - "path" - "strconv" - "strings" - "syscall" - "time" - - "srs.epita.fr/fic-server/libfic" - "srs.epita.fr/fic-server/settings" - - "gopkg.in/fsnotify.v1" -) - -var TeamsDir string -var SubmissionDir string - -func watchsubdir(watcher *fsnotify.Watcher, pathname string) error { - log.Println("Watch new directory:", pathname) - if err := watcher.Add(pathname); err != nil { - return err - } - - if ds, err := ioutil.ReadDir(pathname); err != nil { - return err - } else { - for _, d := range ds { - p := path.Join(pathname, d.Name()) - if d.IsDir() && d.Name() != ".tmp" && d.Mode()&os.ModeSymlink == 0 { - if err := watchsubdir(watcher, p); err != nil { - return err - } - } else if d.Mode().IsRegular() { - go treat(p) - } - } - return nil - } -} - -func walkAndTreat(pathname string) error { - if ds, err := ioutil.ReadDir(pathname); err != nil { - return err - } else { - for _, d := range ds { - p := path.Join(pathname, d.Name()) - if d.IsDir() && d.Name() != ".tmp" && d.Mode()&os.ModeSymlink == 0 { - if err := walkAndTreat(p); err != nil { - return err - } - } else if d.Mode().IsRegular() { - treat(p) - } - } - return nil - } -} - -var ChStarted = false -var lastRegeneration time.Time -var skipInitialGeneration = false - -func reloadSettings(config *settings.Settings) { - allowRegistration = config.AllowRegistration - canJoinTeam = config.CanJoinTeam - denyTeamCreation = config.DenyTeamCreation - canResetProgression = config.WorkInProgress && config.CanResetProgression - fic.HintCoefficient = config.HintCurCoefficient - fic.WChoiceCoefficient = config.WChoiceCurCoefficient - fic.ExerciceCurrentCoefficient = config.ExerciceCurCoefficient - ChStarted = config.Start.Unix() > 0 && time.Since(config.Start) >= 0 - fic.PartialValidation = config.PartialValidation - fic.UnlockedChallengeDepth = config.UnlockedChallengeDepth - fic.UnlockedChallengeUpTo = config.UnlockedChallengeUpTo - fic.UnlockedStandaloneExercices = config.UnlockedStandaloneExercices - fic.UnlockedStandaloneExercicesByThemeStepValidation = config.UnlockedStandaloneExercicesByThemeStepValidation - fic.UnlockedStandaloneExercicesByStandaloneExerciceValidation = config.UnlockedStandaloneExercicesByStandaloneExerciceValidation - fic.DisplayAllFlags = config.DisplayAllFlags - - fic.FirstBlood = config.FirstBlood - fic.SubmissionCostBase = config.SubmissionCostBase - fic.SubmissionUniqueness = config.SubmissionUniqueness - fic.GlobalScoreCoefficient = config.GlobalScoreCoefficient - fic.CountOnlyNotGoodTries = config.CountOnlyNotGoodTries - fic.DiscountedFactor = config.DiscountedFactor - fic.QuestionGainRatio = config.QuestionGainRatio -} - -func main() { - var dsn = flag.String("dsn", fic.DSNGenerator(), "DSN to connect to the MySQL server") - flag.StringVar(&generatorSocket, "generator", "./GENERATOR/generator.socket", "Path to the generator socket") - flag.StringVar(&settings.SettingsDir, "settings", "./SETTINGSDIST", "Base directory where load and save settings") - flag.StringVar(&SubmissionDir, "submission", "./submissions", "Base directory where save submissions") - flag.StringVar(&TeamsDir, "teams", "./TEAMS", "Base directory where save teams JSON files") - var debugINotify = flag.Bool("debuginotify", false, "Show skipped inotofy events") - flag.Parse() - - log.SetPrefix("[checker] ") - - settings.SettingsDir = path.Clean(settings.SettingsDir) - SubmissionDir = path.Clean(SubmissionDir) - TeamsDir = path.Clean(TeamsDir) - - rand.Seed(time.Now().UnixNano()) - - log.Println("Creating submission directory...") - if _, err := os.Stat(path.Join(SubmissionDir, ".tmp")); os.IsNotExist(err) { - if err := os.MkdirAll(path.Join(SubmissionDir, ".tmp"), 0700); err != nil { - log.Fatal("Unable to create submission directory: ", err) - } - } - - log.Println("Opening DB...") - if err := fic.DBInit(*dsn); err != nil { - log.Fatal("Cannot open the database: ", err) - } - defer fic.DBClose() - - // Load configuration - settings.LoadAndWatchSettings(path.Join(settings.SettingsDir, settings.SettingsFile), reloadSettings) - - log.Println("Registering directory events...") - watcher, err := fsnotify.NewWatcher() - if err != nil { - log.Fatal(err) - } - defer watcher.Close() - - if err := watchsubdir(watcher, SubmissionDir); err != nil { - log.Fatal(err) - } - - // Register SIGUSR1 and SIGTERM - interrupt1 := make(chan os.Signal, 1) - signal.Notify(interrupt1, syscall.SIGUSR1) - interrupt := make(chan os.Signal, 1) - signal.Notify(interrupt, syscall.SIGTERM) - - watchedNotify := fsnotify.Create - -loop: - for { - select { - case <-interrupt: - break loop - case <-interrupt1: - log.Println("SIGUSR1 received, retreating all files in queue...") - walkAndTreat(SubmissionDir) - log.Println("SIGUSR1 treated.") - case ev := <-watcher.Events: - if d, err := os.Lstat(ev.Name); err == nil && ev.Op&fsnotify.Create == fsnotify.Create && d.Mode().IsDir() && d.Mode()&os.ModeSymlink == 0 && d.Name() != ".tmp" { - // Register new subdirectory - if err := watchsubdir(watcher, ev.Name); err != nil { - log.Println(err) - } - } else if err == nil && ev.Op&watchedNotify == watchedNotify && d.Mode().IsRegular() { - if *debugINotify { - log.Println("Treating event:", ev, "for", ev.Name) - } - go treat(ev.Name) - } else if err == nil && ev.Op&fsnotify.Write == fsnotify.Write { - log.Println("FSNOTIFY WRITE SEEN. Prefer looking at them, as it appears files are not atomically moved.") - watchedNotify = fsnotify.Write - go treat(ev.Name) - } else if err == nil && *debugINotify { - log.Println("Skipped event:", ev, "for", ev.Name) - } - case err := <-watcher.Errors: - log.Println("error:", err) - } - } -} - -func treat(raw_path string) { - // Extract - spath := strings.Split(strings.TrimPrefix(raw_path, SubmissionDir), "/") - - if len(spath) == 3 { - if spath[1] == "_registration" { - treatRegistration(raw_path, spath[2]) - return - } - - var teamid int64 - var err error - - if teamid, err = strconv.ParseInt(spath[1], 10, 64); err != nil { - if lnk, err := os.Readlink(path.Join(TeamsDir, spath[1])); err != nil { - log.Printf("[ERR] Unable to readlink %q: %s\n", path.Join(TeamsDir, spath[1]), err) - return - } else if teamid, err = strconv.ParseInt(lnk, 10, 64); err != nil { - log.Printf("[ERR] Error during ParseInt team %q: %s\n", lnk, err) - return - } - } - - var team *fic.Team - if team, err = fic.GetTeam(teamid); err != nil { - log.Printf("[ERR] Unable to retrieve team %d: %s\n", teamid, err) - return - } - - switch spath[2] { - case "name": - treatRename(raw_path, team) - case "issue": - treatIssue(raw_path, team) - case "hint": - treatOpeningHint(raw_path, team) - case "choices": - treatWantChoices(raw_path, team) - case "reset_progress": - treatResetProgress(raw_path, team) - case ".locked": - treatLocked(raw_path, team) - default: - treatSubmission(raw_path, team, spath[2]) - } - } else { - log.Println("Invalid new file:", raw_path) - } -} diff --git a/checker/registration.go b/checker/registration.go deleted file mode 100644 index 537eeea9..00000000 --- a/checker/registration.go +++ /dev/null @@ -1,100 +0,0 @@ -package main - -import ( - "encoding/base64" - "encoding/binary" - "encoding/json" - "fmt" - "html" - "io/ioutil" - "log" - "math/rand" - "os" - "path" - - "srs.epita.fr/fic-server/libfic" -) - -var ( - allowRegistration = false - canJoinTeam = false - denyTeamCreation = false -) - -type uTeamRegistration struct { - TeamName string - JTeam int64 - Members []fic.Member -} - -func registrationProcess(id string, team *fic.Team, members []fic.Member, team_id string) { - for i, m := range members { - // Force Id to 0, as it shouldn't have been defined yet - m.Id = 0 - if err := team.GainMember(&members[i]); err != nil { - log.Println("[WRN] Unable to add member (", m, ") to team (", team, "):", err) - } - } - - teamDirPath := fmt.Sprintf("%d", team.Id) - - // Create team directories into TEAMS - if err := os.MkdirAll(path.Join(TeamsDir, teamDirPath), 0751); err != nil { - log.Println(id, "[ERR]", err) - } - if err := os.Symlink(teamDirPath, path.Join(TeamsDir, team_id)); err != nil { - log.Println(id, "[ERR]", err) - } - - appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeam, TeamId: team.Id}) - appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeams}) -} - -func treatRegistration(pathname string, team_id string) { - // Generate a unique identifier to follow the request in logs - bid := make([]byte, 5) - binary.LittleEndian.PutUint32(bid, rand.Uint32()) - id := "[" + base64.StdEncoding.EncodeToString(bid) + "]" - log.Println(id, "New registration receive", pathname) - - var nTeam uTeamRegistration - - if !allowRegistration { - log.Printf("%s [ERR] Registration received, whereas disabled. Skipped.\n", id) - } else if cnt_raw, err := ioutil.ReadFile(pathname); err != nil { - log.Printf("%s [ERR] %s\n", id, err) - } else if err := json.Unmarshal(cnt_raw, &nTeam); err != nil { - log.Printf("%s [ERR] %s\n", id, err) - } else if nTeam.JTeam > 0 { - if !canJoinTeam { - log.Printf("%s [ERR] Join team received, whereas disabled. Skipped.\n", id) - } else if len(nTeam.Members) != 1 { - log.Printf("%s [ERR] Join team received, with incorrect member length: %d. Skipped.\n", id, len(nTeam.Members)) - } else if team, err := fic.GetTeam(nTeam.JTeam); err != nil { - log.Printf("%s [ERR] Unable to join registered team %d: %s\n", id, nTeam.JTeam, err) - } else { - registrationProcess(id, team, nTeam.Members, team_id) - - if err := os.Remove(pathname); err != nil { - log.Printf("%s [WRN] %s\n", id, err) - } - } - } else if denyTeamCreation { - log.Printf("%s [ERR] Registration received, whereas team creation denied. Skipped.\n", id) - } else if validTeamName(nTeam.TeamName) { - if team, err := fic.CreateTeam(nTeam.TeamName, fic.HSL{H: rand.Float64(), L: 1, S: 0.5}.ToRGB(), ""); err != nil { - log.Printf("%s [ERR] Unable to register new team %s: %s\n", id, nTeam.TeamName, err) - } else { - registrationProcess(id, team, nTeam.Members, team_id) - - if err := os.Remove(pathname); err != nil { - log.Printf("%s [WRN] %s\n", id, err) - } - if _, err := fic.NewEvent(fmt.Sprintf("Souhaitons bonne chance à l'équipe %s qui vient de nous rejoindre !", html.EscapeString(team.Name)), "info"); err != nil { - log.Printf("%s [WRN] Unable to create event: %s\n", id, err) - } - - appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenEvents}) - } - } -} diff --git a/checker/rename.go b/checker/rename.go deleted file mode 100644 index 81a7c936..00000000 --- a/checker/rename.go +++ /dev/null @@ -1,50 +0,0 @@ -package main - -import ( - "encoding/base64" - "encoding/binary" - "encoding/json" - "fmt" - "html" - "io/ioutil" - "log" - "math/rand" - "os" - "regexp" - - "srs.epita.fr/fic-server/libfic" -) - -func validTeamName(name string) bool { - match, err := regexp.MatchString("^[A-Za-z0-9 àéèêëîïôùûü_-]{1,32}$", name) - return err == nil && match -} - -func treatRename(pathname string, team *fic.Team) { - // Generate a unique identifier to follow the request in logs - bid := make([]byte, 5) - binary.LittleEndian.PutUint32(bid, rand.Uint32()) - id := "[" + base64.StdEncoding.EncodeToString(bid) + "]" - log.Println(id, "New renameTeam receive", pathname) - - var keys map[string]string - - if cnt_raw, err := ioutil.ReadFile(pathname); err != nil { - log.Printf("%s [ERR] %s\n", id, err) - } else if err := json.Unmarshal(cnt_raw, &keys); err != nil { - log.Printf("%s [ERR] %s\n", id, err) - } else if validTeamName(keys["newName"]) { - team.Name = keys["newName"] - if _, err := team.Update(); err != nil { - log.Printf("%s [WRN] Unable to change team name: %s\n", id, err) - } - appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeam, TeamId: team.Id}) - if _, err := fic.NewEvent(fmt.Sprintf("Souhaitons bonne chance à l'équipe %s qui vient de nous rejoindre !", html.EscapeString(team.Name)), "info"); err != nil { - log.Printf("%s [WRN] Unable to create event: %s\n", id, err) - } - appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenEvents}) - if err := os.Remove(pathname); err != nil { - log.Printf("%s [ERR] %s\n", id, err) - } - } -} diff --git a/checker/reset_progress.go b/checker/reset_progress.go deleted file mode 100644 index b16716b9..00000000 --- a/checker/reset_progress.go +++ /dev/null @@ -1,54 +0,0 @@ -package main - -import ( - "encoding/base64" - "encoding/binary" - "encoding/json" - "io/ioutil" - "log" - "math/rand" - "os" - - "srs.epita.fr/fic-server/libfic" -) - -var canResetProgression = false - -type askResetProgress struct { - ExerciceId int64 `json:"eid"` -} - -func treatResetProgress(pathname string, team *fic.Team) { - if !canResetProgression { - log.Printf("[!!!] Receive reset_progress, whereas desactivated in settings.\n") - return - } - - // Generate a unique identifier to follow the request in logs - bid := make([]byte, 5) - binary.LittleEndian.PutUint32(bid, rand.Uint32()) - id := "[" + base64.StdEncoding.EncodeToString(bid) + "]" - log.Println(id, "New ResetProgress receive", pathname) - - var ask askResetProgress - - if cnt_raw, err := ioutil.ReadFile(pathname); err != nil { - log.Printf("%s [ERR] %s\n", id, err) - } else if err = json.Unmarshal(cnt_raw, &ask); err != nil { - log.Printf("%s [ERR] %s\n", id, err) - } else if ask.ExerciceId == 0 { - log.Printf("%s [WRN] Invalid content in reset_progress file: %s\n", id, pathname) - os.Remove(pathname) - } else if exercice, err := fic.GetExercice(ask.ExerciceId); err != nil { - log.Printf("%s [ERR] Unable to retrieve the given exercice: %s\n", id, err) - } else if exercice.Disabled { - log.Println("[!!!] The team submits something for a disabled exercice") - } else if err := team.ResetProgressionOnExercice(exercice); err != nil { - log.Printf("%s [ERR] Unable to reset progression: %s\n", id, err) - } else { - appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeam, TeamId: team.Id}) - if err = os.Remove(pathname); err != nil { - log.Printf("%s [ERR] %s\n", id, err) - } - } -} diff --git a/checker/submission.go b/checker/submission.go deleted file mode 100644 index 7a92c702..00000000 --- a/checker/submission.go +++ /dev/null @@ -1,201 +0,0 @@ -package main - -import ( - "encoding/base64" - "encoding/binary" - "encoding/json" - "fmt" - "html" - "io/ioutil" - "log" - "math/rand" - "os" - "strconv" - - "golang.org/x/crypto/blake2b" - - "srs.epita.fr/fic-server/libfic" -) - -type ResponsesUpload struct { - Keys map[int]string `json:"flags"` - MCQs map[int]bool `json:"mcqs"` - MCQJ map[int]string `json:"justifications"` -} - -func treatSubmission(pathname string, team *fic.Team, exercice_id string) { - // Generate a unique identifier to follow the request in logs - bid := make([]byte, 5) - binary.LittleEndian.PutUint32(bid, rand.Uint32()) - id := "[" + base64.StdEncoding.EncodeToString(bid) + "]" - log.Println(id, "New submission receive", pathname) - - // Parse exercice_id argument - eid, err := strconv.ParseInt(exercice_id, 10, 64) - if err != nil { - log.Printf("%s [ERR] %s is not a valid number: %s\n", id, exercice_id, err) - return - } - - // Identifier should not be blacklisted for the team - if blacklistteam, ok := TeamLockedExercices[team.Id]; ok { - if locked, ok := blacklistteam[exercice_id]; ok && locked { - log.Printf("%s [!!!] Submission received for team's locked exercice %d\n", id, eid) - return - } - } - - // Find the given exercice - exercice, err := fic.GetExercice(eid) - if err != nil { - log.Printf("%s [ERR] Unable to find exercice %d: %s\n", id, eid, err) - return - } - - // Check the exercice is not disabled - if exercice.Disabled { - log.Println("[!!!] The team submits something for a disabled exercice") - return - } - - // Check the team can access this exercice - if !team.HasAccess(exercice) { - log.Println("[!!!] The team submits something for an exercice it doesn't have access yet") - return - } - - // Find the corresponding theme - var theme *fic.Theme - if exercice.IdTheme != nil { - theme, err = fic.GetTheme(*exercice.IdTheme) - if err != nil { - log.Printf("%s [ERR] Unable to retrieve theme for exercice %d: %s\n", id, eid, err) - return - } - - // Theme should not be locked - if theme.Locked { - log.Printf("%s [!!!] Submission received for locked theme %d (eid=%d): %s\n", id, exercice.IdTheme, eid, theme.Name) - return - } - } - - // Read received file - cnt_raw, err := ioutil.ReadFile(pathname) - if err != nil { - log.Println(id, "[ERR] Unable to read file;", err) - return - } - - // Save checksum to treat duplicates - cksum := blake2b.Sum512(cnt_raw) - if err != nil { - log.Println(id, "[ERR] JSON parsing error:", err) - return - } - - // Parse it - var responses ResponsesUpload - err = json.Unmarshal(cnt_raw, &responses) - if err != nil { - log.Println(id, "[ERR] JSON parsing error:", err) - return - } - - // Ensure the team didn't already solve this exercice - tm := team.HasSolved(exercice) - if tm != nil { - if theme == nil { - log.Printf("%s [WRN] Team %d ALREADY solved standalone exercice %d (%s), continuing for eventual bonus flags\n", id, team.Id, exercice.Id, exercice.Title) - } else { - log.Printf("%s [WRN] Team %d ALREADY solved exercice %d (%s : %s), continuing for eventual bonus flags\n", id, team.Id, exercice.Id, theme.Name, exercice.Title) - } - } - - // Handle MCQ justifications: convert to expected keyid - for cid, j := range responses.MCQJ { - if mcq, choice, err := fic.GetMCQbyChoice(cid); err != nil { - log.Println(id, "[ERR] Unable to retrieve mcq from justification:", err) - return - } else if mcq.IdExercice != exercice.Id { - log.Println(id, "[ERR] We retrieve an invalid MCQ: from exercice", mcq.IdExercice, "whereas expected from exercice", exercice.Id) - return - } else if key, err := choice.GetJustifiedFlag(exercice); err != nil { - // Most probably, we enter here because the selected choice has not to be justified - // So, just ignore this case as it will be invalidated by the mcq validation - continue - } else { - if responses.Keys == nil { - responses.Keys = map[int]string{} - } - responses.Keys[key.Id] = j - } - } - - // Check given answers - solved, err := exercice.CheckResponse(cksum[:], responses.Keys, responses.MCQs, team) - if err != nil { - log.Println(id, "[ERR] Unable to CheckResponse:", err) - appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeam, TeamId: team.Id}) - return - } - - // At this point, we have treated the file, so it can be safely deleted - if err := os.Remove(pathname); err != nil { - log.Println(id, "[ERR] Can't remove file:", err) - } - - if tm != nil { - appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeam, TeamId: team.Id}) - } else if solved { - if theme == nil { - log.Printf("%s Team %d SOLVED exercice %d (%s)\n", id, team.Id, exercice.Id, exercice.Title) - } else { - log.Printf("%s Team %d SOLVED exercice %d (%s : %s)\n", id, team.Id, exercice.Id, theme.Name, exercice.Title) - - } - if err := exercice.Solved(team); err != nil { - log.Println(id, "[ERR] Unable to mark the challenge as solved:", err) - } - - // Write event - if theme == nil { - if _, err := fic.NewEvent(fmt.Sprintf("L'équipe %s a résolu le défi %s !", html.EscapeString(team.Name), exercice.Title), "success"); err != nil { - log.Println(id, "[WRN] Unable to create event:", err) - } - } else { - if lvl, err := exercice.GetLevel(); err != nil { - log.Println(id, "[ERR] Unable to get exercice level:", err) - } else if _, err := fic.NewEvent(fmt.Sprintf("L'équipe %s a résolu le %de défi %s !", html.EscapeString(team.Name), lvl, theme.Name), "success"); err != nil { - log.Println(id, "[WRN] Unable to create event:", err) - } - } - appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeam, TeamId: team.Id}) - appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenThemes}) - appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeams}) - } else { - if theme == nil { - log.Printf("%s Team %d submit an invalid solution for exercice %d (%s)\n", id, team.Id, exercice.Id, exercice.Title) - } else { - log.Printf("%s Team %d submit an invalid solution for exercice %d (%s : %s)\n", id, team.Id, exercice.Id, theme.Name, exercice.Title) - } - - // Write event (only on first try) - if tm == nil { - if theme == nil { - if _, err := fic.NewEvent(fmt.Sprintf("L'équipe %s tente le défi %s !", html.EscapeString(team.Name), exercice.Title), "warning"); err != nil { - log.Println(id, "[WRN] Unable to create event:", err) - } - } else { - if lvl, err := exercice.GetLevel(); err != nil { - log.Println(id, "[ERR] Unable to get exercice level:", err) - } else if _, err := fic.NewEvent(fmt.Sprintf("L'équipe %s tente le %de défi %s !", html.EscapeString(team.Name), lvl, theme.Name), "warning"); err != nil { - log.Println(id, "[WRN] Unable to create event:", err) - } - } - } - appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeam, TeamId: team.Id}) - } - - appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenEvents}) -} diff --git a/clear_cache.sh b/clear_cache.sh new file mode 100755 index 00000000..cb528dd4 --- /dev/null +++ b/clear_cache.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +cd `dirname "$0"` + +for n in "$@" +do + MD5=`echo -n $n | md5sum | cut -d " " -f 1` + if [ -f "onyx/cache/$MD5.cache.php" ] + then + rm -f "onyx/cache/$MD5.cache.php" + echo "--- $n deleted" + else + echo "/!\\ $n not found ($MD5)" + fi +done diff --git a/comm-socket.pl b/comm-socket.pl new file mode 100644 index 00000000..3748db21 --- /dev/null +++ b/comm-socket.pl @@ -0,0 +1,46 @@ +#!/usr/bin/env perl + +use v5.10.1; +use IO::Select; +use IO::Socket::UNIX; +use threads; + +die("Give at least the socket file as argument") if (! @ARGV); + +my $sock_path = shift @ARGV; + +my $socket = IO::Socket::UNIX->new( + Type => SOCK_STREAM, + Peer => $sock_path, + ); + +die "Can't read socket ($sock_path): $!" unless $socket; + +if (@ARGV) +{ + while (my $arg = shift @ARGV) + { + say $socket $arg; + } +} + +my $s = IO::Select->new(); + +$s->add(\*STDIN); +$s->add($socket); + +while ($s->count()) +{ + for my $rd ($s->can_read(0.25)) + { + my $line = <$rd>; + chomp($line); + + if ($rd == \*STDIN) { + say $socket $line; + } + elsif ($rd == $socket) { + say $line; + } + } +} diff --git a/config.sh b/config.sh new file mode 100644 index 00000000..425da818 --- /dev/null +++ b/config.sh @@ -0,0 +1,16 @@ +# Username of the unpriviledge user that runs scripts +SYNCHRO_USER="synchro" + +BASEDIR="/var/www/fic-server" + +# Directory where backup should be made +if [ -z "$TO_BCKP" ] +then + TO_BCKP="/mnt/backup" +fi + +# Password of the MySQL user backup (with RO rights) +if [ -z "$BCKP_PASS" ] +then + BCKP_PASS="Riuy6of sae^W0Sh" +fi diff --git a/configs/.gitignore b/configs/.gitignore deleted file mode 100644 index 4bd07476..00000000 --- a/configs/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -id_ed25519 -id_ed25519.pub -dm-crypt.key -fic.srs.epita.fr -dhparams-4096.pem \ No newline at end of file diff --git a/configs/anim-start-challenge.sh b/configs/anim-start-challenge.sh deleted file mode 100755 index 8fccbda1..00000000 --- a/configs/anim-start-challenge.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh - -echo "Launching challenge..." -echo - -curl -s admin-challenge:8081/admin/api/settings.json | jq "{start:.start, end:.end}" - -echo - -for i in `seq 30 -1 1` -do - echo -n "." - sleep 0.036 -done -echo - -curl -s admin-challenge:8081/admin/api/settings.json | jq . - -for i in `seq 40 -1 1` -do - echo -n "." - sleep 0.02 -done -echo - -echo "Challenge started!" | figlet -echo -echo -echo diff --git a/configs/authorized_keys b/configs/authorized_keys deleted file mode 100644 index eb537ee9..00000000 --- a/configs/authorized_keys +++ /dev/null @@ -1,43 +0,0 @@ -ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO/3qKhSUbGYZBVraFo68oScJahRDNQfG+uwDQlLv7g nemunaire@khonsou -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDDCanbPYZGJPlXAbaUkVZPZbGMngjKctmm4dmBOv+eqAkNioeAtUKfmsNWJRZRqB+N2CcIn+0XtgIILNRS1/YC94oAkrSq7lXQ/Pb1vx/N8xQ+c8HCI+YVilKP3digpuVRa8eeCJLnOoBw0MKaXIvul6bG4rUSV+xxXMfeDb0BN9h1CLAnsdK3Ku0DIX57GqtPYbeqTN3tYZ3l+9uewAqUKJ28jEw+DTLUDLytyAw2XDalWMSlnC+WyoXMmwuYz74yKwiRzDPvOloDQf4cEZcdlywcz6HePTcH0d1oPaiTdDtIsI0eSSvZWSLBKA3hc+J3flH2Xyoz4Y7O5pdjoKK7dLoITHGK1Ue9MMCU8n9cqBFozDY7QtJuiY4bx9bdnhUc5PAXGpPR7vWM4tckEcaP8pEhED+h/u7TD4SyZlmUUZVyv7icrcCSQEbIKVLYi8bfMgXxDwkEiAOnTLCEDXT27VauSfQab68RmiMJvylW8ynl7rrM/3T0pbuzRXKJjGnuvNTNMQZqPwt+yp2BtXB/XdLEkh18PI35j/As+hhubxhtYW+esvuDw4a/JBWUtwO7MacM9VlKOtyV+LWKUljVwpgR5mCljauLnL5LulxmjRrcnm2nFCW7WzeSrilEV4AZ2lA0JJLMVI5VAWc6sP+LyM9TA3rmoaMxTxlJkni6Aw== cardno:18 059 785 - -# adrien.langou -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDDCanbPYZGJPlXAbaUkVZPZbGMngjKctmm4dmBOv+eqAkNioeAtUKfmsNWJRZRqB+N2CcIn+0XtgIILNRS1/YC94oAkrSq7lXQ/Pb1vx/N8xQ+c8HCI+YVilKP3digpuVRa8eeCJLnOoBw0MKaXIvul6bG4rUSV+xxXMfeDb0BN9h1CLAnsdK3Ku0DIX57GqtPYbeqTN3tYZ3l+9uewAqUKJ28jEw+DTLUDLytyAw2XDalWMSlnC+WyoXMmwuYz74yKwiRzDPvOloDQf4cEZcdlywcz6HePTcH0d1oPaiTdDtIsI0eSSvZWSLBKA3hc+J3flH2Xyoz4Y7O5pdjoKK7dLoITHGK1Ue9MMCU8n9cqBFozDY7QtJuiY4bx9bdnhUc5PAXGpPR7vWM4tckEcaP8pEhED+h/u7TD4SyZlmUUZVyv7icrcCSQEbIKVLYi8bfMgXxDwkEiAOnTLCEDXT27VauSfQab68RmiMJvylW8ynl7rrM/3T0pbuzRXKJjGnuvNTNMQZqPwt+yp2BtXB/XdLEkh18PI35j/As+hhubxhtYW+esvuDw4a/JBWUtwO7MacM9VlKOtyV+LWKUljVwpgR5mCljauLnL5LulxmjRrcnm2nFCW7WzeSrilEV4AZ2lA0JJLMVI5VAWc6sP+LyM9TA3rmoaMxTxlJkni6Aw== adrien - -# erwan.poles -ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK89oym8s0B5LyQbLqTvVKS6U+yPGkHsYATDOfpCCBB8 erwan.poles@epita.fr - -# jules.lefebvre -ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJmOU1KFU3mS8Rni/4TOlNp98dTuf9DxYA6mn2Ns+gbH jules@julesdecube.com -ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKrWWr/v6pSF5h0JGox7ncvmgPWfAVX/A8xSXFVqgdY4 jules@cri.epita.fr - -# tanguy.maraux -ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGpS5mMfl832EKi03M0awrTbiuGOh+OII5ojM3V9Onl9 tanguy.maraux@gmail.com - -# paul.leroux -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCUKevt/f1n2byv5oH43iQsZ7b4kAATHlHNUF6WMQjk6BSEpdx3Ux1ly0jKy6TPq+04/H0qSC7xF9vGsv3stcbXNST47aIKRnp+27J0SYsj4oaqIGJ/gtgl1QJfJVB+Sld7Sh65zT30kqm0Il3y5PHfpTk/9Y6HOEpnOO8IvmnL4jwiwKMXM+0EyaTaBvsAFo6AYz3/dvkNKSf3SMQWZJNqGtufZcXqg16gWy2+tqALhtCDdmIV1qtK81MxWgol19ZBukk8+qfnX3SgCY1VaJIbBs0vmkixE0e89Pr+mRIPjDL/3U5yH5StUrVOtJqo2pX9mN+r0G8A+lZouprjhu8rGsIyF4zXdcELgh5/ZVR11/OLnZaThsrh1NjZvFdeEgXNZ5IBEZ9KbLUSj7c6lb1j4PtST+BNc8iQj6DdktYluj7LiEgbfpss7rXmoAUC3y01/z1L1GiKJ+FQkN9ri/eQoT3gwyAApS/80MhP4SfgNbdslN49369hY3z04DC7aDc= paul@paul-82wr - -# ? -ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICo5yumHfQbMwhZAtEZByQR0xIVcoealS7g4MNTMEVaX roote@roote-VirtualBox - -# cyril.blin -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCit9xqC+/EcC0D+x4Irg/AgnTx9rJDbE4FdKK2Z2vE0nSPzp1MbAtijVi5ndvr/JPlY3jUHeGEZBJHmADXeOvdJl1nvkqry/69phfr4nDYacvH0v66nDTRipqmCmebaYOkfXYG55oy40+6C6DwAGETTYq+PIaRcA/mSf6V9UxKBfnLVqdml7LFYEo1SbihAIFd0EZkwq1wevXdVmrDwF7VLiCin/5Axa6LUOe4l1SAYBpsV8pCY3PQ/KxpgCyJuYj2szhOl0shTPiV48f194xGtYrpx1uGhOHRDx6Rm/5LKY/5DUvKbHCa/ZAdUSoMTnd1TshAPJe1sYKSAAI1xPVmffOgF/Jh98QEuAuFmHfZXVgPdvApJ9r9Ea7gEyN6Xe37emkW1Dond4ARdNdaslVu0iwV6bQnDOGcEdAl3x3seRVRAiPAKp2tAEtVEqu7uFwX6v2mmpE5/uw8rfsl/wNgttjuYa/kJURNkto3bN02XNfzOnXXZ3bRtbNjHEyJQuM= cyril@nixos - -# victor.chartraire -ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJgDuQ4FrDuDjoo1Xv7pA0WEev1hhgJ8lXpT/17QXsds celian@DESKTOP-BQA641E -ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPfUVWWSUrfuQYofWwvotQip4uqG34dF1Ybn4tTU1l0n celian@DESKTOP-BQA641E - -# hugo.rubio -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC68cnL/66U9dKfGZe3Bfr+ggosVXZh5OymRuHEdl26lYr3uJAQ7tKtfgXXyfuIW87vxGuzZUOEAnDafJx0J24BVyTwNjJVRarB8efHVIvlL/S9hHPVKWaQyo1ZIgizCiRdljJLWweZKjRE6Yr8xPc6zlC7grQ0wG/WGtAKaedhxYUZcCkdxPMz9Jf2ufx23DB/ALUZ63KVwbWIof8LPtlBP3G74ooeuykR+BTH+6NinoOWlL94JsSTAhuVnKsSnG1eQ742Cv4XhhGleAStP3wFhyakdsvCOfw9bKN/z3dXA0nZGzwOqvHyI9OJx01GLHhyykfRL7PtpD8MMwzyq3r5 hugo0@LAPTOP-C4LN612T -ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA1BINotXNgrTmHrep0BbxtUIAvCXOjSN8GrfTq03Sfd ryugo@LAPTOP-C4LN612T - -# alexandra.delin -ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOJGg902VI7w6nOSiGm6qyUnPn9w3sD3VKlMin3fbYK6 alex@AlexandraSRS -ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIArw17NpbJRNf8m9eji5K+GcD8oiyJi/3ygceojEVtBL alexa@Alexandra_SRS - -# maxence.michot -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCqiBbRBWhnG5+LU/pKevR5oJV3+2ZQRQ6Fu87F8873DkKP0okvAYqVXYEDIm/bq9+VocZHI8y9O+zbCXwIqdWRGESJFiM7r1L3HtimFlmWhzM+nzduS8dWGcGGa9XCw+OBHFMGUFEMyzNbi/YcF7QGybOGETdcyzXQng1JtW4IURdrFJm/0/EavRwO2O2xTexmHv8l7JAe0ChteeuDatADrWgNnoUXIqeQQIsPxKQJ9D4QZ7+QrD5K2iLCx8sqC8Eu10p7cRqB3pCcBzKc6MEOgA1FXJzYgNtWkkLR9s7cOVjKL6mL3DuYQla3VaSvkTh8SAKEkadgYXpYifF6ygL298EmhQIrqRdjDYAm+VDGfCvmtcEW4NM68tt74xLuhdDutLq9Riqx4n2pGPo8ws8ecCokcu5/ESGxpZwYmY+nMj8392yH4sgmW/nrs0WmAqawdwlDj09LIqbYXiGMd5zYYOu8V2nuMKG0zRtRESKbs04Uw2DUd/gTwfP2l5YqTn3K93xDNU2iZY+yLJai3vYaBLHzt6PEvESdhAceYBMTIN2iA6hGZfaMWJ4JHImoqCgwbnJw6qrIPHaj1SCWKWLPMjjYZbwndkH3Iy4oJ6gA6I/vT9YrzNvGYY7rVspa+O0RM+huVH1a0YoXSSzx6dop1k1ZWm7VwRCUJQ2ri+L2jw== thecr@maxence-portable-desktop -ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMwyRf67AH/rUzSFsKpFfGDD/La4JqpYk9xCM04kJ6+P maxence@vokunaav - -# leo.blanc-di-pasquale -ssh-rsa AAAAB4NzaC1yc2EAAAADAQABAAABgQDBovLxpJA0+gJiAVWrpWmjy9fyEQwOXv3Ytou7C4Y1O3lAyvmmHfBWuW1ZqIY/8yKIpJeSLCoGeN5kyMD9ITvMVcfSAxnDHXj04Q4rJzBGjQ/LF/CInx+HHjJWzGVgJC76m0BS7J0J7g5gAxfprGKEb3Z1kJmCTRQ470Azv6WIPDCw7aLNd1o5JqAsrmWSh/LwYBSapyp3Tk0KjcbxRifvnBJfYLHwNm1ZoaLCGAT3u3TKIifawEomx5QEel/211FaEdbglkeDtRL0YuvKQSuZCf4KSg7xSRQoWHmSuPothE9eQGzEKpJONKViqkxBo12O04cBEhYzQBh7GHmH3U3/9weNUX4EaKJWkfqh43eAnWohN07IXDnANYPRxWL4ITv+MNBWxYL5Zp3Zr85rhJZYhkyKEfcGjvolHaESegnsndhC74QW3eOXazuscAeROQbiMShk5iBIceTsEFTqI02/+XqTs9gXkuq3H3StJ6+9cavDwRyObGx5nVHmhQYnMEE= leo21@Aled diff --git a/configs/deploy-initial-config.sh b/configs/deploy-initial-config.sh deleted file mode 100755 index fe3845e8..00000000 --- a/configs/deploy-initial-config.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh - -set -e - -echo "Perform initial config..." - -[ -z "${PXE_IFACE}" ] && PXE_IFACE=$(ip r | grep default | head -1 | cut -d " " -f 5) -[ -z "${DEPLOY_NETWORK}" ] && DEPLOY_NETWORK=192.168.255 - -sed -i "s/eth0/${PXE_IFACE}/;s/192.168.255/${DEPLOY_NETWORK}/" /etc/udhcpd.conf - -ip a show dev "${PXE_IFACE}" | grep "inet " | grep "${DEPLOY_NETWORK}" || - ip a add "${DEPLOY_NETWORK}.2/24" dev "${PXE_IFACE}" - -echo "Initial config done." - -[ -f fickit-boot-kernel ] || { echo "Missing fickit-boot-kernel file. Download the latest fickit artifact."; exit 1; } -[ -f fickit-metadata.iso ] || { echo "Missing fickit-metadata.iso file. Use configs/gen_metadata.sh to generate it."; exit 1; } - -exec $@ diff --git a/configs/deploy-supervisord.conf b/configs/deploy-supervisord.conf deleted file mode 100644 index 956abe41..00000000 --- a/configs/deploy-supervisord.conf +++ /dev/null @@ -1,24 +0,0 @@ -[supervisord] -nodaemon = true -silent = true - -[program:httpd] -command = /usr/sbin/httpd -f -vv -h /srv/s -stdout_logfile = /dev/stdout -stdout_logfile_maxbytes = 0 -stderr_logfile = /dev/stderr -stderr_logfile_maxbytes = 0 - -[program:tftpd] -command = /usr/sbin/in.tftpd -L -a 0.0.0.0:69 -v -s /srv -stdout_logfile = /dev/stdout -stdout_logfile_maxbytes = 0 -stderr_logfile = /dev/stderr -stderr_logfile_maxbytes = 0 - -[program:udhcpd] -command = /usr/sbin/udhcpd -f -stdout_logfile = /dev/stdout -stdout_logfile_maxbytes = 0 -stderr_logfile = /dev/stderr -stderr_logfile_maxbytes = 0 \ No newline at end of file diff --git a/configs/dex-templates/templates/header.html b/configs/dex-templates/templates/header.html deleted file mode 100644 index 13ae0d1d..00000000 --- a/configs/dex-templates/templates/header.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - {{ issuer }} - - - - - - - -
    -
    - -
    -
    - -
    -
    - -
    diff --git a/configs/dex-templates/templates/login.html b/configs/dex-templates/templates/login.html deleted file mode 100644 index 5c64ccd4..00000000 --- a/configs/dex-templates/templates/login.html +++ /dev/null @@ -1,21 +0,0 @@ -{{ template "header.html" . }} - -
    -

    - Bienvenue au challenge Forensic ! -

    -
    - {{ range $c := .Connectors }} - - {{ end }} -
    -
    - -{{ template "footer.html" . }} diff --git a/configs/dex-templates/templates/password.html b/configs/dex-templates/templates/password.html deleted file mode 100644 index 46e8d587..00000000 --- a/configs/dex-templates/templates/password.html +++ /dev/null @@ -1,37 +0,0 @@ -{{ template "header.html" . }} - -
    -

    - Bienvenue au challenge Forensic ! -

    -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    - - {{ if .Invalid }} -
    - Identifiants incorrects. -
    - {{ end }} - - - -
    - {{ if .BackLink }} - - {{ end }} -
    - -{{ template "footer.html" . }} diff --git a/configs/dex-templates/theme/styles.css b/configs/dex-templates/theme/styles.css deleted file mode 100644 index 72430367..00000000 --- a/configs/dex-templates/theme/styles.css +++ /dev/null @@ -1,114 +0,0 @@ -.theme-body { - background-color: white; - color: #272b30; - font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; -} - -.theme-navbar { - background-color: #272b30; - border-bottom: 5px solid #4eaee6; - color: #333; - font-size: 13px; - font-weight: 100; - overflow: hidden; - padding: 0 10px; - display: flex; - align-items: center; - justify-content: space-around; -} - -.theme-navbar__logo-wrap { - display: inline-block; - overflow: hidden; - padding: 10px 15px; - width: 300px; -} - -.theme-navbar__logo { - height: 90px; - max-height: 12vh; -} - -.theme-heading { - font-size: 20px; - font-weight: 500; - margin-bottom: 10px; - margin-top: 0; -} - -.theme-panel { - background-color: #fff; - box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); - padding: 30px; -} - -.theme-btn-provider { - background-color: #fff; - color: #333; - min-width: 250px; -} - -.theme-btn-provider:hover { - color: #999; -} - -.theme-btn--primary { - background-color: #333; - border: none; - color: #fff; - min-width: 200px; - padding: 6px 12px; -} - -.theme-btn--primary:hover { - background-color: #666; - color: #fff; -} - -.theme-btn--success { - background-color: #2FC98E; - color: #fff; - width: 250px; -} - -.theme-btn--success:hover { - background-color: #49E3A8; -} - -.theme-form-row { - display: block; - margin: 20px auto; -} - -.theme-form-input { - border-radius: 4px; - border: 1px solid #CCC; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - color: #666; - display: block; - font-size: 14px; - height: 36px; - line-height: 1.42857143; - margin: auto; - padding: 6px 12px; - width: 250px; -} - -.theme-form-input:focus, -.theme-form-input:active { - border-color: #66AFE9; - outline: none; -} - -.theme-form-label { - font-size: 13px; - font-weight: 600; - margin: 4px auto; - position: relative; - text-align: left; - width: 250px; -} - -.theme-link-back { - margin-top: 4px; -} diff --git a/configs/dex.yaml b/configs/dex.yaml deleted file mode 100644 index 41ed921e..00000000 --- a/configs/dex.yaml +++ /dev/null @@ -1,84 +0,0 @@ -# The base path of Dex and the external name of the OpenID Connect service. -# This is the canonical URL that all clients MUST use to refer to Dex. If a -# path is provided, Dex's HTTP service will listen at a non-root URL. -issuer: https://live.fic.srs.epita.fr - -# The storage configuration determines where dex stores its state. Supported -# options include SQL flavors and Kubernetes third party resources. -storage: - type: sqlite3 - config: - file: /var/dex/dex.db - -# Configuration for the HTTP endpoints. -web: - http: 0.0.0.0:5556 - #allowedOrigins: ['*'] - -# Configuration for dex appearance -frontend: - issuer: Challenge forensic - logoURL: img/fic.png - dir: /srv/dex/web/ -# theme: light - -# Configuration for telemetry -#telemetry: -# http: 0.0.0.0:5558 - - - -oauth2: - #responseTypes: ["code", "token", "id_token"] - skipApprovalScreen: true - -staticClients: -- id: epita-challenge - name: Challenge Forensic - redirectURIs: ['https://live.fic.srs.epita.fr/challenge_access/auth'] - secret: N4n7AXzK9kpXt3TmSn8wAgtxqxhGORgcubLaE2g - - -enablePasswordDB: true - -staticPasswords: - - email: "team01" - hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" - - email: "team02" - hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" - - email: "team03" - hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" - - email: "team04" - hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" - - email: "team05" - hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" - - email: "team06" - hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" - - email: "team07" - hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" - - email: "team08" - hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" - - email: "team09" - hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" - - email: "team10" - hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" - - email: "team11" - hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" - - email: "team12" - hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" - - email: "team13" - hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" - - email: "team14" - hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" - - email: "team15" - hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" - - email: "team16" - hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" - - email: "team17" - hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" - - email: "team18" - hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" - - email: "team19" - hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" - - email: "team20" - hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" diff --git a/configs/dhcpd.conf b/configs/dhcpd.conf deleted file mode 100644 index 5d787e0a..00000000 --- a/configs/dhcpd.conf +++ /dev/null @@ -1,18 +0,0 @@ -default-lease-time 600; -max-lease-time 7200; -authoritative; -option subnet-mask 255.255.255.0; -option broadcast-address 172.23.42.255; -option routers 172.23.42.254; -option domain-name-servers 172.23.42.254; -option rfc3442-classless-static-routes code 121 = array of integer 8; -option ms-classless-static-routes code 249 = array of integer 8; -# Provide Internet -option rfc3442-classless-static-routes 32, 91, 243, 117, 241, 172, 23, 42, 1, 0, 172, 23, 42, 254; -option ms-classless-static-routes 32, 91, 243, 117, 241, 172, 23, 42, 1, 0, 172, 23, 42, 254; -# Don't provide Internet -#option rfc3442-classless-static-routes 32, 91, 243, 117, 241, 172, 23, 42, 1; -#option ms-classless-static-routes 32, 91, 243, 117, 241, 172, 23, 42, 1; -subnet 172.23.42.0 netmask 255.255.255.0 { - range 172.23.42.10 172.23.42.253; -} diff --git a/configs/gen_metadata.sh b/configs/gen_metadata.sh deleted file mode 100755 index f51984eb..00000000 --- a/configs/gen_metadata.sh +++ /dev/null @@ -1,195 +0,0 @@ -#!/bin/bash - -set -e - -export DOMAIN_NAME="live.fic.srs.epita.fr" -export IP_FRONTEND="10.42.192.3/24" -export IP_FRONTEND_ROUTER="10.42.192.1" -export IP_FIC_SRS_FR=$(host ${DOMAIN_NAME} | grep -o '\([0-9]\{1,3\}.\)\+') -export IPS_BACKEND="192.168.3.92/24\\n192.168.4.92/24\\n" -export IP_BACKEND_ROUTER="192.168.3.1" - -export AIRBUS_DESTINATION="gaming.cyberrange.lan" -export AIRBUS_BASEURL="https://${AIRBUS_DESTINATION}/api" -export AIRBUS_TOKEN="abcdef0123456789abcdef0123456789" -export AIRBUS_SESSION_NAME="Forensique" - -export IPS_FRONTEND="${IP_FRONTEND}\\n${IP_FIC_SRS_FR}\\n" - -escape_newline () { - sed 's/$/\\n/g' | tr -d '\n' -} - -which mkisofs > /dev/null 2> /dev/null || { echo "Please install genisoimage (Debian/Ubuntu) or cdrkit (Alpine)" >&2; exit 1; } - -if [ $# -gt 0 ] -then - which jq > /dev/null 2> /dev/null || { echo "Please install jq" >&2; exit 1; } - - # Expect a previous ISO to update: - # Keep: DM_CRYPT, DHPARAMs and SYNCHRO_SSH_KEY - - P=$(pwd) - D=$(mktemp -d) - pushd "${D}" > /dev/null - - isoinfo -i "${P}/$1" -X -find -iname "USER_DAT*" > /dev/null || 7z x "$1" > /dev/null - - FNAME="USER_DAT.;1" - if ! [ -f "$FNAME" ] && [ -f user-data ] - then - FNAME="user-data" - fi - - export DM_CRYPT=$(jq -r '."dm-crypt".entries.key.content' "${FNAME}" | tr -d '\n') - export DHPARAM=$(jq -r '."tls_config".entries."dhparams-4096.pem".content' "${FNAME}" | escape_newline) - export SYNCRO_PRIVATE_KEY=$(jq -r '.synchro.entries.id_ed25519.content' "${FNAME}" | escape_newline) - export SYNCRO_PUBLIC_KEY=$(jq -r '.synchro.entries."id_ed25519.pub".content' "${FNAME}" | escape_newline) - - popd > /dev/null - rm -rf "${D}" -fi - -which vault > /dev/null 2> /dev/null || { echo "Please install vault" >&2; exit 1; } - -export VAULT_ADDR="${VAULT_ADDR:-"https://vault.srs.epita.fr:443"}" -SSH_PATH="${SSH_PATH:-/tmp/fic_ssh}" -DHPARAM_PATH="${DHPARAM_PATH:-/tmp/dhparam.pem}" -OUTPUT_PATH="${OUTPUT_PATH:-"$(mktemp -d)"}" - -command -v vault &> /dev/null || (echo "vault could not be found" && exit) -vault login -method=oidc -no-print 2> /dev/null - -[ -z "${DM_CRYPT}" ] && echo "/!\\ GENERATE NEW DM_CRYPT SECRETS" && export DM_CRYPT="$(tr -d -c "a-zA-Z0-9" < /dev/urandom | fold -w512 | head -n 1)" -export CERT_PEM="$(vault kv get --field=cert.pem fic/cert/${DOMAIN_NAME} | escape_newline)" -export CHAIN_PEM="$(vault kv get --field=chain.pem fic/cert/${DOMAIN_NAME} | escape_newline)" -export FULLCHAIN_PEM="$(vault kv get --field=fullchain.pem fic/cert/${DOMAIN_NAME} | escape_newline)" -export PRIVKEY_PEM="$(vault kv get --field=privkey.pem fic/cert/${DOMAIN_NAME} | escape_newline)" - -if [ -z "${SYNCRO_PUBLIC_KEY}" ] || [ -z "${SYNCRO_PRIVATE_KEY}" ] -then - ssh-keygen -a 100 -t ed25519 -q -f "$SSH_PATH" -N "" <<< 'y' - - export SYNCRO_PUBLIC_KEY="$(cat "$SSH_PATH".pub | escape_newline)" - export SYNCRO_PRIVATE_KEY="$(cat "$SSH_PATH" | escape_newline)" -fi - -if [ -z "${DHPARAM}" ] && ! [ -f "$DHPARAM_PATH" ] -then - command -v openssl &> /dev/null || (echo "openssl could not be found" && exit) - - echo -e "\n\nGenerating DH params please wait" - openssl dhparam -out "$DHPARAM_PATH" 4096 &>/dev/null -elif ! [ -f "$DHPARAM_PATH" ] -then - echo "${DHPARAM}" > "${DHPARAM_PATH}" -fi -export DHPARAM="$(cat "$DHPARAM_PATH" | escape_newline)" - -export AUTHORIZED_KEYS="$(cat "$(dirname $0)/authorized_keys" | escape_newline)" - -TEMPLATE=' -{ - "dm-crypt": { - "entries": { - "key": { - "perm": "0440", - "content": "${DM_CRYPT}" - } - } - }, - "ssh": { - "entries": { - "authorized_keys": { - "perm": "0444", - "content": "${AUTHORIZED_KEYS}" - } - } - }, - "synchro": { - "entries": { - "id_ed25519": { - "perm": "0400", - "content": "${SYNCRO_PRIVATE_KEY}" - }, - "id_ed25519.pub": { - "perm": "0444", - "content": "${SYNCRO_PUBLIC_KEY}" - } - } - }, - "ip_config": { - "entries": { - "frontend-players": { - "perm": "0444", - "content": "${IPS_FRONTEND}" - }, - "frontend-router": { - "perm": "0444", - "content": "${IP_FRONTEND_ROUTER}" - }, - "backend-admin": { - "perm": "0444", - "content": "${IPS_BACKEND}" - }, - "backend-router": { - "perm": "0444", - "content": "${IP_BACKEND_ROUTER}" - }, - "domain": { - "perm": "0444", - "content": "${DOMAIN_NAME}" - } - } - }, - "remote_sync": { - "entries": { - "baseurl": { - "perm": "0444", - "content": "${AIRBUS_BASEURL}" - }, - "destination": { - "perm": "0444", - "content": "${AIRBUS_DESTINATION}" - }, - "token": { - "perm": "0444", - "content": "${AIRBUS_TOKEN}" - }, - "session_name": { - "perm": "0444", - "content": "${AIRBUS_SESSION_NAME}" - } - } - }, - "tls_config": { - "entries": { - "dhparams-4096.pem": { - "perm": "0400", - "content": "${DHPARAM}" - }, - "cert.pem": { - "perm": "0400", - "content": "${CERT_PEM}" - }, - "chain.pem": { - "perm": "0400", - "content": "${CHAIN_PEM}" - }, - "fullchain.pem": { - "perm": "0400", - "content": "${FULLCHAIN_PEM}" - }, - "privkey.pem": { - "perm": "0444", - "content": "${PRIVKEY_PEM}" - } - } - } -}' - -echo "$TEMPLATE" | envsubst > "$OUTPUT_PATH"/user-data - -echo -e "Result in $OUTPUT_PATH\nGenerating iso" - -mkisofs -joliet-long -V CIDATA -o fickit-metadata.iso "${OUTPUT_PATH}" diff --git a/configs/hosts b/configs/hosts deleted file mode 100644 index 9d85f80a..00000000 --- a/configs/hosts +++ /dev/null @@ -1,20 +0,0 @@ -10.10.10.1 deimos - -172.17.0.2 admin -172.17.0.3 checker -172.17.0.4 db -172.17.0.5 generator -172.17.0.6 qa - -10.10.10.2 phobos - -172.17.1.2 nginx -172.17.1.3 receiver -172.17.1.4 auth - -127.0.0.1 localhost -::1 localhost ip6-localhost ip6-loopback -fe00::0 ip6-localnet -ff00::0 ip6-mcastprefix -ff02::1 ip6-allnodes -ff02::2 ip6-allrouters diff --git a/configs/linux-kernel.config b/configs/linux-kernel.config deleted file mode 100644 index c9e03c07..00000000 --- a/configs/linux-kernel.config +++ /dev/null @@ -1,4237 +0,0 @@ -# -# Automatically generated file; DO NOT EDIT. -# Linux/x86 5.10.62 Kernel Configuration -# -CONFIG_CC_VERSION_TEXT="gcc (Alpine 10.2.1_pre1) 10.2.1 20201203" -CONFIG_CC_IS_GCC=y -CONFIG_GCC_VERSION=100201 -CONFIG_LD_VERSION=235020000 -CONFIG_CLANG_VERSION=0 -CONFIG_LLD_VERSION=0 -CONFIG_CC_CAN_LINK=y -CONFIG_CC_CAN_LINK_STATIC=y -CONFIG_CC_HAS_ASM_GOTO=y -CONFIG_CC_HAS_ASM_INLINE=y -CONFIG_IRQ_WORK=y -CONFIG_BUILDTIME_TABLE_SORT=y -CONFIG_THREAD_INFO_IN_TASK=y - -# -# General setup -# -CONFIG_INIT_ENV_ARG_LIMIT=32 -# CONFIG_COMPILE_TEST is not set -CONFIG_LOCALVERSION="-nemufic" -# CONFIG_LOCALVERSION_AUTO is not set -CONFIG_BUILD_SALT="" -CONFIG_HAVE_KERNEL_GZIP=y -CONFIG_HAVE_KERNEL_BZIP2=y -CONFIG_HAVE_KERNEL_LZMA=y -CONFIG_HAVE_KERNEL_XZ=y -CONFIG_HAVE_KERNEL_LZO=y -CONFIG_HAVE_KERNEL_LZ4=y -CONFIG_HAVE_KERNEL_ZSTD=y -# CONFIG_KERNEL_GZIP is not set -# CONFIG_KERNEL_BZIP2 is not set -# CONFIG_KERNEL_LZMA is not set -CONFIG_KERNEL_XZ=y -# CONFIG_KERNEL_LZO is not set -# CONFIG_KERNEL_LZ4 is not set -# CONFIG_KERNEL_ZSTD is not set -CONFIG_DEFAULT_INIT="" -CONFIG_DEFAULT_HOSTNAME="(none)" -CONFIG_SWAP=y -CONFIG_SYSVIPC=y -CONFIG_SYSVIPC_SYSCTL=y -CONFIG_POSIX_MQUEUE=y -CONFIG_POSIX_MQUEUE_SYSCTL=y -# CONFIG_WATCH_QUEUE is not set -CONFIG_CROSS_MEMORY_ATTACH=y -# CONFIG_USELIB is not set -CONFIG_AUDIT=y -CONFIG_HAVE_ARCH_AUDITSYSCALL=y -CONFIG_AUDITSYSCALL=y - -# -# IRQ subsystem -# -CONFIG_GENERIC_IRQ_PROBE=y -CONFIG_GENERIC_IRQ_SHOW=y -CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y -CONFIG_GENERIC_PENDING_IRQ=y -CONFIG_GENERIC_IRQ_MIGRATION=y -CONFIG_HARDIRQS_SW_RESEND=y -CONFIG_IRQ_DOMAIN=y -CONFIG_IRQ_DOMAIN_HIERARCHY=y -CONFIG_GENERIC_MSI_IRQ=y -CONFIG_GENERIC_MSI_IRQ_DOMAIN=y -CONFIG_GENERIC_IRQ_MATRIX_ALLOCATOR=y -CONFIG_GENERIC_IRQ_RESERVATION_MODE=y -CONFIG_IRQ_FORCED_THREADING=y -CONFIG_SPARSE_IRQ=y -# CONFIG_GENERIC_IRQ_DEBUGFS is not set -# end of IRQ subsystem - -CONFIG_CLOCKSOURCE_WATCHDOG=y -CONFIG_ARCH_CLOCKSOURCE_INIT=y -CONFIG_CLOCKSOURCE_VALIDATE_LAST_CYCLE=y -CONFIG_GENERIC_TIME_VSYSCALL=y -CONFIG_GENERIC_CLOCKEVENTS=y -CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y -CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST=y -CONFIG_GENERIC_CMOS_UPDATE=y -CONFIG_HAVE_POSIX_CPU_TIMERS_TASK_WORK=y -CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y - -# -# Timers subsystem -# -CONFIG_TICK_ONESHOT=y -CONFIG_NO_HZ_COMMON=y -# CONFIG_HZ_PERIODIC is not set -CONFIG_NO_HZ_IDLE=y -# CONFIG_NO_HZ_FULL is not set -# CONFIG_NO_HZ is not set -CONFIG_HIGH_RES_TIMERS=y -# end of Timers subsystem - -CONFIG_PREEMPT_NONE=y -# CONFIG_PREEMPT_VOLUNTARY is not set -# CONFIG_PREEMPT is not set - -# -# CPU/Task time and stats accounting -# -CONFIG_TICK_CPU_ACCOUNTING=y -# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set -# CONFIG_IRQ_TIME_ACCOUNTING is not set -# CONFIG_BSD_PROCESS_ACCT is not set -CONFIG_TASKSTATS=y -CONFIG_TASK_DELAY_ACCT=y -CONFIG_TASK_XACCT=y -CONFIG_TASK_IO_ACCOUNTING=y -# CONFIG_PSI is not set -# end of CPU/Task time and stats accounting - -CONFIG_CPU_ISOLATION=y - -# -# RCU Subsystem -# -CONFIG_TREE_RCU=y -# CONFIG_RCU_EXPERT is not set -CONFIG_SRCU=y -CONFIG_TREE_SRCU=y -CONFIG_TASKS_RCU_GENERIC=y -CONFIG_TASKS_RUDE_RCU=y -CONFIG_RCU_STALL_COMMON=y -CONFIG_RCU_NEED_SEGCBLIST=y -# end of RCU Subsystem - -CONFIG_IKCONFIG=y -CONFIG_IKCONFIG_PROC=y -# CONFIG_IKHEADERS is not set -CONFIG_LOG_BUF_SHIFT=17 -CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 -CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=13 -CONFIG_HAVE_UNSTABLE_SCHED_CLOCK=y - -# -# Scheduler features -# -# CONFIG_UCLAMP_TASK is not set -# end of Scheduler features - -CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y -CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH=y -CONFIG_CC_HAS_INT128=y -CONFIG_ARCH_SUPPORTS_INT128=y -CONFIG_CGROUPS=y -CONFIG_PAGE_COUNTER=y -CONFIG_MEMCG=y -CONFIG_MEMCG_SWAP=y -CONFIG_MEMCG_KMEM=y -CONFIG_BLK_CGROUP=y -CONFIG_CGROUP_WRITEBACK=y -CONFIG_CGROUP_SCHED=y -CONFIG_FAIR_GROUP_SCHED=y -CONFIG_CFS_BANDWIDTH=y -CONFIG_RT_GROUP_SCHED=y -CONFIG_CGROUP_PIDS=y -# CONFIG_CGROUP_RDMA is not set -CONFIG_CGROUP_FREEZER=y -CONFIG_CGROUP_HUGETLB=y -CONFIG_CPUSETS=y -CONFIG_PROC_PID_CPUSET=y -CONFIG_CGROUP_DEVICE=y -CONFIG_CGROUP_CPUACCT=y -CONFIG_CGROUP_PERF=y -# CONFIG_CGROUP_DEBUG is not set -CONFIG_SOCK_CGROUP_DATA=y -CONFIG_NAMESPACES=y -CONFIG_UTS_NS=y -CONFIG_TIME_NS=y -CONFIG_IPC_NS=y -CONFIG_USER_NS=y -CONFIG_PID_NS=y -CONFIG_NET_NS=y -# CONFIG_CHECKPOINT_RESTORE is not set -CONFIG_SCHED_AUTOGROUP=y -# CONFIG_SYSFS_DEPRECATED is not set -CONFIG_RELAY=y -CONFIG_BLK_DEV_INITRD=y -CONFIG_INITRAMFS_SOURCE="" -CONFIG_RD_GZIP=y -# CONFIG_RD_BZIP2 is not set -# CONFIG_RD_LZMA is not set -# CONFIG_RD_XZ is not set -# CONFIG_RD_LZO is not set -# CONFIG_RD_LZ4 is not set -CONFIG_RD_ZSTD=y -# CONFIG_BOOT_CONFIG is not set -# CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE is not set -CONFIG_CC_OPTIMIZE_FOR_SIZE=y -CONFIG_LD_ORPHAN_WARN=y -CONFIG_SYSCTL=y -CONFIG_HAVE_UID16=y -CONFIG_SYSCTL_EXCEPTION_TRACE=y -CONFIG_HAVE_PCSPKR_PLATFORM=y -CONFIG_BPF=y -# CONFIG_EXPERT is not set -CONFIG_UID16=y -CONFIG_MULTIUSER=y -CONFIG_SGETMASK_SYSCALL=y -CONFIG_SYSFS_SYSCALL=y -CONFIG_FHANDLE=y -CONFIG_POSIX_TIMERS=y -CONFIG_PRINTK=y -CONFIG_PRINTK_NMI=y -CONFIG_BUG=y -CONFIG_ELF_CORE=y -CONFIG_PCSPKR_PLATFORM=y -CONFIG_BASE_FULL=y -CONFIG_FUTEX=y -CONFIG_FUTEX_PI=y -CONFIG_EPOLL=y -CONFIG_SIGNALFD=y -CONFIG_TIMERFD=y -CONFIG_EVENTFD=y -CONFIG_SHMEM=y -CONFIG_AIO=y -CONFIG_IO_URING=y -CONFIG_ADVISE_SYSCALLS=y -CONFIG_MEMBARRIER=y -CONFIG_KALLSYMS=y -# CONFIG_KALLSYMS_ALL is not set -CONFIG_KALLSYMS_ABSOLUTE_PERCPU=y -CONFIG_KALLSYMS_BASE_RELATIVE=y -# CONFIG_BPF_SYSCALL is not set -CONFIG_ARCH_WANT_DEFAULT_BPF_JIT=y -# CONFIG_USERFAULTFD is not set -CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE=y -CONFIG_KCMP=y -CONFIG_RSEQ=y -# CONFIG_EMBEDDED is not set -CONFIG_HAVE_PERF_EVENTS=y - -# -# Kernel Performance Events And Counters -# -CONFIG_PERF_EVENTS=y -# CONFIG_DEBUG_PERF_USE_VMALLOC is not set -# end of Kernel Performance Events And Counters - -CONFIG_VM_EVENT_COUNTERS=y -CONFIG_SLUB_DEBUG=y -# CONFIG_COMPAT_BRK is not set -# CONFIG_SLAB is not set -CONFIG_SLUB=y -CONFIG_SLAB_MERGE_DEFAULT=y -CONFIG_SLAB_FREELIST_RANDOM=y -CONFIG_SLAB_FREELIST_HARDENED=y -CONFIG_SHUFFLE_PAGE_ALLOCATOR=y -CONFIG_SLUB_CPU_PARTIAL=y -# CONFIG_PROFILING is not set -CONFIG_TRACEPOINTS=y -# end of General setup - -CONFIG_64BIT=y -CONFIG_X86_64=y -CONFIG_X86=y -CONFIG_INSTRUCTION_DECODER=y -CONFIG_OUTPUT_FORMAT="elf64-x86-64" -CONFIG_LOCKDEP_SUPPORT=y -CONFIG_STACKTRACE_SUPPORT=y -CONFIG_MMU=y -CONFIG_ARCH_MMAP_RND_BITS_MIN=28 -CONFIG_ARCH_MMAP_RND_BITS_MAX=32 -CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=8 -CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=16 -CONFIG_GENERIC_ISA_DMA=y -CONFIG_GENERIC_BUG=y -CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y -CONFIG_ARCH_MAY_HAVE_PC_FDC=y -CONFIG_GENERIC_CALIBRATE_DELAY=y -CONFIG_ARCH_HAS_CPU_RELAX=y -CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y -CONFIG_ARCH_HAS_FILTER_PGPROT=y -CONFIG_HAVE_SETUP_PER_CPU_AREA=y -CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y -CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK=y -CONFIG_ARCH_HIBERNATION_POSSIBLE=y -CONFIG_ARCH_SUSPEND_POSSIBLE=y -CONFIG_ARCH_WANT_GENERAL_HUGETLB=y -CONFIG_ZONE_DMA32=y -CONFIG_AUDIT_ARCH=y -CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y -CONFIG_X86_64_SMP=y -CONFIG_ARCH_SUPPORTS_UPROBES=y -CONFIG_FIX_EARLYCON_MEM=y -CONFIG_PGTABLE_LEVELS=5 -CONFIG_CC_HAS_SANE_STACKPROTECTOR=y - -# -# Processor type and features -# -CONFIG_ZONE_DMA=y -CONFIG_SMP=y -CONFIG_X86_FEATURE_NAMES=y -CONFIG_X86_MPPARSE=y -# CONFIG_GOLDFISH is not set -CONFIG_RETPOLINE=y -# CONFIG_X86_CPU_RESCTRL is not set -# CONFIG_X86_EXTENDED_PLATFORM is not set -# CONFIG_X86_INTEL_LPSS is not set -# CONFIG_X86_AMD_PLATFORM_DEVICE is not set -CONFIG_IOSF_MBI=y -# CONFIG_IOSF_MBI_DEBUG is not set -CONFIG_SCHED_OMIT_FRAME_POINTER=y -# CONFIG_HYPERVISOR_GUEST is not set -# CONFIG_MK8 is not set -# CONFIG_MPSC is not set -CONFIG_MCORE2=y -# CONFIG_MATOM is not set -# CONFIG_GENERIC_CPU is not set -CONFIG_X86_INTERNODE_CACHE_SHIFT=6 -CONFIG_X86_L1_CACHE_SHIFT=6 -CONFIG_X86_INTEL_USERCOPY=y -CONFIG_X86_USE_PPRO_CHECKSUM=y -CONFIG_X86_P6_NOP=y -CONFIG_X86_TSC=y -CONFIG_X86_CMPXCHG64=y -CONFIG_X86_CMOV=y -CONFIG_X86_MINIMUM_CPU_FAMILY=64 -CONFIG_X86_DEBUGCTLMSR=y -CONFIG_IA32_FEAT_CTL=y -CONFIG_X86_VMX_FEATURE_NAMES=y -CONFIG_CPU_SUP_INTEL=y -CONFIG_CPU_SUP_AMD=y -CONFIG_CPU_SUP_HYGON=y -CONFIG_CPU_SUP_CENTAUR=y -CONFIG_CPU_SUP_ZHAOXIN=y -CONFIG_HPET_TIMER=y -CONFIG_HPET_EMULATE_RTC=y -CONFIG_DMI=y -# CONFIG_GART_IOMMU is not set -# CONFIG_MAXSMP is not set -CONFIG_NR_CPUS_RANGE_BEGIN=2 -CONFIG_NR_CPUS_RANGE_END=512 -CONFIG_NR_CPUS_DEFAULT=64 -CONFIG_NR_CPUS=8 -CONFIG_SCHED_SMT=y -CONFIG_SCHED_MC=y -CONFIG_SCHED_MC_PRIO=y -CONFIG_X86_LOCAL_APIC=y -CONFIG_X86_IO_APIC=y -CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS=y -# CONFIG_X86_MCE is not set - -# -# Performance monitoring -# -CONFIG_PERF_EVENTS_INTEL_UNCORE=y -CONFIG_PERF_EVENTS_INTEL_RAPL=y -CONFIG_PERF_EVENTS_INTEL_CSTATE=y -# CONFIG_PERF_EVENTS_AMD_POWER is not set -# end of Performance monitoring - -CONFIG_X86_16BIT=y -CONFIG_X86_ESPFIX64=y -CONFIG_X86_VSYSCALL_EMULATION=y -CONFIG_X86_IOPL_IOPERM=y -# CONFIG_I8K is not set -CONFIG_MICROCODE=y -CONFIG_MICROCODE_INTEL=y -# CONFIG_MICROCODE_AMD is not set -CONFIG_MICROCODE_OLD_INTERFACE=y -CONFIG_X86_MSR=y -CONFIG_X86_CPUID=y -CONFIG_X86_5LEVEL=y -CONFIG_X86_DIRECT_GBPAGES=y -# CONFIG_X86_CPA_STATISTICS is not set -# CONFIG_AMD_MEM_ENCRYPT is not set -# CONFIG_NUMA is not set -CONFIG_ARCH_SPARSEMEM_ENABLE=y -CONFIG_ARCH_SPARSEMEM_DEFAULT=y -CONFIG_ARCH_SELECT_MEMORY_MODEL=y -CONFIG_ARCH_PROC_KCORE_TEXT=y -CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 -# CONFIG_X86_PMEM_LEGACY is not set -# CONFIG_X86_CHECK_BIOS_CORRUPTION is not set -CONFIG_X86_RESERVE_LOW=64 -CONFIG_MTRR=y -CONFIG_MTRR_SANITIZER=y -CONFIG_MTRR_SANITIZER_ENABLE_DEFAULT=0 -CONFIG_MTRR_SANITIZER_SPARE_REG_NR_DEFAULT=1 -CONFIG_X86_PAT=y -CONFIG_ARCH_USES_PG_UNCACHED=y -CONFIG_ARCH_RANDOM=y -CONFIG_X86_SMAP=y -CONFIG_X86_UMIP=y -CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS=y -CONFIG_X86_INTEL_TSX_MODE_OFF=y -# CONFIG_X86_INTEL_TSX_MODE_ON is not set -# CONFIG_X86_INTEL_TSX_MODE_AUTO is not set -CONFIG_EFI=y -CONFIG_EFI_STUB=y -# CONFIG_EFI_MIXED is not set -CONFIG_HZ_100=y -# CONFIG_HZ_250 is not set -# CONFIG_HZ_300 is not set -# CONFIG_HZ_1000 is not set -CONFIG_HZ=100 -CONFIG_SCHED_HRTICK=y -# CONFIG_KEXEC is not set -# CONFIG_KEXEC_FILE is not set -# CONFIG_CRASH_DUMP is not set -CONFIG_PHYSICAL_START=0x1000000 -CONFIG_RELOCATABLE=y -CONFIG_RANDOMIZE_BASE=y -CONFIG_X86_NEED_RELOCS=y -CONFIG_PHYSICAL_ALIGN=0x1000000 -CONFIG_DYNAMIC_MEMORY_LAYOUT=y -CONFIG_RANDOMIZE_MEMORY=y -CONFIG_RANDOMIZE_MEMORY_PHYSICAL_PADDING=0x0 -CONFIG_HOTPLUG_CPU=y -# CONFIG_BOOTPARAM_HOTPLUG_CPU0 is not set -# CONFIG_DEBUG_HOTPLUG_CPU0 is not set -# CONFIG_COMPAT_VDSO is not set -# CONFIG_LEGACY_VSYSCALL_EMULATE is not set -# CONFIG_LEGACY_VSYSCALL_XONLY is not set -CONFIG_LEGACY_VSYSCALL_NONE=y -# CONFIG_CMDLINE_BOOL is not set -CONFIG_MODIFY_LDT_SYSCALL=y -CONFIG_HAVE_LIVEPATCH=y -# end of Processor type and features - -CONFIG_ARCH_HAS_ADD_PAGES=y -CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y -CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK=y -CONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION=y -CONFIG_ARCH_ENABLE_THP_MIGRATION=y - -# -# Power management and ACPI options -# -# CONFIG_SUSPEND is not set -# CONFIG_HIBERNATION is not set -CONFIG_PM=y -# CONFIG_PM_DEBUG is not set -CONFIG_PM_CLK=y -# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set -# CONFIG_ENERGY_MODEL is not set -CONFIG_ARCH_SUPPORTS_ACPI=y -CONFIG_ACPI=y -CONFIG_ACPI_LEGACY_TABLES_LOOKUP=y -CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC=y -CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT=y -# CONFIG_ACPI_DEBUGGER is not set -CONFIG_ACPI_SPCR_TABLE=y -CONFIG_ACPI_LPIT=y -# CONFIG_ACPI_REV_OVERRIDE_POSSIBLE is not set -# CONFIG_ACPI_EC_DEBUGFS is not set -CONFIG_ACPI_AC=y -CONFIG_ACPI_BATTERY=y -CONFIG_ACPI_BUTTON=y -CONFIG_ACPI_VIDEO=y -CONFIG_ACPI_FAN=y -CONFIG_ACPI_DOCK=y -CONFIG_ACPI_CPU_FREQ_PSS=y -CONFIG_ACPI_PROCESSOR_CSTATE=y -CONFIG_ACPI_PROCESSOR_IDLE=y -CONFIG_ACPI_CPPC_LIB=y -CONFIG_ACPI_PROCESSOR=y -CONFIG_ACPI_HOTPLUG_CPU=y -CONFIG_ACPI_PROCESSOR_AGGREGATOR=y -CONFIG_ACPI_THERMAL=y -CONFIG_ARCH_HAS_ACPI_TABLE_UPGRADE=y -CONFIG_ACPI_TABLE_UPGRADE=y -# CONFIG_ACPI_DEBUG is not set -# CONFIG_ACPI_PCI_SLOT is not set -CONFIG_ACPI_CONTAINER=y -CONFIG_ACPI_HOTPLUG_IOAPIC=y -CONFIG_ACPI_SBS=y -CONFIG_ACPI_HED=y -# CONFIG_ACPI_CUSTOM_METHOD is not set -# CONFIG_ACPI_BGRT is not set -# CONFIG_ACPI_NFIT is not set -CONFIG_HAVE_ACPI_APEI=y -CONFIG_HAVE_ACPI_APEI_NMI=y -CONFIG_ACPI_APEI=y -CONFIG_ACPI_APEI_GHES=y -# CONFIG_ACPI_APEI_EINJ is not set -# CONFIG_ACPI_APEI_ERST_DEBUG is not set -# CONFIG_ACPI_DPTF is not set -# CONFIG_ACPI_CONFIGFS is not set -# CONFIG_PMIC_OPREGION is not set -CONFIG_X86_PM_TIMER=y -# CONFIG_SFI is not set - -# -# CPU Frequency scaling -# -CONFIG_CPU_FREQ=y -CONFIG_CPU_FREQ_GOV_ATTR_SET=y -# CONFIG_CPU_FREQ_STAT is not set -# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set -# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set -# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set -CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL=y -CONFIG_CPU_FREQ_GOV_PERFORMANCE=y -# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set -# CONFIG_CPU_FREQ_GOV_USERSPACE is not set -# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set -# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set -CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y - -# -# CPU frequency scaling drivers -# -CONFIG_X86_INTEL_PSTATE=y -CONFIG_X86_PCC_CPUFREQ=y -CONFIG_X86_ACPI_CPUFREQ=y -# CONFIG_X86_ACPI_CPUFREQ_CPB is not set -# CONFIG_X86_POWERNOW_K8 is not set -# CONFIG_X86_SPEEDSTEP_CENTRINO is not set -# CONFIG_X86_P4_CLOCKMOD is not set - -# -# shared options -# -# end of CPU Frequency scaling - -# -# CPU Idle -# -CONFIG_CPU_IDLE=y -CONFIG_CPU_IDLE_GOV_LADDER=y -CONFIG_CPU_IDLE_GOV_MENU=y -# CONFIG_CPU_IDLE_GOV_TEO is not set -# end of CPU Idle - -CONFIG_INTEL_IDLE=y -# end of Power management and ACPI options - -# -# Bus options (PCI etc.) -# -CONFIG_PCI_DIRECT=y -CONFIG_PCI_MMCONFIG=y -CONFIG_MMCONF_FAM10H=y -CONFIG_ISA_DMA_API=y -CONFIG_AMD_NB=y -# CONFIG_X86_SYSFB is not set -# end of Bus options (PCI etc.) - -# -# Binary Emulations -# -CONFIG_IA32_EMULATION=y -# CONFIG_X86_X32 is not set -CONFIG_COMPAT_32=y -CONFIG_COMPAT=y -CONFIG_COMPAT_FOR_U64_ALIGNMENT=y -CONFIG_SYSVIPC_COMPAT=y -# end of Binary Emulations - -# -# Firmware Drivers -# -# CONFIG_EDD is not set -CONFIG_FIRMWARE_MEMMAP=y -CONFIG_DMIID=y -CONFIG_DMI_SYSFS=y -CONFIG_DMI_SCAN_MACHINE_NON_EFI_FALLBACK=y -# CONFIG_FW_CFG_SYSFS is not set -# CONFIG_GOOGLE_FIRMWARE is not set - -# -# EFI (Extensible Firmware Interface) Support -# -CONFIG_EFI_VARS=y -CONFIG_EFI_ESRT=y -CONFIG_EFI_VARS_PSTORE=y -# CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE is not set -# CONFIG_EFI_FAKE_MEMMAP is not set -CONFIG_EFI_RUNTIME_WRAPPERS=y -CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER=y -# CONFIG_EFI_BOOTLOADER_CONTROL is not set -# CONFIG_EFI_CAPSULE_LOADER is not set -# CONFIG_EFI_TEST is not set -# CONFIG_APPLE_PROPERTIES is not set -# CONFIG_RESET_ATTACK_MITIGATION is not set -# CONFIG_EFI_RCI2_TABLE is not set -# CONFIG_EFI_DISABLE_PCI_DMA is not set -# end of EFI (Extensible Firmware Interface) Support - -CONFIG_UEFI_CPER=y -CONFIG_UEFI_CPER_X86=y -CONFIG_EFI_EARLYCON=y -CONFIG_EFI_CUSTOM_SSDT_OVERLAYS=y - -# -# Tegra firmware driver -# -# end of Tegra firmware driver -# end of Firmware Drivers - -CONFIG_HAVE_KVM=y -# CONFIG_VIRTUALIZATION is not set -CONFIG_AS_AVX512=y -CONFIG_AS_SHA1_NI=y -CONFIG_AS_SHA256_NI=y -CONFIG_AS_TPAUSE=y - -# -# General architecture-dependent options -# -CONFIG_CRASH_CORE=y -CONFIG_HOTPLUG_SMT=y -CONFIG_GENERIC_ENTRY=y -CONFIG_HAVE_OPROFILE=y -CONFIG_OPROFILE_NMI_TIMER=y -# CONFIG_KPROBES is not set -# CONFIG_JUMP_LABEL is not set -# CONFIG_STATIC_CALL_SELFTEST is not set -CONFIG_UPROBES=y -CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y -CONFIG_ARCH_USE_BUILTIN_BSWAP=y -CONFIG_HAVE_IOREMAP_PROT=y -CONFIG_HAVE_KPROBES=y -CONFIG_HAVE_KRETPROBES=y -CONFIG_HAVE_OPTPROBES=y -CONFIG_HAVE_KPROBES_ON_FTRACE=y -CONFIG_HAVE_FUNCTION_ERROR_INJECTION=y -CONFIG_HAVE_NMI=y -CONFIG_HAVE_ARCH_TRACEHOOK=y -CONFIG_HAVE_DMA_CONTIGUOUS=y -CONFIG_GENERIC_SMP_IDLE_THREAD=y -CONFIG_ARCH_HAS_FORTIFY_SOURCE=y -CONFIG_ARCH_HAS_SET_MEMORY=y -CONFIG_ARCH_HAS_SET_DIRECT_MAP=y -CONFIG_HAVE_ARCH_THREAD_STRUCT_WHITELIST=y -CONFIG_ARCH_WANTS_DYNAMIC_TASK_STRUCT=y -CONFIG_HAVE_ASM_MODVERSIONS=y -CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y -CONFIG_HAVE_RSEQ=y -CONFIG_HAVE_FUNCTION_ARG_ACCESS_API=y -CONFIG_HAVE_HW_BREAKPOINT=y -CONFIG_HAVE_MIXED_BREAKPOINTS_REGS=y -CONFIG_HAVE_USER_RETURN_NOTIFIER=y -CONFIG_HAVE_PERF_EVENTS_NMI=y -CONFIG_HAVE_HARDLOCKUP_DETECTOR_PERF=y -CONFIG_HAVE_PERF_REGS=y -CONFIG_HAVE_PERF_USER_STACK_DUMP=y -CONFIG_HAVE_ARCH_JUMP_LABEL=y -CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE=y -CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y -CONFIG_HAVE_ALIGNED_STRUCT_PAGE=y -CONFIG_HAVE_CMPXCHG_LOCAL=y -CONFIG_HAVE_CMPXCHG_DOUBLE=y -CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION=y -CONFIG_ARCH_WANT_OLD_COMPAT_IPC=y -CONFIG_HAVE_ARCH_SECCOMP=y -CONFIG_HAVE_ARCH_SECCOMP_FILTER=y -CONFIG_SECCOMP=y -CONFIG_SECCOMP_FILTER=y -CONFIG_HAVE_ARCH_STACKLEAK=y -CONFIG_HAVE_STACKPROTECTOR=y -CONFIG_STACKPROTECTOR=y -CONFIG_STACKPROTECTOR_STRONG=y -CONFIG_HAVE_ARCH_WITHIN_STACK_FRAMES=y -CONFIG_HAVE_CONTEXT_TRACKING=y -CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y -CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y -CONFIG_HAVE_MOVE_PMD=y -CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y -CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD=y -CONFIG_HAVE_ARCH_HUGE_VMAP=y -CONFIG_ARCH_WANT_HUGE_PMD_SHARE=y -CONFIG_HAVE_ARCH_SOFT_DIRTY=y -CONFIG_HAVE_MOD_ARCH_SPECIFIC=y -CONFIG_MODULES_USE_ELF_RELA=y -CONFIG_ARCH_HAS_ELF_RANDOMIZE=y -CONFIG_HAVE_ARCH_MMAP_RND_BITS=y -CONFIG_HAVE_EXIT_THREAD=y -CONFIG_ARCH_MMAP_RND_BITS=28 -CONFIG_HAVE_ARCH_MMAP_RND_COMPAT_BITS=y -CONFIG_ARCH_MMAP_RND_COMPAT_BITS=8 -CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES=y -CONFIG_HAVE_STACK_VALIDATION=y -CONFIG_HAVE_RELIABLE_STACKTRACE=y -CONFIG_OLD_SIGSUSPEND3=y -CONFIG_COMPAT_OLD_SIGACTION=y -CONFIG_COMPAT_32BIT_TIME=y -CONFIG_HAVE_ARCH_VMAP_STACK=y -CONFIG_VMAP_STACK=y -CONFIG_ARCH_HAS_STRICT_KERNEL_RWX=y -CONFIG_STRICT_KERNEL_RWX=y -CONFIG_ARCH_HAS_STRICT_MODULE_RWX=y -CONFIG_STRICT_MODULE_RWX=y -CONFIG_HAVE_ARCH_PREL32_RELOCATIONS=y -CONFIG_ARCH_USE_MEMREMAP_PROT=y -# CONFIG_LOCK_EVENT_COUNTS is not set -CONFIG_ARCH_HAS_MEM_ENCRYPT=y -CONFIG_HAVE_STATIC_CALL=y -CONFIG_HAVE_STATIC_CALL_INLINE=y -CONFIG_ARCH_WANT_LD_ORPHAN_WARN=y - -# -# GCOV-based kernel profiling -# -# CONFIG_GCOV_KERNEL is not set -CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y -# end of GCOV-based kernel profiling - -CONFIG_HAVE_GCC_PLUGINS=y -# CONFIG_GCC_PLUGINS is not set -# end of General architecture-dependent options - -CONFIG_RT_MUTEXES=y -CONFIG_BASE_SMALL=0 -CONFIG_MODULES=y -# CONFIG_MODULE_FORCE_LOAD is not set -CONFIG_MODULE_UNLOAD=y -# CONFIG_MODULE_FORCE_UNLOAD is not set -# CONFIG_MODVERSIONS is not set -# CONFIG_MODULE_SRCVERSION_ALL is not set -# CONFIG_MODULE_SIG is not set -# CONFIG_MODULE_COMPRESS is not set -# CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS is not set -# CONFIG_UNUSED_SYMBOLS is not set -# CONFIG_TRIM_UNUSED_KSYMS is not set -CONFIG_MODULES_TREE_LOOKUP=y -CONFIG_BLOCK=y -CONFIG_BLK_SCSI_REQUEST=y -CONFIG_BLK_CGROUP_RWSTAT=y -CONFIG_BLK_DEV_BSG=y -CONFIG_BLK_DEV_BSGLIB=y -CONFIG_BLK_DEV_INTEGRITY=y -CONFIG_BLK_DEV_INTEGRITY_T10=y -# CONFIG_BLK_DEV_ZONED is not set -CONFIG_BLK_DEV_THROTTLING=y -# CONFIG_BLK_DEV_THROTTLING_LOW is not set -# CONFIG_BLK_CMDLINE_PARSER is not set -# CONFIG_BLK_WBT is not set -# CONFIG_BLK_CGROUP_IOLATENCY is not set -# CONFIG_BLK_CGROUP_IOCOST is not set -CONFIG_BLK_DEBUG_FS=y -# CONFIG_BLK_SED_OPAL is not set -# CONFIG_BLK_INLINE_ENCRYPTION is not set - -# -# Partition Types -# -CONFIG_PARTITION_ADVANCED=y -# CONFIG_ACORN_PARTITION is not set -# CONFIG_AIX_PARTITION is not set -# CONFIG_OSF_PARTITION is not set -# CONFIG_AMIGA_PARTITION is not set -# CONFIG_ATARI_PARTITION is not set -# CONFIG_MAC_PARTITION is not set -CONFIG_MSDOS_PARTITION=y -# CONFIG_BSD_DISKLABEL is not set -# CONFIG_MINIX_SUBPARTITION is not set -# CONFIG_SOLARIS_X86_PARTITION is not set -# CONFIG_UNIXWARE_DISKLABEL is not set -# CONFIG_LDM_PARTITION is not set -# CONFIG_SGI_PARTITION is not set -# CONFIG_ULTRIX_PARTITION is not set -# CONFIG_SUN_PARTITION is not set -# CONFIG_KARMA_PARTITION is not set -CONFIG_EFI_PARTITION=y -# CONFIG_SYSV68_PARTITION is not set -# CONFIG_CMDLINE_PARTITION is not set -# end of Partition Types - -CONFIG_BLOCK_COMPAT=y -CONFIG_BLK_MQ_PCI=y -CONFIG_BLK_PM=y - -# -# IO Schedulers -# -CONFIG_MQ_IOSCHED_DEADLINE=y -# CONFIG_MQ_IOSCHED_KYBER is not set -# CONFIG_IOSCHED_BFQ is not set -# end of IO Schedulers - -CONFIG_ASN1=y -CONFIG_INLINE_SPIN_UNLOCK_IRQ=y -CONFIG_INLINE_READ_UNLOCK=y -CONFIG_INLINE_READ_UNLOCK_IRQ=y -CONFIG_INLINE_WRITE_UNLOCK=y -CONFIG_INLINE_WRITE_UNLOCK_IRQ=y -CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y -CONFIG_MUTEX_SPIN_ON_OWNER=y -CONFIG_RWSEM_SPIN_ON_OWNER=y -CONFIG_LOCK_SPIN_ON_OWNER=y -CONFIG_ARCH_USE_QUEUED_SPINLOCKS=y -CONFIG_QUEUED_SPINLOCKS=y -CONFIG_ARCH_USE_QUEUED_RWLOCKS=y -CONFIG_QUEUED_RWLOCKS=y -CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE=y -CONFIG_ARCH_HAS_SYNC_CORE_BEFORE_USERMODE=y -CONFIG_ARCH_HAS_SYSCALL_WRAPPER=y -CONFIG_FREEZER=y - -# -# Executable file formats -# -CONFIG_BINFMT_ELF=y -CONFIG_COMPAT_BINFMT_ELF=y -CONFIG_ELFCORE=y -# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set -CONFIG_BINFMT_SCRIPT=y -CONFIG_BINFMT_MISC=y -CONFIG_COREDUMP=y -# end of Executable file formats - -# -# Memory Management options -# -CONFIG_SELECT_MEMORY_MODEL=y -CONFIG_SPARSEMEM_MANUAL=y -CONFIG_SPARSEMEM=y -CONFIG_SPARSEMEM_EXTREME=y -CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y -CONFIG_SPARSEMEM_VMEMMAP=y -CONFIG_HAVE_FAST_GUP=y -# CONFIG_MEMORY_HOTPLUG is not set -CONFIG_SPLIT_PTLOCK_CPUS=4 -CONFIG_COMPACTION=y -CONFIG_PAGE_REPORTING=y -CONFIG_MIGRATION=y -CONFIG_PHYS_ADDR_T_64BIT=y -CONFIG_BOUNCE=y -CONFIG_VIRT_TO_BUS=y -CONFIG_MMU_NOTIFIER=y -CONFIG_KSM=y -CONFIG_DEFAULT_MMAP_MIN_ADDR=65536 -CONFIG_TRANSPARENT_HUGEPAGE=y -CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y -# CONFIG_TRANSPARENT_HUGEPAGE_MADVISE is not set -CONFIG_ARCH_WANTS_THP_SWAP=y -CONFIG_THP_SWAP=y -# CONFIG_CLEANCACHE is not set -# CONFIG_FRONTSWAP is not set -# CONFIG_CMA is not set -# CONFIG_ZPOOL is not set -# CONFIG_ZBUD is not set -# CONFIG_ZSMALLOC is not set -CONFIG_GENERIC_EARLY_IOREMAP=y -# CONFIG_DEFERRED_STRUCT_PAGE_INIT is not set -# CONFIG_IDLE_PAGE_TRACKING is not set -CONFIG_ARCH_HAS_PTE_DEVMAP=y -CONFIG_VMAP_PFN=y -CONFIG_ARCH_USES_HIGH_VMA_FLAGS=y -CONFIG_ARCH_HAS_PKEYS=y -# CONFIG_PERCPU_STATS is not set -# CONFIG_GUP_BENCHMARK is not set -# CONFIG_READ_ONLY_THP_FOR_FS is not set -CONFIG_ARCH_HAS_PTE_SPECIAL=y -# end of Memory Management options - -CONFIG_NET=y -CONFIG_NET_INGRESS=y -CONFIG_SKB_EXTENSIONS=y - -# -# Networking options -# -CONFIG_PACKET=y -CONFIG_PACKET_DIAG=y -CONFIG_UNIX=y -CONFIG_UNIX_SCM=y -CONFIG_UNIX_DIAG=y -# CONFIG_TLS is not set -# CONFIG_XFRM_USER is not set -# CONFIG_NET_KEY is not set -CONFIG_INET=y -CONFIG_IP_MULTICAST=y -# CONFIG_IP_ADVANCED_ROUTER is not set -CONFIG_IP_ROUTE_CLASSID=y -CONFIG_IP_PNP=y -CONFIG_IP_PNP_DHCP=y -# CONFIG_IP_PNP_BOOTP is not set -# CONFIG_IP_PNP_RARP is not set -# CONFIG_NET_IPIP is not set -# CONFIG_NET_IPGRE_DEMUX is not set -# CONFIG_IP_MROUTE is not set -CONFIG_SYN_COOKIES=y -# CONFIG_NET_IPVTI is not set -# CONFIG_NET_FOU is not set -# CONFIG_INET_AH is not set -# CONFIG_INET_ESP is not set -# CONFIG_INET_IPCOMP is not set -# CONFIG_INET_DIAG is not set -# CONFIG_TCP_CONG_ADVANCED is not set -CONFIG_TCP_CONG_CUBIC=y -CONFIG_DEFAULT_TCP_CONG="cubic" -# CONFIG_TCP_MD5SIG is not set -CONFIG_IPV6=y -# CONFIG_IPV6_ROUTER_PREF is not set -# CONFIG_IPV6_OPTIMISTIC_DAD is not set -# CONFIG_INET6_AH is not set -# CONFIG_INET6_ESP is not set -# CONFIG_INET6_IPCOMP is not set -# CONFIG_IPV6_MIP6 is not set -# CONFIG_IPV6_ILA is not set -# CONFIG_IPV6_VTI is not set -# CONFIG_IPV6_SIT is not set -# CONFIG_IPV6_TUNNEL is not set -# CONFIG_IPV6_MULTIPLE_TABLES is not set -# CONFIG_IPV6_MROUTE is not set -# CONFIG_IPV6_SEG6_LWTUNNEL is not set -# CONFIG_IPV6_SEG6_HMAC is not set -# CONFIG_IPV6_RPL_LWTUNNEL is not set -CONFIG_NETLABEL=y -# CONFIG_MPTCP is not set -CONFIG_NETWORK_SECMARK=y -CONFIG_NET_PTP_CLASSIFY=y -# CONFIG_NETWORK_PHY_TIMESTAMPING is not set -CONFIG_NETFILTER=y -CONFIG_NETFILTER_ADVANCED=y -CONFIG_BRIDGE_NETFILTER=y - -# -# Core Netfilter Configuration -# -CONFIG_NETFILTER_INGRESS=y -CONFIG_NETFILTER_NETLINK=y -CONFIG_NETFILTER_FAMILY_BRIDGE=y -CONFIG_NETFILTER_FAMILY_ARP=y -CONFIG_NETFILTER_NETLINK_ACCT=y -CONFIG_NETFILTER_NETLINK_QUEUE=y -CONFIG_NETFILTER_NETLINK_LOG=y -CONFIG_NETFILTER_NETLINK_OSF=y -CONFIG_NF_CONNTRACK=y -CONFIG_NF_LOG_COMMON=y -# CONFIG_NF_LOG_NETDEV is not set -CONFIG_NETFILTER_CONNCOUNT=y -CONFIG_NF_CONNTRACK_MARK=y -# CONFIG_NF_CONNTRACK_SECMARK is not set -CONFIG_NF_CONNTRACK_ZONES=y -CONFIG_NF_CONNTRACK_PROCFS=y -CONFIG_NF_CONNTRACK_EVENTS=y -CONFIG_NF_CONNTRACK_TIMEOUT=y -CONFIG_NF_CONNTRACK_TIMESTAMP=y -CONFIG_NF_CONNTRACK_LABELS=y -CONFIG_NF_CT_PROTO_DCCP=y -CONFIG_NF_CT_PROTO_GRE=y -CONFIG_NF_CT_PROTO_SCTP=y -CONFIG_NF_CT_PROTO_UDPLITE=y -CONFIG_NF_CONNTRACK_AMANDA=y -CONFIG_NF_CONNTRACK_FTP=y -CONFIG_NF_CONNTRACK_H323=y -CONFIG_NF_CONNTRACK_IRC=y -CONFIG_NF_CONNTRACK_BROADCAST=y -CONFIG_NF_CONNTRACK_NETBIOS_NS=y -CONFIG_NF_CONNTRACK_SNMP=y -CONFIG_NF_CONNTRACK_PPTP=y -CONFIG_NF_CONNTRACK_SANE=y -CONFIG_NF_CONNTRACK_SIP=y -CONFIG_NF_CONNTRACK_TFTP=y -CONFIG_NF_CT_NETLINK=y -CONFIG_NF_CT_NETLINK_TIMEOUT=y -CONFIG_NF_CT_NETLINK_HELPER=y -CONFIG_NETFILTER_NETLINK_GLUE_CT=y -CONFIG_NF_NAT=y -CONFIG_NF_NAT_AMANDA=y -CONFIG_NF_NAT_FTP=y -CONFIG_NF_NAT_IRC=y -CONFIG_NF_NAT_SIP=y -CONFIG_NF_NAT_TFTP=y -CONFIG_NF_NAT_REDIRECT=y -CONFIG_NF_NAT_MASQUERADE=y -CONFIG_NETFILTER_SYNPROXY=y -CONFIG_NF_TABLES=y -CONFIG_NF_TABLES_INET=y -CONFIG_NF_TABLES_NETDEV=y -# CONFIG_NFT_NUMGEN is not set -CONFIG_NFT_CT=y -CONFIG_NFT_COUNTER=y -CONFIG_NFT_CONNLIMIT=y -CONFIG_NFT_LOG=y -CONFIG_NFT_LIMIT=y -CONFIG_NFT_MASQ=y -CONFIG_NFT_REDIR=y -CONFIG_NFT_NAT=y -# CONFIG_NFT_TUNNEL is not set -# CONFIG_NFT_OBJREF is not set -CONFIG_NFT_QUEUE=y -# CONFIG_NFT_QUOTA is not set -CONFIG_NFT_REJECT=y -CONFIG_NFT_REJECT_INET=y -CONFIG_NFT_COMPAT=y -CONFIG_NFT_HASH=y -# CONFIG_NFT_SOCKET is not set -# CONFIG_NFT_OSF is not set -# CONFIG_NFT_TPROXY is not set -# CONFIG_NFT_SYNPROXY is not set -CONFIG_NF_DUP_NETDEV=y -CONFIG_NFT_DUP_NETDEV=y -CONFIG_NFT_FWD_NETDEV=y -# CONFIG_NF_FLOW_TABLE is not set -CONFIG_NETFILTER_XTABLES=y - -# -# Xtables combined modules -# -CONFIG_NETFILTER_XT_MARK=y -CONFIG_NETFILTER_XT_CONNMARK=y -CONFIG_NETFILTER_XT_SET=y - -# -# Xtables targets -# -# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set -CONFIG_NETFILTER_XT_TARGET_CHECKSUM=y -CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y -CONFIG_NETFILTER_XT_TARGET_CONNMARK=y -CONFIG_NETFILTER_XT_TARGET_CT=y -CONFIG_NETFILTER_XT_TARGET_DSCP=y -CONFIG_NETFILTER_XT_TARGET_HL=y -CONFIG_NETFILTER_XT_TARGET_HMARK=y -CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y -CONFIG_NETFILTER_XT_TARGET_LOG=y -CONFIG_NETFILTER_XT_TARGET_MARK=y -CONFIG_NETFILTER_XT_NAT=y -CONFIG_NETFILTER_XT_TARGET_NETMAP=y -CONFIG_NETFILTER_XT_TARGET_NFLOG=y -CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y -CONFIG_NETFILTER_XT_TARGET_NOTRACK=y -CONFIG_NETFILTER_XT_TARGET_RATEEST=y -CONFIG_NETFILTER_XT_TARGET_REDIRECT=y -CONFIG_NETFILTER_XT_TARGET_MASQUERADE=y -CONFIG_NETFILTER_XT_TARGET_TEE=y -CONFIG_NETFILTER_XT_TARGET_TPROXY=y -CONFIG_NETFILTER_XT_TARGET_TRACE=y -# CONFIG_NETFILTER_XT_TARGET_SECMARK is not set -CONFIG_NETFILTER_XT_TARGET_TCPMSS=y -CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=y - -# -# Xtables matches -# -CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y -CONFIG_NETFILTER_XT_MATCH_BPF=y -CONFIG_NETFILTER_XT_MATCH_CGROUP=y -CONFIG_NETFILTER_XT_MATCH_CLUSTER=y -CONFIG_NETFILTER_XT_MATCH_COMMENT=y -CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y -CONFIG_NETFILTER_XT_MATCH_CONNLABEL=y -CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y -CONFIG_NETFILTER_XT_MATCH_CONNMARK=y -CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y -CONFIG_NETFILTER_XT_MATCH_CPU=y -CONFIG_NETFILTER_XT_MATCH_DCCP=y -CONFIG_NETFILTER_XT_MATCH_DEVGROUP=y -CONFIG_NETFILTER_XT_MATCH_DSCP=y -CONFIG_NETFILTER_XT_MATCH_ECN=y -CONFIG_NETFILTER_XT_MATCH_ESP=y -CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y -CONFIG_NETFILTER_XT_MATCH_HELPER=y -CONFIG_NETFILTER_XT_MATCH_HL=y -CONFIG_NETFILTER_XT_MATCH_IPCOMP=y -CONFIG_NETFILTER_XT_MATCH_IPRANGE=y -CONFIG_NETFILTER_XT_MATCH_IPVS=y -CONFIG_NETFILTER_XT_MATCH_L2TP=y -CONFIG_NETFILTER_XT_MATCH_LENGTH=y -CONFIG_NETFILTER_XT_MATCH_LIMIT=y -CONFIG_NETFILTER_XT_MATCH_MAC=y -CONFIG_NETFILTER_XT_MATCH_MARK=y -CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y -CONFIG_NETFILTER_XT_MATCH_NFACCT=y -CONFIG_NETFILTER_XT_MATCH_OSF=y -CONFIG_NETFILTER_XT_MATCH_OWNER=y -CONFIG_NETFILTER_XT_MATCH_PHYSDEV=y -CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y -CONFIG_NETFILTER_XT_MATCH_QUOTA=y -CONFIG_NETFILTER_XT_MATCH_RATEEST=y -CONFIG_NETFILTER_XT_MATCH_REALM=y -CONFIG_NETFILTER_XT_MATCH_RECENT=y -CONFIG_NETFILTER_XT_MATCH_SCTP=y -CONFIG_NETFILTER_XT_MATCH_SOCKET=y -CONFIG_NETFILTER_XT_MATCH_STATE=y -CONFIG_NETFILTER_XT_MATCH_STATISTIC=y -CONFIG_NETFILTER_XT_MATCH_STRING=y -CONFIG_NETFILTER_XT_MATCH_TCPMSS=y -CONFIG_NETFILTER_XT_MATCH_TIME=y -CONFIG_NETFILTER_XT_MATCH_U32=y -# end of Core Netfilter Configuration - -CONFIG_IP_SET=y -CONFIG_IP_SET_MAX=256 -CONFIG_IP_SET_BITMAP_IP=y -CONFIG_IP_SET_BITMAP_IPMAC=y -CONFIG_IP_SET_BITMAP_PORT=y -CONFIG_IP_SET_HASH_IP=y -# CONFIG_IP_SET_HASH_IPMARK is not set -CONFIG_IP_SET_HASH_IPPORT=y -CONFIG_IP_SET_HASH_IPPORTIP=y -CONFIG_IP_SET_HASH_IPPORTNET=y -# CONFIG_IP_SET_HASH_IPMAC is not set -# CONFIG_IP_SET_HASH_MAC is not set -# CONFIG_IP_SET_HASH_NETPORTNET is not set -CONFIG_IP_SET_HASH_NET=y -# CONFIG_IP_SET_HASH_NETNET is not set -CONFIG_IP_SET_HASH_NETPORT=y -CONFIG_IP_SET_HASH_NETIFACE=y -CONFIG_IP_SET_LIST_SET=y -CONFIG_IP_VS=y -CONFIG_IP_VS_IPV6=y -CONFIG_IP_VS_DEBUG=y -CONFIG_IP_VS_TAB_BITS=12 - -# -# IPVS transport protocol load balancing support -# -CONFIG_IP_VS_PROTO_TCP=y -CONFIG_IP_VS_PROTO_UDP=y -CONFIG_IP_VS_PROTO_AH_ESP=y -CONFIG_IP_VS_PROTO_ESP=y -CONFIG_IP_VS_PROTO_AH=y -CONFIG_IP_VS_PROTO_SCTP=y - -# -# IPVS scheduler -# -CONFIG_IP_VS_RR=y -CONFIG_IP_VS_WRR=y -CONFIG_IP_VS_LC=y -CONFIG_IP_VS_WLC=y -CONFIG_IP_VS_FO=y -CONFIG_IP_VS_OVF=y -CONFIG_IP_VS_LBLC=y -CONFIG_IP_VS_LBLCR=y -CONFIG_IP_VS_DH=y -CONFIG_IP_VS_SH=y -# CONFIG_IP_VS_MH is not set -CONFIG_IP_VS_SED=y -CONFIG_IP_VS_NQ=y - -# -# IPVS SH scheduler -# -CONFIG_IP_VS_SH_TAB_BITS=8 - -# -# IPVS MH scheduler -# -CONFIG_IP_VS_MH_TAB_INDEX=12 - -# -# IPVS application helper -# -CONFIG_IP_VS_FTP=y -CONFIG_IP_VS_NFCT=y -# CONFIG_IP_VS_PE_SIP is not set - -# -# IP: Netfilter Configuration -# -CONFIG_NF_DEFRAG_IPV4=y -CONFIG_NF_SOCKET_IPV4=y -CONFIG_NF_TPROXY_IPV4=y -CONFIG_NF_TABLES_IPV4=y -CONFIG_NFT_REJECT_IPV4=y -CONFIG_NFT_DUP_IPV4=y -# CONFIG_NFT_FIB_IPV4 is not set -CONFIG_NF_TABLES_ARP=y -CONFIG_NF_DUP_IPV4=y -CONFIG_NF_LOG_ARP=y -CONFIG_NF_LOG_IPV4=y -CONFIG_NF_REJECT_IPV4=y -CONFIG_NF_NAT_SNMP_BASIC=y -CONFIG_NF_NAT_PPTP=y -CONFIG_NF_NAT_H323=y -CONFIG_IP_NF_IPTABLES=y -CONFIG_IP_NF_MATCH_AH=y -CONFIG_IP_NF_MATCH_ECN=y -CONFIG_IP_NF_MATCH_RPFILTER=y -CONFIG_IP_NF_MATCH_TTL=y -CONFIG_IP_NF_FILTER=y -CONFIG_IP_NF_TARGET_REJECT=y -CONFIG_IP_NF_TARGET_SYNPROXY=y -CONFIG_IP_NF_NAT=y -CONFIG_IP_NF_TARGET_MASQUERADE=y -CONFIG_IP_NF_TARGET_NETMAP=y -CONFIG_IP_NF_TARGET_REDIRECT=y -CONFIG_IP_NF_MANGLE=y -CONFIG_IP_NF_TARGET_CLUSTERIP=y -CONFIG_IP_NF_TARGET_ECN=y -CONFIG_IP_NF_TARGET_TTL=y -CONFIG_IP_NF_RAW=y -CONFIG_IP_NF_SECURITY=y -CONFIG_IP_NF_ARPTABLES=y -CONFIG_IP_NF_ARPFILTER=y -CONFIG_IP_NF_ARP_MANGLE=y -# end of IP: Netfilter Configuration - -# -# IPv6: Netfilter Configuration -# -CONFIG_NF_SOCKET_IPV6=y -CONFIG_NF_TPROXY_IPV6=y -CONFIG_NF_TABLES_IPV6=y -CONFIG_NFT_REJECT_IPV6=y -CONFIG_NFT_DUP_IPV6=y -# CONFIG_NFT_FIB_IPV6 is not set -CONFIG_NF_DUP_IPV6=y -CONFIG_NF_REJECT_IPV6=y -CONFIG_NF_LOG_IPV6=y -CONFIG_IP6_NF_IPTABLES=y -CONFIG_IP6_NF_MATCH_AH=y -CONFIG_IP6_NF_MATCH_EUI64=y -CONFIG_IP6_NF_MATCH_FRAG=y -CONFIG_IP6_NF_MATCH_OPTS=y -CONFIG_IP6_NF_MATCH_HL=y -CONFIG_IP6_NF_MATCH_IPV6HEADER=y -CONFIG_IP6_NF_MATCH_MH=y -CONFIG_IP6_NF_MATCH_RPFILTER=y -CONFIG_IP6_NF_MATCH_RT=y -# CONFIG_IP6_NF_MATCH_SRH is not set -CONFIG_IP6_NF_TARGET_HL=y -CONFIG_IP6_NF_FILTER=y -CONFIG_IP6_NF_TARGET_REJECT=y -CONFIG_IP6_NF_TARGET_SYNPROXY=y -CONFIG_IP6_NF_MANGLE=y -CONFIG_IP6_NF_RAW=y -CONFIG_IP6_NF_SECURITY=y -CONFIG_IP6_NF_NAT=y -CONFIG_IP6_NF_TARGET_MASQUERADE=y -CONFIG_IP6_NF_TARGET_NPT=y -# end of IPv6: Netfilter Configuration - -CONFIG_NF_DEFRAG_IPV6=y -CONFIG_NF_TABLES_BRIDGE=y -CONFIG_NFT_BRIDGE_META=y -CONFIG_NFT_BRIDGE_REJECT=y -CONFIG_NF_LOG_BRIDGE=y -# CONFIG_NF_CONNTRACK_BRIDGE is not set -CONFIG_BRIDGE_NF_EBTABLES=y -CONFIG_BRIDGE_EBT_BROUTE=y -CONFIG_BRIDGE_EBT_T_FILTER=y -CONFIG_BRIDGE_EBT_T_NAT=y -CONFIG_BRIDGE_EBT_802_3=y -CONFIG_BRIDGE_EBT_AMONG=y -CONFIG_BRIDGE_EBT_ARP=y -CONFIG_BRIDGE_EBT_IP=y -CONFIG_BRIDGE_EBT_IP6=y -CONFIG_BRIDGE_EBT_LIMIT=y -CONFIG_BRIDGE_EBT_MARK=y -CONFIG_BRIDGE_EBT_PKTTYPE=y -CONFIG_BRIDGE_EBT_STP=y -CONFIG_BRIDGE_EBT_VLAN=y -CONFIG_BRIDGE_EBT_ARPREPLY=y -CONFIG_BRIDGE_EBT_DNAT=y -CONFIG_BRIDGE_EBT_MARK_T=y -CONFIG_BRIDGE_EBT_REDIRECT=y -CONFIG_BRIDGE_EBT_SNAT=y -CONFIG_BRIDGE_EBT_LOG=y -CONFIG_BRIDGE_EBT_NFLOG=y -# CONFIG_BPFILTER is not set -# CONFIG_IP_DCCP is not set -# CONFIG_IP_SCTP is not set -# CONFIG_RDS is not set -# CONFIG_TIPC is not set -# CONFIG_ATM is not set -# CONFIG_L2TP is not set -CONFIG_STP=y -CONFIG_BRIDGE=y -CONFIG_BRIDGE_IGMP_SNOOPING=y -CONFIG_BRIDGE_VLAN_FILTERING=y -# CONFIG_BRIDGE_MRP is not set -CONFIG_HAVE_NET_DSA=y -# CONFIG_NET_DSA is not set -CONFIG_VLAN_8021Q=y -# CONFIG_VLAN_8021Q_GVRP is not set -# CONFIG_VLAN_8021Q_MVRP is not set -# CONFIG_DECNET is not set -CONFIG_LLC=y -# CONFIG_LLC2 is not set -# CONFIG_ATALK is not set -# CONFIG_X25 is not set -# CONFIG_LAPB is not set -# CONFIG_PHONET is not set -# CONFIG_6LOWPAN is not set -# CONFIG_IEEE802154 is not set -# CONFIG_NET_SCHED is not set -# CONFIG_DCB is not set -CONFIG_DNS_RESOLVER=y -# CONFIG_BATMAN_ADV is not set -# CONFIG_OPENVSWITCH is not set -# CONFIG_VSOCKETS is not set -# CONFIG_NETLINK_DIAG is not set -# CONFIG_MPLS is not set -# CONFIG_NET_NSH is not set -# CONFIG_HSR is not set -# CONFIG_NET_SWITCHDEV is not set -# CONFIG_NET_L3_MASTER_DEV is not set -# CONFIG_QRTR is not set -# CONFIG_NET_NCSI is not set -CONFIG_RPS=y -CONFIG_RFS_ACCEL=y -CONFIG_XPS=y -CONFIG_CGROUP_NET_PRIO=y -CONFIG_CGROUP_NET_CLASSID=y -CONFIG_NET_RX_BUSY_POLL=y -CONFIG_BQL=y -# CONFIG_BPF_JIT is not set -CONFIG_NET_FLOW_LIMIT=y - -# -# Network testing -# -# CONFIG_NET_PKTGEN is not set -# CONFIG_NET_DROP_MONITOR is not set -# end of Network testing -# end of Networking options - -# CONFIG_HAMRADIO is not set -# CONFIG_CAN is not set -# CONFIG_BT is not set -# CONFIG_AF_RXRPC is not set -# CONFIG_AF_KCM is not set -# CONFIG_WIRELESS is not set -# CONFIG_WIMAX is not set -# CONFIG_RFKILL is not set -# CONFIG_NET_9P is not set -# CONFIG_CAIF is not set -# CONFIG_CEPH_LIB is not set -# CONFIG_NFC is not set -# CONFIG_PSAMPLE is not set -# CONFIG_NET_IFE is not set -# CONFIG_LWTUNNEL is not set -# CONFIG_FAILOVER is not set -CONFIG_ETHTOOL_NETLINK=y -CONFIG_HAVE_EBPF_JIT=y - -# -# Device Drivers -# -CONFIG_HAVE_EISA=y -# CONFIG_EISA is not set -CONFIG_HAVE_PCI=y -CONFIG_PCI=y -CONFIG_PCI_DOMAINS=y -CONFIG_PCIEPORTBUS=y -# CONFIG_PCIEAER is not set -CONFIG_PCIEASPM=y -CONFIG_PCIEASPM_DEFAULT=y -# CONFIG_PCIEASPM_POWERSAVE is not set -# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set -# CONFIG_PCIEASPM_PERFORMANCE is not set -CONFIG_PCIE_PME=y -# CONFIG_PCIE_PTM is not set -CONFIG_PCI_MSI=y -CONFIG_PCI_MSI_IRQ_DOMAIN=y -CONFIG_PCI_QUIRKS=y -# CONFIG_PCI_DEBUG is not set -# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set -CONFIG_PCI_STUB=y -# CONFIG_PCI_PF_STUB is not set -CONFIG_PCI_ATS=y -CONFIG_PCI_LOCKLESS_CONFIG=y -CONFIG_PCI_IOV=y -CONFIG_PCI_PRI=y -CONFIG_PCI_PASID=y -CONFIG_PCI_LABEL=y -# CONFIG_HOTPLUG_PCI is not set - -# -# PCI controller drivers -# -# CONFIG_VMD is not set - -# -# DesignWare PCI Core Support -# -# CONFIG_PCIE_DW_PLAT_HOST is not set -# CONFIG_PCI_MESON is not set -# end of DesignWare PCI Core Support - -# -# Mobiveil PCIe Core Support -# -# end of Mobiveil PCIe Core Support - -# -# Cadence PCIe controllers support -# -# end of Cadence PCIe controllers support -# end of PCI controller drivers - -# -# PCI Endpoint -# -# CONFIG_PCI_ENDPOINT is not set -# end of PCI Endpoint - -# -# PCI switch controller drivers -# -# CONFIG_PCI_SW_SWITCHTEC is not set -# end of PCI switch controller drivers - -# CONFIG_PCCARD is not set -# CONFIG_RAPIDIO is not set - -# -# Generic Driver Options -# -CONFIG_UEVENT_HELPER=y -CONFIG_UEVENT_HELPER_PATH="" -CONFIG_DEVTMPFS=y -# CONFIG_DEVTMPFS_MOUNT is not set -CONFIG_STANDALONE=y -CONFIG_PREVENT_FIRMWARE_BUILD=y - -# -# Firmware loader -# -CONFIG_FW_LOADER=y -CONFIG_EXTRA_FIRMWARE="" -# CONFIG_FW_LOADER_USER_HELPER is not set -# CONFIG_FW_LOADER_COMPRESS is not set -# end of Firmware loader - -CONFIG_ALLOW_DEV_COREDUMP=y -# CONFIG_DEBUG_DRIVER is not set -# CONFIG_DEBUG_DEVRES is not set -# CONFIG_DEBUG_TEST_DRIVER_REMOVE is not set -# CONFIG_TEST_ASYNC_DRIVER_PROBE is not set -CONFIG_GENERIC_CPU_AUTOPROBE=y -CONFIG_GENERIC_CPU_VULNERABILITIES=y -CONFIG_DMA_SHARED_BUFFER=y -# CONFIG_DMA_FENCE_TRACE is not set -# end of Generic Driver Options - -# -# Bus devices -# -# CONFIG_MHI_BUS is not set -# end of Bus devices - -CONFIG_CONNECTOR=y -CONFIG_PROC_EVENTS=y -# CONFIG_GNSS is not set -# CONFIG_MTD is not set -# CONFIG_OF is not set -CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y -# CONFIG_PARPORT is not set -CONFIG_PNP=y -# CONFIG_PNP_DEBUG_MESSAGES is not set - -# -# Protocols -# -CONFIG_PNPACPI=y -CONFIG_BLK_DEV=y -# CONFIG_BLK_DEV_NULL_BLK is not set -# CONFIG_BLK_DEV_FD is not set -# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set -# CONFIG_BLK_DEV_UMEM is not set -CONFIG_BLK_DEV_LOOP=y -CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 -# CONFIG_BLK_DEV_CRYPTOLOOP is not set -# CONFIG_BLK_DEV_DRBD is not set -# CONFIG_BLK_DEV_NBD is not set -# CONFIG_BLK_DEV_SKD is not set -# CONFIG_BLK_DEV_SX8 is not set -# CONFIG_BLK_DEV_RAM is not set -# CONFIG_CDROM_PKTCDVD is not set -# CONFIG_ATA_OVER_ETH is not set -# CONFIG_BLK_DEV_RBD is not set -# CONFIG_BLK_DEV_RSXX is not set - -# -# NVME Support -# -# CONFIG_BLK_DEV_NVME is not set -# CONFIG_NVME_FC is not set -# CONFIG_NVME_TCP is not set -# end of NVME Support - -# -# Misc devices -# -# CONFIG_AD525X_DPOT is not set -# CONFIG_DUMMY_IRQ is not set -# CONFIG_IBM_ASM is not set -# CONFIG_PHANTOM is not set -# CONFIG_TIFM_CORE is not set -# CONFIG_ICS932S401 is not set -# CONFIG_ENCLOSURE_SERVICES is not set -# CONFIG_HP_ILO is not set -# CONFIG_APDS9802ALS is not set -# CONFIG_ISL29003 is not set -# CONFIG_ISL29020 is not set -# CONFIG_SENSORS_TSL2550 is not set -# CONFIG_SENSORS_BH1770 is not set -# CONFIG_SENSORS_APDS990X is not set -# CONFIG_HMC6352 is not set -# CONFIG_DS1682 is not set -# CONFIG_SRAM is not set -# CONFIG_PCI_ENDPOINT_TEST is not set -# CONFIG_XILINX_SDFEC is not set -# CONFIG_PVPANIC is not set -# CONFIG_C2PORT is not set - -# -# EEPROM support -# -# CONFIG_EEPROM_AT24 is not set -# CONFIG_EEPROM_LEGACY is not set -# CONFIG_EEPROM_MAX6875 is not set -# CONFIG_EEPROM_93CX6 is not set -# CONFIG_EEPROM_IDT_89HPESX is not set -# CONFIG_EEPROM_EE1004 is not set -# end of EEPROM support - -# CONFIG_CB710_CORE is not set - -# -# Texas Instruments shared transport line discipline -# -# end of Texas Instruments shared transport line discipline - -# CONFIG_SENSORS_LIS3_I2C is not set -# CONFIG_ALTERA_STAPL is not set -# CONFIG_INTEL_MEI is not set -# CONFIG_INTEL_MEI_ME is not set -# CONFIG_INTEL_MEI_TXE is not set -# CONFIG_INTEL_MEI_HDCP is not set -# CONFIG_VMWARE_VMCI is not set -# CONFIG_GENWQE is not set -# CONFIG_ECHO is not set -# CONFIG_MISC_ALCOR_PCI is not set -# CONFIG_MISC_RTSX_PCI is not set -# CONFIG_MISC_RTSX_USB is not set -# CONFIG_HABANA_AI is not set -# end of Misc devices - -CONFIG_HAVE_IDE=y -# CONFIG_IDE is not set - -# -# SCSI device support -# -CONFIG_SCSI_MOD=y -CONFIG_RAID_ATTRS=m -CONFIG_SCSI=y -CONFIG_SCSI_DMA=y -# CONFIG_SCSI_PROC_FS is not set - -# -# SCSI support type (disk, tape, CD-ROM) -# -CONFIG_BLK_DEV_SD=y -# CONFIG_CHR_DEV_ST is not set -# CONFIG_BLK_DEV_SR is not set -CONFIG_CHR_DEV_SG=y -# CONFIG_CHR_DEV_SCH is not set -# CONFIG_SCSI_CONSTANTS is not set -# CONFIG_SCSI_LOGGING is not set -# CONFIG_SCSI_SCAN_ASYNC is not set - -# -# SCSI Transports -# -CONFIG_SCSI_SPI_ATTRS=y -# CONFIG_SCSI_FC_ATTRS is not set -# CONFIG_SCSI_ISCSI_ATTRS is not set -# CONFIG_SCSI_SAS_ATTRS is not set -# CONFIG_SCSI_SAS_LIBSAS is not set -# CONFIG_SCSI_SRP_ATTRS is not set -# end of SCSI Transports - -# CONFIG_SCSI_LOWLEVEL is not set -# CONFIG_SCSI_DH is not set -# end of SCSI device support - -CONFIG_ATA=y -CONFIG_SATA_HOST=y -CONFIG_PATA_TIMINGS=y -# CONFIG_ATA_VERBOSE_ERROR is not set -CONFIG_ATA_FORCE=y -CONFIG_ATA_ACPI=y -# CONFIG_SATA_ZPODD is not set -# CONFIG_SATA_PMP is not set - -# -# Controllers with non-SFF native interface -# -CONFIG_SATA_AHCI=y -CONFIG_SATA_MOBILE_LPM_POLICY=0 -# CONFIG_SATA_AHCI_PLATFORM is not set -# CONFIG_SATA_INIC162X is not set -# CONFIG_SATA_ACARD_AHCI is not set -# CONFIG_SATA_SIL24 is not set -CONFIG_ATA_SFF=y - -# -# SFF controllers with custom DMA interface -# -# CONFIG_PDC_ADMA is not set -# CONFIG_SATA_QSTOR is not set -# CONFIG_SATA_SX4 is not set -CONFIG_ATA_BMDMA=y - -# -# SATA SFF controllers with BMDMA -# -CONFIG_ATA_PIIX=y -CONFIG_SATA_MV=y -CONFIG_SATA_NV=y -CONFIG_SATA_PROMISE=y -CONFIG_SATA_SIL=y -CONFIG_SATA_SIS=y -CONFIG_SATA_SVW=y -CONFIG_SATA_ULI=y -CONFIG_SATA_VIA=y -CONFIG_SATA_VITESSE=y - -# -# PATA SFF controllers with BMDMA -# -# CONFIG_PATA_ALI is not set -# CONFIG_PATA_AMD is not set -# CONFIG_PATA_ARTOP is not set -# CONFIG_PATA_ATIIXP is not set -# CONFIG_PATA_ATP867X is not set -# CONFIG_PATA_CMD64X is not set -# CONFIG_PATA_CYPRESS is not set -# CONFIG_PATA_EFAR is not set -# CONFIG_PATA_HPT366 is not set -# CONFIG_PATA_HPT37X is not set -# CONFIG_PATA_HPT3X2N is not set -# CONFIG_PATA_HPT3X3 is not set -# CONFIG_PATA_IT8213 is not set -# CONFIG_PATA_IT821X is not set -# CONFIG_PATA_JMICRON is not set -# CONFIG_PATA_MARVELL is not set -# CONFIG_PATA_NETCELL is not set -# CONFIG_PATA_NINJA32 is not set -# CONFIG_PATA_NS87415 is not set -# CONFIG_PATA_OLDPIIX is not set -# CONFIG_PATA_OPTIDMA is not set -# CONFIG_PATA_PDC2027X is not set -# CONFIG_PATA_PDC_OLD is not set -# CONFIG_PATA_RADISYS is not set -# CONFIG_PATA_RDC is not set -# CONFIG_PATA_SCH is not set -# CONFIG_PATA_SERVERWORKS is not set -# CONFIG_PATA_SIL680 is not set -CONFIG_PATA_SIS=y -# CONFIG_PATA_TOSHIBA is not set -# CONFIG_PATA_TRIFLEX is not set -# CONFIG_PATA_VIA is not set -# CONFIG_PATA_WINBOND is not set - -# -# PIO-only SFF controllers -# -# CONFIG_PATA_CMD640_PCI is not set -# CONFIG_PATA_MPIIX is not set -# CONFIG_PATA_NS87410 is not set -# CONFIG_PATA_OPTI is not set -# CONFIG_PATA_RZ1000 is not set - -# -# Generic fallback / legacy drivers -# -# CONFIG_PATA_ACPI is not set -CONFIG_ATA_GENERIC=y -# CONFIG_PATA_LEGACY is not set -CONFIG_MD=y -CONFIG_BLK_DEV_MD=y -CONFIG_MD_AUTODETECT=y -# CONFIG_MD_LINEAR is not set -# CONFIG_MD_RAID0 is not set -CONFIG_MD_RAID1=y -# CONFIG_MD_RAID10 is not set -# CONFIG_MD_RAID456 is not set -# CONFIG_MD_MULTIPATH is not set -# CONFIG_MD_FAULTY is not set -# CONFIG_BCACHE is not set -CONFIG_BLK_DEV_DM_BUILTIN=y -CONFIG_BLK_DEV_DM=y -# CONFIG_DM_DEBUG is not set -# CONFIG_DM_UNSTRIPED is not set -CONFIG_DM_CRYPT=y -# CONFIG_DM_SNAPSHOT is not set -# CONFIG_DM_THIN_PROVISIONING is not set -# CONFIG_DM_CACHE is not set -# CONFIG_DM_WRITECACHE is not set -# CONFIG_DM_EBS is not set -# CONFIG_DM_ERA is not set -# CONFIG_DM_CLONE is not set -# CONFIG_DM_MIRROR is not set -# CONFIG_DM_RAID is not set -# CONFIG_DM_ZERO is not set -# CONFIG_DM_MULTIPATH is not set -# CONFIG_DM_DELAY is not set -# CONFIG_DM_DUST is not set -# CONFIG_DM_INIT is not set -# CONFIG_DM_UEVENT is not set -# CONFIG_DM_FLAKEY is not set -# CONFIG_DM_VERITY is not set -# CONFIG_DM_SWITCH is not set -# CONFIG_DM_LOG_WRITES is not set -# CONFIG_DM_INTEGRITY is not set -# CONFIG_TARGET_CORE is not set -CONFIG_FUSION=y -CONFIG_FUSION_SPI=y -# CONFIG_FUSION_SAS is not set -CONFIG_FUSION_MAX_SGE=128 -# CONFIG_FUSION_CTL is not set -# CONFIG_FUSION_LOGGING is not set - -# -# IEEE 1394 (FireWire) support -# -# CONFIG_FIREWIRE is not set -# CONFIG_FIREWIRE_NOSY is not set -# end of IEEE 1394 (FireWire) support - -# CONFIG_MACINTOSH_DRIVERS is not set -CONFIG_NETDEVICES=y -CONFIG_MII=y -CONFIG_NET_CORE=y -CONFIG_BONDING=y -# CONFIG_DUMMY is not set -# CONFIG_WIREGUARD is not set -# CONFIG_EQUALIZER is not set -# CONFIG_NET_FC is not set -# CONFIG_NET_TEAM is not set -CONFIG_MACVLAN=y -CONFIG_MACVTAP=y -# CONFIG_IPVLAN is not set -# CONFIG_VXLAN is not set -# CONFIG_GENEVE is not set -# CONFIG_BAREUDP is not set -# CONFIG_GTP is not set -# CONFIG_MACSEC is not set -# CONFIG_NETCONSOLE is not set -CONFIG_TUN=y -CONFIG_TAP=y -# CONFIG_TUN_VNET_CROSS_LE is not set -CONFIG_VETH=y -# CONFIG_NLMON is not set -# CONFIG_ARCNET is not set - -# -# Distributed Switch Architecture drivers -# -# end of Distributed Switch Architecture drivers - -CONFIG_ETHERNET=y -# CONFIG_NET_VENDOR_3COM is not set -# CONFIG_NET_VENDOR_ADAPTEC is not set -# CONFIG_NET_VENDOR_AGERE is not set -CONFIG_NET_VENDOR_ALACRITECH=y -# CONFIG_SLICOSS is not set -# CONFIG_NET_VENDOR_ALTEON is not set -# CONFIG_ALTERA_TSE is not set -# CONFIG_NET_VENDOR_AMAZON is not set -# CONFIG_NET_VENDOR_AMD is not set -# CONFIG_NET_VENDOR_AQUANTIA is not set -# CONFIG_NET_VENDOR_ARC is not set -# CONFIG_NET_VENDOR_ATHEROS is not set -# CONFIG_NET_VENDOR_AURORA is not set -# CONFIG_NET_VENDOR_BROADCOM is not set -# CONFIG_NET_VENDOR_BROCADE is not set -# CONFIG_NET_VENDOR_CADENCE is not set -# CONFIG_NET_VENDOR_CAVIUM is not set -# CONFIG_NET_VENDOR_CHELSIO is not set -# CONFIG_NET_VENDOR_CISCO is not set -# CONFIG_NET_VENDOR_CORTINA is not set -# CONFIG_CX_ECAT is not set -# CONFIG_DNET is not set -# CONFIG_NET_VENDOR_DEC is not set -# CONFIG_NET_VENDOR_DLINK is not set -# CONFIG_NET_VENDOR_EMULEX is not set -# CONFIG_NET_VENDOR_EZCHIP is not set -# CONFIG_NET_VENDOR_GOOGLE is not set -# CONFIG_NET_VENDOR_HUAWEI is not set -# CONFIG_NET_VENDOR_I825XX is not set -CONFIG_NET_VENDOR_INTEL=y -# CONFIG_E100 is not set -CONFIG_E1000=y -CONFIG_E1000E=y -CONFIG_E1000E_HWTS=y -CONFIG_IGB=y -CONFIG_IGB_HWMON=y -# CONFIG_IGBVF is not set -# CONFIG_IXGB is not set -# CONFIG_IXGBE is not set -# CONFIG_IXGBEVF is not set -# CONFIG_I40E is not set -# CONFIG_I40EVF is not set -# CONFIG_ICE is not set -# CONFIG_FM10K is not set -# CONFIG_IGC is not set -# CONFIG_JME is not set -# CONFIG_NET_VENDOR_MARVELL is not set -# CONFIG_NET_VENDOR_MELLANOX is not set -# CONFIG_NET_VENDOR_MICREL is not set -# CONFIG_NET_VENDOR_MICROCHIP is not set -# CONFIG_NET_VENDOR_MICROSEMI is not set -# CONFIG_NET_VENDOR_MYRI is not set -# CONFIG_FEALNX is not set -# CONFIG_NET_VENDOR_NATSEMI is not set -# CONFIG_NET_VENDOR_NETERION is not set -# CONFIG_NET_VENDOR_NETRONOME is not set -# CONFIG_NET_VENDOR_NI is not set -# CONFIG_NET_VENDOR_NVIDIA is not set -# CONFIG_NET_VENDOR_OKI is not set -# CONFIG_ETHOC is not set -# CONFIG_NET_VENDOR_PACKET_ENGINES is not set -# CONFIG_NET_VENDOR_PENSANDO is not set -# CONFIG_NET_VENDOR_QLOGIC is not set -# CONFIG_NET_VENDOR_QUALCOMM is not set -# CONFIG_NET_VENDOR_RDC is not set -CONFIG_NET_VENDOR_REALTEK=y -CONFIG_8139CP=y -# CONFIG_8139TOO is not set -CONFIG_R8169=y -# CONFIG_NET_VENDOR_RENESAS is not set -# CONFIG_NET_VENDOR_ROCKER is not set -# CONFIG_NET_VENDOR_SAMSUNG is not set -# CONFIG_NET_VENDOR_SEEQ is not set -# CONFIG_NET_VENDOR_SOLARFLARE is not set -# CONFIG_NET_VENDOR_SILAN is not set -# CONFIG_NET_VENDOR_SIS is not set -# CONFIG_NET_VENDOR_SMSC is not set -# CONFIG_NET_VENDOR_SOCIONEXT is not set -# CONFIG_NET_VENDOR_STMICRO is not set -# CONFIG_NET_VENDOR_SUN is not set -# CONFIG_NET_VENDOR_SYNOPSYS is not set -# CONFIG_NET_VENDOR_TEHUTI is not set -# CONFIG_NET_VENDOR_TI is not set -# CONFIG_NET_VENDOR_VIA is not set -# CONFIG_NET_VENDOR_WIZNET is not set -# CONFIG_NET_VENDOR_XILINX is not set -# CONFIG_FDDI is not set -# CONFIG_HIPPI is not set -# CONFIG_NET_SB1000 is not set -CONFIG_PHYLIB=y -# CONFIG_FIXED_PHY is not set - -# -# MII PHY device drivers -# -# CONFIG_AMD_PHY is not set -# CONFIG_ADIN_PHY is not set -# CONFIG_AQUANTIA_PHY is not set -# CONFIG_AX88796B_PHY is not set -# CONFIG_BROADCOM_PHY is not set -# CONFIG_BCM54140_PHY is not set -# CONFIG_BCM7XXX_PHY is not set -# CONFIG_BCM84881_PHY is not set -# CONFIG_BCM87XX_PHY is not set -# CONFIG_CICADA_PHY is not set -# CONFIG_CORTINA_PHY is not set -# CONFIG_DAVICOM_PHY is not set -# CONFIG_ICPLUS_PHY is not set -# CONFIG_LXT_PHY is not set -# CONFIG_INTEL_XWAY_PHY is not set -# CONFIG_LSI_ET1011C_PHY is not set -# CONFIG_MARVELL_PHY is not set -# CONFIG_MARVELL_10G_PHY is not set -# CONFIG_MICREL_PHY is not set -# CONFIG_MICROCHIP_PHY is not set -# CONFIG_MICROCHIP_T1_PHY is not set -# CONFIG_MICROSEMI_PHY is not set -# CONFIG_NATIONAL_PHY is not set -# CONFIG_NXP_TJA11XX_PHY is not set -# CONFIG_QSEMI_PHY is not set -CONFIG_REALTEK_PHY=y -# CONFIG_RENESAS_PHY is not set -# CONFIG_ROCKCHIP_PHY is not set -# CONFIG_SMSC_PHY is not set -# CONFIG_STE10XP is not set -# CONFIG_TERANETICS_PHY is not set -# CONFIG_DP83822_PHY is not set -# CONFIG_DP83TC811_PHY is not set -# CONFIG_DP83848_PHY is not set -# CONFIG_DP83867_PHY is not set -# CONFIG_DP83869_PHY is not set -# CONFIG_VITESSE_PHY is not set -# CONFIG_XILINX_GMII2RGMII is not set -CONFIG_MDIO_DEVICE=y -CONFIG_MDIO_BUS=y -CONFIG_MDIO_DEVRES=y -# CONFIG_MDIO_BITBANG is not set -# CONFIG_MDIO_BCM_UNIMAC is not set -# CONFIG_MDIO_MVUSB is not set -# CONFIG_MDIO_MSCC_MIIM is not set -# CONFIG_MDIO_THUNDER is not set - -# -# MDIO Multiplexers -# - -# -# PCS device drivers -# -# CONFIG_PCS_XPCS is not set -# end of PCS device drivers - -# CONFIG_PPP is not set -# CONFIG_SLIP is not set -# CONFIG_USB_NET_DRIVERS is not set -# CONFIG_WLAN is not set - -# -# Enable WiMAX (Networking options) to see the WiMAX drivers -# -# CONFIG_WAN is not set -# CONFIG_VMXNET3 is not set -# CONFIG_FUJITSU_ES is not set -# CONFIG_NETDEVSIM is not set -# CONFIG_NET_FAILOVER is not set -# CONFIG_ISDN is not set -# CONFIG_NVM is not set - -# -# Input device support -# -CONFIG_INPUT=y -CONFIG_INPUT_FF_MEMLESS=y -CONFIG_INPUT_POLLDEV=y -CONFIG_INPUT_SPARSEKMAP=y -# CONFIG_INPUT_MATRIXKMAP is not set - -# -# Userland interfaces -# -CONFIG_INPUT_MOUSEDEV=y -CONFIG_INPUT_MOUSEDEV_PSAUX=y -CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 -CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 -CONFIG_INPUT_JOYDEV=y -CONFIG_INPUT_EVDEV=y -# CONFIG_INPUT_EVBUG is not set - -# -# Input Device Drivers -# -CONFIG_INPUT_KEYBOARD=y -# CONFIG_KEYBOARD_ADP5588 is not set -# CONFIG_KEYBOARD_ADP5589 is not set -CONFIG_KEYBOARD_ATKBD=y -# CONFIG_KEYBOARD_QT1050 is not set -# CONFIG_KEYBOARD_QT1070 is not set -# CONFIG_KEYBOARD_QT2160 is not set -# CONFIG_KEYBOARD_DLINK_DIR685 is not set -# CONFIG_KEYBOARD_LKKBD is not set -# CONFIG_KEYBOARD_TCA6416 is not set -# CONFIG_KEYBOARD_TCA8418 is not set -# CONFIG_KEYBOARD_LM8333 is not set -# CONFIG_KEYBOARD_MAX7359 is not set -# CONFIG_KEYBOARD_MCS is not set -# CONFIG_KEYBOARD_MPR121 is not set -# CONFIG_KEYBOARD_NEWTON is not set -# CONFIG_KEYBOARD_OPENCORES is not set -# CONFIG_KEYBOARD_SAMSUNG is not set -# CONFIG_KEYBOARD_STOWAWAY is not set -# CONFIG_KEYBOARD_SUNKBD is not set -# CONFIG_KEYBOARD_XTKBD is not set -# CONFIG_INPUT_MOUSE is not set -# CONFIG_INPUT_JOYSTICK is not set -# CONFIG_INPUT_TABLET is not set -# CONFIG_INPUT_TOUCHSCREEN is not set -CONFIG_INPUT_MISC=y -# CONFIG_INPUT_AD714X is not set -# CONFIG_INPUT_BMA150 is not set -# CONFIG_INPUT_E3X0_BUTTON is not set -CONFIG_INPUT_PCSPKR=y -# CONFIG_INPUT_MMA8450 is not set -CONFIG_INPUT_ATLAS_BTNS=y -# CONFIG_INPUT_ATI_REMOTE2 is not set -# CONFIG_INPUT_KEYSPAN_REMOTE is not set -# CONFIG_INPUT_KXTJ9 is not set -# CONFIG_INPUT_POWERMATE is not set -# CONFIG_INPUT_YEALINK is not set -# CONFIG_INPUT_CM109 is not set -CONFIG_INPUT_UINPUT=y -# CONFIG_INPUT_PCF8574 is not set -# CONFIG_INPUT_ADXL34X is not set -# CONFIG_INPUT_IQS269A is not set -# CONFIG_INPUT_CMA3000 is not set -# CONFIG_INPUT_IDEAPAD_SLIDEBAR is not set -# CONFIG_INPUT_DRV2665_HAPTICS is not set -# CONFIG_INPUT_DRV2667_HAPTICS is not set -# CONFIG_RMI4_CORE is not set - -# -# Hardware I/O ports -# -CONFIG_SERIO=y -CONFIG_ARCH_MIGHT_HAVE_PC_SERIO=y -CONFIG_SERIO_I8042=y -CONFIG_SERIO_SERPORT=y -# CONFIG_SERIO_CT82C710 is not set -CONFIG_SERIO_PCIPS2=y -CONFIG_SERIO_LIBPS2=y -CONFIG_SERIO_RAW=y -# CONFIG_SERIO_ALTERA_PS2 is not set -# CONFIG_SERIO_PS2MULT is not set -# CONFIG_SERIO_ARC_PS2 is not set -# CONFIG_USERIO is not set -# CONFIG_GAMEPORT is not set -# end of Hardware I/O ports -# end of Input device support - -# -# Character devices -# -CONFIG_TTY=y -CONFIG_VT=y -CONFIG_CONSOLE_TRANSLATIONS=y -CONFIG_VT_CONSOLE=y -CONFIG_HW_CONSOLE=y -CONFIG_VT_HW_CONSOLE_BINDING=y -CONFIG_UNIX98_PTYS=y -# CONFIG_LEGACY_PTYS is not set -CONFIG_LDISC_AUTOLOAD=y - -# -# Serial drivers -# -CONFIG_SERIAL_EARLYCON=y -CONFIG_SERIAL_8250=y -CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y -CONFIG_SERIAL_8250_PNP=y -# CONFIG_SERIAL_8250_16550A_VARIANTS is not set -# CONFIG_SERIAL_8250_FINTEK is not set -CONFIG_SERIAL_8250_CONSOLE=y -CONFIG_SERIAL_8250_PCI=y -CONFIG_SERIAL_8250_EXAR=y -CONFIG_SERIAL_8250_NR_UARTS=32 -CONFIG_SERIAL_8250_RUNTIME_UARTS=4 -# CONFIG_SERIAL_8250_EXTENDED is not set -CONFIG_SERIAL_8250_DWLIB=y -# CONFIG_SERIAL_8250_DW is not set -# CONFIG_SERIAL_8250_RT288X is not set -CONFIG_SERIAL_8250_LPSS=y -CONFIG_SERIAL_8250_MID=y - -# -# Non-8250 serial port support -# -# CONFIG_SERIAL_UARTLITE is not set -CONFIG_SERIAL_CORE=y -CONFIG_SERIAL_CORE_CONSOLE=y -# CONFIG_SERIAL_JSM is not set -# CONFIG_SERIAL_LANTIQ is not set -# CONFIG_SERIAL_SCCNXP is not set -# CONFIG_SERIAL_SC16IS7XX is not set -# CONFIG_SERIAL_ALTERA_JTAGUART is not set -# CONFIG_SERIAL_ALTERA_UART is not set -# CONFIG_SERIAL_ARC is not set -# CONFIG_SERIAL_RP2 is not set -# CONFIG_SERIAL_FSL_LPUART is not set -# CONFIG_SERIAL_FSL_LINFLEXUART is not set -# CONFIG_SERIAL_SPRD is not set -# end of Serial drivers - -# CONFIG_SERIAL_NONSTANDARD is not set -# CONFIG_N_GSM is not set -# CONFIG_NOZOMI is not set -# CONFIG_NULL_TTY is not set -# CONFIG_TRACE_SINK is not set -# CONFIG_SERIAL_DEV_BUS is not set -# CONFIG_VIRTIO_CONSOLE is not set -# CONFIG_IPMI_HANDLER is not set -CONFIG_HW_RANDOM=y -CONFIG_HW_RANDOM_TIMERIOMEM=y -CONFIG_HW_RANDOM_INTEL=y -# CONFIG_HW_RANDOM_AMD is not set -# CONFIG_HW_RANDOM_BA431 is not set -# CONFIG_HW_RANDOM_VIA is not set -# CONFIG_HW_RANDOM_XIPHERA is not set -# CONFIG_APPLICOM is not set -# CONFIG_MWAVE is not set -# CONFIG_DEVMEM is not set -# CONFIG_DEVKMEM is not set -CONFIG_NVRAM=y -# CONFIG_RAW_DRIVER is not set -CONFIG_DEVPORT=y -CONFIG_HPET=y -CONFIG_HPET_MMAP=y -CONFIG_HPET_MMAP_DEFAULT=y -CONFIG_HANGCHECK_TIMER=y -CONFIG_TCG_TPM=y -CONFIG_HW_RANDOM_TPM=y -CONFIG_TCG_TIS_CORE=y -CONFIG_TCG_TIS=y -# CONFIG_TCG_TIS_I2C_ATMEL is not set -CONFIG_TCG_TIS_I2C_INFINEON=y -# CONFIG_TCG_TIS_I2C_NUVOTON is not set -# CONFIG_TCG_NSC is not set -# CONFIG_TCG_ATMEL is not set -# CONFIG_TCG_INFINEON is not set -# CONFIG_TCG_CRB is not set -# CONFIG_TCG_VTPM_PROXY is not set -# CONFIG_TCG_TIS_ST33ZP24_I2C is not set -# CONFIG_TELCLOCK is not set -# CONFIG_XILLYBUS is not set -# end of Character devices - -# CONFIG_RANDOM_TRUST_CPU is not set -# CONFIG_RANDOM_TRUST_BOOTLOADER is not set - -# -# I2C support -# -CONFIG_I2C=y -CONFIG_ACPI_I2C_OPREGION=y -CONFIG_I2C_BOARDINFO=y -CONFIG_I2C_COMPAT=y -CONFIG_I2C_CHARDEV=y -CONFIG_I2C_MUX=y - -# -# Multiplexer I2C Chip support -# -# CONFIG_I2C_MUX_LTC4306 is not set -# CONFIG_I2C_MUX_PCA9541 is not set -# CONFIG_I2C_MUX_REG is not set -# CONFIG_I2C_MUX_MLXCPLD is not set -# end of Multiplexer I2C Chip support - -CONFIG_I2C_HELPER_AUTO=y -CONFIG_I2C_ALGOBIT=y - -# -# I2C Hardware Bus support -# - -# -# PC SMBus host controller drivers -# -# CONFIG_I2C_ALI1535 is not set -# CONFIG_I2C_ALI1563 is not set -# CONFIG_I2C_ALI15X3 is not set -# CONFIG_I2C_AMD756 is not set -# CONFIG_I2C_AMD8111 is not set -# CONFIG_I2C_AMD_MP2 is not set -# CONFIG_I2C_I801 is not set -# CONFIG_I2C_ISCH is not set -# CONFIG_I2C_ISMT is not set -# CONFIG_I2C_PIIX4 is not set -# CONFIG_I2C_NFORCE2 is not set -# CONFIG_I2C_NVIDIA_GPU is not set -# CONFIG_I2C_SIS5595 is not set -# CONFIG_I2C_SIS630 is not set -# CONFIG_I2C_SIS96X is not set -# CONFIG_I2C_VIA is not set -# CONFIG_I2C_VIAPRO is not set - -# -# ACPI drivers -# -# CONFIG_I2C_SCMI is not set - -# -# I2C system bus drivers (mostly embedded / system-on-chip) -# -# CONFIG_I2C_DESIGNWARE_PLATFORM is not set -# CONFIG_I2C_DESIGNWARE_PCI is not set -# CONFIG_I2C_EMEV2 is not set -# CONFIG_I2C_OCORES is not set -# CONFIG_I2C_PCA_PLATFORM is not set -# CONFIG_I2C_SIMTEC is not set -# CONFIG_I2C_XILINX is not set - -# -# External I2C/SMBus adapter drivers -# -# CONFIG_I2C_DIOLAN_U2C is not set -# CONFIG_I2C_ROBOTFUZZ_OSIF is not set -# CONFIG_I2C_TAOS_EVM is not set -# CONFIG_I2C_TINY_USB is not set - -# -# Other I2C/SMBus bus drivers -# -# CONFIG_I2C_MLXCPLD is not set -# end of I2C Hardware Bus support - -# CONFIG_I2C_STUB is not set -# CONFIG_I2C_SLAVE is not set -# CONFIG_I2C_DEBUG_CORE is not set -# CONFIG_I2C_DEBUG_ALGO is not set -# CONFIG_I2C_DEBUG_BUS is not set -# end of I2C support - -# CONFIG_I3C is not set -# CONFIG_SPI is not set -# CONFIG_SPMI is not set -# CONFIG_HSI is not set -CONFIG_PPS=y -# CONFIG_PPS_DEBUG is not set - -# -# PPS clients support -# -# CONFIG_PPS_CLIENT_KTIMER is not set -# CONFIG_PPS_CLIENT_LDISC is not set -# CONFIG_PPS_CLIENT_GPIO is not set - -# -# PPS generators support -# - -# -# PTP clock support -# -CONFIG_PTP_1588_CLOCK=y - -# -# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. -# -# CONFIG_PTP_1588_CLOCK_IDT82P33 is not set -# CONFIG_PTP_1588_CLOCK_IDTCM is not set -# end of PTP clock support - -# CONFIG_PINCTRL is not set -# CONFIG_GPIOLIB is not set -# CONFIG_W1 is not set -# CONFIG_POWER_RESET is not set -CONFIG_POWER_SUPPLY=y -# CONFIG_POWER_SUPPLY_DEBUG is not set -CONFIG_POWER_SUPPLY_HWMON=y -# CONFIG_PDA_POWER is not set -# CONFIG_TEST_POWER is not set -# CONFIG_CHARGER_ADP5061 is not set -# CONFIG_BATTERY_CW2015 is not set -# CONFIG_BATTERY_DS2780 is not set -# CONFIG_BATTERY_DS2781 is not set -# CONFIG_BATTERY_DS2782 is not set -# CONFIG_BATTERY_SBS is not set -# CONFIG_CHARGER_SBS is not set -# CONFIG_BATTERY_BQ27XXX is not set -# CONFIG_BATTERY_MAX17040 is not set -# CONFIG_BATTERY_MAX17042 is not set -# CONFIG_CHARGER_MAX8903 is not set -# CONFIG_CHARGER_LP8727 is not set -# CONFIG_CHARGER_BQ2415X is not set -# CONFIG_CHARGER_SMB347 is not set -# CONFIG_BATTERY_GAUGE_LTC2941 is not set -# CONFIG_BATTERY_RT5033 is not set -# CONFIG_CHARGER_BD99954 is not set -CONFIG_HWMON=y -# CONFIG_HWMON_DEBUG_CHIP is not set - -# -# Native drivers -# -# CONFIG_SENSORS_ABITUGURU is not set -# CONFIG_SENSORS_ABITUGURU3 is not set -# CONFIG_SENSORS_AD7414 is not set -# CONFIG_SENSORS_AD7418 is not set -# CONFIG_SENSORS_ADM1021 is not set -# CONFIG_SENSORS_ADM1025 is not set -# CONFIG_SENSORS_ADM1026 is not set -# CONFIG_SENSORS_ADM1029 is not set -# CONFIG_SENSORS_ADM1031 is not set -# CONFIG_SENSORS_ADM1177 is not set -# CONFIG_SENSORS_ADM9240 is not set -# CONFIG_SENSORS_ADT7410 is not set -# CONFIG_SENSORS_ADT7411 is not set -# CONFIG_SENSORS_ADT7462 is not set -# CONFIG_SENSORS_ADT7470 is not set -# CONFIG_SENSORS_ADT7475 is not set -# CONFIG_SENSORS_AS370 is not set -# CONFIG_SENSORS_ASC7621 is not set -# CONFIG_SENSORS_AXI_FAN_CONTROL is not set -# CONFIG_SENSORS_K8TEMP is not set -# CONFIG_SENSORS_K10TEMP is not set -# CONFIG_SENSORS_FAM15H_POWER is not set -# CONFIG_SENSORS_AMD_ENERGY is not set -# CONFIG_SENSORS_APPLESMC is not set -# CONFIG_SENSORS_ASB100 is not set -# CONFIG_SENSORS_ASPEED is not set -# CONFIG_SENSORS_ATXP1 is not set -# CONFIG_SENSORS_CORSAIR_CPRO is not set -# CONFIG_SENSORS_DRIVETEMP is not set -# CONFIG_SENSORS_DS620 is not set -# CONFIG_SENSORS_DS1621 is not set -# CONFIG_SENSORS_DELL_SMM is not set -# CONFIG_SENSORS_I5K_AMB is not set -# CONFIG_SENSORS_F71805F is not set -# CONFIG_SENSORS_F71882FG is not set -# CONFIG_SENSORS_F75375S is not set -# CONFIG_SENSORS_FSCHMD is not set -# CONFIG_SENSORS_GL518SM is not set -# CONFIG_SENSORS_GL520SM is not set -# CONFIG_SENSORS_G760A is not set -# CONFIG_SENSORS_G762 is not set -# CONFIG_SENSORS_HIH6130 is not set -# CONFIG_SENSORS_I5500 is not set -# CONFIG_SENSORS_CORETEMP is not set -# CONFIG_SENSORS_IT87 is not set -# CONFIG_SENSORS_JC42 is not set -# CONFIG_SENSORS_POWR1220 is not set -# CONFIG_SENSORS_LINEAGE is not set -# CONFIG_SENSORS_LTC2945 is not set -# CONFIG_SENSORS_LTC2947_I2C is not set -# CONFIG_SENSORS_LTC2990 is not set -# CONFIG_SENSORS_LTC4151 is not set -# CONFIG_SENSORS_LTC4215 is not set -# CONFIG_SENSORS_LTC4222 is not set -# CONFIG_SENSORS_LTC4245 is not set -# CONFIG_SENSORS_LTC4260 is not set -# CONFIG_SENSORS_LTC4261 is not set -# CONFIG_SENSORS_MAX16065 is not set -# CONFIG_SENSORS_MAX1619 is not set -# CONFIG_SENSORS_MAX1668 is not set -# CONFIG_SENSORS_MAX197 is not set -# CONFIG_SENSORS_MAX31730 is not set -# CONFIG_SENSORS_MAX6621 is not set -# CONFIG_SENSORS_MAX6639 is not set -# CONFIG_SENSORS_MAX6642 is not set -# CONFIG_SENSORS_MAX6650 is not set -# CONFIG_SENSORS_MAX6697 is not set -# CONFIG_SENSORS_MAX31790 is not set -# CONFIG_SENSORS_MCP3021 is not set -# CONFIG_SENSORS_TC654 is not set -# CONFIG_SENSORS_MR75203 is not set -# CONFIG_SENSORS_LM63 is not set -# CONFIG_SENSORS_LM73 is not set -# CONFIG_SENSORS_LM75 is not set -# CONFIG_SENSORS_LM77 is not set -# CONFIG_SENSORS_LM78 is not set -# CONFIG_SENSORS_LM80 is not set -# CONFIG_SENSORS_LM83 is not set -# CONFIG_SENSORS_LM85 is not set -# CONFIG_SENSORS_LM87 is not set -# CONFIG_SENSORS_LM90 is not set -# CONFIG_SENSORS_LM92 is not set -# CONFIG_SENSORS_LM93 is not set -# CONFIG_SENSORS_LM95234 is not set -# CONFIG_SENSORS_LM95241 is not set -# CONFIG_SENSORS_LM95245 is not set -# CONFIG_SENSORS_PC87360 is not set -# CONFIG_SENSORS_PC87427 is not set -# CONFIG_SENSORS_NTC_THERMISTOR is not set -# CONFIG_SENSORS_NCT6683 is not set -# CONFIG_SENSORS_NCT6775 is not set -# CONFIG_SENSORS_NCT7802 is not set -# CONFIG_SENSORS_NPCM7XX is not set -# CONFIG_SENSORS_PCF8591 is not set -# CONFIG_PMBUS is not set -# CONFIG_SENSORS_SHT21 is not set -# CONFIG_SENSORS_SHT3x is not set -# CONFIG_SENSORS_SHTC1 is not set -# CONFIG_SENSORS_SIS5595 is not set -# CONFIG_SENSORS_DME1737 is not set -# CONFIG_SENSORS_EMC1403 is not set -# CONFIG_SENSORS_EMC2103 is not set -# CONFIG_SENSORS_EMC6W201 is not set -# CONFIG_SENSORS_SMSC47M1 is not set -# CONFIG_SENSORS_SMSC47M192 is not set -# CONFIG_SENSORS_SMSC47B397 is not set -# CONFIG_SENSORS_STTS751 is not set -# CONFIG_SENSORS_SMM665 is not set -# CONFIG_SENSORS_ADC128D818 is not set -# CONFIG_SENSORS_ADS7828 is not set -# CONFIG_SENSORS_AMC6821 is not set -# CONFIG_SENSORS_INA209 is not set -# CONFIG_SENSORS_INA2XX is not set -# CONFIG_SENSORS_INA3221 is not set -# CONFIG_SENSORS_TC74 is not set -# CONFIG_SENSORS_THMC50 is not set -# CONFIG_SENSORS_TMP102 is not set -# CONFIG_SENSORS_TMP103 is not set -# CONFIG_SENSORS_TMP108 is not set -# CONFIG_SENSORS_TMP401 is not set -# CONFIG_SENSORS_TMP421 is not set -# CONFIG_SENSORS_TMP513 is not set -# CONFIG_SENSORS_VIA_CPUTEMP is not set -# CONFIG_SENSORS_VIA686A is not set -# CONFIG_SENSORS_VT1211 is not set -# CONFIG_SENSORS_VT8231 is not set -# CONFIG_SENSORS_W83773G is not set -# CONFIG_SENSORS_W83781D is not set -# CONFIG_SENSORS_W83791D is not set -# CONFIG_SENSORS_W83792D is not set -# CONFIG_SENSORS_W83793 is not set -# CONFIG_SENSORS_W83795 is not set -# CONFIG_SENSORS_W83L785TS is not set -# CONFIG_SENSORS_W83L786NG is not set -# CONFIG_SENSORS_W83627HF is not set -# CONFIG_SENSORS_W83627EHF is not set -# CONFIG_SENSORS_XGENE is not set - -# -# ACPI drivers -# -# CONFIG_SENSORS_ACPI_POWER is not set -# CONFIG_SENSORS_ATK0110 is not set -CONFIG_THERMAL=y -# CONFIG_THERMAL_NETLINK is not set -# CONFIG_THERMAL_STATISTICS is not set -CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 -# CONFIG_THERMAL_HWMON is not set -# CONFIG_THERMAL_WRITABLE_TRIPS is not set -CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y -# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set -# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set -# CONFIG_THERMAL_GOV_FAIR_SHARE is not set -CONFIG_THERMAL_GOV_STEP_WISE=y -# CONFIG_THERMAL_GOV_BANG_BANG is not set -# CONFIG_THERMAL_GOV_USER_SPACE is not set -# CONFIG_DEVFREQ_THERMAL is not set -# CONFIG_THERMAL_EMULATION is not set - -# -# Intel thermal drivers -# -# CONFIG_INTEL_POWERCLAMP is not set -# CONFIG_INTEL_SOC_DTS_THERMAL is not set - -# -# ACPI INT340X thermal drivers -# -# CONFIG_INT340X_THERMAL is not set -# end of ACPI INT340X thermal drivers - -# CONFIG_INTEL_PCH_THERMAL is not set -# end of Intel thermal drivers - -# CONFIG_WATCHDOG is not set -CONFIG_SSB_POSSIBLE=y -# CONFIG_SSB is not set -CONFIG_BCMA_POSSIBLE=y -# CONFIG_BCMA is not set - -# -# Multifunction device drivers -# -CONFIG_MFD_CORE=y -# CONFIG_MFD_AS3711 is not set -# CONFIG_PMIC_ADP5520 is not set -# CONFIG_MFD_BCM590XX is not set -# CONFIG_MFD_BD9571MWV is not set -# CONFIG_MFD_AXP20X_I2C is not set -# CONFIG_MFD_MADERA is not set -# CONFIG_PMIC_DA903X is not set -# CONFIG_MFD_DA9052_I2C is not set -# CONFIG_MFD_DA9055 is not set -# CONFIG_MFD_DA9062 is not set -# CONFIG_MFD_DA9063 is not set -# CONFIG_MFD_DA9150 is not set -# CONFIG_MFD_DLN2 is not set -# CONFIG_MFD_MC13XXX_I2C is not set -# CONFIG_MFD_MP2629 is not set -# CONFIG_HTC_PASIC3 is not set -# CONFIG_MFD_INTEL_QUARK_I2C_GPIO is not set -CONFIG_LPC_ICH=y -CONFIG_LPC_SCH=y -# CONFIG_MFD_INTEL_LPSS_ACPI is not set -# CONFIG_MFD_INTEL_LPSS_PCI is not set -# CONFIG_MFD_INTEL_PMC_BXT is not set -# CONFIG_MFD_IQS62X is not set -# CONFIG_MFD_JANZ_CMODIO is not set -# CONFIG_MFD_KEMPLD is not set -# CONFIG_MFD_88PM800 is not set -# CONFIG_MFD_88PM805 is not set -# CONFIG_MFD_88PM860X is not set -# CONFIG_MFD_MAX14577 is not set -# CONFIG_MFD_MAX77693 is not set -# CONFIG_MFD_MAX77843 is not set -# CONFIG_MFD_MAX8907 is not set -# CONFIG_MFD_MAX8925 is not set -# CONFIG_MFD_MAX8997 is not set -# CONFIG_MFD_MAX8998 is not set -# CONFIG_MFD_MT6360 is not set -# CONFIG_MFD_MT6397 is not set -# CONFIG_MFD_MENF21BMC is not set -# CONFIG_MFD_VIPERBOARD is not set -# CONFIG_MFD_RETU is not set -# CONFIG_MFD_PCF50633 is not set -# CONFIG_MFD_RDC321X is not set -# CONFIG_MFD_RT5033 is not set -# CONFIG_MFD_RC5T583 is not set -# CONFIG_MFD_SEC_CORE is not set -# CONFIG_MFD_SI476X_CORE is not set -CONFIG_MFD_SM501=y -# CONFIG_MFD_SKY81452 is not set -# CONFIG_ABX500_CORE is not set -# CONFIG_MFD_SYSCON is not set -# CONFIG_MFD_TI_AM335X_TSCADC is not set -# CONFIG_MFD_LP3943 is not set -# CONFIG_MFD_LP8788 is not set -# CONFIG_MFD_TI_LMU is not set -# CONFIG_MFD_PALMAS is not set -# CONFIG_TPS6105X is not set -# CONFIG_TPS6507X is not set -# CONFIG_MFD_TPS65086 is not set -# CONFIG_MFD_TPS65090 is not set -# CONFIG_MFD_TI_LP873X is not set -# CONFIG_MFD_TPS6586X is not set -# CONFIG_MFD_TPS65912_I2C is not set -# CONFIG_MFD_TPS80031 is not set -# CONFIG_TWL4030_CORE is not set -# CONFIG_TWL6040_CORE is not set -CONFIG_MFD_WL1273_CORE=y -# CONFIG_MFD_LM3533 is not set -# CONFIG_MFD_TQMX86 is not set -CONFIG_MFD_VX855=y -# CONFIG_MFD_ARIZONA_I2C is not set -# CONFIG_MFD_WM8400 is not set -# CONFIG_MFD_WM831X_I2C is not set -# CONFIG_MFD_WM8350_I2C is not set -# CONFIG_MFD_WM8994 is not set -# end of Multifunction device drivers - -# CONFIG_REGULATOR is not set -# CONFIG_RC_CORE is not set -# CONFIG_MEDIA_CEC_SUPPORT is not set -# CONFIG_MEDIA_SUPPORT is not set - -# -# Graphics support -# -# CONFIG_AGP is not set -CONFIG_INTEL_GTT=y -CONFIG_VGA_ARB=y -CONFIG_VGA_ARB_MAX_GPUS=16 -# CONFIG_VGA_SWITCHEROO is not set -CONFIG_DRM=y -CONFIG_DRM_MIPI_DSI=y -# CONFIG_DRM_DP_AUX_CHARDEV is not set -# CONFIG_DRM_DEBUG_MM is not set -# CONFIG_DRM_DEBUG_SELFTEST is not set -CONFIG_DRM_KMS_HELPER=y -CONFIG_DRM_KMS_FB_HELPER=y -CONFIG_DRM_FBDEV_EMULATION=y -CONFIG_DRM_FBDEV_OVERALLOC=100 -# CONFIG_DRM_LOAD_EDID_FIRMWARE is not set -# CONFIG_DRM_DP_CEC is not set - -# -# I2C encoder or helper chips -# -# CONFIG_DRM_I2C_CH7006 is not set -# CONFIG_DRM_I2C_SIL164 is not set -# CONFIG_DRM_I2C_NXP_TDA998X is not set -# CONFIG_DRM_I2C_NXP_TDA9950 is not set -# end of I2C encoder or helper chips - -# -# ARM devices -# -# end of ARM devices - -# CONFIG_DRM_RADEON is not set -# CONFIG_DRM_AMDGPU is not set -# CONFIG_DRM_NOUVEAU is not set -CONFIG_DRM_I915=y -CONFIG_DRM_I915_FORCE_PROBE="" -CONFIG_DRM_I915_CAPTURE_ERROR=y -CONFIG_DRM_I915_COMPRESS_ERROR=y -CONFIG_DRM_I915_USERPTR=y -# CONFIG_DRM_I915_GVT is not set -CONFIG_DRM_I915_FENCE_TIMEOUT=10000 -CONFIG_DRM_I915_USERFAULT_AUTOSUSPEND=250 -CONFIG_DRM_I915_HEARTBEAT_INTERVAL=2500 -CONFIG_DRM_I915_PREEMPT_TIMEOUT=640 -CONFIG_DRM_I915_MAX_REQUEST_BUSYWAIT=8000 -CONFIG_DRM_I915_STOP_TIMEOUT=100 -CONFIG_DRM_I915_TIMESLICE_DURATION=1 -# CONFIG_DRM_VGEM is not set -# CONFIG_DRM_VKMS is not set -# CONFIG_DRM_VMWGFX is not set -# CONFIG_DRM_GMA500 is not set -# CONFIG_DRM_UDL is not set -# CONFIG_DRM_AST is not set -# CONFIG_DRM_MGAG200 is not set -# CONFIG_DRM_QXL is not set -# CONFIG_DRM_BOCHS is not set -CONFIG_DRM_PANEL=y - -# -# Display Panels -# -# CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN is not set -# end of Display Panels - -CONFIG_DRM_BRIDGE=y -CONFIG_DRM_PANEL_BRIDGE=y - -# -# Display Interface Bridges -# -# CONFIG_DRM_ANALOGIX_ANX78XX is not set -# end of Display Interface Bridges - -# CONFIG_DRM_ETNAVIV is not set -# CONFIG_DRM_CIRRUS_QEMU is not set -# CONFIG_DRM_GM12U320 is not set -# CONFIG_DRM_VBOXVIDEO is not set -# CONFIG_DRM_LEGACY is not set -CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=y - -# -# Frame buffer Devices -# -CONFIG_FB_CMDLINE=y -CONFIG_FB_NOTIFY=y -CONFIG_FB=y -CONFIG_FIRMWARE_EDID=y -CONFIG_FB_BOOT_VESA_SUPPORT=y -CONFIG_FB_CFB_FILLRECT=y -CONFIG_FB_CFB_COPYAREA=y -CONFIG_FB_CFB_IMAGEBLIT=y -CONFIG_FB_SYS_FILLRECT=y -CONFIG_FB_SYS_COPYAREA=y -CONFIG_FB_SYS_IMAGEBLIT=y -# CONFIG_FB_FOREIGN_ENDIAN is not set -CONFIG_FB_SYS_FOPS=y -CONFIG_FB_DEFERRED_IO=y -# CONFIG_FB_MODE_HELPERS is not set -# CONFIG_FB_TILEBLITTING is not set - -# -# Frame buffer hardware drivers -# -# CONFIG_FB_CIRRUS is not set -# CONFIG_FB_PM2 is not set -# CONFIG_FB_CYBER2000 is not set -# CONFIG_FB_ARC is not set -# CONFIG_FB_ASILIANT is not set -# CONFIG_FB_IMSTT is not set -# CONFIG_FB_VGA16 is not set -# CONFIG_FB_UVESA is not set -CONFIG_FB_VESA=y -CONFIG_FB_EFI=y -# CONFIG_FB_N411 is not set -# CONFIG_FB_HGA is not set -# CONFIG_FB_OPENCORES is not set -# CONFIG_FB_S1D13XXX is not set -# CONFIG_FB_NVIDIA is not set -# CONFIG_FB_RIVA is not set -# CONFIG_FB_I740 is not set -# CONFIG_FB_LE80578 is not set -# CONFIG_FB_MATROX is not set -# CONFIG_FB_RADEON is not set -# CONFIG_FB_ATY128 is not set -# CONFIG_FB_ATY is not set -# CONFIG_FB_S3 is not set -# CONFIG_FB_SAVAGE is not set -# CONFIG_FB_SIS is not set -# CONFIG_FB_NEOMAGIC is not set -# CONFIG_FB_KYRO is not set -# CONFIG_FB_3DFX is not set -# CONFIG_FB_VOODOO1 is not set -# CONFIG_FB_VT8623 is not set -# CONFIG_FB_TRIDENT is not set -# CONFIG_FB_ARK is not set -# CONFIG_FB_PM3 is not set -# CONFIG_FB_CARMINE is not set -# CONFIG_FB_SM501 is not set -# CONFIG_FB_SMSCUFX is not set -# CONFIG_FB_UDL is not set -# CONFIG_FB_IBM_GXT4500 is not set -# CONFIG_FB_VIRTUAL is not set -# CONFIG_FB_METRONOME is not set -# CONFIG_FB_MB862XX is not set -# CONFIG_FB_SIMPLE is not set -# CONFIG_FB_SM712 is not set -# end of Frame buffer Devices - -# -# Backlight & LCD device support -# -CONFIG_LCD_CLASS_DEVICE=m -# CONFIG_LCD_PLATFORM is not set -CONFIG_BACKLIGHT_CLASS_DEVICE=y -# CONFIG_BACKLIGHT_APPLE is not set -# CONFIG_BACKLIGHT_QCOM_WLED is not set -# CONFIG_BACKLIGHT_SAHARA is not set -# CONFIG_BACKLIGHT_ADP8860 is not set -# CONFIG_BACKLIGHT_ADP8870 is not set -# CONFIG_BACKLIGHT_LM3639 is not set -# CONFIG_BACKLIGHT_LV5207LP is not set -# CONFIG_BACKLIGHT_BD6107 is not set -# CONFIG_BACKLIGHT_ARCXCNN is not set -# end of Backlight & LCD device support - -CONFIG_HDMI=y - -# -# Console display driver support -# -CONFIG_VGA_CONSOLE=y -CONFIG_DUMMY_CONSOLE=y -CONFIG_DUMMY_CONSOLE_COLUMNS=80 -CONFIG_DUMMY_CONSOLE_ROWS=25 -CONFIG_FRAMEBUFFER_CONSOLE=y -CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y -# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set -# CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER is not set -# end of Console display driver support - -# CONFIG_LOGO is not set -# end of Graphics support - -# CONFIG_SOUND is not set - -# -# HID support -# -CONFIG_HID=y -# CONFIG_HID_BATTERY_STRENGTH is not set -# CONFIG_HIDRAW is not set -# CONFIG_UHID is not set -CONFIG_HID_GENERIC=y - -# -# Special HID drivers -# -# CONFIG_HID_A4TECH is not set -# CONFIG_HID_ACCUTOUCH is not set -# CONFIG_HID_ACRUX is not set -# CONFIG_HID_APPLE is not set -# CONFIG_HID_APPLEIR is not set -# CONFIG_HID_AUREAL is not set -# CONFIG_HID_BELKIN is not set -# CONFIG_HID_BETOP_FF is not set -# CONFIG_HID_CHERRY is not set -# CONFIG_HID_CHICONY is not set -# CONFIG_HID_COUGAR is not set -# CONFIG_HID_MACALLY is not set -# CONFIG_HID_CMEDIA is not set -# CONFIG_HID_CREATIVE_SB0540 is not set -# CONFIG_HID_CYPRESS is not set -# CONFIG_HID_DRAGONRISE is not set -# CONFIG_HID_EMS_FF is not set -# CONFIG_HID_ELECOM is not set -# CONFIG_HID_ELO is not set -# CONFIG_HID_EZKEY is not set -# CONFIG_HID_GEMBIRD is not set -# CONFIG_HID_GFRM is not set -# CONFIG_HID_GLORIOUS is not set -# CONFIG_HID_HOLTEK is not set -# CONFIG_HID_VIVALDI is not set -# CONFIG_HID_KEYTOUCH is not set -# CONFIG_HID_KYE is not set -# CONFIG_HID_UCLOGIC is not set -# CONFIG_HID_WALTOP is not set -# CONFIG_HID_VIEWSONIC is not set -# CONFIG_HID_GYRATION is not set -# CONFIG_HID_ICADE is not set -CONFIG_HID_ITE=y -# CONFIG_HID_JABRA is not set -# CONFIG_HID_TWINHAN is not set -# CONFIG_HID_KENSINGTON is not set -# CONFIG_HID_LCPOWER is not set -# CONFIG_HID_LENOVO is not set -# CONFIG_HID_MAGICMOUSE is not set -# CONFIG_HID_MALTRON is not set -# CONFIG_HID_MAYFLASH is not set -CONFIG_HID_REDRAGON=y -# CONFIG_HID_MICROSOFT is not set -# CONFIG_HID_MONTEREY is not set -# CONFIG_HID_MULTITOUCH is not set -# CONFIG_HID_NTI is not set -# CONFIG_HID_NTRIG is not set -# CONFIG_HID_ORTEK is not set -# CONFIG_HID_PANTHERLORD is not set -# CONFIG_HID_PENMOUNT is not set -# CONFIG_HID_PETALYNX is not set -# CONFIG_HID_PICOLCD is not set -# CONFIG_HID_PLANTRONICS is not set -# CONFIG_HID_PRIMAX is not set -# CONFIG_HID_RETRODE is not set -# CONFIG_HID_ROCCAT is not set -# CONFIG_HID_SAITEK is not set -# CONFIG_HID_SAMSUNG is not set -# CONFIG_HID_SPEEDLINK is not set -# CONFIG_HID_STEAM is not set -# CONFIG_HID_STEELSERIES is not set -# CONFIG_HID_SUNPLUS is not set -# CONFIG_HID_RMI is not set -# CONFIG_HID_GREENASIA is not set -# CONFIG_HID_SMARTJOYPLUS is not set -# CONFIG_HID_TIVO is not set -# CONFIG_HID_TOPSEED is not set -# CONFIG_HID_THRUSTMASTER is not set -# CONFIG_HID_UDRAW_PS3 is not set -# CONFIG_HID_WACOM is not set -# CONFIG_HID_XINMO is not set -# CONFIG_HID_ZEROPLUS is not set -# CONFIG_HID_ZYDACRON is not set -# CONFIG_HID_SENSOR_HUB is not set -# CONFIG_HID_ALPS is not set -# end of Special HID drivers - -# -# USB HID support -# -CONFIG_USB_HID=y -# CONFIG_HID_PID is not set -# CONFIG_USB_HIDDEV is not set -# end of USB HID support - -# -# I2C HID support -# -# CONFIG_I2C_HID is not set -# end of I2C HID support - -# -# Intel ISH HID support -# -# CONFIG_INTEL_ISH_HID is not set -# end of Intel ISH HID support -# end of HID support - -CONFIG_USB_OHCI_LITTLE_ENDIAN=y -CONFIG_USB_SUPPORT=y -CONFIG_USB_COMMON=y -# CONFIG_USB_ULPI_BUS is not set -CONFIG_USB_ARCH_HAS_HCD=y -CONFIG_USB=y -CONFIG_USB_PCI=y -# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set - -# -# Miscellaneous USB options -# -CONFIG_USB_DEFAULT_PERSIST=y -# CONFIG_USB_FEW_INIT_RETRIES is not set -# CONFIG_USB_DYNAMIC_MINORS is not set -# CONFIG_USB_OTG is not set -# CONFIG_USB_OTG_PRODUCTLIST is not set -CONFIG_USB_AUTOSUSPEND_DELAY=2 -# CONFIG_USB_MON is not set - -# -# USB Host Controller Drivers -# -# CONFIG_USB_C67X00_HCD is not set -CONFIG_USB_XHCI_HCD=y -# CONFIG_USB_XHCI_DBGCAP is not set -CONFIG_USB_XHCI_PCI=y -# CONFIG_USB_XHCI_PCI_RENESAS is not set -# CONFIG_USB_XHCI_PLATFORM is not set -CONFIG_USB_EHCI_HCD=y -# CONFIG_USB_EHCI_ROOT_HUB_TT is not set -CONFIG_USB_EHCI_TT_NEWSCHED=y -CONFIG_USB_EHCI_PCI=y -# CONFIG_USB_EHCI_FSL is not set -# CONFIG_USB_EHCI_HCD_PLATFORM is not set -# CONFIG_USB_OXU210HP_HCD is not set -# CONFIG_USB_ISP116X_HCD is not set -# CONFIG_USB_FOTG210_HCD is not set -CONFIG_USB_OHCI_HCD=y -CONFIG_USB_OHCI_HCD_PCI=y -# CONFIG_USB_OHCI_HCD_PLATFORM is not set -CONFIG_USB_UHCI_HCD=y -# CONFIG_USB_SL811_HCD is not set -# CONFIG_USB_R8A66597_HCD is not set -# CONFIG_USB_HCD_TEST_MODE is not set - -# -# USB Device Class drivers -# -# CONFIG_USB_ACM is not set -# CONFIG_USB_PRINTER is not set -# CONFIG_USB_WDM is not set -# CONFIG_USB_TMC is not set - -# -# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may -# - -# -# also be needed; see USB_STORAGE Help for more info -# -CONFIG_USB_STORAGE=m -# CONFIG_USB_STORAGE_DEBUG is not set -# CONFIG_USB_STORAGE_REALTEK is not set -# CONFIG_USB_STORAGE_DATAFAB is not set -# CONFIG_USB_STORAGE_FREECOM is not set -# CONFIG_USB_STORAGE_ISD200 is not set -# CONFIG_USB_STORAGE_USBAT is not set -# CONFIG_USB_STORAGE_SDDR09 is not set -# CONFIG_USB_STORAGE_SDDR55 is not set -# CONFIG_USB_STORAGE_JUMPSHOT is not set -# CONFIG_USB_STORAGE_ALAUDA is not set -# CONFIG_USB_STORAGE_ONETOUCH is not set -# CONFIG_USB_STORAGE_KARMA is not set -# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set -# CONFIG_USB_STORAGE_ENE_UB6250 is not set -# CONFIG_USB_UAS is not set - -# -# USB Imaging devices -# -# CONFIG_USB_MDC800 is not set -# CONFIG_USB_MICROTEK is not set -# CONFIG_USBIP_CORE is not set -# CONFIG_USB_CDNS3 is not set -# CONFIG_USB_MUSB_HDRC is not set -# CONFIG_USB_DWC3 is not set -# CONFIG_USB_DWC2 is not set -# CONFIG_USB_CHIPIDEA is not set -# CONFIG_USB_ISP1760 is not set - -# -# USB port drivers -# -# CONFIG_USB_SERIAL is not set - -# -# USB Miscellaneous drivers -# -# CONFIG_USB_EMI62 is not set -# CONFIG_USB_EMI26 is not set -# CONFIG_USB_ADUTUX is not set -# CONFIG_USB_SEVSEG is not set -# CONFIG_USB_LEGOTOWER is not set -# CONFIG_USB_LCD is not set -# CONFIG_USB_CYPRESS_CY7C63 is not set -# CONFIG_USB_CYTHERM is not set -# CONFIG_USB_IDMOUSE is not set -# CONFIG_USB_FTDI_ELAN is not set -# CONFIG_USB_APPLEDISPLAY is not set -# CONFIG_APPLE_MFI_FASTCHARGE is not set -# CONFIG_USB_SISUSBVGA is not set -# CONFIG_USB_LD is not set -# CONFIG_USB_TRANCEVIBRATOR is not set -# CONFIG_USB_IOWARRIOR is not set -# CONFIG_USB_TEST is not set -# CONFIG_USB_EHSET_TEST_FIXTURE is not set -# CONFIG_USB_ISIGHTFW is not set -# CONFIG_USB_YUREX is not set -# CONFIG_USB_EZUSB_FX2 is not set -# CONFIG_USB_HUB_USB251XB is not set -# CONFIG_USB_HSIC_USB3503 is not set -# CONFIG_USB_HSIC_USB4604 is not set -# CONFIG_USB_LINK_LAYER_TEST is not set -# CONFIG_USB_CHAOSKEY is not set - -# -# USB Physical Layer drivers -# -# CONFIG_NOP_USB_XCEIV is not set -# CONFIG_USB_ISP1301 is not set -# end of USB Physical Layer drivers - -# CONFIG_USB_GADGET is not set -# CONFIG_TYPEC is not set -# CONFIG_USB_ROLE_SWITCH is not set -# CONFIG_MMC is not set -# CONFIG_MEMSTICK is not set -# CONFIG_NEW_LEDS is not set -# CONFIG_ACCESSIBILITY is not set -# CONFIG_INFINIBAND is not set -CONFIG_EDAC_ATOMIC_SCRUB=y -CONFIG_EDAC_SUPPORT=y -CONFIG_RTC_LIB=y -CONFIG_RTC_MC146818_LIB=y -CONFIG_RTC_CLASS=y -CONFIG_RTC_HCTOSYS=y -CONFIG_RTC_HCTOSYS_DEVICE="rtc0" -CONFIG_RTC_SYSTOHC=y -CONFIG_RTC_SYSTOHC_DEVICE="rtc0" -# CONFIG_RTC_DEBUG is not set -CONFIG_RTC_NVMEM=y - -# -# RTC interfaces -# -CONFIG_RTC_INTF_SYSFS=y -CONFIG_RTC_INTF_PROC=y -CONFIG_RTC_INTF_DEV=y -# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set -# CONFIG_RTC_DRV_TEST is not set - -# -# I2C RTC drivers -# -# CONFIG_RTC_DRV_ABB5ZES3 is not set -# CONFIG_RTC_DRV_ABEOZ9 is not set -# CONFIG_RTC_DRV_ABX80X is not set -# CONFIG_RTC_DRV_DS1307 is not set -# CONFIG_RTC_DRV_DS1374 is not set -# CONFIG_RTC_DRV_DS1672 is not set -# CONFIG_RTC_DRV_MAX6900 is not set -# CONFIG_RTC_DRV_RS5C372 is not set -# CONFIG_RTC_DRV_ISL1208 is not set -# CONFIG_RTC_DRV_ISL12022 is not set -# CONFIG_RTC_DRV_X1205 is not set -# CONFIG_RTC_DRV_PCF8523 is not set -# CONFIG_RTC_DRV_PCF85063 is not set -# CONFIG_RTC_DRV_PCF85363 is not set -# CONFIG_RTC_DRV_PCF8563 is not set -# CONFIG_RTC_DRV_PCF8583 is not set -# CONFIG_RTC_DRV_M41T80 is not set -# CONFIG_RTC_DRV_BQ32K is not set -# CONFIG_RTC_DRV_S35390A is not set -# CONFIG_RTC_DRV_FM3130 is not set -# CONFIG_RTC_DRV_RX8010 is not set -# CONFIG_RTC_DRV_RX8581 is not set -# CONFIG_RTC_DRV_RX8025 is not set -# CONFIG_RTC_DRV_EM3027 is not set -# CONFIG_RTC_DRV_RV3028 is not set -# CONFIG_RTC_DRV_RV3032 is not set -# CONFIG_RTC_DRV_RV8803 is not set -# CONFIG_RTC_DRV_SD3078 is not set - -# -# SPI RTC drivers -# -CONFIG_RTC_I2C_AND_SPI=y - -# -# SPI and I2C RTC drivers -# -# CONFIG_RTC_DRV_DS3232 is not set -# CONFIG_RTC_DRV_PCF2127 is not set -# CONFIG_RTC_DRV_RV3029C2 is not set - -# -# Platform RTC drivers -# -CONFIG_RTC_DRV_CMOS=y -# CONFIG_RTC_DRV_DS1286 is not set -# CONFIG_RTC_DRV_DS1511 is not set -# CONFIG_RTC_DRV_DS1553 is not set -# CONFIG_RTC_DRV_DS1685_FAMILY is not set -# CONFIG_RTC_DRV_DS1742 is not set -# CONFIG_RTC_DRV_DS2404 is not set -# CONFIG_RTC_DRV_STK17TA8 is not set -# CONFIG_RTC_DRV_M48T86 is not set -# CONFIG_RTC_DRV_M48T35 is not set -# CONFIG_RTC_DRV_M48T59 is not set -# CONFIG_RTC_DRV_MSM6242 is not set -# CONFIG_RTC_DRV_BQ4802 is not set -# CONFIG_RTC_DRV_RP5C01 is not set -# CONFIG_RTC_DRV_V3020 is not set - -# -# on-CPU RTC drivers -# -# CONFIG_RTC_DRV_FTRTC010 is not set - -# -# HID Sensor RTC drivers -# -# CONFIG_DMADEVICES is not set - -# -# DMABUF options -# -CONFIG_SYNC_FILE=y -# CONFIG_SW_SYNC is not set -# CONFIG_UDMABUF is not set -# CONFIG_DMABUF_MOVE_NOTIFY is not set -# CONFIG_DMABUF_SELFTESTS is not set -# CONFIG_DMABUF_HEAPS is not set -# end of DMABUF options - -# CONFIG_AUXDISPLAY is not set -CONFIG_UIO=m -# CONFIG_UIO_CIF is not set -# CONFIG_UIO_PDRV_GENIRQ is not set -# CONFIG_UIO_DMEM_GENIRQ is not set -# CONFIG_UIO_AEC is not set -# CONFIG_UIO_SERCOS3 is not set -# CONFIG_UIO_PCI_GENERIC is not set -# CONFIG_UIO_NETX is not set -# CONFIG_UIO_PRUSS is not set -# CONFIG_UIO_MF624 is not set -# CONFIG_VFIO is not set -# CONFIG_VIRT_DRIVERS is not set -CONFIG_VIRTIO_MENU=y -# CONFIG_VIRTIO_PCI is not set -# CONFIG_VIRTIO_MMIO is not set -# CONFIG_VDPA is not set -CONFIG_VHOST_MENU=y -# CONFIG_VHOST_NET is not set -# CONFIG_VHOST_CROSS_ENDIAN_LEGACY is not set - -# -# Microsoft Hyper-V guest support -# -# end of Microsoft Hyper-V guest support - -# CONFIG_GREYBUS is not set -# CONFIG_STAGING is not set -CONFIG_X86_PLATFORM_DEVICES=y -CONFIG_ACPI_WMI=y -CONFIG_WMI_BMOF=y -# CONFIG_HUAWEI_WMI is not set -# CONFIG_INTEL_WMI_SBL_FW_UPDATE is not set -# CONFIG_INTEL_WMI_THUNDERBOLT is not set -# CONFIG_MXM_WMI is not set -# CONFIG_PEAQ_WMI is not set -# CONFIG_XIAOMI_WMI is not set -# CONFIG_ACERHDF is not set -# CONFIG_ACER_WIRELESS is not set -# CONFIG_ACER_WMI is not set -# CONFIG_APPLE_GMUX is not set -# CONFIG_ASUS_LAPTOP is not set -# CONFIG_ASUS_WIRELESS is not set -# CONFIG_DCDBAS is not set -# CONFIG_DELL_SMBIOS is not set -# CONFIG_DELL_RBU is not set -# CONFIG_DELL_SMO8800 is not set -# CONFIG_DELL_WMI_AIO is not set -# CONFIG_FUJITSU_LAPTOP is not set -# CONFIG_FUJITSU_TABLET is not set -# CONFIG_GPD_POCKET_FAN is not set -# CONFIG_HP_ACCEL is not set -# CONFIG_HP_WIRELESS is not set -# CONFIG_HP_WMI is not set -# CONFIG_IBM_RTL is not set -# CONFIG_SENSORS_HDAPS is not set -# CONFIG_THINKPAD_ACPI is not set -# CONFIG_INTEL_ATOMISP2_PM is not set -# CONFIG_INTEL_HID_EVENT is not set -# CONFIG_INTEL_MENLOW is not set -# CONFIG_INTEL_VBTN is not set -# CONFIG_SURFACE_3_POWER_OPREGION is not set -# CONFIG_SURFACE_PRO3_BUTTON is not set -# CONFIG_MSI_WMI is not set -# CONFIG_SAMSUNG_LAPTOP is not set -# CONFIG_SAMSUNG_Q10 is not set -# CONFIG_TOSHIBA_BT_RFKILL is not set -# CONFIG_TOSHIBA_HAPS is not set -# CONFIG_TOSHIBA_WMI is not set -# CONFIG_ACPI_CMPC is not set -# CONFIG_LG_LAPTOP is not set -# CONFIG_PANASONIC_LAPTOP is not set -# CONFIG_SYSTEM76_ACPI is not set -# CONFIG_TOPSTAR_LAPTOP is not set -# CONFIG_I2C_MULTI_INSTANTIATE is not set -CONFIG_INTEL_IPS=y -# CONFIG_INTEL_RST is not set -# CONFIG_INTEL_SMARTCONNECT is not set - -# -# Intel Speed Select Technology interface support -# -# CONFIG_INTEL_SPEED_SELECT_INTERFACE is not set -# end of Intel Speed Select Technology interface support - -# CONFIG_INTEL_TURBO_MAX_3 is not set -# CONFIG_INTEL_UNCORE_FREQ_CONTROL is not set -# CONFIG_INTEL_PMC_CORE is not set -# CONFIG_INTEL_PUNIT_IPC is not set -# CONFIG_INTEL_SCU_PCI is not set -# CONFIG_INTEL_SCU_PLATFORM is not set -CONFIG_PMC_ATOM=y -# CONFIG_CHROME_PLATFORMS is not set -# CONFIG_MELLANOX_PLATFORM is not set -CONFIG_HAVE_CLK=y -CONFIG_CLKDEV_LOOKUP=y -CONFIG_HAVE_CLK_PREPARE=y -CONFIG_COMMON_CLK=y -# CONFIG_COMMON_CLK_MAX9485 is not set -# CONFIG_COMMON_CLK_SI5341 is not set -# CONFIG_COMMON_CLK_SI5351 is not set -# CONFIG_COMMON_CLK_SI544 is not set -# CONFIG_COMMON_CLK_CDCE706 is not set -# CONFIG_COMMON_CLK_CS2000_CP is not set -# CONFIG_HWSPINLOCK is not set - -# -# Clock Source drivers -# -CONFIG_CLKEVT_I8253=y -CONFIG_I8253_LOCK=y -CONFIG_CLKBLD_I8253=y -# end of Clock Source drivers - -CONFIG_MAILBOX=y -CONFIG_PCC=y -# CONFIG_ALTERA_MBOX is not set -# CONFIG_IOMMU_SUPPORT is not set - -# -# Remoteproc drivers -# -# CONFIG_REMOTEPROC is not set -# end of Remoteproc drivers - -# -# Rpmsg drivers -# -# CONFIG_RPMSG_QCOM_GLINK_RPM is not set -# CONFIG_RPMSG_VIRTIO is not set -# end of Rpmsg drivers - -# CONFIG_SOUNDWIRE is not set - -# -# SOC (System On Chip) specific Drivers -# - -# -# Amlogic SoC drivers -# -# end of Amlogic SoC drivers - -# -# Aspeed SoC drivers -# -# end of Aspeed SoC drivers - -# -# Broadcom SoC drivers -# -# end of Broadcom SoC drivers - -# -# NXP/Freescale QorIQ SoC drivers -# -# end of NXP/Freescale QorIQ SoC drivers - -# -# i.MX SoC drivers -# -# end of i.MX SoC drivers - -# -# Qualcomm SoC drivers -# -# end of Qualcomm SoC drivers - -# CONFIG_SOC_TI is not set - -# -# Xilinx SoC drivers -# -# CONFIG_XILINX_VCU is not set -# end of Xilinx SoC drivers -# end of SOC (System On Chip) specific Drivers - -CONFIG_PM_DEVFREQ=y - -# -# DEVFREQ Governors -# -CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y -# CONFIG_DEVFREQ_GOV_PERFORMANCE is not set -# CONFIG_DEVFREQ_GOV_POWERSAVE is not set -# CONFIG_DEVFREQ_GOV_USERSPACE is not set -# CONFIG_DEVFREQ_GOV_PASSIVE is not set - -# -# DEVFREQ Drivers -# -# CONFIG_PM_DEVFREQ_EVENT is not set -# CONFIG_EXTCON is not set -# CONFIG_MEMORY is not set -# CONFIG_IIO is not set -# CONFIG_NTB is not set -# CONFIG_VME_BUS is not set -# CONFIG_PWM is not set - -# -# IRQ chip support -# -# end of IRQ chip support - -# CONFIG_IPACK_BUS is not set -CONFIG_RESET_CONTROLLER=y -# CONFIG_RESET_TI_SYSCON is not set - -# -# PHY Subsystem -# -CONFIG_GENERIC_PHY=y -# CONFIG_USB_LGM_PHY is not set -# CONFIG_BCM_KONA_USB2_PHY is not set -# CONFIG_PHY_PXA_28NM_HSIC is not set -# CONFIG_PHY_PXA_28NM_USB2 is not set -# CONFIG_PHY_INTEL_LGM_EMMC is not set -# end of PHY Subsystem - -# CONFIG_POWERCAP is not set -# CONFIG_MCB is not set - -# -# Performance monitor support -# -# end of Performance monitor support - -# CONFIG_RAS is not set -# CONFIG_USB4 is not set - -# -# Android -# -# CONFIG_ANDROID is not set -# end of Android - -# CONFIG_LIBNVDIMM is not set -# CONFIG_DAX is not set -CONFIG_NVMEM=y -CONFIG_NVMEM_SYSFS=y - -# -# HW tracing support -# -# CONFIG_STM is not set -# CONFIG_INTEL_TH is not set -# end of HW tracing support - -# CONFIG_FPGA is not set -# CONFIG_TEE is not set -CONFIG_PM_OPP=y -# CONFIG_UNISYS_VISORBUS is not set -# CONFIG_SIOX is not set -# CONFIG_SLIMBUS is not set -# CONFIG_INTERCONNECT is not set -# CONFIG_COUNTER is not set -# end of Device Drivers - -# -# File systems -# -CONFIG_DCACHE_WORD_ACCESS=y -# CONFIG_VALIDATE_FS_PARSER is not set -CONFIG_FS_IOMAP=y -# CONFIG_EXT2_FS is not set -# CONFIG_EXT3_FS is not set -CONFIG_EXT4_FS=y -CONFIG_EXT4_USE_FOR_EXT2=y -CONFIG_EXT4_FS_POSIX_ACL=y -CONFIG_EXT4_FS_SECURITY=y -# CONFIG_EXT4_DEBUG is not set -CONFIG_JBD2=y -# CONFIG_JBD2_DEBUG is not set -CONFIG_FS_MBCACHE=y -# CONFIG_REISERFS_FS is not set -# CONFIG_JFS_FS is not set -# CONFIG_XFS_FS is not set -# CONFIG_GFS2_FS is not set -# CONFIG_BTRFS_FS is not set -# CONFIG_NILFS2_FS is not set -# CONFIG_F2FS_FS is not set -# CONFIG_FS_DAX is not set -CONFIG_FS_POSIX_ACL=y -CONFIG_EXPORTFS=y -# CONFIG_EXPORTFS_BLOCK_OPS is not set -CONFIG_FILE_LOCKING=y -CONFIG_MANDATORY_FILE_LOCKING=y -# CONFIG_FS_ENCRYPTION is not set -# CONFIG_FS_VERITY is not set -CONFIG_FSNOTIFY=y -CONFIG_DNOTIFY=y -CONFIG_INOTIFY_USER=y -CONFIG_FANOTIFY=y -# CONFIG_FANOTIFY_ACCESS_PERMISSIONS is not set -# CONFIG_QUOTA is not set -# CONFIG_AUTOFS4_FS is not set -# CONFIG_AUTOFS_FS is not set -# CONFIG_FUSE_FS is not set -CONFIG_OVERLAY_FS=y -# CONFIG_OVERLAY_FS_REDIRECT_DIR is not set -CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW=y -# CONFIG_OVERLAY_FS_INDEX is not set -# CONFIG_OVERLAY_FS_XINO_AUTO is not set -# CONFIG_OVERLAY_FS_METACOPY is not set - -# -# Caches -# -CONFIG_FSCACHE=y -CONFIG_FSCACHE_STATS=y -# CONFIG_FSCACHE_HISTOGRAM is not set -# CONFIG_FSCACHE_DEBUG is not set -# CONFIG_FSCACHE_OBJECT_LIST is not set -CONFIG_CACHEFILES=y -# CONFIG_CACHEFILES_DEBUG is not set -# CONFIG_CACHEFILES_HISTOGRAM is not set -# end of Caches - -# -# CD-ROM/DVD Filesystems -# -CONFIG_ISO9660_FS=y -CONFIG_JOLIET=y -CONFIG_ZISOFS=y -CONFIG_UDF_FS=y -# end of CD-ROM/DVD Filesystems - -# -# DOS/FAT/EXFAT/NT Filesystems -# -CONFIG_FAT_FS=y -CONFIG_MSDOS_FS=y -CONFIG_VFAT_FS=y -CONFIG_FAT_DEFAULT_CODEPAGE=437 -CONFIG_FAT_DEFAULT_IOCHARSET="utf8" -# CONFIG_FAT_DEFAULT_UTF8 is not set -# CONFIG_EXFAT_FS is not set -# CONFIG_NTFS_FS is not set -# end of DOS/FAT/EXFAT/NT Filesystems - -# -# Pseudo filesystems -# -CONFIG_PROC_FS=y -CONFIG_PROC_KCORE=y -CONFIG_PROC_SYSCTL=y -CONFIG_PROC_PAGE_MONITOR=y -CONFIG_PROC_CHILDREN=y -CONFIG_PROC_PID_ARCH_STATUS=y -CONFIG_KERNFS=y -CONFIG_SYSFS=y -CONFIG_TMPFS=y -# CONFIG_TMPFS_POSIX_ACL is not set -CONFIG_TMPFS_XATTR=y -# CONFIG_TMPFS_INODE64 is not set -CONFIG_HUGETLBFS=y -CONFIG_HUGETLB_PAGE=y -CONFIG_MEMFD_CREATE=y -CONFIG_ARCH_HAS_GIGANTIC_PAGE=y -# CONFIG_CONFIGFS_FS is not set -CONFIG_EFIVAR_FS=y -# end of Pseudo filesystems - -CONFIG_MISC_FILESYSTEMS=y -# CONFIG_ORANGEFS_FS is not set -# CONFIG_ADFS_FS is not set -# CONFIG_AFFS_FS is not set -# CONFIG_ECRYPT_FS is not set -# CONFIG_HFS_FS is not set -# CONFIG_HFSPLUS_FS is not set -# CONFIG_BEFS_FS is not set -# CONFIG_BFS_FS is not set -# CONFIG_EFS_FS is not set -# CONFIG_CRAMFS is not set -CONFIG_SQUASHFS=y -CONFIG_SQUASHFS_FILE_CACHE=y -# CONFIG_SQUASHFS_FILE_DIRECT is not set -CONFIG_SQUASHFS_DECOMP_SINGLE=y -# CONFIG_SQUASHFS_DECOMP_MULTI is not set -# CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU is not set -CONFIG_SQUASHFS_XATTR=y -CONFIG_SQUASHFS_ZLIB=y -CONFIG_SQUASHFS_LZ4=y -CONFIG_SQUASHFS_LZO=y -CONFIG_SQUASHFS_XZ=y -# CONFIG_SQUASHFS_ZSTD is not set -# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set -# CONFIG_SQUASHFS_EMBEDDED is not set -CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 -# CONFIG_VXFS_FS is not set -# CONFIG_MINIX_FS is not set -# CONFIG_OMFS_FS is not set -# CONFIG_HPFS_FS is not set -# CONFIG_QNX4FS_FS is not set -# CONFIG_QNX6FS_FS is not set -# CONFIG_ROMFS_FS is not set -CONFIG_PSTORE=y -CONFIG_PSTORE_DEFLATE_COMPRESS=y -# CONFIG_PSTORE_LZO_COMPRESS is not set -# CONFIG_PSTORE_LZ4_COMPRESS is not set -# CONFIG_PSTORE_LZ4HC_COMPRESS is not set -# CONFIG_PSTORE_842_COMPRESS is not set -# CONFIG_PSTORE_ZSTD_COMPRESS is not set -CONFIG_PSTORE_COMPRESS=y -CONFIG_PSTORE_DEFLATE_COMPRESS_DEFAULT=y -CONFIG_PSTORE_COMPRESS_DEFAULT="deflate" -# CONFIG_PSTORE_CONSOLE is not set -# CONFIG_PSTORE_PMSG is not set -# CONFIG_PSTORE_FTRACE is not set -# CONFIG_PSTORE_RAM is not set -# CONFIG_SYSV_FS is not set -# CONFIG_UFS_FS is not set -# CONFIG_EROFS_FS is not set -# CONFIG_NETWORK_FILESYSTEMS is not set -CONFIG_NLS=y -CONFIG_NLS_DEFAULT="iso8859-1" -CONFIG_NLS_CODEPAGE_437=y -# CONFIG_NLS_CODEPAGE_737 is not set -# CONFIG_NLS_CODEPAGE_775 is not set -# CONFIG_NLS_CODEPAGE_850 is not set -# CONFIG_NLS_CODEPAGE_852 is not set -# CONFIG_NLS_CODEPAGE_855 is not set -# CONFIG_NLS_CODEPAGE_857 is not set -# CONFIG_NLS_CODEPAGE_860 is not set -# CONFIG_NLS_CODEPAGE_861 is not set -# CONFIG_NLS_CODEPAGE_862 is not set -# CONFIG_NLS_CODEPAGE_863 is not set -# CONFIG_NLS_CODEPAGE_864 is not set -# CONFIG_NLS_CODEPAGE_865 is not set -# CONFIG_NLS_CODEPAGE_866 is not set -# CONFIG_NLS_CODEPAGE_869 is not set -# CONFIG_NLS_CODEPAGE_936 is not set -# CONFIG_NLS_CODEPAGE_950 is not set -# CONFIG_NLS_CODEPAGE_932 is not set -# CONFIG_NLS_CODEPAGE_949 is not set -# CONFIG_NLS_CODEPAGE_874 is not set -# CONFIG_NLS_ISO8859_8 is not set -# CONFIG_NLS_CODEPAGE_1250 is not set -# CONFIG_NLS_CODEPAGE_1251 is not set -CONFIG_NLS_ASCII=y -CONFIG_NLS_ISO8859_1=y -# CONFIG_NLS_ISO8859_2 is not set -# CONFIG_NLS_ISO8859_3 is not set -# CONFIG_NLS_ISO8859_4 is not set -# CONFIG_NLS_ISO8859_5 is not set -# CONFIG_NLS_ISO8859_6 is not set -# CONFIG_NLS_ISO8859_7 is not set -# CONFIG_NLS_ISO8859_9 is not set -# CONFIG_NLS_ISO8859_13 is not set -# CONFIG_NLS_ISO8859_14 is not set -# CONFIG_NLS_ISO8859_15 is not set -# CONFIG_NLS_KOI8_R is not set -# CONFIG_NLS_KOI8_U is not set -# CONFIG_NLS_MAC_ROMAN is not set -# CONFIG_NLS_MAC_CELTIC is not set -# CONFIG_NLS_MAC_CENTEURO is not set -# CONFIG_NLS_MAC_CROATIAN is not set -# CONFIG_NLS_MAC_CYRILLIC is not set -# CONFIG_NLS_MAC_GAELIC is not set -# CONFIG_NLS_MAC_GREEK is not set -# CONFIG_NLS_MAC_ICELAND is not set -# CONFIG_NLS_MAC_INUIT is not set -# CONFIG_NLS_MAC_ROMANIAN is not set -# CONFIG_NLS_MAC_TURKISH is not set -CONFIG_NLS_UTF8=y -# CONFIG_UNICODE is not set -CONFIG_IO_WQ=y -# end of File systems - -# -# Security options -# -CONFIG_KEYS=y -# CONFIG_KEYS_REQUEST_CACHE is not set -CONFIG_PERSISTENT_KEYRINGS=y -CONFIG_TRUSTED_KEYS=m -CONFIG_ENCRYPTED_KEYS=y -CONFIG_KEY_DH_OPERATIONS=y -CONFIG_SECURITY_DMESG_RESTRICT=y -CONFIG_SECURITY=y -CONFIG_SECURITYFS=y -CONFIG_SECURITY_NETWORK=y -CONFIG_PAGE_TABLE_ISOLATION=y -CONFIG_SECURITY_PATH=y -CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR=y -CONFIG_HARDENED_USERCOPY=y -# CONFIG_HARDENED_USERCOPY_FALLBACK is not set -CONFIG_FORTIFY_SOURCE=y -# CONFIG_STATIC_USERMODEHELPER is not set -# CONFIG_SECURITY_SELINUX is not set -# CONFIG_SECURITY_SMACK is not set -# CONFIG_SECURITY_TOMOYO is not set -# CONFIG_SECURITY_APPARMOR is not set -# CONFIG_SECURITY_LOADPIN is not set -CONFIG_SECURITY_YAMA=y -# CONFIG_SECURITY_SAFESETID is not set -# CONFIG_SECURITY_LOCKDOWN_LSM is not set -# CONFIG_INTEGRITY is not set -# CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT is not set -CONFIG_DEFAULT_SECURITY_DAC=y -CONFIG_LSM="lockdown,yama,loadpin,safesetid,integrity,bpf" - -# -# Kernel hardening options -# - -# -# Memory initialization -# -CONFIG_INIT_STACK_NONE=y -CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y -CONFIG_INIT_ON_FREE_DEFAULT_ON=y -# end of Memory initialization -# end of Kernel hardening options -# end of Security options - -CONFIG_CRYPTO=y - -# -# Crypto core or helper -# -CONFIG_CRYPTO_ALGAPI=y -CONFIG_CRYPTO_ALGAPI2=y -CONFIG_CRYPTO_AEAD=y -CONFIG_CRYPTO_AEAD2=y -CONFIG_CRYPTO_SKCIPHER=y -CONFIG_CRYPTO_SKCIPHER2=y -CONFIG_CRYPTO_HASH=y -CONFIG_CRYPTO_HASH2=y -CONFIG_CRYPTO_RNG=y -CONFIG_CRYPTO_RNG2=y -CONFIG_CRYPTO_RNG_DEFAULT=y -CONFIG_CRYPTO_AKCIPHER2=y -CONFIG_CRYPTO_AKCIPHER=y -CONFIG_CRYPTO_KPP2=y -CONFIG_CRYPTO_KPP=y -CONFIG_CRYPTO_ACOMP2=y -CONFIG_CRYPTO_MANAGER=y -CONFIG_CRYPTO_MANAGER2=y -CONFIG_CRYPTO_USER=y -CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y -CONFIG_CRYPTO_GF128MUL=y -CONFIG_CRYPTO_NULL=y -CONFIG_CRYPTO_NULL2=y -# CONFIG_CRYPTO_PCRYPT is not set -CONFIG_CRYPTO_CRYPTD=y -CONFIG_CRYPTO_AUTHENC=y -# CONFIG_CRYPTO_TEST is not set -CONFIG_CRYPTO_SIMD=y -CONFIG_CRYPTO_GLUE_HELPER_X86=y - -# -# Public-key cryptography -# -CONFIG_CRYPTO_RSA=y -CONFIG_CRYPTO_DH=y -# CONFIG_CRYPTO_ECDH is not set -# CONFIG_CRYPTO_ECRDSA is not set -# CONFIG_CRYPTO_SM2 is not set -# CONFIG_CRYPTO_CURVE25519 is not set -# CONFIG_CRYPTO_CURVE25519_X86 is not set - -# -# Authenticated Encryption with Associated Data -# -CONFIG_CRYPTO_CCM=y -CONFIG_CRYPTO_GCM=y -CONFIG_CRYPTO_CHACHA20POLY1305=y -# CONFIG_CRYPTO_AEGIS128 is not set -# CONFIG_CRYPTO_AEGIS128_AESNI_SSE2 is not set -CONFIG_CRYPTO_SEQIV=y -CONFIG_CRYPTO_ECHAINIV=y - -# -# Block modes -# -CONFIG_CRYPTO_CBC=y -# CONFIG_CRYPTO_CFB is not set -CONFIG_CRYPTO_CTR=y -CONFIG_CRYPTO_CTS=y -CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_LRW=y -# CONFIG_CRYPTO_OFB is not set -CONFIG_CRYPTO_PCBC=y -CONFIG_CRYPTO_XTS=y -CONFIG_CRYPTO_KEYWRAP=y -# CONFIG_CRYPTO_NHPOLY1305_SSE2 is not set -# CONFIG_CRYPTO_NHPOLY1305_AVX2 is not set -# CONFIG_CRYPTO_ADIANTUM is not set -CONFIG_CRYPTO_ESSIV=y - -# -# Hash modes -# -CONFIG_CRYPTO_CMAC=y -CONFIG_CRYPTO_HMAC=y -CONFIG_CRYPTO_XCBC=y -CONFIG_CRYPTO_VMAC=y - -# -# Digest -# -CONFIG_CRYPTO_CRC32C=y -CONFIG_CRYPTO_CRC32C_INTEL=y -CONFIG_CRYPTO_CRC32=y -CONFIG_CRYPTO_CRC32_PCLMUL=y -# CONFIG_CRYPTO_XXHASH is not set -# CONFIG_CRYPTO_BLAKE2B is not set -# CONFIG_CRYPTO_BLAKE2S is not set -# CONFIG_CRYPTO_BLAKE2S_X86 is not set -CONFIG_CRYPTO_CRCT10DIF=y -# CONFIG_CRYPTO_CRCT10DIF_PCLMUL is not set -CONFIG_CRYPTO_GHASH=y -CONFIG_CRYPTO_POLY1305=y -CONFIG_CRYPTO_POLY1305_X86_64=y -CONFIG_CRYPTO_MD4=y -CONFIG_CRYPTO_MD5=y -CONFIG_CRYPTO_MICHAEL_MIC=y -CONFIG_CRYPTO_RMD128=y -CONFIG_CRYPTO_RMD160=y -CONFIG_CRYPTO_RMD256=y -CONFIG_CRYPTO_RMD320=y -CONFIG_CRYPTO_SHA1=y -CONFIG_CRYPTO_SHA1_SSSE3=y -CONFIG_CRYPTO_SHA256_SSSE3=y -CONFIG_CRYPTO_SHA512_SSSE3=y -CONFIG_CRYPTO_SHA256=y -CONFIG_CRYPTO_SHA512=y -# CONFIG_CRYPTO_SHA3 is not set -# CONFIG_CRYPTO_SM3 is not set -# CONFIG_CRYPTO_STREEBOG is not set -CONFIG_CRYPTO_TGR192=y -CONFIG_CRYPTO_WP512=y -CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL=y - -# -# Ciphers -# -CONFIG_CRYPTO_AES=y -# CONFIG_CRYPTO_AES_TI is not set -CONFIG_CRYPTO_AES_NI_INTEL=y -CONFIG_CRYPTO_ANUBIS=y -CONFIG_CRYPTO_ARC4=y -CONFIG_CRYPTO_BLOWFISH=y -CONFIG_CRYPTO_BLOWFISH_COMMON=y -CONFIG_CRYPTO_BLOWFISH_X86_64=y -CONFIG_CRYPTO_CAMELLIA=y -CONFIG_CRYPTO_CAMELLIA_X86_64=y -CONFIG_CRYPTO_CAMELLIA_AESNI_AVX_X86_64=y -CONFIG_CRYPTO_CAMELLIA_AESNI_AVX2_X86_64=y -CONFIG_CRYPTO_CAST_COMMON=y -CONFIG_CRYPTO_CAST5=y -CONFIG_CRYPTO_CAST5_AVX_X86_64=y -CONFIG_CRYPTO_CAST6=y -CONFIG_CRYPTO_CAST6_AVX_X86_64=y -CONFIG_CRYPTO_DES=y -CONFIG_CRYPTO_DES3_EDE_X86_64=y -CONFIG_CRYPTO_FCRYPT=y -CONFIG_CRYPTO_KHAZAD=y -CONFIG_CRYPTO_SALSA20=y -CONFIG_CRYPTO_CHACHA20=y -CONFIG_CRYPTO_CHACHA20_X86_64=y -CONFIG_CRYPTO_SEED=y -CONFIG_CRYPTO_SERPENT=y -CONFIG_CRYPTO_SERPENT_SSE2_X86_64=y -CONFIG_CRYPTO_SERPENT_AVX_X86_64=y -CONFIG_CRYPTO_SERPENT_AVX2_X86_64=y -# CONFIG_CRYPTO_SM4 is not set -CONFIG_CRYPTO_TEA=y -CONFIG_CRYPTO_TWOFISH=y -CONFIG_CRYPTO_TWOFISH_COMMON=y -CONFIG_CRYPTO_TWOFISH_X86_64=y -CONFIG_CRYPTO_TWOFISH_X86_64_3WAY=y -CONFIG_CRYPTO_TWOFISH_AVX_X86_64=y - -# -# Compression -# -CONFIG_CRYPTO_DEFLATE=y -CONFIG_CRYPTO_LZO=y -CONFIG_CRYPTO_842=y -CONFIG_CRYPTO_LZ4=y -CONFIG_CRYPTO_LZ4HC=y -# CONFIG_CRYPTO_ZSTD is not set - -# -# Random Number Generation -# -CONFIG_CRYPTO_ANSI_CPRNG=y -CONFIG_CRYPTO_DRBG_MENU=y -CONFIG_CRYPTO_DRBG_HMAC=y -# CONFIG_CRYPTO_DRBG_HASH is not set -# CONFIG_CRYPTO_DRBG_CTR is not set -CONFIG_CRYPTO_DRBG=y -CONFIG_CRYPTO_JITTERENTROPY=y -CONFIG_CRYPTO_USER_API=y -CONFIG_CRYPTO_USER_API_HASH=y -CONFIG_CRYPTO_USER_API_SKCIPHER=y -CONFIG_CRYPTO_USER_API_RNG=y -# CONFIG_CRYPTO_USER_API_RNG_CAVP is not set -CONFIG_CRYPTO_USER_API_AEAD=y -CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE=y -# CONFIG_CRYPTO_STATS is not set -CONFIG_CRYPTO_HASH_INFO=y - -# -# Crypto library routines -# -CONFIG_CRYPTO_LIB_AES=y -CONFIG_CRYPTO_LIB_ARC4=y -# CONFIG_CRYPTO_LIB_BLAKE2S is not set -CONFIG_CRYPTO_ARCH_HAVE_LIB_CHACHA=y -CONFIG_CRYPTO_LIB_CHACHA_GENERIC=y -# CONFIG_CRYPTO_LIB_CHACHA is not set -# CONFIG_CRYPTO_LIB_CURVE25519 is not set -CONFIG_CRYPTO_LIB_DES=y -CONFIG_CRYPTO_LIB_POLY1305_RSIZE=11 -CONFIG_CRYPTO_ARCH_HAVE_LIB_POLY1305=y -CONFIG_CRYPTO_LIB_POLY1305_GENERIC=y -# CONFIG_CRYPTO_LIB_POLY1305 is not set -# CONFIG_CRYPTO_LIB_CHACHA20POLY1305 is not set -CONFIG_CRYPTO_LIB_SHA256=y -CONFIG_CRYPTO_HW=y -CONFIG_CRYPTO_DEV_PADLOCK=y -CONFIG_CRYPTO_DEV_PADLOCK_AES=y -CONFIG_CRYPTO_DEV_PADLOCK_SHA=y -# CONFIG_CRYPTO_DEV_ATMEL_ECC is not set -# CONFIG_CRYPTO_DEV_ATMEL_SHA204A is not set -# CONFIG_CRYPTO_DEV_CCP is not set -# CONFIG_CRYPTO_DEV_QAT_DH895xCC is not set -# CONFIG_CRYPTO_DEV_QAT_C3XXX is not set -# CONFIG_CRYPTO_DEV_QAT_C62X is not set -# CONFIG_CRYPTO_DEV_QAT_DH895xCCVF is not set -# CONFIG_CRYPTO_DEV_QAT_C3XXXVF is not set -# CONFIG_CRYPTO_DEV_QAT_C62XVF is not set -# CONFIG_CRYPTO_DEV_NITROX_CNN55XX is not set -# CONFIG_CRYPTO_DEV_SAFEXCEL is not set -# CONFIG_CRYPTO_DEV_AMLOGIC_GXL is not set -CONFIG_ASYMMETRIC_KEY_TYPE=y -CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y -# CONFIG_ASYMMETRIC_TPM_KEY_SUBTYPE is not set -CONFIG_X509_CERTIFICATE_PARSER=y -# CONFIG_PKCS8_PRIVATE_KEY_PARSER is not set -CONFIG_PKCS7_MESSAGE_PARSER=y - -# -# Certificates for signature checking -# -# CONFIG_SYSTEM_TRUSTED_KEYRING is not set -# CONFIG_SYSTEM_BLACKLIST_KEYRING is not set -# end of Certificates for signature checking - -CONFIG_BINARY_PRINTF=y - -# -# Library routines -# -# CONFIG_PACKING is not set -CONFIG_BITREVERSE=y -CONFIG_GENERIC_STRNCPY_FROM_USER=y -CONFIG_GENERIC_STRNLEN_USER=y -CONFIG_GENERIC_NET_UTILS=y -CONFIG_GENERIC_FIND_FIRST_BIT=y -# CONFIG_CORDIC is not set -# CONFIG_PRIME_NUMBERS is not set -CONFIG_RATIONAL=y -CONFIG_GENERIC_PCI_IOMAP=y -CONFIG_GENERIC_IOMAP=y -CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y -CONFIG_ARCH_HAS_FAST_MULTIPLIER=y -CONFIG_ARCH_USE_SYM_ANNOTATIONS=y -CONFIG_CRC_CCITT=y -CONFIG_CRC16=y -CONFIG_CRC_T10DIF=y -CONFIG_CRC_ITU_T=y -CONFIG_CRC32=y -# CONFIG_CRC32_SELFTEST is not set -CONFIG_CRC32_SLICEBY8=y -# CONFIG_CRC32_SLICEBY4 is not set -# CONFIG_CRC32_SARWATE is not set -# CONFIG_CRC32_BIT is not set -# CONFIG_CRC64 is not set -# CONFIG_CRC4 is not set -# CONFIG_CRC7 is not set -CONFIG_LIBCRC32C=y -# CONFIG_CRC8 is not set -CONFIG_XXHASH=y -# CONFIG_RANDOM32_SELFTEST is not set -CONFIG_842_COMPRESS=y -CONFIG_842_DECOMPRESS=y -CONFIG_ZLIB_INFLATE=y -CONFIG_ZLIB_DEFLATE=y -CONFIG_LZO_COMPRESS=y -CONFIG_LZO_DECOMPRESS=y -CONFIG_LZ4_COMPRESS=y -CONFIG_LZ4HC_COMPRESS=y -CONFIG_LZ4_DECOMPRESS=y -CONFIG_ZSTD_DECOMPRESS=y -CONFIG_XZ_DEC=y -CONFIG_XZ_DEC_X86=y -CONFIG_XZ_DEC_POWERPC=y -CONFIG_XZ_DEC_IA64=y -CONFIG_XZ_DEC_ARM=y -CONFIG_XZ_DEC_ARMTHUMB=y -CONFIG_XZ_DEC_SPARC=y -CONFIG_XZ_DEC_BCJ=y -# CONFIG_XZ_DEC_TEST is not set -CONFIG_DECOMPRESS_GZIP=y -CONFIG_DECOMPRESS_ZSTD=y -CONFIG_GENERIC_ALLOCATOR=y -CONFIG_TEXTSEARCH=y -CONFIG_TEXTSEARCH_KMP=y -CONFIG_TEXTSEARCH_BM=y -CONFIG_TEXTSEARCH_FSM=y -CONFIG_INTERVAL_TREE=y -CONFIG_XARRAY_MULTI=y -CONFIG_ASSOCIATIVE_ARRAY=y -CONFIG_HAS_IOMEM=y -CONFIG_HAS_IOPORT_MAP=y -CONFIG_HAS_DMA=y -CONFIG_NEED_SG_DMA_LENGTH=y -CONFIG_NEED_DMA_MAP_STATE=y -CONFIG_ARCH_DMA_ADDR_T_64BIT=y -CONFIG_SWIOTLB=y -# CONFIG_DMA_API_DEBUG is not set -CONFIG_SGL_ALLOC=y -CONFIG_CPU_RMAP=y -CONFIG_DQL=y -CONFIG_GLOB=y -# CONFIG_GLOB_SELFTEST is not set -CONFIG_NLATTR=y -CONFIG_CLZ_TAB=y -# CONFIG_IRQ_POLL is not set -CONFIG_MPILIB=y -CONFIG_OID_REGISTRY=y -CONFIG_UCS2_STRING=y -CONFIG_HAVE_GENERIC_VDSO=y -CONFIG_GENERIC_GETTIMEOFDAY=y -CONFIG_GENERIC_VDSO_TIME_NS=y -CONFIG_FONT_SUPPORT=y -# CONFIG_FONTS is not set -CONFIG_FONT_8x8=y -CONFIG_FONT_8x16=y -CONFIG_SG_POOL=y -CONFIG_ARCH_HAS_PMEM_API=y -CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE=y -CONFIG_ARCH_HAS_COPY_MC=y -CONFIG_ARCH_STACKWALK=y -CONFIG_SBITMAP=y -# CONFIG_STRING_SELFTEST is not set -# end of Library routines - -# -# Kernel hacking -# - -# -# printk and dmesg options -# -CONFIG_PRINTK_TIME=y -# CONFIG_PRINTK_CALLER is not set -CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7 -CONFIG_CONSOLE_LOGLEVEL_QUIET=4 -CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 -# CONFIG_BOOT_PRINTK_DELAY is not set -# CONFIG_DYNAMIC_DEBUG is not set -# CONFIG_DYNAMIC_DEBUG_CORE is not set -CONFIG_SYMBOLIC_ERRNAME=y -CONFIG_DEBUG_BUGVERBOSE=y -# end of printk and dmesg options - -# -# Compile-time checks and compiler options -# -CONFIG_DEBUG_INFO=y -# CONFIG_DEBUG_INFO_REDUCED is not set -# CONFIG_DEBUG_INFO_COMPRESSED is not set -CONFIG_DEBUG_INFO_SPLIT=y -# CONFIG_DEBUG_INFO_DWARF4 is not set -# CONFIG_GDB_SCRIPTS is not set -CONFIG_ENABLE_MUST_CHECK=y -CONFIG_FRAME_WARN=1024 -# CONFIG_STRIP_ASM_SYMS is not set -# CONFIG_READABLE_ASM is not set -# CONFIG_HEADERS_INSTALL is not set -# CONFIG_DEBUG_SECTION_MISMATCH is not set -CONFIG_SECTION_MISMATCH_WARN_ONLY=y -CONFIG_STACK_VALIDATION=y -# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set -# end of Compile-time checks and compiler options - -# -# Generic Kernel Debugging Instruments -# -CONFIG_MAGIC_SYSRQ=y -CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1 -CONFIG_MAGIC_SYSRQ_SERIAL=y -CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE="" -CONFIG_DEBUG_FS=y -CONFIG_DEBUG_FS_ALLOW_ALL=y -# CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set -# CONFIG_DEBUG_FS_ALLOW_NONE is not set -CONFIG_HAVE_ARCH_KGDB=y -# CONFIG_KGDB is not set -CONFIG_ARCH_HAS_UBSAN_SANITIZE_ALL=y -CONFIG_UBSAN=y -# CONFIG_UBSAN_TRAP is not set -CONFIG_UBSAN_BOUNDS=y -CONFIG_UBSAN_MISC=y -# CONFIG_UBSAN_SANITIZE_ALL is not set -# CONFIG_UBSAN_ALIGNMENT is not set -# CONFIG_TEST_UBSAN is not set -CONFIG_HAVE_ARCH_KCSAN=y -# end of Generic Kernel Debugging Instruments - -CONFIG_DEBUG_KERNEL=y -CONFIG_DEBUG_MISC=y - -# -# Memory Debugging -# -CONFIG_PAGE_EXTENSION=y -# CONFIG_DEBUG_PAGEALLOC is not set -# CONFIG_PAGE_OWNER is not set -CONFIG_PAGE_POISONING=y -CONFIG_PAGE_POISONING_NO_SANITY=y -CONFIG_PAGE_POISONING_ZERO=y -# CONFIG_DEBUG_PAGE_REF is not set -# CONFIG_DEBUG_RODATA_TEST is not set -CONFIG_ARCH_HAS_DEBUG_WX=y -CONFIG_DEBUG_WX=y -CONFIG_GENERIC_PTDUMP=y -CONFIG_PTDUMP_CORE=y -# CONFIG_PTDUMP_DEBUGFS is not set -# CONFIG_DEBUG_OBJECTS is not set -# CONFIG_SLUB_DEBUG_ON is not set -# CONFIG_SLUB_STATS is not set -CONFIG_HAVE_DEBUG_KMEMLEAK=y -# CONFIG_DEBUG_KMEMLEAK is not set -# CONFIG_DEBUG_STACK_USAGE is not set -# CONFIG_SCHED_STACK_END_CHECK is not set -CONFIG_ARCH_HAS_DEBUG_VM_PGTABLE=y -# CONFIG_DEBUG_VM is not set -# CONFIG_DEBUG_VM_PGTABLE is not set -CONFIG_ARCH_HAS_DEBUG_VIRTUAL=y -# CONFIG_DEBUG_VIRTUAL is not set -CONFIG_DEBUG_MEMORY_INIT=y -# CONFIG_DEBUG_PER_CPU_MAPS is not set -CONFIG_HAVE_ARCH_KASAN=y -CONFIG_HAVE_ARCH_KASAN_VMALLOC=y -CONFIG_CC_HAS_KASAN_GENERIC=y -CONFIG_CC_HAS_WORKING_NOSANITIZE_ADDRESS=y -# CONFIG_KASAN is not set -# end of Memory Debugging - -# CONFIG_DEBUG_SHIRQ is not set - -# -# Debug Oops, Lockups and Hangs -# -CONFIG_PANIC_ON_OOPS=y -CONFIG_PANIC_ON_OOPS_VALUE=1 -CONFIG_PANIC_TIMEOUT=0 -CONFIG_LOCKUP_DETECTOR=y -CONFIG_SOFTLOCKUP_DETECTOR=y -# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set -CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 -CONFIG_HARDLOCKUP_DETECTOR_PERF=y -CONFIG_HARDLOCKUP_CHECK_TIMESTAMP=y -CONFIG_HARDLOCKUP_DETECTOR=y -# CONFIG_BOOTPARAM_HARDLOCKUP_PANIC is not set -CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE=0 -CONFIG_DETECT_HUNG_TASK=y -CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120 -# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set -CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 -CONFIG_WQ_WATCHDOG=y -# CONFIG_TEST_LOCKUP is not set -# end of Debug Oops, Lockups and Hangs - -# -# Scheduler Debugging -# -CONFIG_SCHED_DEBUG=y -CONFIG_SCHED_INFO=y -# CONFIG_SCHEDSTATS is not set -# end of Scheduler Debugging - -# CONFIG_DEBUG_TIMEKEEPING is not set - -# -# Lock Debugging (spinlocks, mutexes, etc...) -# -CONFIG_LOCK_DEBUGGING_SUPPORT=y -# CONFIG_PROVE_LOCKING is not set -# CONFIG_LOCK_STAT is not set -# CONFIG_DEBUG_RT_MUTEXES is not set -# CONFIG_DEBUG_SPINLOCK is not set -# CONFIG_DEBUG_MUTEXES is not set -# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set -# CONFIG_DEBUG_RWSEMS is not set -# CONFIG_DEBUG_LOCK_ALLOC is not set -# CONFIG_DEBUG_ATOMIC_SLEEP is not set -# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set -# CONFIG_LOCK_TORTURE_TEST is not set -# CONFIG_WW_MUTEX_SELFTEST is not set -# CONFIG_SCF_TORTURE_TEST is not set -# CONFIG_CSD_LOCK_WAIT_DEBUG is not set -# end of Lock Debugging (spinlocks, mutexes, etc...) - -CONFIG_STACKTRACE=y -# CONFIG_WARN_ALL_UNSEEDED_RANDOM is not set -# CONFIG_DEBUG_KOBJECT is not set - -# -# Debug kernel data structures -# -CONFIG_DEBUG_LIST=y -# CONFIG_DEBUG_PLIST is not set -# CONFIG_DEBUG_SG is not set -CONFIG_DEBUG_NOTIFIERS=y -# CONFIG_BUG_ON_DATA_CORRUPTION is not set -# end of Debug kernel data structures - -CONFIG_DEBUG_CREDENTIALS=y - -# -# RCU Debugging -# -# CONFIG_RCU_SCALE_TEST is not set -# CONFIG_RCU_TORTURE_TEST is not set -# CONFIG_RCU_REF_SCALE_TEST is not set -CONFIG_RCU_CPU_STALL_TIMEOUT=60 -# CONFIG_RCU_TRACE is not set -# CONFIG_RCU_EQS_DEBUG is not set -# end of RCU Debugging - -# CONFIG_DEBUG_WQ_FORCE_RR_CPU is not set -# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set -# CONFIG_CPU_HOTPLUG_STATE_CONTROL is not set -# CONFIG_LATENCYTOP is not set -CONFIG_USER_STACKTRACE_SUPPORT=y -CONFIG_NOP_TRACER=y -CONFIG_HAVE_FUNCTION_TRACER=y -CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y -CONFIG_HAVE_DYNAMIC_FTRACE=y -CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y -CONFIG_HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS=y -CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y -CONFIG_HAVE_SYSCALL_TRACEPOINTS=y -CONFIG_HAVE_FENTRY=y -CONFIG_HAVE_C_RECORDMCOUNT=y -CONFIG_TRACE_CLOCK=y -CONFIG_RING_BUFFER=y -CONFIG_EVENT_TRACING=y -CONFIG_CONTEXT_SWITCH_TRACER=y -CONFIG_TRACING=y -CONFIG_GENERIC_TRACER=y -CONFIG_TRACING_SUPPORT=y -CONFIG_FTRACE=y -# CONFIG_BOOTTIME_TRACING is not set -CONFIG_FUNCTION_TRACER=y -CONFIG_FUNCTION_GRAPH_TRACER=y -CONFIG_DYNAMIC_FTRACE=y -CONFIG_DYNAMIC_FTRACE_WITH_REGS=y -CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS=y -CONFIG_FUNCTION_PROFILER=y -CONFIG_STACK_TRACER=y -# CONFIG_IRQSOFF_TRACER is not set -# CONFIG_SCHED_TRACER is not set -# CONFIG_HWLAT_TRACER is not set -CONFIG_MMIOTRACE=y -CONFIG_FTRACE_SYSCALLS=y -# CONFIG_TRACER_SNAPSHOT is not set -CONFIG_BRANCH_PROFILE_NONE=y -# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set -CONFIG_BLK_DEV_IO_TRACE=y -CONFIG_UPROBE_EVENTS=y -CONFIG_DYNAMIC_EVENTS=y -CONFIG_PROBE_EVENTS=y -CONFIG_FTRACE_MCOUNT_RECORD=y -# CONFIG_SYNTH_EVENTS is not set -# CONFIG_HIST_TRIGGERS is not set -# CONFIG_TRACE_EVENT_INJECT is not set -# CONFIG_TRACEPOINT_BENCHMARK is not set -# CONFIG_RING_BUFFER_BENCHMARK is not set -# CONFIG_TRACE_EVAL_MAP_FILE is not set -# CONFIG_FTRACE_STARTUP_TEST is not set -# CONFIG_RING_BUFFER_STARTUP_TEST is not set -# CONFIG_MMIOTRACE_TEST is not set -# CONFIG_PREEMPTIRQ_DELAY_TEST is not set -# CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set -# CONFIG_SAMPLES is not set -CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y -# CONFIG_STRICT_DEVMEM is not set - -# -# x86 Debugging -# -CONFIG_TRACE_IRQFLAGS_SUPPORT=y -CONFIG_TRACE_IRQFLAGS_NMI_SUPPORT=y -CONFIG_X86_VERBOSE_BOOTUP=y -CONFIG_EARLY_PRINTK=y -# CONFIG_EARLY_PRINTK_DBGP is not set -# CONFIG_EARLY_PRINTK_USB_XDBC is not set -# CONFIG_EFI_PGT_DUMP is not set -# CONFIG_DEBUG_TLBFLUSH is not set -CONFIG_HAVE_MMIOTRACE_SUPPORT=y -# CONFIG_X86_DECODER_SELFTEST is not set -CONFIG_IO_DELAY_0X80=y -# CONFIG_IO_DELAY_0XED is not set -# CONFIG_IO_DELAY_UDELAY is not set -# CONFIG_IO_DELAY_NONE is not set -# CONFIG_DEBUG_BOOT_PARAMS is not set -# CONFIG_CPA_DEBUG is not set -# CONFIG_DEBUG_ENTRY is not set -# CONFIG_DEBUG_NMI_SELFTEST is not set -CONFIG_X86_DEBUG_FPU=y -# CONFIG_PUNIT_ATOM_DEBUG is not set -CONFIG_UNWINDER_ORC=y -# CONFIG_UNWINDER_FRAME_POINTER is not set -# end of x86 Debugging - -# -# Kernel Testing and Coverage -# -# CONFIG_KUNIT is not set -# CONFIG_NOTIFIER_ERROR_INJECTION is not set -# CONFIG_FAULT_INJECTION is not set -CONFIG_ARCH_HAS_KCOV=y -CONFIG_CC_HAS_SANCOV_TRACE_PC=y -# CONFIG_KCOV is not set -CONFIG_RUNTIME_TESTING_MENU=y -# CONFIG_LKDTM is not set -# CONFIG_TEST_LIST_SORT is not set -# CONFIG_TEST_MIN_HEAP is not set -# CONFIG_TEST_SORT is not set -# CONFIG_BACKTRACE_SELF_TEST is not set -# CONFIG_RBTREE_TEST is not set -# CONFIG_REED_SOLOMON_TEST is not set -# CONFIG_INTERVAL_TREE_TEST is not set -# CONFIG_PERCPU_TEST is not set -# CONFIG_ATOMIC64_SELFTEST is not set -# CONFIG_TEST_HEXDUMP is not set -# CONFIG_TEST_STRING_HELPERS is not set -# CONFIG_TEST_STRSCPY is not set -# CONFIG_TEST_KSTRTOX is not set -# CONFIG_TEST_PRINTF is not set -# CONFIG_TEST_BITMAP is not set -# CONFIG_TEST_UUID is not set -# CONFIG_TEST_XARRAY is not set -# CONFIG_TEST_OVERFLOW is not set -# CONFIG_TEST_RHASHTABLE is not set -# CONFIG_TEST_HASH is not set -# CONFIG_TEST_IDA is not set -# CONFIG_TEST_LKM is not set -# CONFIG_TEST_BITOPS is not set -# CONFIG_TEST_VMALLOC is not set -# CONFIG_TEST_USER_COPY is not set -# CONFIG_TEST_BPF is not set -# CONFIG_TEST_BLACKHOLE_DEV is not set -# CONFIG_FIND_BIT_BENCHMARK is not set -# CONFIG_TEST_FIRMWARE is not set -# CONFIG_TEST_SYSCTL is not set -# CONFIG_TEST_UDELAY is not set -# CONFIG_TEST_STATIC_KEYS is not set -# CONFIG_TEST_KMOD is not set -# CONFIG_TEST_MEMCAT_P is not set -# CONFIG_TEST_STACKINIT is not set -# CONFIG_TEST_MEMINIT is not set -# CONFIG_TEST_FREE_PAGES is not set -# CONFIG_TEST_FPU is not set -# CONFIG_MEMTEST is not set -# end of Kernel Testing and Coverage -# end of Kernel hacking diff --git a/configs/mysql_backup.sh b/configs/mysql_backup.sh deleted file mode 100644 index 07a4d272..00000000 --- a/configs/mysql_backup.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -[ -z "${MYSQL_PASSWORD}" ] && [ -n "${MYSQL_PASSWORD_FILE}" ] && MYSQL_PASSWORD=$(cat "${MYSQL_PASSWORD_FILE}" | tr -d '\n') - -while true -do - sleep 360 - mysqldump -h "${MYSQL_HOST}" -u "${MYSQL_USER}" --password="${MYSQL_PASSWORD}" "${MYSQL_DATABASE}" | gzip > /var/lib/fic/backups/db-$(date +%Y%m%d%H%M).sql.gz -done diff --git a/configs/nginx-chbase.sh b/configs/nginx-chbase.sh deleted file mode 100755 index 258e0dbd..00000000 --- a/configs/nginx-chbase.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/sh - -[ -f "/base_changed" ] && exit 0 - -[ -z "${FIC_BASEURL}" ] && exit 0 -[ -z "${PATH_STATIC}" ] && { >&2 echo "PATH_STATIC not defined"; exit 1; } -[ -n "${CURRENT_BASE}" ] || CURRENT_BASE="/" - -run() { - local NEWBASE=$1 - local FILE=$2 - - if [ -d "${FILE}" ] - then - for f in "${FILE}/"* - do - case "${f}" in - "${FILE}/"*.html|"${FILE}/"*.js|"${FILE}/"*.css) - run "${NEWBASE}" "${f}";; - esac - done - elif [ -f "${FILE}" ] - then - echo "Updating base path for $FILE..." - sed -ri "s@@@;s@\"${CURRENT_BASE}_app/@\"${NEWBASE}_app/@;s@base: \"${CURRENT_BASE%/}\"@base: \"${NEWBASE%/}\"@" ${FILE} - fi -} - -run "${FIC_BASEURL}" "${PATH_STATIC}" -touch "/base_changed" - -if [ "${FIC_BASEURL}" == "/" ] && [ -f /etc/nginx/conf.d/default.conf ] -then - sed -i "s:location / {:location @notneeded {:" /etc/nginx/conf.d/default.conf -fi - -[ -n "${FIC_CUSTOM_HEAD}" ] && sed -i "s||${FIC_CUSTOM_HEAD}|" "${PATH_STATIC}/index.html" -[ -n "${FIC_CUSTOM_BODY}" ] && sed -i "s||${FIC_CUSTOM_BODY}|" "${PATH_STATIC}/index.html" - -exit 0 diff --git a/configs/nginx/auth/client-cert.conf b/configs/nginx/auth/client-cert.conf deleted file mode 100644 index 7fcbb0c7..00000000 --- a/configs/nginx/auth/client-cert.conf +++ /dev/null @@ -1,3 +0,0 @@ -ssl_client_certificate /srv/PKI/shared/ca.pem; -ssl_trusted_certificate /srv/PKI/shared/ca.pem; -ssl_verify_client optional; diff --git a/configs/nginx/auth/oidc.conf b/configs/nginx/auth/oidc.conf deleted file mode 100644 index 8e629385..00000000 --- a/configs/nginx/auth/oidc.conf +++ /dev/null @@ -1,50 +0,0 @@ -error_page 401 = @error401; - -location /challenge_access { - # forward the /validate request to Vouch Proxy - proxy_pass http://auth:9090; - # be sure to pass the original host header - proxy_set_header Host $http_host; - - # Vouch Proxy only acts on the request headers - proxy_pass_request_body off; - proxy_set_header Content-Length ""; - - # optionally add X-Vouch-User as returned by Vouch Proxy along with the request - auth_request_set $auth_resp_x_vouch_user $upstream_http_x_vouch_user; - - # these return values are used by the @error401 call - auth_request_set $auth_resp_jwt $upstream_http_x_vouch_jwt; - auth_request_set $auth_resp_err $upstream_http_x_vouch_err; - auth_request_set $auth_resp_failcount $upstream_http_x_vouch_failcount; -} - -# If the user is not logged in, redirect them to Vouch's login URL -location @error401 { - return 302 https://live.fic.srs.epita.fr/challenge_access/login?url=https://live.fic.srs.epita.fr$request_uri&vouch-failcount=$auth_resp_failcount&X-Vouch-Token=$auth_resp_jwt&error=$auth_resp_err; -} - -location /auth { - proxy_pass http://auth:5556; -} -location /approval { - proxy_pass http://auth:5556; -} -location /token { - proxy_pass http://auth:5556; -} -location /keys { - proxy_pass http://auth:5556; -} -location /userinfo { - proxy_pass http://auth:5556; -} -location /static { - proxy_pass http://auth:5556; -} -location /theme { - proxy_pass http://auth:5556; -} -location /.well-known/openid-configuration { - proxy_pass http://auth:5556; -} diff --git a/configs/nginx/base/demo.conf b/configs/nginx/base/demo.conf deleted file mode 100644 index 1c607ede..00000000 --- a/configs/nginx/base/demo.conf +++ /dev/null @@ -1,245 +0,0 @@ -proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=STATIC:10m inactive=24h max_size=1g; -proxy_connect_timeout 1s; - -server_tokens off; - -server { - listen 80 default; - - rewrite ^ https://$host$request_uri permanent; -} - -server { - listen 443 default ssl http2; - - ssl_protocols TLSv1.2 TLSv1.3; - ssl_dhparam /etc/nginx/ssl/dhparams-4096.pem; - ssl_prefer_server_ciphers on; - - ssl_certificate /etc/nginx/ssl/fullchain.pem; - ssl_certificate_key /etc/nginx/ssl/privkey.pem; - - ssl_client_certificate /srv/PKI/shared/ca.pem; - ssl_trusted_certificate /srv/PKI/shared/ca.pem; - ssl_verify_client optional; - - root /srv/htdocs-frontend/; - - error_page 401 /welcome.html; - error_page 403 404 /e404.html; - error_page 413 /e413.html; - error_page 500 502 504 /e500.html; - - add_header Strict-Transport-Security max-age=31536000; - add_header X-Frame-Options deny; - add_header Content-Security-Policy "script-src 'unsafe-inline' 'self' 'unsafe-eval'; img-src 'self' data:; style-src 'unsafe-inline' 'self'; font-src 'self'; default-src 'self'"; - add_header X-Xss-Protection "1; mode=block"; - add_header X-Content-Type-Options nosniff; - add_header Referrer-Policy strict-origin; - add_header Feature-Policy "accelerometer 'none'; ambient-light-sensor 'none'; autoplay 'none'; battery 'none'; camera 'none'; display-capture 'none'; document-domain 'none'; encrypted-media 'none'; fullscreen 'none'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; midi 'none'; payment 'none'; picture-in-picture 'none'; speaker 'none'; sync-xhr 'none'; usb 'none'; vr 'none'; wake-lock 'none'; xr-spatial-tracking 'none'"; - - location = / { - include fic-auth.conf; - } - location = /auth { - internal; - proxy_pass https://163.5.55.58/remote.php/webdav/; - proxy_pass_request_body off; - proxy_set_header Host "owncloud.srs.epita.fr"; - proxy_set_header Content-Length ""; - proxy_set_header X-Original-URI $request_uri; - } - location = /index.html { - include fic-auth.conf; - } - location = /welcome.html { - internal; - if ($http_accept ~ "^application/json") { - rewrite ^/(.*).html$ /$1.json; - } - } - location = /e404.html { - internal; - if ($http_accept ~ "^application/json") { - rewrite ^/(.*).html$ /$1.json; - } - } - location = /e413.html { - internal; - if ($http_accept ~ "^application/json") { - rewrite ^/(.*).html$ /$1.json; - } - } - location = /e500.html { - internal; - if ($http_accept ~ "^application/json") { - rewrite ^/(.*).html$ /$1.json; - } - } - - location ~ ^/([A-Z]|_/) { - include fic-auth.conf; - - rewrite ^/.*$ /index.html; - } - - location /edit { - include fic-auth.conf; - - rewrite ^/.*$ /index.html; - } - location /issues { - include fic-auth.conf; - - rewrite ^/.*$ /index.html; - } - location /rank { - include fic-auth.conf; - - rewrite ^/.*$ /index.html; - } - location /tags/ { - include fic-auth.conf; - - rewrite ^/.*$ /index.html; - } - location /register { - include fic-auth.conf; - - rewrite ^/.*$ /index.html; - } - location /rules { - include fic-auth.conf; - - rewrite ^/.*$ /index.html; - } - - location /files/ { - alias /srv/FILES/; - sendfile on; - tcp_nodelay on; - gzip_static always; - } - - location /wait.json { - include fic-auth.conf; - - root /srv/TEAMS/$team/; - expires epoch; - add_header Cache-Control no-cache; - } - location /stats.json { - root /srv/TEAMS/; - expires epoch; - add_header Cache-Control no-cache; - } - location /my.json { - include fic-auth.conf; - - root /srv/TEAMS/$team/; - expires epoch; - add_header Cache-Control no-cache; - - if (!-f /srv/startingblock/started) { - rewrite ^/.* /wait.json; - } - } - location /issues.json { - include fic-auth.conf; - - root /srv/TEAMS/$team/; - expires epoch; - add_header Cache-Control no-cache; - } - location /scores.json { - include fic-auth.conf; - - root /srv/TEAMS/$team/; - expires epoch; - add_header Cache-Control no-cache; - } - location = /events.json { - root /srv/TEAMS/; - expires epoch; - add_header Cache-Control no-cache; - } - location = /teams.json { - root /srv/TEAMS/; - expires epoch; - add_header Cache-Control no-cache; - } - location = /themes.json { - root /srv/TEAMS/; - expires epoch; - add_header Cache-Control no-cache; - } - location = /challenge.json { - root /srv/SETTINGSDIST/; - expires epoch; - add_header X-FIC-time $msec; - add_header Cache-Control no-cache; - } - location = /settings.json { - root /srv/SETTINGSDIST/; - expires epoch; - add_header X-FIC-time $msec; - add_header Cache-Control no-cache; - } - - location /submit/ { - include fic-auth.conf; - - proxy_pass http://receiver:8080/submission; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - location /issue { - include fic-auth.conf; - - proxy_pass http://receiver:8080; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - location /chname { - include fic-auth.conf; - - proxy_pass http://receiver:8080; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - location /registration { - include fic-auth.conf; - - proxy_pass http://receiver:8080; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - location /reset_progress { - include fic-auth.conf; - - proxy_pass http://receiver:8080; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - location /openhint/ { - include fic-auth.conf; - - proxy_pass http://receiver:8080; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - location /wantchoices/ { - include fic-auth.conf; - - proxy_pass http://receiver:8080; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } -} diff --git a/configs/nginx/base/docker.conf b/configs/nginx/base/docker.conf deleted file mode 100644 index 63d29b78..00000000 --- a/configs/nginx/base/docker.conf +++ /dev/null @@ -1,282 +0,0 @@ -server_tokens off; -proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=STATIC:10m inactive=24h max_size=1g; -proxy_connect_timeout 1s; - -server { - listen 80 default; - listen [::]:80 default; - - include fic-auth.conf; - - root ${PATH_STATIC}; - - error_page 401 /welcome.html; - error_page 403 404 /e404.html; - error_page 413 404 /e413.html; - error_page 500 502 504 /e500.html; - - error_page 401 /welcome.html; - error_page 403 404 /e404.html; - error_page 413 /e413.html; - error_page 500 502 504 /e500.html; - - add_header Strict-Transport-Security max-age=31536000; - add_header X-Frame-Options deny; - add_header Content-Security-Policy "script-src 'unsafe-inline' 'self' 'unsafe-eval'; img-src 'self' data:; style-src 'unsafe-inline' 'self'; font-src 'self'; default-src 'self'"; - add_header X-Xss-Protection "1; mode=block"; - add_header X-Content-Type-Options nosniff; - add_header Referrer-Policy strict-origin; - add_header Feature-Policy "accelerometer 'none'; ambient-light-sensor 'none'; autoplay 'none'; battery 'none'; camera 'none'; display-capture 'none'; document-domain 'none'; encrypted-media 'none'; fullscreen 'none'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; midi 'none'; payment 'none'; picture-in-picture 'none'; speaker 'none'; sync-xhr 'none'; usb 'none'; vr 'none'; wake-lock 'none'; xr-spatial-tracking 'none'"; - - location = / { - include fic-get-team.conf; - } - location = /index.html { - include fic-get-team.conf; - } - location = /welcome.html { - internal; - if ($http_accept ~ "^application/json") { - rewrite ^/(.*).html$ /$1.json; - } - } - location = /e404.html { - internal; - if ($http_accept ~ "^application/json") { - rewrite ^/(.*).html$ /$1.json; - } - } - location = /e413.html { - internal; - if ($http_accept ~ "^application/json") { - rewrite ^/(.*).html$ /$1.json; - } - } - location = /e500.html { - internal; - if ($http_accept ~ "^application/json") { - rewrite ^/(.*).html$ /$1.json; - } - } - - location ${FIC_BASEURL2} { - rewrite ^${FIC_BASEURL2}(.*)$ /$1; - } - - location ~ ^/([A-Z]|_/) { - include fic-get-team.conf; - - rewrite ^/.*$ /index.html; - } - - location /edit { - include fic-get-team.conf; - - rewrite ^/.*$ /index.html; - } - location /issues { - include fic-get-team.conf; - - rewrite ^/.*$ /index.html; - } - location /rank { - include fic-get-team.conf; - - rewrite ^/.*$ /index.html; - } - location /tags/ { - include fic-get-team.conf; - - rewrite ^/.*$ /index.html; - } - location /register { - include fic-get-team.conf; - - rewrite ^/.*$ /index.html; - } - location /rules { - include fic-get-team.conf; - - rewrite ^/.*$ /index.html; - } - - location /files/ { - alias ${PATH_FILES}/; - sendfile on; - tcp_nodelay on; - gzip_static always; - } - - location /wait.json { - include fic-get-team.conf; - - root ${PATH_TEAMS}/$team/; - expires epoch; - add_header Cache-Control no-cache; - } - location /stats.json { - root ${PATH_TEAMS}/; - expires epoch; - add_header Cache-Control no-cache; - } - location /my.json { - include fic-get-team.conf; - - root ${PATH_TEAMS}/$team/; - expires epoch; - add_header Cache-Control no-cache; - - if (!-f ${PATH_STARTINGBLOCK}/started) { - rewrite ^/ /wait.json; - } - } - location /issues.json { - include fic-get-team.conf; - - root ${PATH_TEAMS}/$team/; - expires epoch; - add_header Cache-Control no-cache; - } - location /scores.json { - include fic-get-team.conf; - - root ${PATH_TEAMS}/$team/; - expires epoch; - add_header Cache-Control no-cache; - } - location /teams.json { - root ${PATH_TEAMS}; - expires epoch; - add_header Cache-Control no-cache; - } - location /themes.json { - include fic-get-team.conf; - - root ${PATH_TEAMS}; - expires epoch; - add_header Cache-Control no-cache; - - if (!-f ${PATH_TEAMS}/$team/my.json) { - rewrite ^/ /themes-wait.json break; - } - } - location /challenge.json { - root ${PATH_SETTINGS}/; - expires epoch; - add_header X-FIC-time $msec; - add_header Cache-Control no-cache; - } - location /settings.json { - root ${PATH_SETTINGS}/; - expires epoch; - add_header X-FIC-time $msec; - add_header Cache-Control no-cache; - } - - location /submit/ { - include fic-get-team.conf; - - proxy_pass http://${HOST_RECEIVER}/submission/; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - location /issue { - include fic-get-team.conf; - - proxy_pass http://${HOST_RECEIVER}; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - location /chname { - include fic-get-team.conf; - - proxy_pass http://${HOST_RECEIVER}; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - location /registration { - include fic-get-team.conf; - - proxy_pass http://${HOST_RECEIVER}; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - location /reset_progress { - include fic-get-team.conf; - - proxy_pass http://${HOST_RECEIVER}; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - location /openhint/ { - include fic-get-team.conf; - - proxy_pass http://${HOST_RECEIVER}; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - location /wantchoices/ { - include fic-get-team.conf; - - proxy_pass http://${HOST_RECEIVER}; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - - location /api { - include fic-get-team.conf; - - proxy_pass http://${HOST_ADMIN}${FIC_BASEURL}admin/api; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - - location ${FIC_BASEURL}admin { - proxy_pass http://${HOST_ADMIN}; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_redirect off; - } - - location ${FIC_BASEURL}admin/api { - proxy_pass http://${HOST_ADMIN}; - proxy_read_timeout 400s; - proxy_set_header X-Forwarded-For $remote_addr; - } - - location ${FIC_BASEURL}qa { - include fic-get-team.conf; - - proxy_pass http://${HOST_QA}; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - - location ${FIC_BASEURL}dashboard { - include fic-get-team.conf; - - proxy_pass http://${HOST_DASHBOARD}; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - - location = /events.json { - proxy_pass http://${HOST_ADMIN}/api/events/; - proxy_method GET; - proxy_pass_request_body off; - proxy_set_header Content-Length ""; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_redirect off; - proxy_cache STATIC; - proxy_cache_valid 3s; - } -} diff --git a/configs/nginx/base/prod.conf b/configs/nginx/base/prod.conf deleted file mode 100644 index 07d7d27e..00000000 --- a/configs/nginx/base/prod.conf +++ /dev/null @@ -1,237 +0,0 @@ -proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=STATIC:10m inactive=24h max_size=1g; -proxy_connect_timeout 1s; - -server_tokens off; - -server { - listen 80 default; - - rewrite ^ https://$host$request_uri permanent; -} - -server { - listen 443 default ssl http2; - - ssl_protocols TLSv1.2 TLSv1.3; - ssl_dhparam /etc/nginx/ssl/dhparams-4096.pem; - ssl_prefer_server_ciphers on; - - ssl_certificate /etc/nginx/ssl/fullchain.pem; - ssl_certificate_key /etc/nginx/ssl/privkey.pem; - - include fic-auth.conf; - - root /srv/htdocs-frontend/; - - error_page 401 /welcome.html; - error_page 403 404 /e404.html; - error_page 413 /e413.html; - error_page 500 502 504 /e500.html; - - add_header Strict-Transport-Security max-age=31536000; - add_header X-Frame-Options deny; - add_header Content-Security-Policy "script-src 'unsafe-inline' 'self' 'unsafe-eval'; img-src 'self' data:; style-src 'unsafe-inline' 'self'; font-src 'self'; default-src 'self'"; - add_header X-Xss-Protection "1; mode=block"; - add_header X-Content-Type-Options nosniff; - add_header Referrer-Policy strict-origin; - add_header Feature-Policy "accelerometer 'none'; ambient-light-sensor 'none'; autoplay 'none'; battery 'none'; camera 'none'; display-capture 'none'; document-domain 'none'; encrypted-media 'none'; fullscreen 'none'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; midi 'none'; payment 'none'; picture-in-picture 'none'; speaker 'none'; sync-xhr 'none'; usb 'none'; vr 'none'; wake-lock 'none'; xr-spatial-tracking 'none'"; - - location = / { - include fic-get-team.conf; - } - location = /index.html { - include fic-get-team.conf; - } - location = /welcome.html { - internal; - if ($http_accept ~ "^application/json") { - rewrite ^/(.*).html$ /$1.json; - } - } - location = /e404.html { - internal; - if ($http_accept ~ "^application/json") { - rewrite ^/(.*).html$ /$1.json; - } - } - location = /e413.html { - internal; - if ($http_accept ~ "^application/json") { - rewrite ^/(.*).html$ /$1.json; - } - } - location = /e500.html { - internal; - if ($http_accept ~ "^application/json") { - rewrite ^/(.*).html$ /$1.json; - } - } - - location ~ ^/([A-Z]|_/) { - include fic-get-team.conf; - - rewrite ^/.*$ /index.html; - } - - location /edit { - include fic-get-team.conf; - - rewrite ^/.*$ /index.html; - } - location /issues { - include fic-get-team.conf; - - rewrite ^/.*$ /index.html; - } - location /rank { - include fic-get-team.conf; - - rewrite ^/.*$ /index.html; - } - location /tags/ { - include fic-get-team.conf; - - rewrite ^/.*$ /index.html; - } - location /register { - include fic-get-team.conf; - - rewrite ^/.*$ /index.html; - } - location /rules { - include fic-get-team.conf; - - rewrite ^/.*$ /index.html; - } - - location /files/ { - alias /srv/FILES/; - sendfile on; - tcp_nodelay on; - gzip_static always; - } - - location /wait.json { - include fic-get-team.conf; - - root /srv/TEAMS/$team/; - expires epoch; - add_header Cache-Control no-cache; - } - location /stats.json { - root /srv/TEAMS/; - expires epoch; - add_header Cache-Control no-cache; - } - location /my.json { - include fic-get-team.conf; - - root /srv/TEAMS/$team/; - expires epoch; - add_header Cache-Control no-cache; - - if (!-f /srv/startingblock/started) { - rewrite ^/.* /wait.json; - } - } - location /issues.json { - include fic-get-team.conf; - - root /srv/TEAMS/$team/; - expires epoch; - add_header Cache-Control no-cache; - } - location /scores.json { - include fic-get-team.conf; - - root /srv/TEAMS/$team/; - expires epoch; - add_header Cache-Control no-cache; - } - location = /events.json { - root /srv/TEAMS/; - expires epoch; - add_header Cache-Control no-cache; - } - location = /teams.json { - root /srv/TEAMS/; - expires epoch; - add_header Cache-Control no-cache; - } - location = /themes.json { - root /srv/TEAMS/; - expires epoch; - add_header Cache-Control no-cache; - } - location = /challenge.json { - root /srv/SETTINGSDIST/; - expires epoch; - add_header X-FIC-time $msec; - add_header Cache-Control no-cache; - } - location = /settings.json { - root /srv/SETTINGSDIST/; - expires epoch; - add_header X-FIC-time $msec; - add_header Cache-Control no-cache; - } - - location /submit/ { - include fic-get-team.conf; - - proxy_pass http://receiver:8080/submission/; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - location /issue { - include fic-get-team.conf; - - proxy_pass http://receiver:8080; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - location /chname { - include fic-get-team.conf; - - proxy_pass http://receiver:8080; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - location /registration { - include fic-get-team.conf; - - proxy_pass http://receiver:8080; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - location /reset_progress { - include fic-get-team.conf; - - proxy_pass http://receiver:8080; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - location /openhint/ { - include fic-get-team.conf; - - proxy_pass http://receiver:8080; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - location /wantchoices/ { - include fic-get-team.conf; - - proxy_pass http://receiver:8080; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - - include /etc/nginx/conf.d/*.fic-conf; -} diff --git a/configs/nginx/base/static.conf b/configs/nginx/base/static.conf deleted file mode 100644 index bada9896..00000000 --- a/configs/nginx/base/static.conf +++ /dev/null @@ -1,69 +0,0 @@ -server { - listen 80 default_server; - listen [::]:80 default_server; - - server_name fic.srs.epita.fr; - - access_log /var/log/nginx/fic2016.access_log main; - error_log /var/log/nginx/fic2016.error_log info; - - root /srv/www/fic2016-static/; - - error_page 403 404 /e404.html; - error_page 413 404 /e413.html; - error_page 500 502 504 /e500.html; - - location /.htaccess { - return 404; - } - location /chbase.sh { - return 404; - } - - location ~ ^/[0-9] { - rewrite ^/.*$ /index.html; - } - - location /edit { - rewrite ^/.*$ /index.html; - } - - location /rank { - rewrite ^/.*$ /index.html; - } - - location /tags/ { - rewrite ^/.*$ /index.html; - } - - location /files/ { - sendfile on; - tcp_nodelay on; - gzip_static always; - } - - location = /welcome.html { - internal; - if ($http_accept ~ "^application/json") { - rewrite ^/(.*).html$ /$1.json; - } - } - location = /e404.html { - internal; - if ($http_accept ~ "^application/json") { - rewrite ^/(.*).html$ /$1.json; - } - } - location = /e413.html { - internal; - if ($http_accept ~ "^application/json") { - rewrite ^/(.*).html$ /$1.json; - } - } - location = /e500.html { - internal; - if ($http_accept ~ "^application/json") { - rewrite ^/(.*).html$ /$1.json; - } - } -} diff --git a/configs/nginx/get-team/client-cert.conf b/configs/nginx/get-team/client-cert.conf deleted file mode 100644 index e0026b09..00000000 --- a/configs/nginx/get-team/client-cert.conf +++ /dev/null @@ -1,19 +0,0 @@ -set $auth_basic "Challenge FIC"; -if ($ssl_client_verify != "SUCCESS") { - set $team "$remote_user"; - set $needauth "1"; -} -if ($ssl_client_verify = "SUCCESS") { - set $team "_AUTH_ID_$ssl_client_serial"; - set $auth_basic off; - set $needauth "0"; -} -if (!-f /srv/PKI/shared/ficpasswd) { - set $needauth "${needauth}0"; -} -if ($needauth = "10") { - return 401; -} - -auth_basic $auth_basic; -auth_basic_user_file /srv/PKI/shared/ficpasswd; diff --git a/configs/nginx/get-team/oidc.conf b/configs/nginx/get-team/oidc.conf deleted file mode 100644 index 32709ab7..00000000 --- a/configs/nginx/get-team/oidc.conf +++ /dev/null @@ -1,3 +0,0 @@ -auth_request /challenge_access/validate; - -auth_request_set $team "$upstream_http_x_vouch_user"; diff --git a/configs/nginx/get-team/request.conf b/configs/nginx/get-team/request.conf deleted file mode 100644 index b3453f71..00000000 --- a/configs/nginx/get-team/request.conf +++ /dev/null @@ -1,3 +0,0 @@ -auth_request /auth; - -set $team "$remote_user"; diff --git a/configs/nginx/get-team/team-1.conf b/configs/nginx/get-team/team-1.conf deleted file mode 100644 index a8900e81..00000000 --- a/configs/nginx/get-team/team-1.conf +++ /dev/null @@ -1 +0,0 @@ -set $team 1; \ No newline at end of file diff --git a/configs/nginx/get-team/upstream.conf b/configs/nginx/get-team/upstream.conf deleted file mode 100644 index 370ae10f..00000000 --- a/configs/nginx/get-team/upstream.conf +++ /dev/null @@ -1 +0,0 @@ -set $team "$http_x_fic_team"; diff --git a/configs/nsenter_iptables.sh b/configs/nsenter_iptables.sh deleted file mode 100755 index 80b6986f..00000000 --- a/configs/nsenter_iptables.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -if [ -d /containers/onboot/006-synchro-ip-setup ]; then - LOWER=/containers/onboot/006-synchro-ip-setup/lower -elif [ -d /containers/onboot/006-nginx-ip-setup ]; then - LOWER=/containers/onboot/006-nginx-ip-setup/lower -else - nsenter -t 1 -m -u -i -p -- "$0" $@ - exit $? -fi - -mount -t tmpfs none $LOWER/run - -chroot $LOWER iptables $@ -EXIT=$? - -umount $LOWER/run - -exit ${EXIT} diff --git a/configs/nsenter_mysql.sh b/configs/nsenter_mysql.sh deleted file mode 100755 index e3d4b29d..00000000 --- a/configs/nsenter_mysql.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -nsenter -t $(pgrep mariadb | head -1) -m -u -i -n -p -- mariadb $@ diff --git a/configs/nsenter_process.sh b/configs/nsenter_process.sh deleted file mode 100755 index 8e78f753..00000000 --- a/configs/nsenter_process.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -PROC="$1" -shift - -nsenter -t $(pgrep "$PROC" | head -1) $@ -exit $? diff --git a/configs/pxelinux.cfg b/configs/pxelinux.cfg deleted file mode 100644 index 8f9a58b7..00000000 --- a/configs/pxelinux.cfg +++ /dev/null @@ -1,35 +0,0 @@ -TIMEOUT 30 -ONTIMEOUT update - -MENU background #00000000 * * -MENU color title * #FF22BBCC * -MENU color sel * #FFFFFFFF #FF22BBCC * -MENU color hotsel 1;7;37;40 #ffffffff #76a1d0ff * - -UI vesamenu.c32 -MENU TITLE FICKIT PXE BOOT - -LABEL backend - MENU LABEL Prepare for ^backend - LINUX /s/fickit-boot-kernel - INITRD /s/fickit-prepare-initrd.img - APPEND console=tty0 fickit.autoprepare=backend -LABEL frontend - MENU LABEL Prepare for ^frontend - LINUX /s/fickit-boot-kernel - INITRD /s/fickit-prepare-initrd.img - APPEND console=tty0 fickit.autoprepare=frontend -LABEL prepare - MENU LABEL Prepare with ^shell - LINUX /s/fickit-boot-kernel - INITRD /s/fickit-prepare-initrd.img - APPEND console=tty0 -LABEL update - MENU LABEL ^Update images - LINUX /s/fickit-boot-kernel - INITRD /s/fickit-update-initrd.img - APPEND console=ttyS0 console=tty0 -MENU SEPARATOR -LABEL poweroff - MENU LABEL ^Shutdown - KERNEL poweroff.c32 diff --git a/configs/sshd-setup.sh b/configs/sshd-setup.sh deleted file mode 100644 index e5f969f8..00000000 --- a/configs/sshd-setup.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -[ -f /var/lib/fic/ssh/sshd_config ] && exit 0 - -mkdir -p /var/lib/fic/ssh/ - -cp /containers/services/sshd/rootfs/etc/ssh/* /var/lib/fic/ssh/ - -mount -o bind /dev /containers/services/sshd/rootfs/dev -mount -o bind /proc /containers/services/sshd/rootfs/proc -mount -o bind /sys /containers/services/sshd/rootfs/sys -mount -o bind /var/lib/fic/ssh/ /containers/services/sshd/rootfs/etc/ssh - -chroot /containers/services/sshd/rootfs/ ssh-keygen -A - -umount /containers/services/sshd/rootfs/dev /containers/services/sshd/rootfs/proc /containers/services/sshd/rootfs/sys /containers/services/sshd/rootfs/etc/ssh diff --git a/configs/synchro.sh b/configs/synchro.sh deleted file mode 100755 index 8fd8f8df..00000000 --- a/configs/synchro.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash - -# This script synchronizes first, the generated frontend and then -# retrieves submissions - -BASEDIR="/srv" -FRONTEND_HOSTNAME="phobos" - -SSH_OPTS="ssh -p 22 -i ~/.ssh/id_ed25519 -o ControlMaster=auto -o ControlPath=/root/.ssh/%r@%h:%p -o ControlPersist=2 -o PasswordAuthentication=no -o StrictHostKeyChecking=no" - -cd "${BASEDIR}" - -touch /tmp/stop - -# Establish first ssh connection for controlpersist socket, to avoid delay during time synchronization -${SSH_OPTS} ${FRONTEND_HOSTNAME} ls > /dev/null - -# Synchronize the date one time -${SSH_OPTS} ${FRONTEND_HOSTNAME} date -s @"$(date +%s)" - -# Synchronize static files in a separate loop (to avoid submissions delays during file synchronization) -while ! [ -f SETTINGS/stop ] || [ /tmp/stop -nt SETTINGS/stop ] -do - rsync -e "$SSH_OPTS" -av --delete FILES "${FRONTEND_HOSTNAME}":"${BASEDIR}" - - # Synchronize logs - rsync -e "$SSH_OPTS" -av "${FRONTEND_HOSTNAME}":/var/log/ /var/log/frontend - - sleep 5 -done & - -while ! [ -f SETTINGS/stop ] || [ /tmp/stop -nt SETTINGS/stop ] -do - # Synchronize static files pages - rsync -e "$SSH_OPTS" -av --delete --delay-updates --partial-dir=.tmp/ PKI TEAMS SETTINGSDIST "${FRONTEND_HOSTNAME}":"${BASEDIR}" - - # Synchronize submissions - rsync -e "$SSH_OPTS" -av --ignore-existing --delay-updates --temp-dir=.tmp/ --partial-dir=.tmp/ --remove-source-files "${FRONTEND_HOSTNAME}":"${BASEDIR}"/submissions/ submissions/ - - sleep 0.3 -done - -wait -echo See you diff --git a/configs/sysctl-backend.conf b/configs/sysctl-backend.conf deleted file mode 100644 index 50ca3f42..00000000 --- a/configs/sysctl-backend.conf +++ /dev/null @@ -1,7 +0,0 @@ -net.ipv6.conf.all.disable_ipv6 = 1 - -# Increase system file descriptor limit -fs.file-max = 65535 - -# Increase system IP port limits -net.ipv4.ip_local_port_range = 2000 65000 diff --git a/configs/sysctl-frontend.conf b/configs/sysctl-frontend.conf deleted file mode 100644 index 50ca3f42..00000000 --- a/configs/sysctl-frontend.conf +++ /dev/null @@ -1,7 +0,0 @@ -net.ipv6.conf.all.disable_ipv6 = 1 - -# Increase system file descriptor limit -fs.file-max = 65535 - -# Increase system IP port limits -net.ipv4.ip_local_port_range = 2000 65000 diff --git a/configs/udhcpd-sample.conf b/configs/udhcpd-sample.conf deleted file mode 100644 index 30f9b119..00000000 --- a/configs/udhcpd-sample.conf +++ /dev/null @@ -1,59 +0,0 @@ -# Sample udhcpd configuration file (/etc/udhcpd.conf) -# Values shown are defaults - -# The start and end of the IP lease block -start 192.168.255.100 -end 192.168.255.200 - -# The interface that udhcpd will use -interface eth0 - -# The maximum number of leases (includes addresses reserved -# by OFFER's, DECLINE's, and ARP conflicts). Will be corrected -# if it's bigger than IP lease block, but it ok to make it -# smaller than lease block. -max_leases 100 - -# The amount of time that an IP will be reserved (leased to nobody) -# if a DHCP decline message is received (seconds) -#decline_time 3600 - -# The amount of time that an IP will be reserved -# if an ARP conflict occurs (seconds) -#conflict_time 3600 - -# How long an offered address is reserved (seconds) -#offer_time 60 - -# If client asks for lease below this value, it will be rounded up -# to this value (seconds) -#min_lease 60 - -# The location of the pid file -#pidfile /var/run/udhcpd.pid - -# The location of the leases file -#lease_file /var/lib/misc/udhcpd.leases - -# The following are BOOTP specific options -# next server to use in bootstrap -siaddr 192.168.255.2 # default: 0.0.0.0 (none) -# tftp server name -#sname zorak # default: none -# tftp file to download (e.g. kernel image) -boot_file pxelinux.0 # default: none - -# NOTE: "boot_file FILE" and "opt bootfile FILE" are conceptually the same, -# but "boot_file" goes into BOOTP-defined fixed-size field in the packet, -# whereas "opt bootfile" goes into DHCP option 0x43. -# Same for "sname HOST" and "opt tftp HOST". - -# The remainder of options are DHCP options and can be specified with the -# keyword 'opt' or 'option'. If an option can take multiple items, such -# as the dns option, they can be listed on the same line, or multiple -# lines. -# Examples: -#opt dns 192.168.10.2 192.168.10.10 -option subnet 255.255.255.0 -opt router 192.168.255.2 -option lease 3600 diff --git a/configs/update-backend.sh b/configs/update-backend.sh deleted file mode 100755 index efeb1aab..00000000 --- a/configs/update-backend.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -IP_BACKEND=192.168.3.92 -IMG_BACKEND=fickit-backend-squashfs.img -IMG_METADATA=fickit-metadata.iso - -echo "Sending image..." -rsync -v -e ssh "${IMG_BACKEND}" "${IMG_METADATA}" "root@${IP_BACKEND}:/var/lib/fic/outofsync/" || exit 1 - -echo "Done!" -echo "Now, execute upgrade_image on backend, through iDRAC interface." diff --git a/configs/update_imgs.sh b/configs/update_imgs.sh deleted file mode 100644 index fa507cee..00000000 --- a/configs/update_imgs.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/sh - -mkdir -p /boot/imgs - -# Backup the previous metadata -/usr/bin/metadata -v cdrom -mv /boot/imgs/fickit-metadata.iso /boot/imgs/fickit-metadata.iso.bak - -for img in fickit-boot-kernel fickit-metadata.iso fickit-boot-initrd.img fickit-prepare-initrd.img fickit-frontend-squashfs.img fickit-backend-squashfs.img fickit-update-initrd.img -do - wget -O "/boot/imgs/${img}" "$1/${img}" -done - -# Check dm-crypt key not changed -ISO=$(mktemp -d) -mount /boot/imgs/fickit-metadata.iso "${ISO}" - -NEW_KEY=$(sed -rn 's/.*"content": "([^"]+)"$/\1/p' "${ISO}/user-data" | head -n 1) -OLD_KEY=$(cat /run/config/dm-crypt/key) - -[ "${NEW_KEY}" != "${OLD_KEY}" ] && { - read -p "DM-CRYPT key changed in metadata, are you sure you want to erase it? (y/N) " V - [ "$V" != "y" ] && [ "$V" != "Y" ] && while true; do - mv /boot/imgs/fickit-metadata.iso /boot/imgs/fickit-metadata.iso.skipped - cp /boot/imgs/fickit-metadata.iso.bak /boot/imgs/fickit-metadata.iso - echo - echo "Metadata drive not erased" - echo - /bin/ash - sync - reboot -f - done -} - -umount "${ISO}" - -dd if=/boot/imgs/fickit-metadata.iso of="$2" diff --git a/dashboard/.gitignore b/dashboard/.gitignore deleted file mode 100644 index ef360c84..00000000 --- a/dashboard/.gitignore +++ /dev/null @@ -1 +0,0 @@ -dashboard \ No newline at end of file diff --git a/dashboard/app.go b/dashboard/app.go deleted file mode 100644 index 6f6ecaaf..00000000 --- a/dashboard/app.go +++ /dev/null @@ -1,75 +0,0 @@ -package main - -import ( - "context" - "log" - "net/http" - "time" - - "github.com/gin-gonic/gin" -) - -type App struct { - router *gin.Engine - srv *http.Server - bind string - ips []string -} - -func NewApp(htpasswd_file *string, restrict_to_ips *string, baseURL string, bind string) App { - gin.ForceConsoleColor() - router := gin.Default() - - var baserouter *gin.RouterGroup - if len(baseURL) > 1 { - router.GET("/", func(c *gin.Context) { - c.Redirect(http.StatusFound, baseURL) - }) - - baserouter = router.Group(baseURL) - } else { - baserouter = router.Group("") - } - - if htpasswd_file != nil && len(*htpasswd_file) > 0 { - baserouter.Use(Htpassword(*htpasswd_file)) - } - - app := App{ - router: router, - bind: bind, - } - - if restrict_to_ips != nil && len(*restrict_to_ips) > 0 { - app.loadAndWatchIPs(*restrict_to_ips) - baserouter.Use(app.Restrict2IPs) - } - - declareStaticRoutes(baserouter, baseURL) - - return app -} - -func (app *App) Start() { - app.srv = &http.Server{ - Addr: app.bind, - Handler: app.router, - ReadHeaderTimeout: 15 * time.Second, - ReadTimeout: 15 * time.Second, - WriteTimeout: 10 * time.Second, - IdleTimeout: 30 * time.Second, - } - - log.Printf("Ready, listening on %s\n", app.bind) - if err := app.srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { - log.Fatalf("listen: %s\n", err) - } -} - -func (app *App) Stop() { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - if err := app.srv.Shutdown(ctx); err != nil { - log.Fatal("Server Shutdown:", err) - } -} diff --git a/dashboard/fwd.go b/dashboard/fwd.go deleted file mode 100644 index 9514a134..00000000 --- a/dashboard/fwd.go +++ /dev/null @@ -1,50 +0,0 @@ -package main - -import ( - "io" - "net/http" - "net/url" - "os" - "path" -) - -func fwd_request(w http.ResponseWriter, r *http.Request, fwd string) { - if u, err := url.Parse(fwd); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } else { - var user, pass string - if u.User != nil { - user = u.User.Username() - pass, _ = u.User.Password() - u.User = nil - } - if v, exists := os.LookupEnv("FICCLOUD_USER"); exists { - user = v - } else if v, exists := os.LookupEnv("FICCLOUD_PASS"); exists { - pass = v - } - - u.Path = path.Join(u.Path, r.URL.Path) - - if r, err := http.NewRequest(r.Method, u.String(), r.Body); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } else { - if len(user) != 0 || len(pass) != 0 { - r.SetBasicAuth(user, pass) - } - - if resp, err := http.DefaultClient.Do(r); err != nil { - http.Error(w, err.Error(), http.StatusBadGateway) - } else { - defer resp.Body.Close() - - for key := range resp.Header { - w.Header().Add(key, resp.Header.Get(key)) - } - w.WriteHeader(resp.StatusCode) - - io.Copy(w, resp.Body) - } - } - } -} diff --git a/dashboard/htpasswd.go b/dashboard/htpasswd.go deleted file mode 100644 index 1f62bf9a..00000000 --- a/dashboard/htpasswd.go +++ /dev/null @@ -1,78 +0,0 @@ -package main - -import ( - "bufio" - "fmt" - "log" - "net/http" - "os" - "strings" - - "github.com/gin-gonic/gin" - "gitlab.com/nyarla/go-crypt" -) - -func Htpassword(file string) func(c *gin.Context) { - htpasswd := &Htpasswd{} - - log.Println("Reading htpasswd file...") - var err error - if htpasswd, err = NewHtpasswd(file); htpasswd == nil { - log.Fatal("Unable to parse htpasswd:", err) - } - - return func(c *gin.Context) { - username, password, ok := c.Request.BasicAuth() - if !ok { - c.Writer.Header().Add("WWW-Authenticate", "Basic realm=\"FIC challenge Dashboard\"") - c.AbortWithError(http.StatusUnauthorized, fmt.Errorf("Please login")) - return - } - - if !htpasswd.Authenticate(username, password) { - c.Writer.Header().Add("WWW-Authenticate", "Basic realm=\"FIC challenge Dashboard\"") - c.AbortWithError(http.StatusUnauthorized, fmt.Errorf("Not authorized")) - return - } - - c.Next() - } -} - -type Htpasswd struct { - entries map[string]string -} - -func NewHtpasswd(path string) (*Htpasswd, error) { - if fd, err := os.Open(path); err != nil { - return nil, err - } else { - defer fd.Close() - - htpasswd := Htpasswd{ - map[string]string{}, - } - - scanner := bufio.NewScanner(fd) - for scanner.Scan() { - line := strings.SplitN(strings.TrimSpace(scanner.Text()), ":", 2) - if len(line) == 2 && len(line[1]) > 2 { - htpasswd.entries[line[0]] = line[1] - } - } - if err := scanner.Err(); err != nil { - return nil, err - } - return &htpasswd, nil - } -} - -func (h Htpasswd) Authenticate(username, password string) bool { - if hash, ok := h.entries[username]; !ok { - return false - } else if crypt.Crypt(password, hash[:2]) != hash { - return false - } else { - return true - } -} diff --git a/dashboard/main.go b/dashboard/main.go deleted file mode 100644 index 4c6de9e0..00000000 --- a/dashboard/main.go +++ /dev/null @@ -1,90 +0,0 @@ -package main - -import ( - "flag" - "io/fs" - "log" - "net/http" - "os" - "os/signal" - "path" - "path/filepath" - "syscall" - - "srs.epita.fr/fic-server/libfic" - "srs.epita.fr/fic-server/settings" -) - -var DashboardDir string -var TeamsDir string - -func main() { - var baseURL string - // Read paremeters from environment - if v, exists := os.LookupEnv("FIC_BASEURL"); exists { - baseURL = v - } - - // Read parameters from command line - var bind = flag.String("bind", "127.0.0.1:8082", "Bind port/socket") - htpasswd_file := flag.String("htpasswd", "", "Restrict access with password, Apache htpasswd format") - restrict_ip := flag.String("restrict-to-ips", "", "Restrict access to IP listed in this JSON array") - flag.StringVar(&baseURL, "baseurl", baseURL, "URL prepended to each URL") - staticDir := flag.String("static", "./htdocs-dashboard/", "Directory containing static files") - flag.StringVar(&fic.FilesDir, "files", fic.FilesDir, "Base directory where found challenges files, local part") - flag.StringVar(&DashboardDir, "dashbord", "./DASHBOARD", "Base directory where save public JSON files") - flag.StringVar(&TeamsDir, "teams", "./TEAMS", "Base directory where save teams JSON files") - flag.StringVar(&settings.SettingsDir, "settings", "./SETTINGSDIST", "Base directory where load and save settings") - var fwdr = flag.String("forwarder", "", "URL of another dashboard where send traffic to, except static assets") - flag.BoolVar(&fwdPublicJson, "fwdpublicjson", fwdPublicJson, "Also forward public.json files to forwarder") - flag.Parse() - - log.SetPrefix("[public] ") - - // Sanitize options - var err error - log.Println("Checking paths...") - if staticDir != nil && *staticDir != "" { - if sDir, err := filepath.Abs(*staticDir); err != nil { - log.Fatal(err) - } else { - log.Println("Serving pages from", sDir) - staticFS = http.Dir(sDir) - } - } else { - sub, err := fs.Sub(assets, "static") - if err != nil { - log.Fatal("Unable to cd to static/ directory:", err) - } - log.Println("Serving pages from memory.") - staticFS = http.FS(sub) - } - if fic.FilesDir, err = filepath.Abs(fic.FilesDir); err != nil { - log.Fatal(err) - } - if settings.SettingsDir, err = filepath.Abs(settings.SettingsDir); err != nil { - log.Fatal(err) - } - if baseURL != "/" { - baseURL = path.Clean(baseURL) - } else { - baseURL = "" - } - if fwdr != nil && len(*fwdr) > 0 { - forwarder = fwdr - } - - // Prepare graceful shutdown - interrupt := make(chan os.Signal, 1) - signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM) - - app := NewApp(htpasswd_file, restrict_ip, baseURL, *bind) - go app.Start() - - // Wait shutdown signal - <-interrupt - - log.Print("The service is shutting down...") - app.Stop() - log.Println("done") -} diff --git a/dashboard/restrict_ip.go b/dashboard/restrict_ip.go deleted file mode 100644 index e2ec172c..00000000 --- a/dashboard/restrict_ip.go +++ /dev/null @@ -1,99 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "log" - "net/http" - "os" - "os/signal" - "path" - "strings" - "syscall" - - "github.com/gin-gonic/gin" - "gopkg.in/fsnotify.v1" -) - -func (app *App) loadAndWatchIPs(ipfile string) { - // First load of file if it exists - app.tryReloadIPList(ipfile) - - // Register SIGHUP - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGHUP) - go func() { - for range c { - log.Println("SIGHUP received, reloading ip list...") - go app.tryReloadIPList(ipfile) - } - }() - - // Watch the configuration file - if watcher, err := fsnotify.NewWatcher(); err != nil { - log.Fatal(err) - } else { - if err := watcher.Add(path.Dir(ipfile)); err != nil { - log.Fatal("Unable to watch: ", path.Dir(ipfile), ": ", err) - } - - go func() { - defer watcher.Close() - for { - select { - case ev := <-watcher.Events: - if path.Base(ev.Name) == path.Base(ipfile) && ev.Op&(fsnotify.Write|fsnotify.Create) != 0 { - log.Println("IPs file changes, reloading them!") - go app.tryReloadIPList(ipfile) - } - case err := <-watcher.Errors: - log.Println("watcher error:", err) - } - } - }() - } -} - -func (app *App) tryReloadIPList(ipfile string) { - if _, err := os.Stat(ipfile); !os.IsNotExist(err) { - if iplist, err := loadIPs(ipfile); err != nil { - log.Println("ERROR: Unable to read ip file:", err) - } else { - log.Printf("Loading %d IPs to authorized list", len(iplist)) - app.ips = iplist - } - } -} - -func loadIPs(file string) ([]string, error) { - fd, err := os.Open(file) - if err != nil { - return nil, err - } - defer fd.Close() - - var ret []string - jdec := json.NewDecoder(fd) - - if err := jdec.Decode(&ret); err != nil { - return ret, err - } - - return ret, nil -} - -func (app *App) Restrict2IPs(c *gin.Context) { - found := false - for _, ip := range app.ips { - if strings.HasPrefix(c.Request.RemoteAddr, ip) { - found = true - break - } - } - - if found { - c.Next() - } else { - c.AbortWithError(http.StatusForbidden, fmt.Errorf("IP not authorized: %s", c.Request.RemoteAddr)) - } -} diff --git a/dashboard/static.go b/dashboard/static.go deleted file mode 100644 index 55308ce0..00000000 --- a/dashboard/static.go +++ /dev/null @@ -1,248 +0,0 @@ -package main - -import ( - "bytes" - "embed" - "fmt" - "io" - "io/ioutil" - "log" - "net/http" - "path" - "strings" - "time" - - "srs.epita.fr/fic-server/libfic" - "srs.epita.fr/fic-server/settings" - - "github.com/gin-gonic/gin" -) - -//go:embed static - -var assets embed.FS -var staticFS http.FileSystem - -var forwarder *string = nil -var fwdPublicJson = false - -var indexTmpl []byte - -func getIndexHtml(w io.Writer, baseURL string) { - if len(indexTmpl) == 0 { - if file, err := staticFS.Open("index.html"); err != nil { - log.Println("Unable to open index.html: ", err) - } else { - defer file.Close() - - if indexTmpl, err = ioutil.ReadAll(file); err != nil { - log.Println("Cannot read whole index.html: ", err) - } else { - indexTmpl = bytes.Replace(indexTmpl, []byte("{{.urlbase}}"), []byte(path.Clean(path.Join(baseURL+"/", "nuke"))[:len(path.Clean(path.Join(baseURL+"/", "nuke")))-4]), -1) - } - } - } - - w.Write(indexTmpl) -} - -func serveFile(c *gin.Context, url string) { - c.Request.URL.Path = url - http.FileServer(staticFS).ServeHTTP(c.Writer, c.Request) -} - -func declareStaticRoutes(router *gin.RouterGroup, baseURL string) { - router.GET("/", func(c *gin.Context) { - http.Redirect(c.Writer, c.Request, "public0.html", http.StatusFound) - }) - for i := 0; i <= 9; i++ { - router.GET(fmt.Sprintf("/public%d.html", i), func(c *gin.Context) { - getIndexHtml(c.Writer, baseURL) - }) - } - - router.GET("/css/*_", func(c *gin.Context) { - serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL)) - }) - router.GET("/fonts/*_", func(c *gin.Context) { - serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL)) - }) - router.GET("/img/*_", func(c *gin.Context) { - serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL)) - }) - router.GET("/js/*_", func(c *gin.Context) { - serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL)) - }) - router.GET("/views/*_", func(c *gin.Context) { - serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL)) - }) - - router.GET("/files/*_", func(c *gin.Context) { - if forwarder != nil { - fwd_request(c.Writer, c.Request, *forwarder) - } else { - http.ServeFile(c.Writer, c.Request, path.Join(fic.FilesDir, strings.TrimPrefix(c.Request.URL.Path, "/"+path.Join(baseURL, "files")))) - } - }) - - router.GET("/events.json", func(c *gin.Context) { - c.Writer.Header().Set("Cache-Control", "no-cache") - if forwarder != nil { - fwd_request(c.Writer, c.Request, *forwarder) - } else { - http.ServeFile(c.Writer, c.Request, path.Join(TeamsDir, "events.json")) - } - }) - router.GET("/my.json", func(c *gin.Context) { - c.Writer.Header().Set("Cache-Control", "no-cache") - if forwarder != nil { - fwd_request(c.Writer, c.Request, *forwarder) - } else { - http.ServeFile(c.Writer, c.Request, path.Join(TeamsDir, "public", "my.json")) - } - }) - router.GET("/api/teams/:tid/score-grid.json", func(c *gin.Context) { - if forwarder != nil { - fwd_request(c.Writer, c.Request, *forwarder) - } else { - fwd_request(c.Writer, c.Request, "http://127.0.0.1:8081/") - } - }) - router.GET("/api/teams/:tid/stats.json", func(c *gin.Context) { - if forwarder != nil { - fwd_request(c.Writer, c.Request, *forwarder) - } else { - fwd_request(c.Writer, c.Request, "http://127.0.0.1:8081/") - } - }) - router.GET("/challenge.json", func(c *gin.Context) { - c.Writer.Header().Set("Cache-Control", "no-cache") - if forwarder != nil { - fwd_request(c.Writer, c.Request, *forwarder) - } else { - http.ServeFile(c.Writer, c.Request, path.Join(settings.SettingsDir, settings.ChallengeFile)) - } - }) - router.GET("/settings.json", func(c *gin.Context) { - c.Writer.Header().Set("Cache-Control", "no-cache") - if forwarder != nil { - fwd_request(c.Writer, c.Request, *forwarder) - } else { - c.Writer.Header().Set("X-FIC-Time", fmt.Sprintf("%f", float64(time.Now().UnixNano()/1000)/1000000)) - http.ServeFile(c.Writer, c.Request, path.Join(settings.SettingsDir, settings.SettingsFile)) - } - }) - router.GET("/teams.json", func(c *gin.Context) { - c.Writer.Header().Set("Cache-Control", "no-cache") - if forwarder != nil { - fwd_request(c.Writer, c.Request, *forwarder) - } else { - http.ServeFile(c.Writer, c.Request, path.Join(TeamsDir, "public", "teams.json")) - } - }) - router.GET("/themes.json", func(c *gin.Context) { - c.Writer.Header().Set("Cache-Control", "no-cache") - if forwarder != nil { - fwd_request(c.Writer, c.Request, *forwarder) - } else { - http.ServeFile(c.Writer, c.Request, path.Join(TeamsDir, "themes.json")) - } - }) - - router.GET("/background.png", func(c *gin.Context) { - if forwarder != nil && fwdPublicJson { - fwd_request(c.Writer, c.Request, *forwarder) - } else { - http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "background.png")) - } - }) - - router.GET("/public.json", func(c *gin.Context) { - c.Writer.Header().Set("Cache-Control", "no-cache") - if forwarder != nil && fwdPublicJson { - fwd_request(c.Writer, c.Request, *forwarder) - } else { - http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public.json")) - } - }) - router.GET("/public0.json", func(c *gin.Context) { - c.Writer.Header().Set("Cache-Control", "no-cache") - if forwarder != nil && fwdPublicJson { - fwd_request(c.Writer, c.Request, *forwarder) - } else { - http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public0.json")) - } - }) - router.GET("/public1.json", func(c *gin.Context) { - c.Writer.Header().Set("Cache-Control", "no-cache") - if forwarder != nil && fwdPublicJson { - fwd_request(c.Writer, c.Request, *forwarder) - } else { - http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public1.json")) - } - }) - router.GET("/public2.json", func(c *gin.Context) { - c.Writer.Header().Set("Cache-Control", "no-cache") - if forwarder != nil && fwdPublicJson { - fwd_request(c.Writer, c.Request, *forwarder) - } else { - http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public2.json")) - } - }) - router.GET("/public3.json", func(c *gin.Context) { - c.Writer.Header().Set("Cache-Control", "no-cache") - if forwarder != nil && fwdPublicJson { - fwd_request(c.Writer, c.Request, *forwarder) - } else { - http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public3.json")) - } - }) - router.GET("/public4.json", func(c *gin.Context) { - c.Writer.Header().Set("Cache-Control", "no-cache") - if forwarder != nil && fwdPublicJson { - fwd_request(c.Writer, c.Request, *forwarder) - } else { - http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public4.json")) - } - }) - router.GET("/public5.json", func(c *gin.Context) { - c.Writer.Header().Set("Cache-Control", "no-cache") - if forwarder != nil && fwdPublicJson { - fwd_request(c.Writer, c.Request, *forwarder) - } else { - http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public5.json")) - } - }) - router.GET("/public6.json", func(c *gin.Context) { - c.Writer.Header().Set("Cache-Control", "no-cache") - if forwarder != nil && fwdPublicJson { - fwd_request(c.Writer, c.Request, *forwarder) - } else { - http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public6.json")) - } - }) - router.GET("/public7.json", func(c *gin.Context) { - c.Writer.Header().Set("Cache-Control", "no-cache") - if forwarder != nil && fwdPublicJson { - fwd_request(c.Writer, c.Request, *forwarder) - } else { - http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public7.json")) - } - }) - router.GET("/public8.json", func(c *gin.Context) { - c.Writer.Header().Set("Cache-Control", "no-cache") - if forwarder != nil && fwdPublicJson { - fwd_request(c.Writer, c.Request, *forwarder) - } else { - http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public8.json")) - } - }) - router.GET("/public9.json", func(c *gin.Context) { - c.Writer.Header().Set("Cache-Control", "no-cache") - if forwarder != nil && fwdPublicJson { - fwd_request(c.Writer, c.Request, *forwarder) - } else { - http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public9.json")) - } - }) -} diff --git a/dashboard/static/css/bootstrap.min.css b/dashboard/static/css/bootstrap.min.css deleted file mode 120000 index 8dd0a066..00000000 --- a/dashboard/static/css/bootstrap.min.css +++ /dev/null @@ -1 +0,0 @@ -../../../admin/static/css/bootstrap.min.css \ No newline at end of file diff --git a/dashboard/static/css/fic.css b/dashboard/static/css/fic.css deleted file mode 100644 index 9c88d5ca..00000000 --- a/dashboard/static/css/fic.css +++ /dev/null @@ -1,387 +0,0 @@ -@font-face { - font-family: "Linux Biolinum"; - src: url('../fonts/LinBiolinum_R.woff') format('woff'); -} -@font-face { - font-family: "Linux Biolinum"; - src: url('../fonts/LinBiolinum_RB.woff') format('woff'); - font-weight: bold; -} -@font-face { - font-family: "Linux Biolinum"; - src: url('../fonts/LinBiolinum_RI.woff') format('woff'); - font-style: italic; -} -@font-face { - font-family: 'FantasqueSansMonoRegular'; - src: url('../fonts/FantasqueSansMono-Regular.woff') format('woff'); - font-weight: normal; - font-style: normal; -} - -b, strong { - font-weight: bold; -} - -[ng-cloak] { - display:none !important; -} - -.popover.bs-popover-left .arrow::after { - border-left-color: #7A8288; -} - -body { - overflow-y: scroll; -} - -.bg-public { - background-image: url('../background.png'); - background-repeat: no-repeat; - background-size: contain; - height: 100vh; -} - -.bg-public .carousel h3 { - font-size: 1.5rem; - line-height: 1.1rem; -} - -.flag { - font-family: 'FantasqueSansMonoRegular', monospace; -} - -.card-img-top { - background-position: center; - background-repeat: no-repeat; - background-size: cover; -} -.theme-card { - height: 10rem; -} - -.beautiful { - font-family: "Linux Biolinum",Helvetica,Arial,sans-serif; -} -.beautiful ol { - font-size: 133%; -} -.beautiful ol ol { - font-size: 90%; -} - -.text-bold { - font-weight: bolder; -} -.text-indent p { - text-indent: 1em; -} - -.navbar { - margin-bottom: 0; -} -.niceborder { - border-bottom: 5px #4eaee6 solid; -} -.navbar img { - margin: 3px auto; - height: 100px; -} -.navbar .clock { - font-size: 70px; -} -.clock:not(.expired):not(.wait) .point, .clock.expired { - transition: color text-shadow 1s; - position: relative; - animation: clockanim 1s ease infinite; - -moz-animation: clockanim 1s ease infinite; - -webkit-animation: clockanim 1s ease infinite; -} -.clock.wait .point { - transition: color text-shadow 1s; - position: relative; - animation: clockwait 1s ease infinite; - -moz-animation: clockwait 1s ease infinite; - -webkit-animation: clockwait 1s ease infinite; -} -.end { - color: #e64143; -} -.point { - text-shadow: 0 0 20px #4eaee6; -} -.end .point { - text-shadow: 0 0 20px #e64143; -} -@-webkit-keyframes clockanim { - 0% { opacity: 1.0; } - 50% { opacity: 0; } - 100% { opacity: 1.0; }; -} -@-moz-keyframes clockanim { - 0% { opacity: 1.0; } - 50% { opacity: 0; } - 100% { opacity: 1.0; }; -} -keyframes clockanim { - 0% { opacity: 1.0; } - 50% { opacity: 0; } - 100% { opacity: 1.0; }; -} -@-webkit-keyframes clockwait { - 0% { text-shadow: 0 0 20px #A6D6F2; } - 50% { text-shadow: 0 0 2px #A6D6F2; } - 100% { text-shadow: 0 0 20px #A6D6F2; } -} -@-moz-keyframes clockwait { - 0% { text-shadow: 0 0 20px #A6D6F2; } - 50% { text-shadow: 0 0 2px #A6D6F2; } - 100% { text-shadow: 0 0 20px #A6D6F2; } -} -keyframes clockwait { - 0% { text-shadow: 0 0 20px #A6D6F2; } - 50% { text-shadow: 0 0 2px #A6D6F2; } - 100% { text-shadow: 0 0 20px #A6D6F2; } -} - -samp.cksum { - overflow-x: hidden; - text-overflow: ellipsis; - max-width: 16vw; - display: inline-block; - vertical-align: middle; - word-wrap: normal; -} - -h1 small.authors { - float: right; - font-style: italic; - font-size: 42%; -} -.lead small.authors { - color: #7a8288; - font-style: italic; -} - -a.badge:hover { - text-decoration: none; -} -.teamname { - -webkit-filter: invert(100%); - filter: invert(100%); -} -a:hover .teamname { - text-shadow: 0px 0px 10px #888888; -} - -.authors a { - color: #3A3F44; -} - -.heading { - font-style: italic; - margin-top: -7px; - text-align: right; -} - -#eventsList { - overflow:hidden; - max-height: 90vh; -} - -.swap-animation .alert { - margin-bottom: 0px; -} -.swap-animation { - margin-bottom: 0.5rem; - max-height: 30vh; - transition: max-height 1.0s linear,opacity 1.0s linear,transform 0.5s linear; -} -.swap-animation.ng-enter { - transform: translateY(-25vh); - max-height: 0vh; -} -.swap-animation.ng-enter-active { - opacity: 1; - transform: translateY(0px); - max-height: 30vh; -} -.swap-animation.ng-leave { - opacity: 1; - max-height: 30vh; - transform: translateY(0px); -} -.swap-animation.ng-leave-active { - opacity: 0; - transform: translateX(120vw); - max-height: 0vh; -} - -.carousel-indicators { - bottom: -10px; -} -.carousel-caption { - padding: 0; - position: static; -} -.carousel .table { - margin-bottom: 0; -} -.carousel .table-sm td { - padding: 2px; -} - -.table th.frotated { - border: 0; -} -.table th.rotated { - height: 100px; - width: 40px; - min-width: 40px; - max-width: 40px; - position: relative; - vertical-align: bottom; - padding: 0; - font-size: 12px; - line-height: 0.9; - border: 0; -} - -th.rotated > div { - position: relative; - top: 0px; - left: -50px; - height: 100%; - transform: skew(45deg,0deg); - overflow: hidden; - border: 1px solid #000; -} -th.rotated div span { - transform: skew(-45deg,0deg) rotate(45deg); - position: absolute; - bottom: 40px; - left: -35px; - display: inline-block; - width: 110px; - text-align: left; - text-overflow: ellipsis; -} - -ul.list-inline li { - display: inline; -} -ul.list-inline li:not(:last-child)::after { - content: " ● " -} - -.breadcrumb-item + .breadcrumb-item::before { - content: ">" -} - -.excard { - transition: transform 250ms; -} -.excard:hover { - transform: scale(1.07); -} - -#tagsMenu + .dropdown-menu div { - overflow-y: auto; - max-height: calc(66vh - 100px); -} - -blockquote { - border-left: solid 2px; - margin-left: 1em; - padding-left: 1em; -} - -.jumbotron img { - margin-left: -1em; - padding-left: 2em; - padding-right: 2em; -} -img { - max-width: 100%; -} - -#eventsList .card { - border-left-color: rgba(0,0,0,.125) !important; - border-right-color: rgba(0,0,0,.125) !important; - border-top-color: rgba(0,0,0,.125) !important; -} - -.bg-public .card-body { - padding:1rem; - padding-bottom:0; -} - -#themesSummary .card-body { - padding:0; -} -#themesSummary h3 { - background: rgba(64,64,64,0.66); - border-radius: 2px; - padding: 0.5rem; - margin-left: 0.5rem; - margin-right: 0.5rem; - margin-top: -40px; -} -#themesSummary p { - font-size: 90%; - margin: 0.2rem; - text-indent: 0.6em; -} - -.card-sm .card-header, .card-sm .card-footer { - padding: 0.2rem 0.75rem; -} -.card-sm .card-body { - padding: 0.4rem 0.75rem; -} -.card-sm .card-body.text-indent p { - text-indent: 0.4rem; -} - -.carousel-item, .carousel-caption { - height: inherit; -} - -.page-header { - background-size: cover; - background-position: center; - margin-bottom: -15rem; -} -.page-header h1 { - text-shadow: 0 0 15px rgba(255,255,255,0.95), 0 0 5px rgb(255,255,255) -} -.page-header h1, .page-header h1 a { - color: black; -} -.page-header h1 a:hover { - text-decoration: none; -} -.page-header h2 { - font-size: 100%; - text-shadow: 1px 1px 1px rgba(0,0,0,0.9) -} -.page-header h2, .page-header h2 a { - color: #4eaee6; -} -.page-header h1 { - padding-top: 4rem; - text-align: center; -} -.page-header h2 { - padding-bottom: 14rem; - text-align: center; -} - -.page-header .headerfade { - background: linear-gradient(transparent 0%, rgb(233,236,239) 100%); - height: 3rem; -} - -a.list-group-item:hover { - text-decoration: none; -} diff --git a/dashboard/static/css/glyphicon.css b/dashboard/static/css/glyphicon.css deleted file mode 120000 index 14cd8c56..00000000 --- a/dashboard/static/css/glyphicon.css +++ /dev/null @@ -1 +0,0 @@ -../../../admin/static/css/glyphicon.css \ No newline at end of file diff --git a/dashboard/static/fonts b/dashboard/static/fonts deleted file mode 120000 index 0ef2f8d8..00000000 --- a/dashboard/static/fonts +++ /dev/null @@ -1 +0,0 @@ -../../admin/static/fonts/ \ No newline at end of file diff --git a/dashboard/static/img/srs.png b/dashboard/static/img/srs.png deleted file mode 100644 index c25b6af4..00000000 Binary files a/dashboard/static/img/srs.png and /dev/null differ diff --git a/dashboard/static/index.html b/dashboard/static/index.html deleted file mode 100644 index 99afa26f..00000000 --- a/dashboard/static/index.html +++ /dev/null @@ -1,640 +0,0 @@ - - - - - Tableau de bord du challenge forensic - - - - - - - - - - - - - -
    -
    - - -
    - -
    -
    -

    Bienvenue au {{ challenge.title }} !

    -

    - Avant de vous installer, venez récupérer votre clef USB auprès - de notre équipe. Elle contient le certificat qui vous permettra - de vous authentifier auprès de notre serveur. -

    -

    - Une fois connecté au réseau, contactez notre serveur sur : - - - -

    -
    -
    - -
    -
    -

    -
    -
    -
    {{ duration / 60 | time }} : {{ duration % 60 | time }}
    -
    {{ s.params.lead }}
    -
    -
    - -
    -
    -

    Bienvenue au {{ challenge.title }} !

    -

    - Durant ce challenge, les équipes doivent remonter des scénarii - d'attaques auxquels nos systèmes d'information font face - chaque jour : fuite de données, compromission d'un poste de - travail, exploitation de vulnérabilités d'un site web, ... -

    -

    - Pour valider un challenge, chaque participant va télécharger : - soit des journaux d'évènements, des extraits de trafic réseau - ou même des copies figées de la mémoire vive de machines - malveillantes, pour essayer de comprendre comment l'attaquant a - contourné la sécurité de la machine et quelles actions hostiles - ont été effectuées. -

    -
    -
    - -
    -
    -

    -

    -

    -

    -
    -
    - -
    -
    -

    -
    -
    -

    -

    -
    -
    -
    - -
    -
    -

    - Challenge {{ themes[my.exercices[s.params.exercice].theme_id].exercices[s.params.exercice].title }} du thème {{ themes[my.exercices[s.params.exercice].theme_id].name }} - par {{ themes[my.exercices[s.params.exercice].theme_id].authors | stripHTML }} -

    -

    -
      -
    • Rapporte
    • -
    • -
    • -
    • Tenté par
    • -
    • -
    • Résolu par
    • -
    -
    -
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - -
    {{ th.name }}
    Challenge {{ lvl }} - - {{ exercice.solved }} - {{ exercice.tried }} - -
    {{ team.rank }}ere — {{ team.score | number:0 }} points
    {{ team.name }}
    - {{ mystats.themes[tid].solved }}/{{ mystats.themes[tid].total }} - ({{ mystats.themes[tid].tries }}) -
    -    Résolus -    Total résolus
    -    Tentatives -
    - {{ mystats.themes[tid].solved }}
    - {{ mystats.themes[tid].tries }} -
    -
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    PlaceÉquipeScore
    {{ r.rank }}ere   {{ r.name }}{{ r.score | number:0 }}
    {{ rank[0].rank }}er   {{ rank[0].name }}{{ rank[0].score | number:0 }}
    -
      -
    • {{ member.firstname }} {{ member.lastname | capitalize }} ({{ member.company }})
    • -
    -
    {{ rank[1].rank }}e   {{ rank[1].name }}{{ rank[1].score | number:0 }}
    -
      -
    • {{ member.firstname }} {{ member.lastname | capitalize }} ({{ member.company }})
    • -
    -
    {{ rank[2].rank }}e   {{ rank[2].name }}{{ rank[2].score | number:0 }}
    -
      -
    • {{ member.firstname }} {{ member.lastname | capitalize }} ({{ member.company }})
    • -
    -
    -
    - -
    -
    -

    -

    -
    -
    -
    -
    - -
    -
    -

    -

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

    Le {{ challenge.title }} !

    -

    - Durant ce challenge, les équipes doivent remonter des scénarii - d'attaques auxquels nos systèmes d'information font face - chaque jour : fuite de données, compromission d'un poste de - travail, exploitation de vulnérabilités d'un site web, ... -

    -

    - Pour valider un challenge, chaque participant va télécharger : - soit des journaux d'évènements, des extraits de trafic réseau - ou même des copies figées de la mémoire vive de machines - malveillantes, pour essayer de comprendre comment l'attaquant a - contourné la sécurité de la machine et quelles actions hostiles - ont été effectuées. -

    -
    -
    - -
    -
    - - Les entreprises ciblées - -
    -
    - -
    -
    - -
    -
    -
    -
    -

    Défi {{ exercices[s.params.exercice].title }} du thème {{ themes[my.exercices[s.params.exercice].theme_id].name }}

    -

    -
      -
    • -
    • -
    • -
    • Tenté par
    • -
    • Résolu par
    • -
    -
    -
    - -
    -
    - - Challenges à la une - -
    -
    -
    -
    -

    {{ exercices[lastExercice].title }} du thème {{ themes[my.exercices[lastExercice].theme_id].name }}

    -

    -
      -
    • -
    • -
    • -
    • Tenté par
    • -
    • Résolu par
    • -
    -
    -
    - -
    -
    -

    -

    -

    -

    -
    -
    - -
    -
    -

    -
    -
    -

    -

    -
    -
    -
    - -
    -
    -

    -

    -
    -
    -
    -
    - -
    - - - - - - - - - - - - - - - -
    PlaceÉquipeScore
    {{ r.rank }}ere{{ r.name }}{{ r.score | number:0 }}
    - - -
    - -
    -
    - -
    -
    -
    -
    - {{ e.since | since }} -
    -
    -

    -
    -
    -
    - -
    -
    -
    {{ duration / 60 | time }} : {{ duration % 60 | time }}
    -
    00 : 00
    -
    - - -
    -
    -
    - -
    -
    -
    - {{ time.hours | time }} - : - {{ time.minutes | time }} - : - {{ time.seconds | time }} -
    -
    - Temps restant du challenge forensic - Le challenge forensic va bientôt commencer ! - Le challenge forensic est terminé ! -
    -
    -
    - {{ time.start | date:"shortDate" }} -
    -
    - -
    - -
    -
    -
    - -
    - - - - - - - - - - - - diff --git a/dashboard/static/js/angular-animate.min.js b/dashboard/static/js/angular-animate.min.js deleted file mode 100644 index 96dcfb63..00000000 --- a/dashboard/static/js/angular-animate.min.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - AngularJS v1.7.9 - (c) 2010-2018 Google, Inc. http://angularjs.org - License: MIT -*/ -(function(Y,z){'use strict';function Fa(a,b,c){if(!a)throw Pa("areq",b||"?",c||"required");return a}function Ga(a,b){if(!a&&!b)return"";if(!a)return b;if(!b)return a;Z(a)&&(a=a.join(" "));Z(b)&&(b=b.join(" "));return a+" "+b}function Qa(a){var b={};a&&(a.to||a.from)&&(b.to=a.to,b.from=a.from);return b}function $(a,b,c){var d="";a=Z(a)?a:a&&G(a)&&a.length?a.split(/\s+/):[];s(a,function(a,k){a&&0=a&&(a=t,t=0,b.push(f),f=[]);f.push(g);g.children.forEach(function(a){t++;c.push(a)});a--}f.length&&b.push(f);return b}(c)}var C=[],U=aa(a);return function(e, -H,u){function t(a){a=a.hasAttribute("ng-animate-ref")?[a]:a.querySelectorAll("[ng-animate-ref]");var b=[];s(a,function(a){var c=a.getAttribute("ng-animate-ref");c&&c.length&&b.push(a)});return b}function I(a){var b=[],c={};s(a,function(a,d){var l=K(a.element),g=0<=["enter","move"].indexOf(a.event),l=a.structural?t(l):[];if(l.length){var f=g?"to":"from";s(l,function(a){var b=a.getAttribute("ng-animate-ref");c[b]=c[b]||{};c[b][f]={animationID:d,element:A(a)}})}else b.push(a)});var d={},g={};s(c,function(c, -t){var f=c.from,e=c.to;if(f&&e){var h=a[f.animationID],k=a[e.animationID],E=f.animationID.toString();if(!g[E]){var I=g[E]={structural:!0,beforeStart:function(){h.beforeStart();k.beforeStart()},close:function(){h.close();k.close()},classes:da(h.classes,k.classes),from:h,to:k,anchors:[]};I.classes.length?b.push(I):(b.push(h),b.push(k))}g[E].anchors.push({out:f.element,"in":e.element})}else f=f?f.animationID:e.animationID,e=f.toString(),d[e]||(d[e]=!0,b.push(a[f]))});return b}function da(a,b){a=a.split(" "); -b=b.split(" ");for(var c=[],d=0;d=G&&b>=D&&(la=!0,v()))}function F(){function b(){if(!P){u(!1);s(y,function(a){l.style[a[0]]=a[1]});H(a,g);c.addClass(a,ba);if(p.recalculateTimingStyles){T=l.getAttribute("class")+" "+V;ka=k.cacheKey(l,ja,g.addClass,g.removeClass);r=z(l,T,ka,!1);ga=r.maxDelay;W= -Math.max(ga,0);D=r.maxDuration;if(0===D){v();return}p.hasTransitions=0n.expectedEndTime)?f.cancel(n.timer):h.push(v)}F&&(m=f(d,m,!1),h[0]={timer:m,expectedEndTime:e},h.push(v),a.data("$$animateCss",h));if(w.length)a.on(w.join(" "),q);g.to&&(g.cleanupStyles&&Ma(E,l,Object.keys(g.to)),Ja(a,g))}}function d(){var b=a.data("$$animateCss");if(b){for(var c=1;c(\d+)e<\/sup><\/strong> défi ([^&]+)/); - if (res) { - for (var tid in $scope.themes) { - if ($scope.themes[tid].name == res[2]) { - for (var eid in $scope.themes[tid].exercices) { - lastExercice = $scope.themes[tid].exercices[parseInt(res[1])-1].id; - break; - } - } - } - } - } - event.time = Date.parse(event.time); - event.since = now - event.time; - event.kind = ["border-" + event.kind, "alert-" + event.kind]; - }); - - $rootScope.lastExercice = lastExercice; - }); - } - $interval(refreshEvents, 2100); - }) - .controller("TimerController", function($scope, $rootScope, $interval, $timeout) { - $scope.duration = 0; - - $scope.init = function(end) { - $scope.initT(Date.parse(end)); - } - $scope.initStart = function() { - $scope.$watch("settings", function(settings){ - if (settings) - $scope.initT(settings.start); - }) - } - $scope.initT = function(end) { - var time = angular.fromJson(sessionStorage.time); - if (time) { - var srv_cur = new Date(Date.now() + (time.cu - time.he)); - $scope.duration = Math.floor((end - srv_cur)/1000); - } else - $timeout(function() { $scope.initT(end); }, 1000); - } - - var stop = $interval(function() { - $scope.duration -= 1; - if ($scope.duration < -10) - $interval.cancel(stop); - }, 1000); - }) - .controller("DataController", function($scope, $http, $rootScope, $interval) { - var pathname = window.location.pathname; - if (pathname == "/") - pathname = "/public0.html"; - pathname = pathname.replace(".html", ".json"); - - var refreshScene = function() { - $http.get(pathname).then(function(response) { - if ($scope.lastpublicetag != undefined && $scope.lastpublicetag == response.headers()["last-modified"]) - return; - $scope.lastpublicetag = response.headers()["last-modified"]; - - $scope.display = response.data; - }); - } - refreshScene(); - var refreshSceneInterval = $interval(refreshScene, 900); - - var refreshSettings = function() { - $http.get("./settings.json").then(function(response) { - $rootScope.recvTime(response); - response.data.start = new Date(response.data.start); - response.data.end = new Date(response.data.end); - response.data.generation = new Date(response.data.generation); - response.data.awards = new Date(response.data.awards?response.data.awards:"2023-04-06T16:00:00Z"); - $rootScope.settings = response.data; - }); - } - refreshSettings(); - var refreshSettingsInterval = $interval(refreshSettings, 4200); - - var refreshChallengeInfo = function() { - $http.get("./challenge.json").then(function(response) { - $rootScope.challenge = response.data; - }); - } - refreshChallengeInfo(); - var refreshChallengeInfoInterval = $interval(refreshChallengeInfo, 900000); - - - $rootScope.refresh = function() { - $http.get("./my.json").then(function(response) { - if ($scope.lastmyetag != undefined && $scope.lastmyetag == response.headers()["last-modified"]) - return; - $scope.lastmyetag = response.headers()["last-modified"]; - - $scope.my = response.data; - }); - $http.get("./settings.json").then(function(response) { - $rootScope.recvTime(response); - response.data.start = new Date(response.data.start); - response.data.end = new Date(response.data.end); - response.data.generation = new Date(response.data.generation); - response.data.awards = new Date(response.data.awards?response.data.awards:"2023-04-06T16:00:00Z"); - $rootScope.settings = response.data; - }); - $http.get("./themes.json").then(function(response) { - if ($scope.lastthemeetag != undefined && $scope.lastthemeetag == response.headers()["last-modified"]) - return; - $scope.lastthemeetag = response.headers()["last-modified"]; - - var themes = response.data; - var exercices = {}; - $scope.themes = themes; - $scope.max_gain = 0; - angular.forEach(themes, function(theme, key) { - if (theme.exercices) - this[key].exercice_count = Object.keys(theme.exercices).length; - else - this[key].exercice_count = 0; - this[key].gain = 0; - angular.forEach(theme.exercices, function(ex, k) { - exercices[ex.id] = ex; - this.gain += ex.gain; - }, theme); - $scope.max_gain += theme.gain; - }, themes); - $scope.exercices = exercices; - }); - $http.get("./teams.json").then(function(response) { - if ($scope.lastteametag != undefined && $scope.lastteametag == response.headers()["last-modified"]) - return; - $scope.lastteametag = response.headers()["last-modified"]; - - var teams = response.data; - - $scope.teams_count = Object.keys(teams).length - $scope.teams = teams; - - $scope.rank = []; - angular.forEach($scope.teams, function(team, tid) { - team.id = tid; - if (team.rank) { - this.push(team); - } - }, $scope.rank); - $scope.rank.sort(function(a,b) { return a.rank > b.rank ? 1 : -1 }) - $scope.pagesrank = Array(Math.ceil($scope.rank.length / 7)).fill(0).map(function(v, i){ return i; }); - }); - } - $rootScope.refresh(); - $interval($rootScope.refresh, 4200); - }) - .controller("TeamController", function($scope, $http, $interval) { - $scope.mystats = null; - $http.get("./api/teams/" + $scope.team.id + "/stats.json").then(function(response) { - $scope.mystats = response.data; - }); - }) - .controller("RankGraphController", function($scope, $q) { - var margin = {left: 50, right: 20, top: 20, bottom: 50 }; - - var width = $("#rank_graph").width() - margin.left - margin.right; - var height = $scope.s.params.height - margin.top - margin.bottom; - - var xNudge = 50; - var yNudge = 20; - - var max = 0; - var minDate = new Date(); - var maxDate = new Date("2017-12-01"); - if ($scope.settings && $scope.settings.end) - maxDate = $scope.settings.end; - - - var teams = {} - - var loopPromises = []; - $scope.s.params.teams.forEach(function (teamid) { - var deferred = $q.defer(); - loopPromises.push(deferred.promise); - d3.json("./api/teams/" + teamid + "/score-grid.json", function (rows) { - if (rows == null) return; - rows.sort(function (a,b) { return a.time > b.time ? 1 : -1 }) - var nrows = {}; - var sum = 0; - rows.forEach(function (row) { - if (!nrows[row.time]) - nrows[row.time] = 0; - var pts = row.points * row.coeff; - sum += pts; - nrows[row.time] = sum; - if (sum > max) - max = sum - }) - teams[teamid] = [] - Object.keys(nrows).forEach(function (t) { - var d = new Date(t) - if (d < minDate) - minDate = d; - if (d > maxDate) - maxDate = d; - teams[teamid].push({time: d, points: nrows[t]}) - }) - deferred.resolve(); - }); - }) - - $q.all(loopPromises).then(function(){ - var y = d3.scale.linear() - .domain([0,max]) - .range([height,0]); - - var x = d3.time.scale() - .domain([minDate,maxDate]) - .range([0,width]); - - var yAxis = d3.svg.axis() - .orient("left") - .scale(y); - - var xAxis = d3.svg.axis() - .orient("bottom") - .tickFormat(d3.time.format("%H:%M")) - .scale(x); - - var line = d3.svg.line() - .x(function(d){ return x(d.time); }) - .y(function(d){ return y(d.points); }) - .interpolate("cardinal"); - - var svg = d3.select("#rank_graph").append("svg").attr("id","svg").attr("height",$scope.s.params.height).attr("width","100%"); - var chartGroup = svg.append("g").attr("class","chartGroup").attr("transform","translate("+xNudge+","+yNudge+")"); - - chartGroup.append("g") - .attr("class","axis x") - .attr("transform","translate(0,"+height+")") - .call(xAxis); - - chartGroup.append("g") - .attr("class","axis y") - .call(yAxis); - - angular.forEach(teams, function(rows, tid) { - rows.unshift({time: minDate, points: 0}) - var team = $scope.teams[tid] - chartGroup.append("path") - .attr("style","fill: none; stroke: " + team.color + "; stroke-width: 2px;") - .attr("d",function(d){ return line(rows); }) - }) - - if ($scope.s.params.legend) { - var legend = svg.append("g") - .attr("style", "fill: white;") - .attr("height", 100) - .attr("width", 100) - .attr('transform', 'translate(70,20)') - - legend.selectAll('rect') - .data(Object.keys(teams)) - .enter() - .append("rect") - .attr("x", 0) - .attr("y", function(d, i){ return i * 23;}) - .attr("width", 15) - .attr("height", 15) - .style("fill", function(d) { - return $scope.teams[d].color; - }) - - legend.selectAll('text') - .data(Object.keys(teams)) - .enter() - .append("text") - .attr("font-size", "23px") - .attr("x", 20) - .attr("y", function(d, i){ return i * 23 + 14;}) - .text(function(d) { - return $scope.teams[d].name + " (" + $scope.teams[d].rank + "e : " + Math.ceil($scope.teams[d].score) + " pts)"; - }); - } - }); - }) diff --git a/dashboard/static/js/i18n b/dashboard/static/js/i18n deleted file mode 120000 index 0bb28e47..00000000 --- a/dashboard/static/js/i18n +++ /dev/null @@ -1 +0,0 @@ -../../../admin/static/js/i18n/ \ No newline at end of file diff --git a/dashboard/static/js/jquery.min.js b/dashboard/static/js/jquery.min.js deleted file mode 120000 index 3065bbb1..00000000 --- a/dashboard/static/js/jquery.min.js +++ /dev/null @@ -1 +0,0 @@ -../../../admin/static/js/jquery.min.js \ No newline at end of file diff --git a/db/feed.sql b/db/feed.sql new file mode 100644 index 00000000..bb064b01 --- /dev/null +++ b/db/feed.sql @@ -0,0 +1,234 @@ +INSERT INTO `teams` ( +`id` , +`team_name` , +`key_hash` , +`auth_level` , +`slogan` +) +VALUES ( +'1', 'Team1', '2134s65df423sdf132sdg431dsg', '1', 'Epita' +), ( +'2', 'Team2', '2134s65df423sef132sdg431dsg', '1', 'Epita' +), ( +'3', 'Team3', '2134s45df423sdf132sdg431dsg', '1', 'Epita' +), ( +'4', 'Team4', '2134s65df423sdf131sdg431dsg', '1', 'Epita' +), ( +'5', 'Team5', '2134s65df423sdf132sdg431dfg', '1', 'Epita' +), ( +'6', 'Team6', '2134s65df423shf132sdg431dsg', '1', 'Epita' +), ( +'7', 'Team7', '2134s65df423sdf1f2sdg431dsg', '1', 'Epita' +), ( +'8', 'Team8', '2134s65df423sdf13zsdg431dsg', '1', 'Epita' +), ( +'9', 'Team9', '2134s65df423sdf13csdg431dsg', '1', 'Epita' +), ( +'10', 'Team10', '2134s65df4q3sdf132sdg431dsg', '1', 'Epita' +), ( +'11', 'Team11', '2134s65df423sdf132s2g431dsg', '1', 'Epita' +), ( +'12', 'Team12', '2134s65df423sdf132sdg401dsg', '1', 'Epita' +), ( +'13' , 'Team13', '2134s65df423sdf132sdg401dsg', '1', 'Epita' +), ( +'14' , 'Team14', '2134s65df423sdf132sdg401dsg', '1', 'Epita' +), ( +'15' , 'Team15', '2134s65df423sdf132sdg401dsg', '1', 'Epita' +); + +INSERT INTO `team_members` ( +`id` , +`id_team` , +`firstname` , +`lastname` , +`nickname` +) +VALUES ( +'1', '1', 'Alph', 'A', 'Alominia' +), ( +'2', '1', 'Bet', 'A', 'CaptainSandwich' +), ( +'3', '2', 'Charl', 'I', 'Tintin' +), ( +'4', '2', 'Delt', 'A', 'Wolwerin' +), ( +'5', '3', 'Ech', 'O', 'Homer' +), ( +'6', '3', 'Fox', 'Trot', 'KevBG91' +), ( +'7', '4', 'Gol', 'F', 'R2D2' +), ( +'8', '4', 'Hot', 'El', 'Corbaine' +), ( +'9', '5', 'Ind', 'Ia', 'Lelama' +), ( +'10', '5', 'Jule', 'Yer', 'Betrave' +), ( +'11', '6', 'Krev', 'Lard', 'BlueSunday' +), ( +'12', '6', 'Lamb', 'Ada', 'Chatpitre' +), ( +'13' , '7', 'Mi', 'Ke', 'Tournewsol' +), ( +'14' , '7', 'Nov', 'Ember', 'Dasilva' +), ( +'15' , '8', 'Os', 'Car', 'Laurie' +), ( +'16' , '8', 'Pa', 'Pa', 'Nowel' +), ( +'17' , '9', 'Que', 'Bec', 'Polyr' +), ( +'18' , '9', 'Rom', 'Eo', 'Granola' +), ( +'19' , '10', 'Sier', 'Ra', 'Petilus75' +), ( +'20' , '11', 'Tan', 'Go', 'Lolilol42' +), ( +'21' , '12', 'Uni', 'Form', 'JmLayfrite' +), ( +'22' , '13', 'Vic', 'Tor', 'Minerva' +), ( +'23' , '13', 'Whis', 'Key', 'Moly' +), ( +'24' , '14', 'X', 'Rey', 'Mistigri' +), ( +'25' , '14', 'Yan', 'Kee', 'Astroboy' +), ( +'26' , '15', 'Zul', 'Lu', 'Salameche' +), ( +'27' , '15', 'Ze', 'Ro', 'Mandy' +); + +INSERT INTO `themes` ( +`id` , +`name` +) +VALUES ( +'1' , 'pdf' +), ( +'2' , 'flash' +), ( +'3' , 'image' +), ( +'4' , 'se' +), ( +'5' , 'memdump' +), ( +'6' , 'java' +), ( +'7' , 'dotnet' +), ( +'8' , 'pcap' +); + +INSERT INTO `exercices` ( +`id` , +`id_theme` , +`require` , +`level` , +`points` , +`statement` +) +VALUES +('1', '1', '', '1', '1', 'Description 1'), +('2', '1', '1', '2', '5', 'Description 2'), +('3', '1', '2', '3', '10', 'Description 3'), +('4', '1', '3', '4', '20', 'Description 4'), +('5', '1', '4', '5', '40', 'Description 5'), +('6', '2', '', '1', '1', 'Description 6'), +('7', '2', '6', '2', '5', 'Description 7'), +('8', '2', '7', '3', '10', 'Description 8'), +('9', '2', '8', '4', '20', 'Description 9'), +('10', '2', '9', '5', '40', 'Description 10'), +('11', '3', '', '1', '1', 'Description 11'), +('12', '3', '11', '2', '5', 'Description 12'), +('13', '3', '12', '3', '10', 'Description 13'), +('14', '3', '13', '4', '20', 'Description 14'), +('15', '3', '14', '5', '40', 'Description 15'), +('16', '4', '', '1', '1', 'Description 16'), +('17', '4', '16', '2', '5', 'Description 17'), +('18', '4', '17', '3', '10', 'Description 18'), +('19', '4', '18', '4', '20', 'Description 19'), +('20', '4', '19', '5', '40', 'Description 20'), +('21', '5', '', '1', '1', 'Description 21'), +('22', '5', '23', '2', '5', 'Description 22'), +('23', '5', '25', '3', '10', 'Description 23'), +('24', '5', '21', '4', '20', 'Description 24'), +('25', '5', '24', '5', '40', 'Description 25'), +('26', '6', '', '1', '1', 'Description 26'), +('27', '6', '26', '2', '5', 'Description 27'), +('28', '6', '28', '3', '10', 'Description 28'), +('29', '6', '27', '4', '20', 'Description 29'), +('30', '6', '29', '5', '40', 'Description 30'), +('31', '7', '', '1', '1', 'Description 31'), +('32', '7', '35', '2', '5', 'Description 32'), +('33', '7', '31', '3', '10', 'Description 33'), +('34', '7', '32', '4', '20', 'Description 34'), +('35', '7', '33', '5', '40', 'Description 35'), +('36', '8', '', '1', '1', 'Description 36'), +('37', '8', '40', '2', '5', 'Description 37'), +('38', '8', '36', '3', '10', 'Description 38'), +('39', '8', '37', '4', '20', 'Description 39'), +('40', '8', '38', '5', '40', 'Description 40'); + +INSERT INTO `solved` ( +`id` , +`id_team` , +`id_exercice` , +`time` +) +VALUES ( +'1', '1', '1', '2013-10-09 00:01:00' +), ( +'2', '1', '2', '2013-10-09 00:05:00' +), ( +'3', '2', '3', '2013-10-09 00:10:00' +), ( +'4', '6', '4', '2013-10-09 00:30:00' +), ( +'5', '3', '6', '2013-10-09 00:05:00' +), ( +'6', '6', '7', '2013-10-09 00:10:00' +), ( +'7', '10', '8', '2013-10-09 00:30:00' +), ( +'8', '11', '9', '2013-10-09 00:40:00' +), ( +'9', '4', '10', '2013-10-09 00:50:00' +), ( +'10', '5', '11', '2013-10-09 00:01:00' +), ( +'11', '1', '16', '2013-10-09 00:01:00' +), ( +'12', '10', '21', '2013-10-09 00:01:00' +), ( +'13', '9', '22', '2013-10-09 00:05:00' +), ( +'14', '5', '22', '2013-10-09 00:15:00' +), ( +'15', '3', '22', '2013-10-09 00:35:00' +), ( +'16', '8', '25', '2013-10-09 00:45:00' +), ( +'17', '7', '26', '2013-10-09 00:01:00' +), ( +'18', '7', '31', '2013-10-09 00:01:00' +), ( +'19', '10', '32', '2013-10-09 00:05:00' +), ( +'20', '2', '33', '2013-10-09 00:10:00' +), ( +'21', '11', '36', '2013-10-09 00:01:00' +), ( +'22', '14', '37', '2013-10-09 00:05:00' +), ( +'23', '9', '38', '2013-10-09 00:15:00' +), ( +'24', '9', '39', '2013-10-09 00:35:00' +); + + +INSERT INTO `exercice_files` (`id`, `id_exercice`, `path`, `name`, `sha1`) VALUES +('1', '22', 'theme/exo/file1.txt', 'file1.txt', '38be7d1b981f2fb6a4a0a052453f887373dc1fe8'), +('2', '22', 'theme/exo/file2.txt', 'file2.txt', '639daad06642a8eb86821ff7649e86f5f59c6139'); diff --git a/db/fic2014.sql b/db/fic2014.sql new file mode 100644 index 00000000..0b8f5e2c --- /dev/null +++ b/db/fic2014.sql @@ -0,0 +1,151 @@ +-- phpMyAdmin SQL Dump +-- version 4.0.5 +-- http://www.phpmyadmin.net +-- +-- Host: localhost +-- Generation Time: Oct 23, 2013 at 07:34 PM +-- Server version: 5.1.70-log +-- PHP Version: 5.5.4-pl0-gentoo + +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +SET time_zone = "+00:00"; + +-- +-- Database: `fic2014` +-- + +-- -------------------------------------------------------- + +-- +-- Table structure for table `exercices` +-- + +CREATE TABLE IF NOT EXISTS `exercices` ( + `id` varchar(100) COLLATE utf8_unicode_ci NOT NULL, + `id_theme` int(10) unsigned NOT NULL, + `require` varchar(100) COLLATE utf8_unicode_ci NULL, + `level` tinyint(4) NOT NULL, + `points` smallint(6) NOT NULL, + `statement` text COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `exercice_files` +-- + +CREATE TABLE IF NOT EXISTS `exercice_files` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `id_exercice` varchar(100) COLLATE utf8_unicode_ci NOT NULL, + `path` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `sha1` binary(20) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `exercice_tries` +-- + +CREATE TABLE IF NOT EXISTS `exercice_tries` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `id_exercice` varchar(100) COLLATE utf8_unicode_ci NOT NULL, + `id_team` smallint(5) unsigned NOT NULL, + `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `exercice_keys` +-- + +CREATE TABLE IF NOT EXISTS `exercice_keys` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `id_exercice` varchar(100) COLLATE utf8_unicode_ci NOT NULL, + `format` enum('raw','md5','sha1','sha256','sha512') COLLATE utf8_unicode_ci NOT NULL, + `value` varbinary(150) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `sessions` +-- + +CREATE TABLE IF NOT EXISTS `sessions` ( + `session` binary(32) NOT NULL, + `uid` binary(16) NOT NULL, + `time` int(11) NOT NULL, + `ip` varbinary(16) NOT NULL, + `var` varchar(9999) COLLATE utf8_unicode_ci NOT NULL, + `level` tinyint(2) NOT NULL, + `active` enum('1','0') COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`session`) +) ENGINE=MEMORY DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `solved` +-- + +CREATE TABLE IF NOT EXISTS `solved` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `id_team` int(10) unsigned NOT NULL, + `id_exercice` varchar(100) COLLATE utf8_unicode_ci NOT NULL, + `time` datetime NOT NULL, + PRIMARY KEY (`id`), + UNIQUE (id_team, id_exercice) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `teams` +-- + +CREATE TABLE IF NOT EXISTS `teams` ( + `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, + `team_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL UNIQUE, + `key_hash` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `auth_level` tinyint(1) NOT NULL, + `slogan` varchar(64) COLLATE utf8_unicode_ci NOT NULL, + `revoked` boolean NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `team_members` +-- + +CREATE TABLE IF NOT EXISTS `team_members` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `id_team` int(10) unsigned NOT NULL, + `firstname` varchar(32) COLLATE utf8_unicode_ci NOT NULL, + `lastname` varchar(32) COLLATE utf8_unicode_ci NOT NULL, + `nickname` varchar(32) COLLATE utf8_unicode_ci NOT NULL UNIQUE, + `company` varchar(32) COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`id`), + UNIQUE (firstname, lastname) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `themes` +-- + +CREATE TABLE IF NOT EXISTS `themes` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(32) COLLATE utf8_unicode_ci NOT NULL UNIQUE, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ; diff --git a/default.nix b/default.nix deleted file mode 100644 index f8a169dc..00000000 --- a/default.nix +++ /dev/null @@ -1,9 +0,0 @@ -(import ( - let - lock = builtins.fromJSON (builtins.readFile ./flake.lock); - in fetchTarball { - url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; - sha256 = lock.nodes.flake-compat.locked.narHash; } -) { - src = ./.; -}).defaultNix diff --git a/doc/directories.png b/doc/directories.png deleted file mode 100644 index affe98f8..00000000 Binary files a/doc/directories.png and /dev/null differ diff --git a/doc/l2.png b/doc/l2.png deleted file mode 100644 index 7b963a8b..00000000 Binary files a/doc/l2.png and /dev/null differ diff --git a/doc/micro-services.png b/doc/micro-services.png deleted file mode 100644 index da00e380..00000000 Binary files a/doc/micro-services.png and /dev/null differ diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index e9bd6afa..00000000 --- a/docker-compose.yml +++ /dev/null @@ -1,190 +0,0 @@ -version: '3' - -services: - mysql: - image: mariadb - networks: - - fic-net - volumes: - - mysql-data:/var/lib/mysql - environment: - - MYSQL_ROOT_PASSWORD=toto - - MYSQL_DATABASE=fic - - MYSQL_USER=fic - - MYSQL_PASSWORD=fic - - admin: - build: - context: . - dockerfile: Dockerfile-admin - image: nemunaire/fic-admin:latest - ports: - - "8081:8081" - links: - - mysql - networks: - - fic-net - volumes: - - /mnt/fic:/mnt/fic - - dashboard:/srv/DASHBOARD - - files:/srv/FILES - - generator:/srv/GENERATOR:ro - - pki:/srv/PKI - - settings:/srv/SETTINGS - - settingsdist:/srv/SETTINGSDIST - - submissions:/srv/submissions:ro - - sync:/srv/SYNC - - teams:/srv/TEAMS - command: -baseurl /admin/ -localimport /mnt/fic -localimportsymlink - depends_on: - - mysql - environment: - - MYSQL_HOST=mysql - - FICCA_PASS - - evdist: - build: - context: . - dockerfile: Dockerfile-evdist - image: nemunaire/fic-evdist:latest - networks: - - fic-net - volumes: - - dashboard:/srv/DASHBOARD - - settings:/srv/SETTINGS - - settingsdist:/srv/SETTINGSDIST - - checker: - build: - context: . - dockerfile: Dockerfile-checker - image: nemunaire/fic-checker:latest - links: - - mysql - networks: - - fic-net - volumes: - - generator:/srv/GENERATOR:ro - - teams:/srv/TEAMS:ro - - settingsdist:/srv/SETTINGSDIST:ro - - submissions:/srv/submissions - depends_on: - - mysql - - generator - environment: - - MYSQL_HOST=mysql - - generator: - build: - context: . - dockerfile: Dockerfile-generator - image: nemunaire/fic-generator:latest - links: - - mysql - networks: - - fic-net - volumes: - - generator:/srv/GENERATOR - - teams:/srv/TEAMS - - settingsdist:/srv/SETTINGSDIST:ro - depends_on: - - mysql - environment: - - MYSQL_HOST=mysql - - qa: - build: - context: . - dockerfile: Dockerfile-qa - image: nemunaire/fic-qa:latest - ports: - - "8083:8083" - links: - - mysql - networks: - - fic-net - volumes: - - teams:/srv/TEAMS - - settingsdist:/srv/SETTINGSDIST - command: -baseurl /qa/ - depends_on: - - mysql - environment: - - MYSQL_HOST=mysql - - receiver: - build: - context: . - dockerfile: Dockerfile-receiver - image: nemunaire/fic-receiver:latest - command: "-startedFile /srv/startingblock/started -bind :8080" - ports: - - "8080:8080" - networks: - - fic-net - volumes: - - htdocs:/srv/htdocs-frontend:ro - - files:/srv/FILES:ro - - teams:/srv/TEAMS:ro - - settingsdist:/srv/SETTINGSDIST:ro - - submissions:/srv/submissions - - startingblock:/srv/startingblock - - dashboard: - build: - context: . - dockerfile: Dockerfile-dashboard - image: nemunaire/fic-dashboard:latest - command: "-baseurl /dashboard/" - ports: - - "8082:8082" - networks: - - fic-net - volumes: - - dashboard:/srv/DASHBOARD - - teams:/srv/TEAMS:ro - - settingsdist:/srv/SETTINGSDIST:ro - - front: - build: - context: . - dockerfile: Dockerfile-nginx - image: nemunaire/fic-nginx:latest - ports: - - "8042:80" - networks: - - fic-net - volumes: - - ./configs/nginx/get-team/team-1.conf:/etc/nginx/fic-get-team.conf:ro - - /mnt/fic:/mnt/fic:ro - - files:/srv/FILES:ro - - settingsdist:/srv/SETTINGSDIST:ro - - sync:/srv/SYNC:ro - - teams:/srv/TEAMS:ro - - startingblock:/srv/STARTINGBLOCK:ro - depends_on: - - qa - - receiver - - dashboard - - checker - - admin - -volumes: - mysql-data: - dashboard: - files: - htdocs: - generator: - pki: - settings: - settingsdist: - startingblock: - driver_opts: - type: tmpfs - device: tmpfs - submissions: - sync: - teams: - -networks: - fic-net: diff --git a/entrypoint-receiver.sh b/entrypoint-receiver.sh deleted file mode 100755 index 415f0948..00000000 --- a/entrypoint-receiver.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/sh - -[ -s /chbase-done ] && CURRENT_BASE=$(cat /chbase-done) || CURRENT_BASE="/" -[ -n "${BASEURL}" ] || BASEURL="/" -[ "${BASEURL}" == "/" ] && BASEURL2="@baseurl" || BASEURL2="${BASEURL}" - -run() { - local NEWBASE=$1 - local FILE=$2 - - if [ -d "${FILE}" ] - then - for f in "${FILE}/"* - do - case "${f}" in - "${FILE}/"*.html|"${FILE}/"*.js) - run "${NEWBASE}" "${f}";; - esac - done - elif [ -f "${FILE}" ] - then - echo "Updating base path for $FILE..." - sed -ri "s@@@;s@\"${CURRENT_BASE}_app/@\"${NEWBASE}_app/@;s@base: \"${CURRENT_BASE%/}\"@base: \"${NEWBASE%/}\"@" ${FILE} - fi -} - -[ "${CURRENT_BASE}" != "${BASEURL}" ] && { - run "${BASEURL}" /srv/htdocs-frontend - echo "${BASEURL}" > /chbase-done -} - -exec /srv/receiver $@ diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100755 index 00000000..52b20a95 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,51 @@ +#!/bin/sh + +# Docker entrypoint + +cd `dirname "$0"` + +. ./config.sh + +# Creating directory and set permissions +mkdir -p ${BASEDIR}/logs ${BASEDIR}/onyx/log +chown -R www-data:www-data ${BASEDIR}/pki ${BASEDIR}/PKI ${BASEDIR}/onyx/log +chown -R ${SYNCHRO_USER}:www-data ${BASEDIR}/submission ${BASEDIR}/logs ${BASEDIR}/out +chmod 770 ${BASEDIR}/submission +chown ${SYNCHRO_USER} ${BASEDIR}/.ssh/id_rsa +chown www-data:www-data ${BASEDIR}/shared ${BASEDIR}/shared/crl.pem + +# Update database profile +cat < ${BASEDIR}/onyx/db/docker.profile.php && +sampledocker0101 /dev/null && { ip a add 10.10.10.1/29 dev eth1; ip link set eth1 up; }; ip a add 172.17.0.6/24 dev vethin-qa; ip link set vethin-qa up" ] - net: new - runtime: - interfaces: -# - name: eth1 # for QA - - name: vethin-qa - add: veth - peer: veth-qa - bindNS: - net: /run/netns/fic-qa - - name: admin-ip-setup - image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156 - #command: ["/bin/sh", "-c", "ip link add link eth3 name adminiface type vlan id 99; ip a add 172.16.99.219/24 dev adminiface; ip link set eth3 up; ip link set adminiface up; ip r add default via 172.16.99.1; ip a add 172.17.0.2/24 dev vethin-admin; ip link set vethin-admin up; ping -W 10 -c 1 172.16.99.1;" ] - command: ["/bin/sh", "-c", "ip link set eth3 up; while read IP; do ip a add ${IP} dev eth3; done < /run/config/ip_config/backend-admin; ip r add default via $(cat /run/config/ip_config/backend-router); ip a add 172.17.0.2/24 dev vethin-admin; ip link set vethin-admin up; echo 'Waiting for' $(cat /run/config/ip_config/backend-router); ping -W 10 -c 1 $(cat /run/config/ip_config/backend-router); ip link show eth1 2> /dev/null && { ip a add 10.0.0.1/24 dev eth1; ip link set eth1 up; };" ] - net: new - binds: - - /run/config/ip_config/:/run/config/ip_config/:ro - runtime: - interfaces: - - name: eth1 #iDRAC - - name: eth3 - - name: vethin-admin - add: veth - peer: veth-admin - bindNS: - net: /run/netns/fic-admin - - name: checker-ip-setup - image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156 - command: ["/bin/sh", "-c", "ip a add 172.17.0.3/24 dev vethin-checker; ip link set vethin-checker up;" ] - net: new - runtime: - interfaces: - - name: vethin-checker - add: veth - peer: veth-checker - bindNS: - net: /run/netns/fic-checker - - name: generator-ip-setup - image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156 - command: ["/bin/sh", "-c", "ip a add 172.17.0.5/24 dev vethin-generat; ip link set vethin-generat up;" ] - net: new - runtime: - interfaces: - - name: vethin-generat - add: veth - peer: veth-generator - bindNS: - net: /run/netns/fic-generator - - name: mysql-ip-setup - image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156 - command: ["/bin/sh", "-c", "ip a add 172.17.0.4/24 dev vethin-db; ip link set vethin-db up;" ] - net: new - runtime: - interfaces: - - name: vethin-db - add: veth - peer: veth-db - bindNS: - net: /run/netns/db - - name: bridge-setup - image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156 - command: ["/bin/sh", "-c", "ip a add 172.17.0.1/24 dev br0; ip link set veth-admin master br0; ip link set veth-checker master br0; ip link set veth-generator master br0; ip link set veth-db master br0; ip link set veth-qa master br0; ip link set br0 up; ip link set veth-admin up; ip link set veth-checker up; ip link set veth-generator up; ip link set veth-db up; ip link set veth-qa up;" ] - runtime: - interfaces: - - name: br0 - add: bridge - - - name: firewall-synchro - image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156 - command: ["/bin/bash", "-c", "/sbin/iptables-restore < /etc/iptables/rules-synchro.v4; /sbin/ip6tables-restore < /etc/iptables/rules.v6" ] - binds: - - /etc/iptables/rules-synchro.v4:/etc/iptables/rules-synchro.v4:ro - - /etc/iptables/rules.v6:/etc/iptables/rules.v6:ro - net: /run/netns/synchro - runtime: - mkdir: - - /var/lib/fic/teams - - name: firewall-admin - image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156 - command: ["/bin/bash", "-c", "/sbin/iptables-restore < /etc/iptables/rules-admin.v4; /sbin/ip6tables-restore < /etc/iptables/rules.v6" ] - binds: - - /etc/iptables/rules-admin.v4:/etc/iptables/rules-admin.v4:ro - - /etc/iptables/rules.v6:/etc/iptables/rules.v6:ro - net: /run/netns/fic-admin - - - name: create-secrets - image: alpine:3.21 - command: ["/bin/init_secrets.sh"] - binds: - - /bin/init_secrets.sh:/bin/init_secrets.sh:ro - - /var/lib/fic/secrets:/var/lib/fic/secrets - runtime: - mkdir: - - /var/lib/fic/secrets - - - name: create-ssh-keys - image: nemunaire/rsync:a3d76b2dd0a9ad73be44dc77ad765b20d96a3285 - command: ["/bin/sh", "-c", "touch /etc/ssh/sshd_config && ssh-keygen -A"] - binds: - - /var/lib/fic/ssh:/etc/ssh - runtime: - mkdir: - - /var/lib/fic/ssh - -services: -# - name: getty -# image: linuxkit/getty:05eca453695984a69617f1f1f0bcdae7f7032967 -# env: -# - INSECURE=true - - # Enable acpi to shutdown on power events - - name: acpid - image: linuxkit/acpid:6cb5575e487a8fcbd4c3eb6721c23299e6ea452f - - name: rngd - image: linuxkit/rngd:1a18f2149e42a0a1cb9e7d37608a494342c26032 - - name: db - image: mariadb:11 - command: ["/bin/bash", "/usr/local/bin/docker-entrypoint.sh", "mariadbd"] - capabilities: - - CAP_CHOWN - - CAP_SETUID - - CAP_SETGID - - CAP_DAC_OVERRIDE - env: - - MYSQL_DATABASE=fic - - MYSQL_USER=fic - - MYSQL_PASSWORD_FILE=/run/secrets/mysql_password - - MYSQL_RANDOM_ROOT_PASSWORD=yes - binds: - - /etc/hosts:/etc/hosts:ro - - /etc/mysql/conf.d:/etc/mysql/conf.d:ro - - /var/lib/fic/mysql:/var/lib/mysql - - /var/lib/fic/secrets/mysql_password:/run/secrets/mysql_password:ro - net: /run/netns/db - pid: new - ipc: new - uts: new - runtime: - mkdir: - - /var/lib/fic/mysql - - name: db-backup - image: nemunaire/mariadb-client:a77db996e46335c35c74ba8dbef3c65286386c6d - command: ["/bin/sh", "/root/mysql_backup.sh"] - env: - - MYSQL_HOST=db - - MYSQL_DATABASE=fic - - MYSQL_USER=fic - - MYSQL_PASSWORD_FILE=/run/secrets/mysql_password - binds: - - /etc/hosts:/etc/hosts:ro - - /root/mysql_backup.sh:/root/mysql_backup.sh:ro - - /var/lib/fic/backups/:/var/lib/fic/backups/ - - /var/lib/fic/secrets/mysql_password:/run/secrets/mysql_password:ro - net: /run/netns/db - runtime: - mkdir: - - /var/lib/fic/backups - - name: fic-admin - image: nemunaire/fic-admin:latest - command: ["/srv/admin", "-4real", "-bind=127.0.0.1:8081", "-baseurl=/admin/", "-localimport=/mnt/fic", "-timestampCheck=/srv/submissions", "-git-import-remote=git@gitlab.cri.epita.fr:ing/majeures/srs/fic/2024/challenges.git"] - env: - - PATH=/usr/sbin:/usr/bin:/sbin:/bin - - MYSQL_HOST=db - - MYSQL_PASSWORD_FILE=/run/secrets/mysql_password - - FICCA_PASS_FILE=/run/secrets/fic_ca_pass - - FICOIDC_ISSUER_FILE=/run/config/ip_config/domain - - FICOIDC_SECRET_FILE=/run/secrets/fic_oidc_secret - binds: - - /etc/hosts:/etc/hosts:ro - - /run/config/ip_config/domain:/run/config/ip_config/domain:ro - - /var/lib/fic/secrets/fic_ca_pass:/run/secrets/fic_ca_pass:ro - - /var/lib/fic/secrets/fic_oidc_secret:/run/secrets/fic_oidc_secret:ro - - /var/lib/fic/secrets/mysql_password:/run/secrets/mysql_password:ro - - /var/lib/fic/raw_files:/mnt/fic - - /var/lib/fic/dashboard:/srv/DASHBOARD - - /var/lib/fic/files:/srv/FILES - - /var/lib/fic/generator:/srv/GENERATOR:ro - - /var/lib/fic/pki:/srv/PKI - - /var/lib/fic/settings:/srv/SETTINGS - - /var/lib/fic/submissions:/srv/submissions - - /var/lib/fic/sync:/srv/SYNC - - /var/lib/fic/teams:/srv/TEAMS - net: /run/netns/fic-admin - pid: new - ipc: new - uts: new - runtime: - mkdir: - - /var/lib/fic/dashboard - - /var/lib/fic/files - - /var/lib/fic/generator - - /var/lib/fic/raw_files - - /var/lib/fic/pki - - /var/lib/fic/settings - - /var/lib/fic/submissions - - /var/lib/fic/sync - - /var/lib/fic/teams - - name: fic-evdist - image: nemunaire/fic-evdist:latest - binds: - - /etc/hosts:/etc/hosts:ro - - /var/lib/fic/dashboard:/srv/DASHBOARD - - /var/lib/fic/settings:/srv/SETTINGS - - /var/lib/fic/settingsdist:/srv/SETTINGSDIST - net: new - pid: new - ipc: new - uts: new - runtime: - mkdir: - - /var/lib/fic/settings - - /var/lib/fic/settingsdist - - name: fic-checker - image: nemunaire/fic-checker:latest - env: - - MYSQL_HOST=db - - MYSQL_PASSWORD_FILE=/run/secrets/mysql_password - binds: - - /etc/hosts:/etc/hosts:ro - - /var/lib/fic/generator:/srv/GENERATOR:ro - # Uncomment this to disallow registrations - - /var/lib/fic/teams:/srv/TEAMS:ro - # Uncomment this to allow registrations - #- /var/lib/fic/teams:/srv/TEAMS - - /var/lib/fic/secrets/mysql_password:/run/secrets/mysql_password:ro - - /var/lib/fic/settingsdist:/srv/SETTINGSDIST:ro - - /var/lib/fic/submissions:/srv/submissions - net: /run/netns/fic-checker - pid: new - ipc: new - uts: new - runtime: - mkdir: - - /var/lib/fic/generator - - /var/lib/fic/settingsdist - - /var/lib/fic/submissions - - /var/lib/fic/teams - - name: fic-dashboard - image: nemunaire/fic-dashboard:latest - command: ["/srv/dashboard", "-bind=:8082", "-restrict-to-ips=/srv/DASHBOARD/restricted-ips.json"] - binds: - - /etc/hosts:/etc/hosts:ro - - /var/lib/fic/dashboard:/srv/DASHBOARD:ro - - /var/lib/fic/files:/srv/FILES:ro - - /var/lib/fic/teams:/srv/TEAMS:ro - - /var/lib/fic/settingsdist:/srv/SETTINGSDIST:ro - net: /run/netns/fic-admin - pid: new - ipc: new - uts: new - runtime: - mkdir: - - /var/lib/fic/dashboard - - /var/lib/fic/teams - - /var/lib/fic/settingsdist - - name: fic-generator - image: nemunaire/fic-generator:latest - command: ["/srv/generator", "-bind=/srv/GENERATOR/generator.socket"] - env: - - MYSQL_HOST=db - - MYSQL_PASSWORD_FILE=/run/secrets/mysql_password - binds: - - /etc/hosts:/etc/hosts:ro - - /var/lib/fic/generator:/srv/GENERATOR - - /var/lib/fic/teams:/srv/TEAMS - - /var/lib/fic/secrets/mysql_password:/run/secrets/mysql_password:ro - - /var/lib/fic/settingsdist:/srv/SETTINGSDIST:ro - net: /run/netns/fic-generator - pid: new - ipc: new - uts: new - runtime: - mkdir: - - /var/lib/fic/generator - - /var/lib/fic/settingsdist - - /var/lib/fic/teams - - name: fic-qa - image: nemunaire/fic-qa:latest - command: ["/srv/qa", "--bind=:8083", "-baseurl=/qa"] - env: - - MYSQL_HOST=db - - MYSQL_PASSWORD_FILE=/run/secrets/mysql_password - binds: - - /etc/hosts:/etc/hosts:ro - - /var/lib/fic/teams:/srv/TEAMS:ro - - /var/lib/fic/secrets/mysql_password:/run/secrets/mysql_password:ro - - /var/lib/fic/settingsdist:/srv/SETTINGSDIST:ro - net: /run/netns/fic-qa - pid: new - ipc: new - uts: new - runtime: - mkdir: - - /var/lib/fic/teams - - name: fic-synchro - image: nemunaire/rsync:a3d76b2dd0a9ad73be44dc77ad765b20d96a3285 - command: ["/bin/ash", "/root/synchro.sh"] - binds: - - /etc/hosts:/etc/hosts:ro - - /var/lib/fic/ssh:/etc/ssh:ro - - /run/config/synchro/id_ed25519:/root/.ssh/id_ed25519:ro - - /root/synchro.sh:/root/synchro.sh:ro - - /var/lib/fic/files:/srv/FILES:ro - #- /var/lib/fic/pki/ca.key:/srv/PKI/ca.key:ro - - /var/lib/fic/pki/shared:/srv/PKI/shared:ro - - /var/lib/fic/settingsdist:/srv/SETTINGSDIST:ro - - /var/lib/fic/submissions:/srv/submissions - - /var/lib/fic/teams:/srv/TEAMS:ro - - /var/log/frontend:/var/log/frontend - net: /run/netns/synchro - runtime: - mkdir: - - /var/lib/fic/files - - /var/lib/fic/pki/shared - - /var/lib/fic/settingsdist - - /var/lib/fic/submissions - - /var/lib/fic/teams - - /var/log/frontend - - name: sshd - image: nemunaire/rsync:a3d76b2dd0a9ad73be44dc77ad765b20d96a3285 - binds: - - /etc/hosts:/etc/hosts:ro - - /var/lib/fic/ssh:/etc/ssh:ro - - /run/config/ssh/authorized_keys:/root/.ssh/authorized_keys:ro - - /usr/bin/iptables:/usr/bin/iptables:ro - - /usr/bin/mariadb:/usr/bin/mariadb:ro - - /usr/bin/mysql:/usr/bin/mysql:ro - - /usr/bin/pnsenter:/usr/bin/pnsenter:ro - - /usr/bin/status:/usr/bin/status:ro - - /var/lib/fic/outofsync:/var/lib/fic/outofsync - - /var/lib/fic/raw_files:/mnt/fic - - /var/lib/fic/secrets:/var/lib/fic/secrets:ro - - /var/log:/var/log:ro - capabilities: - - all - net: /run/netns/fic-admin - pid: host - runtime: - mkdir: - - /var/lib/fic/outofsync -# - name: dhcp-server -# image: joebiellik/dhcpd -# binds: -# - /etc/dhcp/dhcpd.conf:/etc/dhcp/dhcpd.conf:ro -# capabilities: -# - CAP_NET_BIND_SERVICE -# - CAP_NET_RAW -# - CAP_DAC_OVERRIDE -# net: /run/netns/fic-admin -# pid: new -# ipc: new -# uts: new - -files: - - path: etc/init.d/001-hostname - contents: | - #!/bin/sh - /bin/hostname deimos - mode: "0555" - - - path: bin/init_secrets.sh - contents: | - #!/bin/sh - - SECRETS_DIR="/var/lib/fic/secrets" - - [ -f "${SECRETS_DIR}/mysql_password" ] || tr -d -c "a-zA-Z0-9" < /dev/urandom | fold -w31 | head -n 1 > "${SECRETS_DIR}/mysql_password" - - [ -f "${SECRETS_DIR}/fic_ca_pass" ] || tr -d -c "a-zA-Z0-9" < /dev/urandom | fold -w31 | head -n 1 > "${SECRETS_DIR}/fic_ca_pass" - [ -f "${SECRETS_DIR}/fic_oidc_secret" ] || tr -d -c "a-zA-Z0-9" < /dev/urandom | fold -w31 | head -n 1 > "${SECRETS_DIR}/fic_oidc_secret" - mode: "0755" - - - path: etc/profile.d/color_prompt.sh - contents: | - PS1='\[\e[1;33m\]'$PS1'\[\e[0m\]' - mode: "0444" - - - path: etc/init.d/015-setup-sshd - source: configs/sshd-setup.sh - mode: "0555" - - - path: etc/sysctl.d/01-fic.conf - source: configs/sysctl-backend.conf - mode: "0444" - - - path: root/synchro.sh - source: configs/synchro.sh - mode: "0755" - - path: root/mysql_backup.sh - source: configs/mysql_backup.sh - mode: "0755" - - path: etc/hosts - source: configs/hosts - mode: "0644" - - - path: usr/bin/status - contents: | - #!/bin/sh - nsenter -t 1 -m ctr -n services.linuxkit t ls - mode: "0755" - - path: usr/bin/upgrade_image - contents: | - #!/bin/sh - - echo "Erasing image..." - [ -d /boot/imgs ] || mount /dev/sda1 /boot || exit 1 - mv /var/lib/fic/outofsync/fickit-backend-squashfs.img /boot/imgs/fickit-backend-squashfs.img || \ - exit 1 - - if [ $(sha3sum /var/lib/fic/outofsync/fickit-metadata.iso | cut -d " " -f 1) != $(sha3sum /boot/imgs/fickit-metadata.iso | cut -d " " -f 1) ] - then - ISO=$(mktemp -d) - mount /var/lib/fic/outofsync/fickit-metadata.iso "${ISO}" - - NEW_KEY=$(sed -rn 's/.*"content": "([^"]+)"$/\1/p' "${ISO}/user-data" | head -n 1) - OLD_KEY=$(cat /run/config/dm-crypt/key) - - [ "${NEW_KEY}" != "${OLD_KEY}" ] && { - read -p "DM-CRYPT key changed in metadata, are you sure you want to erase it? (y/N) " V - [ "$V" != "y" ] && [ "$V" != "Y" ] && exit 1; - }; - - cp /boot/imgs/fickit-metadata.iso /boot/imgs/fickit-metadata.iso.bak || exit 1; - mv /var/lib/fic/outofsync/fickit-metadata.iso /boot/imgs/fickit-backend-squashfs.img || exit 1; - dd if=/boot/imgs/fickit-metadata.iso of="$2" || exit 1; - - echo - echo "Metadata erased" - fi - - echo - echo "Done! You can reboot now." - mode: "0755" - - path: usr/bin/iptables - source: configs/nsenter_iptables.sh - mode: "0755" - - path: usr/bin/mysql - source: configs/nsenter_mysql.sh - mode: "0755" - - path: usr/bin/mariadb - source: configs/nsenter_mysql.sh - mode: "0755" - - path: usr/bin/pnsenter - source: configs/nsenter_process.sh - mode: "0755" - - - path: etc/mysql/conf.d/max-conn.cnf - contents: | - [mysqld] - max_connections = 99999 - mode: "0444" - - path: etc/dhcp/dhcpd.conf - contents: | - default-lease-time 600; - max-lease-time 7200; - option subnet-mask 255.255.255.0; - option broadcast-address 192.168.3.255; - subnet 192.168.3.0 netmask 255.255.255.0 { - range 192.168.3.11 192.168.3.250; - } - mode: "0440" - - path: etc/iptables/rules.v6 - contents: | - *filter - :INPUT DROP [0:0] - :FORWARD DROP [0:0] - :OUTPUT DROP [0:0] - COMMIT - mode: "0440" - - path: etc/iptables/rules-admin.v4 - contents: | - *filter - :INPUT DROP [0:0] - :FORWARD DROP [0:0] - :OUTPUT DROP [0:0] - [0:0] -A INPUT -i lo -j ACCEPT - [0:0] -A INPUT -m conntrack --ctstate INVALID -j DROP - [0:0] -A INPUT -p icmp -j ACCEPT - [0:0] -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT - [0:0] -A INPUT -p tcp -m conntrack --ctstate NEW -m tcp --dport ssh -j ACCEPT - [0:0] -A INPUT -p tcp -m conntrack --ctstate NEW -m tcp --dport 8082 -j ACCEPT - [0:0] -A INPUT -i vethin-admin -s 172.17.0.0/24 -p tcp -m conntrack --ctstate NEW -j ACCEPT - [0:0] -A INPUT -j LOG - [0:0] -A FORWARD -j LOG - [0:0] -A OUTPUT -o lo -j ACCEPT - [0:0] -A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT - [0:0] -A OUTPUT -p icmp -j ACCEPT - [0:0] -A OUTPUT -o eth1 -d 10.0.0.0/24 -p tcp -m conntrack --ctstate NEW -m tcp --dport 443 -j ACCEPT - [0:0] -A OUTPUT -o vethin-admin -d 172.17.0.0/24 -p tcp -m conntrack --ctstate NEW -j ACCEPT - [0:0] -A OUTPUT -j LOG - [0:0] -A OUTPUT -j REJECT - COMMIT - mode: "0440" - - path: etc/iptables/rules-synchro.v4 - contents: | - *filter - :INPUT DROP [0:0] - :FORWARD DROP [0:0] - :OUTPUT DROP [0:0] - [0:0] -A INPUT -i lo -j ACCEPT - [0:0] -A INPUT -m conntrack --ctstate INVALID -j DROP - [0:0] -A INPUT -p icmp --icmp-type 8 -j ACCEPT - [0:0] -A INPUT -p icmp --icmp-type 0 -j ACCEPT - [0:0] -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT - [0:0] -A INPUT -j LOG - [0:0] -A FORWARD -j LOG - [0:0] -A OUTPUT -o lo -j ACCEPT - [0:0] -A OUTPUT -p icmp --icmp-type 0 -j ACCEPT - [0:0] -A OUTPUT -p icmp --icmp-type 8 -j ACCEPT - [0:0] -A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT - [0:0] -A OUTPUT -o eth2 -d 10.10.10.2 -p tcp -m conntrack --ctstate NEW -m tcp --dport ssh -j ACCEPT - [0:0] -A OUTPUT -j LOG - [0:0] -A OUTPUT -j REJECT - COMMIT - mode: "0440" - -trust: - org: - - linuxkit - - library diff --git a/fickit-boot.yml b/fickit-boot.yml deleted file mode 100644 index 9c08b4a8..00000000 --- a/fickit-boot.yml +++ /dev/null @@ -1,7 +0,0 @@ -kernel: - #image: nemunaire/kernel:5.10.62-0b705d955f5e283f62583c4e227d64a7924c138f-amd64 - image: linuxkit/kernel:6.6.71 - cmdline: "console=ttyS0 console=tty0" - -init: - - nemunaire/boot:66b0e88eb0f8befe2c85cc34f6c6554ee94d6fbb diff --git a/fickit-frontend.yml b/fickit-frontend.yml deleted file mode 100644 index 68814429..00000000 --- a/fickit-frontend.yml +++ /dev/null @@ -1,512 +0,0 @@ -kernel: - #image: nemunaire/kernel:5.10.62-0b705d955f5e283f62583c4e227d64a7924c138f-amd64 - image: linuxkit/kernel:6.6.71 - cmdline: "console=ttyS0 console=tty0" - -init: - - linuxkit/init:8eea386739975a43af558eec757a7dcb3a3d2e7b - - linuxkit/runc:667e7ea2c426a2460ca21e3da065a57dbb3369c9 - - linuxkit/containerd:a988a1a8bcbacc2c0390ca0c08f949e2b4b5915d - - linuxkit/ca-certificates:7b32a26ca9c275d3ef32b11fe2a83dbd2aee2fdb - - linuxkit/getty:05eca453695984a69617f1f1f0bcdae7f7032967 - - nemunaire/mdadm:04814350d71ba9417e1f861be1685de26adf7a67 - - nemunaire/kexec:839b4eedfce02a56c581dec2383dc6faff120855 - - nemunaire/fic-frontend-ui:latest - -onboot: - - name: mod - image: linuxkit/modprobe:773ee174006ecbb412830e48889795bae40b62f9 - command: ["/bin/sh", "-c", "modprobe xhci_pci ahci intel_lpss_pci i2c_i801 megaraid_sas tg3 bnxt_en"] - - - name: sysctl - image: linuxkit/sysctl:5f56434b81004b50b47ed629b222619168c2bcdf - - # Metadata - - name: metadata - image: linuxkit/metadata:4f81c0c3a2b245567fd7d32d799018c9614a9907 - command: ["/usr/bin/metadata", "-v", "cdrom"] - - # Filesystem - - name: swap - image: linuxkit/swap:f4b8ffef87c8c72165bd8a92b790ac252ccf1821 - command: ["/sbin/swapon", "/dev/sda3"] - - name: dm-crypt - image: linuxkit/dm-crypt:981fde241bb84616a5ba94c04cdefa1489431a25 - command: ["/usr/bin/crypto", "-l", "crypt_fic", "/dev/sda4"] - binds: - - /dev:/dev - - /run/config/dm-crypt:/etc/dm-crypt - - name: mount - image: linuxkit/mount:cb8caa72248f7082fc2074ce843d53cdc15df04a - command: ["/usr/bin/mountie", "-device", "/dev/mapper/crypt_fic", "/var/lib/fic" ] - - # Network -# - name: ntp -# image: linuxkit/openntpd:f99c4117763480815553b72022b426639a13ce86 - - name: nginx-ip-setup - image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156 - command: ["/bin/sh", "-c", "ip a add 172.17.1.2/24 dev vethin-nginx; ip link set vethin-nginx up;" ] - net: new - runtime: - interfaces: - - name: vethin-nginx - add: veth - peer: veth-nginx - bindNS: - net: /run/netns/nginx - - name: frontal-ip-setup # without bonding - image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156 - command: ["/bin/sh", "-c", "ip link set name bond-frontal eth3; ip link set bond-frontal up; while read IP; do ip a add ${IP} dev bond-frontal; done < /run/config/ip_config/frontend-players; ip r add default via $(cat /run/config/ip_config/frontend-router); ip link add link bond-frontal name internet type vlan id 4; ip a add 10.10.10.2/29 dev internet; ip link set internet up;" ] - net: /run/netns/nginx - binds: - - /run/config/ip_config/:/run/config/ip_config/:ro - runtime: - interfaces: -# - name: eth1 -# - name: eth2 - - name: eth3 -# - name: eth4 -# - name: frontal-ip-setup # with bonding -# image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156 -# command: ["/bin/sh", "-c", "ip link set dev bond-frontal type bond mode balance-alb; ip link set bond-frontal up; ifenslave bond-frontal eth1 eth2 eth3 eth4; while read IP; do ip a add ${IP} dev bond-frontal; done < /run/config/ip_config/frontend-players; ip r add default via $(cat /run/config/ip_config/frontend-router); ip link add link bond-frontal name internet type vlan id 4; ip link set internet up; sysctl -w net.ipv4.ip_forward=1;" ] -# net: /run/netns/nginx -# binds: -# - /run/config/ip_config/:/run/config/ip_config/:ro -# runtime: -# interfaces: -# - name: eth1 -# - name: eth2 -# - name: eth3 -# - name: eth4 -# - name: bond-frontal -# add: bond - - name: receiver-ip-setup - image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156 - command: ["/bin/sh", "-c", "ip a add 172.17.1.3/24 dev vethin-receiver; ip link set vethin-receiver up;" ] - net: new - runtime: - interfaces: - - name: vethin-receiver - add: veth - peer: veth-receiver - bindNS: - net: /run/netns/fic-receiver - - name: sshd-ip-setup - image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156 - command: ["/bin/sh", "-c", "ip a add 10.10.10.2/29 dev eth2; ip link set eth2 up;" ] - net: new - runtime: - interfaces: - - name: eth2 - bindNS: - net: /run/netns/sshd - - name: auth-ip-setup - image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156 - command: ["/bin/sh", "-c", "ip a add 172.17.1.4/24 dev vethin-auth; ip link set vethin-auth up;" ] - net: new - runtime: - interfaces: - - name: vethin-auth - add: veth - peer: veth-auth - bindNS: - net: /run/netns/auth - - name: bridge-setup - image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156 - command: ["/bin/sh", "-c", "ip a add 172.17.1.1/24 dev br0; ip link set veth-nginx master br0; ip link set veth-receiver master br0; ip link set veth-auth master br0; ip link set br0 up; ip link set veth-nginx up; ip link set veth-receiver up; ip link set veth-auth up;" ] - runtime: - interfaces: - - name: br0 - add: bridge - - - name: firewall-frontal - image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156 - command: ["/bin/bash", "-c", "/sbin/iptables-restore < /etc/iptables/rules-frontal.v4; /sbin/ip6tables-restore < /etc/iptables/rules.v6; [ -f /run/config/remote_sync/destination ] && /sbin/iptables -I OUTPUT 7 -o bond-frontal -d $(cat /run/config/remote_sync/destination | tr -d '\n') -p tcp -m tcp --dport https -j ACCEPT;" ] - binds: - - /etc/iptables/rules-frontal.v4:/etc/iptables/rules-frontal.v4:ro - - /etc/iptables/rules.v6:/etc/iptables/rules.v6:ro - - /etc/resolv.conf:/etc/resolv.conf:ro - - /run/config/remote_sync/:/run/config/remote_sync/:ro - net: /run/netns/nginx - - name: firewall-sshd - image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156 - command: ["/bin/bash", "-c", "/sbin/iptables-restore < /etc/iptables/rules-sshd.v4; /sbin/ip6tables-restore < /etc/iptables/rules.v6" ] - binds: - - /etc/iptables/rules-sshd.v4:/etc/iptables/rules-sshd.v4:ro - - /etc/iptables/rules.v6:/etc/iptables/rules.v6:ro - net: /run/netns/sshd - - - name: create-ssh-keys - image: nemunaire/rsync:a3d76b2dd0a9ad73be44dc77ad765b20d96a3285 - command: ["/bin/sh", "-c", "touch /etc/ssh/sshd_config && ssh-keygen -A"] - binds: - - /var/lib/fic/ssh:/etc/ssh - runtime: - mkdir: - - /var/lib/fic/ssh - -services: -# - name: getty -# image: linuxkit/getty:05eca453695984a69617f1f1f0bcdae7f7032967 -# env: -# - INSECURE=true - - # Enable acpi to shutdown on power events - - name: acpid - image: linuxkit/acpid:6cb5575e487a8fcbd4c3eb6721c23299e6ea452f - - name: rngd - image: linuxkit/rngd:1a18f2149e42a0a1cb9e7d37608a494342c26032 - - name: dhcpcd - image: linuxkit/dhcpcd:157df9ef45a035f1542ec2270e374f18efef98a5 - net: /run/netns/nginx - binds: - - /etc/dhcpcd.conf:/dhcpcd.conf:ro - - name: nginx - image: nginx:1-alpine - capabilities: - - CAP_NET_BIND_SERVICE - - CAP_CHOWN - - CAP_SETUID - - CAP_SETGID - - CAP_DAC_OVERRIDE - tmpfs: - - /var/cache - binds: - - /etc/hosts:/etc/hosts:ro - - /etc/resolv.conf:/etc/resolv.conf:ro - - /etc/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf:ro - - /run/config/tls_config/:/etc/nginx/ssl/:ro - - /etc/nginx/fic-auth.conf:/etc/nginx/fic-auth.conf:ro - - /etc/nginx/fic-get-team.conf:/etc/nginx/fic-get-team.conf:ro - - /www/htdocs-frontend:/srv/htdocs-frontend:ro - - /var/lib/fic/files:/srv/FILES:ro - - /var/lib/fic/pki:/srv/PKI:ro - - /var/lib/fic/settingsdist:/srv/SETTINGSDIST:ro - - /var/lib/fic/startingblock:/srv/startingblock:ro - - /var/lib/fic/teams:/srv/TEAMS:ro - net: /run/netns/nginx - pid: new - ipc: new - uts: new - runtime: - mkdir: - - /var/lib/fic/files - - /var/lib/fic/pki - - /var/lib/fic/startingblock - - /var/lib/fic/settingsdist - - /var/lib/fic/teams - - name: fic-receiver - image: nemunaire/fic-receiver:latest - command: ["/srv/receiver", "-bind=:8080", "-startedFile=/srv/startingblock/started"] - binds: - - /etc/hosts:/etc/hosts:ro - - /var/lib/fic/files:/srv/FILES:ro - - /var/lib/fic/settingsdist:/srv/SETTINGSDIST:ro - - /var/lib/fic/startingblock:/srv/startingblock - - /var/lib/fic/submissions:/srv/submissions - - /var/lib/fic/teams:/srv/TEAMS:ro - net: /run/netns/fic-receiver - runtime: - mkdir: - - /var/lib/fic/files - - /var/lib/fic/settingsdist - - /var/lib/fic/startingblock - - /var/lib/fic/submissions - - /var/lib/fic/teams - - name: fic-remote-challenge-sync-airbus - image: nemunaire/fic-remote-challenge-sync-airbus:latest - command: ["/srv/challenge-sync-airbus", "--no-validate-challenge", "--watch"] - env: - - AIRBUS_BASEURL_FILE=/run/config/remote_sync/baseurl - - AIRBUS_TOKEN_FILE=/run/config/remote_sync/token - - AIRBUS_SESSION_NAME_FILE=/run/config/remote_sync/session_name - - AIRBUS_SKIP_TLS_VERIFY=true - binds: - - /etc/hosts:/etc/hosts:ro - - /etc/resolv.conf:/etc/resolv.conf:ro - - /run/config/remote_sync/:/run/config/remote_sync/:ro - - /var/lib/fic/teams:/srv/TEAMS:ro - - /var/lib/fic/remote:/srv/REMOTE - runtime: - mkdir: - - /var/lib/fic/remote - net: /run/netns/nginx - - name: fic-remote-challenge-sync - image: alpine:3 - command: ["/bin/sh", "-c", "while true; do sleep 300; done;"] - env: - - AIRBUS_BASEURL_FILE=/run/config/remote_sync/baseurl - - AIRBUS_TOKEN_FILE=/run/config/remote_sync/token - - AIRBUS_SESSION_NAME_FILE=/run/config/remote_sync/session_name - - AIRBUS_SKIP_TLS_VERIFY=true - binds: - - /etc/hosts:/etc/hosts:ro - - /etc/resolv.conf:/etc/resolv.conf:ro - - /run/config/remote_sync/:/run/config/remote_sync/:ro - - /var/lib/fic/teams:/srv/TEAMS:ro - - /var/lib/fic/remote:/srv/REMOTE - net: /run/netns/nginx - - name: sshd - image: nemunaire/rsync:a3d76b2dd0a9ad73be44dc77ad765b20d96a3285 - capabilities: - - all - binds: - - /etc/hosts:/etc/hosts:ro - - /var/lib/fic/ssh:/etc/ssh:ro - - /run/config/synchro/id_ed25519.pub:/root/.ssh/authorized_keys:ro - - /var/lib/fic/files:/srv/FILES - - /var/lib/fic/pki:/srv/PKI - - /var/lib/fic/settingsdist:/srv/SETTINGSDIST - - /var/lib/fic/submissions:/srv/submissions - - /var/lib/fic/teams:/srv/TEAMS - - /var/log:/var/log:ro - net: /run/netns/sshd - runtime: - mkdir: - - /var/lib/fic/files - - /var/lib/fic/pki - - /var/lib/fic/settingsdist - - /var/lib/fic/submissions - - /var/lib/fic/teams - -# - name: dhcp-server -# image: joebiellik/dhcpd -# binds: -# - /etc/dhcp/dhcpd.conf:/etc/dhcp/dhcpd.conf:ro -# capabilities: -# - CAP_NET_BIND_SERVICE -# - CAP_NET_RAW -# - CAP_DAC_OVERRIDE -# net: /run/netns/nginx -# pid: new -# ipc: new -# uts: new -# - name: dns-server -# image: nemunaire/unbound:8a5c8b7be1392fea9300bc884926141cb6db6792 -# binds: -# - /etc/unbound/unbound.d:/etc/unbound/unbound.d:ro -# net: /run/netns/nginx - - - name: dexidp - image: ghcr.io/dexidp/dex:v2.42.0 - net: /run/netns/auth - binds: - - /etc/hosts:/etc/hosts:ro - - /var/lib/fic/pki/shared/dex-config.yaml:/etc/dex/config.docker.yaml:ro - - /var/lib/fic/dex:/var/dex - - /www/dex-templates/theme/styles.css:/srv/dex/web/themes/light/styles.css - - /www/dex-templates/templates/header.html:/srv/dex/web/templates/header.html - - /var/lib/fic/pki/shared/dex-password.tpl:/srv/dex/web/templates/password.html - - /var/lib/fic/files/logo/ec2.png:/srv/dex/web/themes/light/favicon.png - runtime: - mkdir: - - /var/lib/fic/dex - - name: vouch-proxy - image: quay.io/vouch/vouch-proxy:alpine-0.41 - env: - - VOUCH_CONFIG=/etc/vouch/config.yml - net: /run/netns/auth - binds: - - /var/lib/fic/pki/shared/vouch-config.yaml:/etc/vouch/config.yml:ro - - -files: - - path: etc/init.d/001-hostname - contents: | - #!/bin/sh - /bin/hostname phobos - mode: "0555" - - - path: etc/profile.d/color_prompt.sh - contents: | - PS1='\[\e[1;35m\]'$PS1'\[\e[0m\]' - mode: "0444" - - - path: etc/init.d/015-setup-sshd - source: configs/sshd-setup.sh - mode: "0555" - - path: etc/init.d/016-copy-dex-config - contents: | - #!/bin/sh - [ -f /var/lib/fic/pki/shared/dex-config.yaml ] || { - cp /etc/dex/config.auth.yaml /var/lib/fic/pki/shared/dex-config.yaml - } - [ -e /var/lib/fic/pki/shared ] || mkdir -p /var/lib/fic/pki/shared - [ -f /var/lib/fic/pki/shared/dex-password.tpl ] || { - cp /www/dex-templates/templates/password.html /var/lib/fic/pki/shared/dex-password.tpl - } - [ -f /var/lib/fic/pki/shared/vouch-config.yaml ] || { - touch /var/lib/fic/pki/shared/vouch-config.yaml - } - mode: "0555" - - - path: /containers/onboot/000-sysctl/rootfs/etc/sysctl.d/01-fic.conf - source: configs/sysctl-frontend.conf - mode: "0444" - - - path: etc/hosts - source: configs/hosts - mode: "0644" - - - path: etc/dhcp/dhcpd.conf - source: configs/dhcpd.conf - mode: "0400" - - path: etc/nginx/conf.d/default.conf - source: configs/nginx/base/prod.conf - mode: "0400" - - path: etc/nginx/fic-auth.conf - source: configs/nginx/auth/oidc.conf - mode: "0400" - - path: etc/nginx/fic-get-team.conf - source: configs/nginx/get-team/oidc.conf - mode: "0400" - - path: etc/nginx/conf.d/qa.fic-conf - contents: | - location /qa { - include fic-get-team.conf; - - proxy_pass http://deimos:8083; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-FIC-Team $team; - proxy_redirect off; - } - mode: "0400" - - - path: etc/dex/config.auth.yaml - source: configs/dex.yaml - mode: "0400" - - path: www/dex-templates/theme/styles.css - source: configs/dex-templates/theme/styles.css - mode: "0444" - - path: www/dex-templates/templates/header.html - source: configs/dex-templates/templates/header.html - mode: "0444" - - path: www/dex-templates/templates/password.html - source: configs/dex-templates/templates/password.html - mode: "0444" - - - path: usr/bin/status - contents: | - #!/bin/sh - ctr -n services.linuxkit t ls - mode: "0755" - - path: usr/bin/iptables - source: configs/nsenter_iptables.sh - mode: "0755" - - path: usr/bin/pnsenter - source: configs/nsenter_process.sh - mode: "0755" - - - path: etc/resolv.conf - contents: | - nameserver 9.9.9.9 - mode: "0444" - - - path: etc/dhcpcd.conf - contents: | - allowinterfaces internet - hostname - clientid - persistent - option rapid_commit - option domain_name_servers, domain_name, domain_search, host_name - option classless_static_routes - option interface_mtu - require dhcp_server_identifier - slaac private - nodelay - noarp - waitip 4 - mode: "0440" - - path: etc/unbound/unbound.d/tmp-user.conf - contents: | - username: "" - mode: "0440" -# - path: etc/unbound/unbound.d/forwarder.conf -# contents: | -# forward-zone: -# name: "." -# forward-addr: 9.9.9.9 -# forward-addr: 1.1.1.1 -# mode: "0440" - - path: etc/unbound/unbound.d/access-control.conf - contents: | - access-control: 172.23.0.0/16 allow - mode: "0440" - - path: etc/unbound/unbound.d/local-zone.conf - contents: | - local-zone: "fic.srs.epita.fr" typetransparent - local-data: "live.fic.srs.epita.fr A 172.23.42.1" - local-data-ptr: "172.23.42.1 live.fic.srs.epita.fr" - mode: "0440" - - path: etc/iptables/rules.v6 - contents: | - *filter - :INPUT DROP [0:0] - :FORWARD DROP [0:0] - :OUTPUT DROP [0:0] - COMMIT - mode: "0440" - - path: etc/iptables/rules-sshd.v4 - contents: | - *filter - :INPUT DROP [0:0] - :FORWARD DROP [0:0] - :OUTPUT DROP [0:0] - [0:0] -A INPUT -i lo -j ACCEPT - [0:0] -A INPUT -m conntrack --ctstate INVALID -j DROP - [0:0] -A INPUT -p icmp -j ACCEPT - [0:0] -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT - [0:0] -A INPUT -i eth2 -s 10.10.10.0/29 -p tcp -m conntrack --ctstate NEW -m tcp --dport ssh -j ACCEPT - [0:0] -A INPUT -j LOG - [0:0] -A FORWARD -j LOG - [0:0] -A OUTPUT -o lo -j ACCEPT - [0:0] -A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT - [0:0] -A OUTPUT -j LOG - [0:0] -A OUTPUT -j REJECT - COMMIT - mode: "0440" - - path: etc/iptables/rules-frontal.v4 - contents: | - *nat - :PREROUTING ACCEPT [0:0] - :INPUT ACCEPT [0:0] - :OUTPUT ACCEPT [0:0] - :POSTROUTING ACCEPT [0:0] - [0:0] -A POSTROUTING -o internet -j MASQUERADE - COMMIT - *filter - :INPUT DROP [0:0] - :FORWARD ACCEPT [0:0] - :OUTPUT DROP [0:0] - [0:0] -A INPUT -i lo -j ACCEPT - [0:0] -A INPUT -m conntrack --ctstate INVALID -j DROP - [0:0] -A INPUT -p icmp --icmp-type 8 -j ACCEPT - [0:0] -A INPUT -p icmp --icmp-type 0 -j ACCEPT - [0:0] -A INPUT -i bond-frontal -p udp -m udp --dport domain -j ACCEPT - [0:0] -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT - [0:0] -A INPUT -i bond-frontal -p tcp -m conntrack --ctstate NEW -m tcp --dport domain -j ACCEPT - [0:0] -A INPUT -i bond-frontal -p tcp -m conntrack --ctstate NEW -m tcp --dport http -j ACCEPT - [0:0] -A INPUT -i bond-frontal -p tcp -m conntrack --ctstate NEW -m tcp --dport https -j ACCEPT - [0:0] -A INPUT -j LOG - [0:0] -A FORWARD -j LOG - [0:0] -A OUTPUT -o lo -j ACCEPT - [0:0] -A OUTPUT -p icmp --icmp-type 0 -j ACCEPT - [0:0] -A OUTPUT -p icmp --icmp-type 8 -j ACCEPT - [0:0] -A OUTPUT -o bond-frontal -p udp -m udp --sport domain -j ACCEPT - [0:0] -A OUTPUT -o bond-frontal -d 9.9.9.9 -p udp -m udp --dport domain -j ACCEPT - [0:0] -A OUTPUT -o bond-frontal -d 9.9.9.9 -p tcp -m tcp --dport domain -j ACCEPT - [0:0] -A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT - [0:0] -A OUTPUT -o vethin-nginx -d 172.17.1.3 -p tcp -m conntrack --ctstate NEW -m tcp --dport 8080 -j ACCEPT - [0:0] -A OUTPUT -o vethin-nginx -d 172.17.1.4 -p tcp -m conntrack --ctstate NEW -m tcp --dport 5556 -j ACCEPT - [0:0] -A OUTPUT -o vethin-nginx -d 172.17.1.4 -p tcp -m conntrack --ctstate NEW -m tcp --dport 9090 -j ACCEPT - [0:0] -A OUTPUT -o internet -j ACCEPT - [0:0] -A OUTPUT -j LOG - [0:0] -A OUTPUT -j REJECT - COMMIT - mode: "0440" - -trust: - org: - - linuxkit - - library diff --git a/fickit-pkg/boot/Dockerfile b/fickit-pkg/boot/Dockerfile deleted file mode 100644 index 39c59490..00000000 --- a/fickit-pkg/boot/Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM alpine:latest - -COPY init.sh /init diff --git a/fickit-pkg/boot/build.yml b/fickit-pkg/boot/build.yml deleted file mode 100644 index 241b4f3e..00000000 --- a/fickit-pkg/boot/build.yml +++ /dev/null @@ -1,5 +0,0 @@ -image: boot -org: nemunaire -network: true -arches: - - amd64 diff --git a/fickit-pkg/boot/init.sh b/fickit-pkg/boot/init.sh deleted file mode 100755 index 29027b97..00000000 --- a/fickit-pkg/boot/init.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/sh - -# /proc/cmdline parser (from Gentoo Wiki) -cmdline() { - local value - value=" $(cat /proc/cmdline) " - value="${value##* $1=}" - value="${value%% *}" - [ "$value" != "" ] && echo "$value" -} - -modprobe xhci_pci -modprobe ahci -modprobe intel_lpss_pci -modprobe i2c_i801 -modprobe megaraid_sas -modprobe tg3 -modprobe bnxt_en - -mount -t devtmpfs none /dev -mount -t proc none /proc - -mount -o ro /dev/sda1 /boot - -mount -o loop -t squashfs /boot/imgs/$(cmdline root) /mnt || /bin/sh - -umount /dev -umount /proc -mount --move /boot /mnt/boot - -# Switch -exec switch_root /mnt /init diff --git a/fickit-pkg/kexec/Dockerfile b/fickit-pkg/kexec/Dockerfile deleted file mode 100644 index b7216f6c..00000000 --- a/fickit-pkg/kexec/Dockerfile +++ /dev/null @@ -1,9 +0,0 @@ -FROM alpine:edge AS mirror - -RUN mkdir -p /out/etc/apk && cp -r /etc/apk/* /out/etc/apk/ -RUN apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/testing/ --initdb -p /out \ - kexec-tools -RUN rm -rf /out/etc/apk /out/lib/apk /out/var/cache - -FROM scratch -COPY --from=mirror /out/ / diff --git a/fickit-pkg/kexec/build.yml b/fickit-pkg/kexec/build.yml deleted file mode 100644 index 34fe2af2..00000000 --- a/fickit-pkg/kexec/build.yml +++ /dev/null @@ -1,12 +0,0 @@ -image: kexec -org: nemunaire -network: true -config: - binds: - - /dev:/dev - capabilities: - - CAP_SYS_BOOT - net: new - ipc: new -arches: - - amd64 diff --git a/fickit-pkg/mariadb-client/Dockerfile b/fickit-pkg/mariadb-client/Dockerfile deleted file mode 100644 index d7e952c8..00000000 --- a/fickit-pkg/mariadb-client/Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM alpine AS mirror - -RUN mkdir -p /out/etc/apk && cp -r /etc/apk/* /out/etc/apk/ -RUN apk add --no-cache --initdb -p /out \ - mariadb-client \ - busybox -RUN rm -rf /out/etc/apk /out/lib/apk /out/var/cache - -FROM scratch -COPY --from=mirror /out/ / diff --git a/fickit-pkg/mariadb-client/build.yml b/fickit-pkg/mariadb-client/build.yml deleted file mode 100644 index 9c28d944..00000000 --- a/fickit-pkg/mariadb-client/build.yml +++ /dev/null @@ -1,10 +0,0 @@ -image: mariadb-client -org: nemunaire -network: true -config: - ipc: new - net: new - pid: new - uts: new -arches: - - amd64 diff --git a/fickit-pkg/mdadm/Dockerfile b/fickit-pkg/mdadm/Dockerfile deleted file mode 100644 index ebaf0803..00000000 --- a/fickit-pkg/mdadm/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM alpine AS mirror - -RUN mkdir -p /out/etc/apk && cp -r /etc/apk/* /out/etc/apk/ -RUN apk add --no-cache --initdb -p /out \ - busybox \ - mdadm -RUN rm -rf /out/etc/apk /out/lib/apk /out/var/cache - -FROM scratch -COPY --from=mirror /out/ / -RUN rm -f /etc/init.d/* -COPY etc/ /etc/ -CMD ["/sbin/mdadm", "--monitor", "--scan"] diff --git a/fickit-pkg/mdadm/build.yml b/fickit-pkg/mdadm/build.yml deleted file mode 100644 index a983b095..00000000 --- a/fickit-pkg/mdadm/build.yml +++ /dev/null @@ -1,13 +0,0 @@ -image: mdadm -org: nemunaire -network: true -config: - binds: - - /dev:/dev - capabilities: - - CAP_SYS_ADMIN - - CAP_MKNOD - net: new - ipc: new -arches: - - amd64 diff --git a/fickit-pkg/mdadm/etc/init.d/005-mdadm b/fickit-pkg/mdadm/etc/init.d/005-mdadm deleted file mode 100755 index 1b52ae87..00000000 --- a/fickit-pkg/mdadm/etc/init.d/005-mdadm +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -mdadm --assemble /dev/md0 /dev/sda3 /dev/sdb3 diff --git a/fickit-pkg/rsync/Dockerfile b/fickit-pkg/rsync/Dockerfile deleted file mode 100644 index 6f68310e..00000000 --- a/fickit-pkg/rsync/Dockerfile +++ /dev/null @@ -1,25 +0,0 @@ -FROM alpine:latest AS mirror - -RUN mkdir -p /out/etc/apk && cp -r /etc/apk/* /out/etc/apk/ -RUN apk add --no-cache --initdb -p /out \ - alpine-baselayout \ - apk-tools \ - busybox \ - ca-certificates \ - musl \ - openssh-server \ - openssh-client \ - rsync \ - tini \ - util-linux \ - && true -#RUN mv /out/etc/apk/repositories.upstream /out/etc/apk/repositories - -FROM scratch -ENTRYPOINT [] -WORKDIR / -COPY --from=mirror /out/ / -COPY etc/ /etc/ -COPY usr/ /usr/ -RUN mkdir -p /etc/ssh /root/.ssh && chmod 0700 /root/.ssh -CMD ["/sbin/tini", "/usr/bin/ssh.sh"] diff --git a/fickit-pkg/rsync/build.yml b/fickit-pkg/rsync/build.yml deleted file mode 100644 index 2cf18981..00000000 --- a/fickit-pkg/rsync/build.yml +++ /dev/null @@ -1,20 +0,0 @@ -image: rsync -org: nemunaire -network: true -config: - binds: - - /root/.ssh:/root/.ssh - - /etc/resolv.conf:/etc/resolv.conf - capabilities: - - CAP_NET_BIND_SERVICE - - CAP_SYS_CHROOT - - CAP_SETGID - - CAP_SETUID - - CAP_KILL - - CAP_CHOWN - - CAP_FOWNER - pid: new - ipc: new - uts: new -arches: - - amd64 diff --git a/fickit-pkg/rsync/etc/motd b/fickit-pkg/rsync/etc/motd deleted file mode 100644 index 2414d0d9..00000000 --- a/fickit-pkg/rsync/etc/motd +++ /dev/null @@ -1,4 +0,0 @@ -Welcome to LinuxKit! - -NOTE: This system is namespaced. -The namespace you are currently in may not be the root. diff --git a/fickit-pkg/rsync/etc/profile.d/namespace.sh b/fickit-pkg/rsync/etc/profile.d/namespace.sh deleted file mode 100644 index 45775ea0..00000000 --- a/fickit-pkg/rsync/etc/profile.d/namespace.sh +++ /dev/null @@ -1 +0,0 @@ -export PS1="(ns: sshd) $PS1" diff --git a/fickit-pkg/rsync/etc/ssh/sshd_config b/fickit-pkg/rsync/etc/ssh/sshd_config deleted file mode 100644 index 8cfa9570..00000000 --- a/fickit-pkg/rsync/etc/ssh/sshd_config +++ /dev/null @@ -1,144 +0,0 @@ -# $OpenBSD: sshd_config,v 1.98 2016/02/17 05:29:04 djm Exp $ - -# This is the sshd server system-wide configuration file. See -# sshd_config(5) for more information. - -# This sshd was compiled with PATH=/bin:/usr/bin:/sbin:/usr/sbin - -# The strategy used for options in the default sshd_config shipped with -# OpenSSH is to specify options with their default value where -# possible, but leave them commented. Uncommented options override the -# default value. - -#Port 22 -#AddressFamily any -#ListenAddress 0.0.0.0 -#ListenAddress :: - -# The default requires explicit activation of protocol 1 -#Protocol 2 - -# HostKey for protocol version 1 -#HostKey /etc/ssh/ssh_host_key -# HostKeys for protocol version 2 -#HostKey /etc/ssh/ssh_host_rsa_key -#HostKey /etc/ssh/ssh_host_dsa_key -#HostKey /etc/ssh/ssh_host_ecdsa_key -#HostKey /etc/ssh/ssh_host_ed25519_key - -# Lifetime and size of ephemeral version 1 server key -#KeyRegenerationInterval 1h -#ServerKeyBits 1024 - -# Ciphers and keying -#RekeyLimit default none - -# Logging -# obsoletes QuietMode and FascistLogging -#SyslogFacility AUTH -#LogLevel INFO - -# Authentication: - -#LoginGraceTime 2m -#PermitRootLogin prohibit-password -#StrictModes yes -#MaxAuthTries 6 -#MaxSessions 10 - -#RSAAuthentication yes -#PubkeyAuthentication yes - -# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2 -# but this is overridden so installations will only check .ssh/authorized_keys -AuthorizedKeysFile .ssh/authorized_keys - -#AuthorizedPrincipalsFile none - -#AuthorizedKeysCommand none -#AuthorizedKeysCommandUser nobody - -# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts -#RhostsRSAAuthentication no -# similar for protocol version 2 -#HostbasedAuthentication no -# Change to yes if you don't trust ~/.ssh/known_hosts for -# RhostsRSAAuthentication and HostbasedAuthentication -#IgnoreUserKnownHosts no -# Don't read the user's ~/.rhosts and ~/.shosts files -#IgnoreRhosts yes - -# To disable tunneled clear text passwords, change to no here! -PasswordAuthentication no -#PermitEmptyPasswords no - -# Change to no to disable s/key passwords -ChallengeResponseAuthentication no - -# Kerberos options -#KerberosAuthentication no -#KerberosOrLocalPasswd yes -#KerberosTicketCleanup yes -#KerberosGetAFSToken no - -# GSSAPI options -#GSSAPIAuthentication no -#GSSAPICleanupCredentials yes - -# Set this to 'yes' to enable PAM authentication, account processing, -# and session processing. If this is enabled, PAM authentication will -# be allowed through the ChallengeResponseAuthentication and -# PasswordAuthentication. Depending on your PAM configuration, -# PAM authentication via ChallengeResponseAuthentication may bypass -# the setting of "PermitRootLogin without-password". -# If you just want the PAM account and session checks to run without -# PAM authentication, then enable this but set PasswordAuthentication -# and ChallengeResponseAuthentication to 'no'. -#UsePAM no - -#AllowAgentForwarding yes -#AllowTcpForwarding yes -#GatewayPorts no -#X11Forwarding no -#X11DisplayOffset 10 -#X11UseLocalhost yes -#PermitTTY yes -#PrintMotd yes -#PrintLastLog yes -#TCPKeepAlive yes -#UseLogin no -#UsePrivilegeSeparation sandbox -#PermitUserEnvironment no -#Compression delayed -#ClientAliveInterval 0 -#ClientAliveCountMax 3 -#UseDNS no -#PidFile /run/sshd.pid -#MaxStartups 10:30:100 -#PermitTunnel no -#ChrootDirectory none -#VersionAddendum none - -# no default banner path -#Banner none - -# Default of no subsystems -#Subsystem sftp /usr/lib/ssh/sftp-server - -# the following are HPN related configuration options -# tcp receive buffer polling. disable in non autotuning kernels -#TcpRcvBufPoll yes - -# disable hpn performance boosts -#HPNDisabled no - -# buffer size for hpn to non-hpn connections -#HPNBufferSize 2048 - - -# Example of overriding settings on a per-user basis -#Match User anoncvs -# X11Forwarding no -# AllowTcpForwarding no -# PermitTTY no -# ForceCommand cvs server diff --git a/fickit-pkg/rsync/usr/bin/ssh.sh b/fickit-pkg/rsync/usr/bin/ssh.sh deleted file mode 100755 index 46dbf090..00000000 --- a/fickit-pkg/rsync/usr/bin/ssh.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -KEYS=$(find /etc/ssh -name 'ssh_host_*_key') -[ -z "$KEYS" ] && ssh-keygen -A >/dev/null 2>/dev/null - -exec /usr/sbin/sshd -D -e diff --git a/fickit-pkg/syslinux/Dockerfile b/fickit-pkg/syslinux/Dockerfile deleted file mode 100644 index 4eabde17..00000000 --- a/fickit-pkg/syslinux/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -FROM alpine AS mirror - -RUN mkdir -p /out/etc/apk && cp -r /etc/apk/* /out/etc/apk/ -RUN apk add --no-cache --initdb -p /out \ - dosfstools \ - syslinux \ - grub-bios grub-efi -RUN rm -rf /out/etc/apk /out/lib/apk /out/var/cache - -FROM scratch -COPY --from=mirror /out/ / diff --git a/fickit-pkg/syslinux/build.yml b/fickit-pkg/syslinux/build.yml deleted file mode 100644 index 92bf71e9..00000000 --- a/fickit-pkg/syslinux/build.yml +++ /dev/null @@ -1,11 +0,0 @@ -image: syslinux -org: nemunaire -network: true -config: - binds: - - /dev:/dev - capabilities: - - CAP_SYS_ADMIN - ipc: new -arches: - - amd64 diff --git a/fickit-pkg/unbound/Dockerfile b/fickit-pkg/unbound/Dockerfile deleted file mode 100644 index d4951d44..00000000 --- a/fickit-pkg/unbound/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM alpine AS mirror - -RUN mkdir -p /out/etc/apk && cp -r /etc/apk/* /out/etc/apk/ -RUN apk add --no-cache --initdb -p /out \ - unbound -RUN rm -rf /out/etc/apk /out/lib/apk /out/var/cache -RUN wget -O /out/etc/unbound/root.hints ftp://ftp.internic.net/domain/named.cache - -FROM scratch -COPY --from=mirror /out/ / -COPY etc/unbound/ /etc/unbound - -CMD ["/usr/sbin/unbound", "-d"] diff --git a/fickit-pkg/unbound/build.yml b/fickit-pkg/unbound/build.yml deleted file mode 100644 index afb1849a..00000000 --- a/fickit-pkg/unbound/build.yml +++ /dev/null @@ -1,11 +0,0 @@ -image: unbound -org: nemunaire -network: true -config: - capabilities: - - CAP_NET_BIND_SERVICE - ipc: new - pid: new - uts: new -arches: - - amd64 diff --git a/fickit-pkg/unbound/etc/unbound/unbound.conf b/fickit-pkg/unbound/etc/unbound/unbound.conf deleted file mode 100644 index 122e74fc..00000000 --- a/fickit-pkg/unbound/etc/unbound/unbound.conf +++ /dev/null @@ -1,135 +0,0 @@ -server: - verbosity: 1 - - # number of threads to create. 1 disables threading. - # num-threads: 1 - - # specify the interfaces to answer queries from by ip-address. - interface: 0.0.0.0 - interface: ::0 - - # Detach from the terminal, run in background, "yes" or "no". - # Set the value to "no" when unbound runs as systemd service. - do-daemonize: no - - # control which clients are allowed to make (recursive) queries - # to this server. Specify classless netblocks with /size and action. - # By default everything is refused, except for localhost. - # Choose deny (drop message), refuse (polite error reply), - # allow (recursive ok), allow_snoop (recursive and nonrecursive ok) - # deny_non_local (drop queries unless can be answered from local-data) - # refuse_non_local (like deny_non_local but polite error reply). - #access-control: 172.23.0.0/16 allow - - # if given, user privileges are dropped (after binding port), - # and the given username is assumed. Default is user "unbound". - # If you give "" no privileges are dropped. - # username: "unbound" - - logfile: "" - use-syslog: no - - # print one line with time, IP, name, type, class for every query. - # log-queries: no - log-queries: yes - - # print one line per reply, with time, IP, name, type, class, rcode, - # timetoresolve, fromcache and responsesize. - # log-replies: no - log-replies: yes - - # log the local-zone actions, like local-zone type inform is enabled - # also for the other local zone types. - # log-local-actions: no - - # print log lines that say why queries return SERVFAIL to clients. - log-servfail: yes - - # file to read root hints from. - # get one from https://www.internic.net/domain/named.cache - # root-hints: "" - root-hints: "/usr/share/dns-root-hints/named.root" - - # enable to not answer id.server and hostname.bind queries. - # hide-identity: no - hide-identity: yes - - # enable to not answer version.server and version.bind queries. - # hide-version: no - hide-version: yes - - # enable to not answer trustanchor.unbound queries. - # hide-trustanchor: no - - # Sent minimum amount of information to upstream servers to enhance - # privacy. Only sent minimum required labels of the QNAME and set QTYPE - # to NS when possible. - qname-minimisation: yes - - # Enforce privacy of these addresses. Strips them away from answers. - # It may cause DNSSEC validation to additionally mark it as bogus. - # Protects against 'DNS Rebinding' (uses browser as network proxy). - # Only 'private-domain' and 'local-data' names are allowed to have - # these private addresses. No default. - # private-address: 10.0.0.0/8 - # private-address: 172.16.0.0/12 - # private-address: 192.168.0.0/16 - # private-address: 169.254.0.0/16 - # private-address: fd00::/8 - # private-address: fe80::/10 - # private-address: ::ffff:0:0/96 - - # if yes, Unbound rotates RRSet order in response. - rrset-roundrobin: yes - - # if yes, Unbound doesn't insert authority/additional sections - # into response messages when those sections are not required. - minimal-responses: yes - - # true to disable DNSSEC lameness check in iterator. - # disable-dnssec-lame-check: no - - # module configuration of the server. A string with identifiers - # separated by spaces. Syntax: "[dns64] [validator] iterator" - module-config: "validator iterator" - - # File with trusted keys, kept uptodate using RFC5011 probes, - # initial file like trust-anchor-file, then it stores metadata. - # Use several entries, one per domain name, to track multiple zones. - # - # If you want to perform DNSSEC validation, run unbound-anchor before - # you start unbound (i.e. in the system boot scripts). And enable: - # Please note usage of unbound-anchor root anchor is at your own risk - # and under the terms of our LICENSE (see that file in the source). - #auto-trust-anchor-file: "var/root-anchors.txt" - - # trust anchor signaling sends a RFC8145 key tag query after priming. - # trust-anchor-signaling: yes - - # File with DLV trusted keys. Same format as trust-anchor-file. - # There can be only one DLV configured, it is trusted from root down. - # DLV is going to be decommissioned. Please do not use it any more. - # dlv-anchor-file: "dlv.isc.org.key" - - # File with trusted keys for validation. Specify more than one file - # with several entries, one file per entry. - # Zone file format, with DS and DNSKEY entries. - # Note this gets out of date, use auto-trust-anchor-file please. - # trust-anchor-file: "/etc/dnssec/root-anchors.txt" - trust-anchor-file: "/usr/share/dnssec-root/trusted-key.key" - - # Trusted key for validation. DS or DNSKEY. specify the RR on a - # single line, surrounded by "". TTL is ignored. class is IN default. - # Note this gets out of date, use auto-trust-anchor-file please. - # (These examples are from August 2007 and may not be valid anymore). - # trust-anchor: "nlnetlabs.nl. DNSKEY 257 3 5 AQPzzTWMz8qSWIQlfRnPckx2BiVmkVN6LPupO3mbz7FhLSnm26n6iG9N Lby97Ji453aWZY3M5/xJBSOS2vWtco2t8C0+xeO1bc/d6ZTy32DHchpW 6rDH1vp86Ll+ha0tmwyy9QP7y2bVw5zSbFCrefk8qCUBgfHm9bHzMG1U BYtEIQ==" - # trust-anchor: "jelte.nlnetlabs.nl. DS 42860 5 1 14D739EB566D2B1A5E216A0BA4D17FA9B038BE4A" - - # Turn permissive mode on to permit bogus messages. Thus, messages - # for which security checks failed will be returned to clients, - # instead of SERVFAIL. It still performs the security checks, which - # result in interesting log files and possibly the AD bit in - # replies if the message is found secure. The default is off. - #val-permissive-mode: yes - - include: /etc/unbound/unbound.d/*.conf diff --git a/fickit-prepare.yml b/fickit-prepare.yml deleted file mode 100644 index 24c5287e..00000000 --- a/fickit-prepare.yml +++ /dev/null @@ -1,259 +0,0 @@ -kernel: - #image: nemunaire/kernel:5.10.62-0b705d955f5e283f62583c4e227d64a7924c138f-amd64 - image: linuxkit/kernel:6.6.71 - cmdline: "console=ttyS0 console=tty0" - - -init: - - nemunaire/mdadm:04814350d71ba9417e1f861be1685de26adf7a67 - - nemunaire/syslinux:086f221f281d577d300949aa1094fb20c5cd90dc - - linuxkit/format:3fb088f60ed73ba4a15be41e44654b74112fd3f9 - - linuxkit/dm-crypt:981fde241bb84616a5ba94c04cdefa1489431a25 - - linuxkit/metadata:4f81c0c3a2b245567fd7d32d799018c9614a9907 - - alpine:latest - -files: - - path: /init - contents: | - #!/bin/sh - modprobe xhci_pci - modprobe ahci - modprobe megaraid_sas - modprobe e1000e - modprobe tg3 - modprobe bnxt_en - - echo -n "Waiting module loading... " - sleep 3 - echo - - mount -t devtmpfs none /dev - mount -t proc none /proc - mount -t sysfs none /sys - - mdev -s - mdadm --auto-detect - - if [ -b /dev/sdb ]; then - DISKS="/dev/sda /dev/sdb" - BOOT_PART=/dev/md2 - META_PART=/dev/md3 - SWAP_PART=/dev/md1 - ROOT_PART=/dev/md0 - RAID=1 - else - DISKS="/dev/sda" - BOOT_PART=/dev/sda1 - META_PART=/dev/sda2 - SWAP_PART=/dev/sda3 - ROOT_PART=/dev/sda4 - RAID=0 - fi - - ip link set eth0 up - udhcpc -i eth0 - - # /proc/cmdline parser (from Gentoo Wiki) - cmdline() { - local value - value=" $(cat /proc/cmdline) " - value="${value##* $1=}" - value="${value%% *}" - [ "$value" != "" ] && echo "$value" - } - - # Retrieve metadata - wget -O /tmp/metadata.iso "$(ip r | grep default | awk '{ print $3 }')/fickit-metadata.iso" - mount /tmp/metadata.iso /mnt - /usr/bin/metadata -v file=/mnt/user-data - - AUTOPREPARE=$(cmdline fickit.autoprepare) - if [ -z "${AUTOPREPARE}" ] - then - # Try to detect backend/frontend setup - if ip l | grep -q eth3 - then - DEFAULT_BOOT=1 - echo -n "Detected: FRONTEND host " - else - DEFAULT_BOOT=0 - echo -n "Detected: BACKEND host " - fi - - [ "${RAID}" -eq 1 ] && echo "with RAID setup" || echo "without raid" - - echo - read -p "Proceed? (y/N/Front/Back) " V - if [ "$V" == "F" ] || [ "$V" == "f" ]; then - DEFAULT_BOOT=1 - elif [ "$V" == "B" ] || [ "$V" == "b" ]; then - DEFAULT_BOOT=0 - elif [ "$V" != "y" ]; then - while true; do - /bin/ash - done - fi - elif [ "${AUTOPREPARE}" == "backend" ] - then - DEFAULT_BOOT=0 - elif [ "${AUTOPREPARE}" == "frontend" ] - then - DEFAULT_BOOT=1 - else - echo - echo "Invalid fickit.autoprepare value: got $AUTOPREPARE, expected frontend or backend." - echo - - while true; do - /bin/ash - done - fi - - # Create partition table and boot records - for DISK in ${DISKS} - do - cat /etc/fdisk_cmd | fdisk "${DISK}" && - cat /etc/sfdisk_schema | sfdisk --force "${DISK}" || - /bin/ash - done - - # Create RAID arrays - if [ "${RAID}" -eq 1 ]; then - /sbin/mdadm --create "${BOOT_PART}" --run --level=1 --metadata=1.0 --raid-devices=2 /dev/sda1 /dev/sdb1 - /sbin/mdadm --create "${META_PART}" --run --level=1 --metadata=1.1 --raid-devices=2 /dev/sda2 /dev/sdb2 - /sbin/mdadm --create "${SWAP_PART}" --run --level=1 --metadata=1.1 --raid-devices=2 /dev/sda3 /dev/sdb3 - /sbin/mdadm --create "${ROOT_PART}" --run --level=1 --metadata=0 --raid-devices=2 /dev/sda4 /dev/sdb4 - fi - - # Format partitions - mkswap "${SWAP_PART}" - #mkfs.ext4 -F "${ROOT_PART}" - cryptsetup -q -s 512 luksFormat "${ROOT_PART}" /run/config/dm-crypt/key - cryptsetup luksOpen -d /run/config/dm-crypt/key "${ROOT_PART}" crypt_fic - mkfs.ext4 -F /dev/mapper/crypt_fic - sync - - mkfs.vfat "${BOOT_PART}" - mkdir -p /boot - - mount "${BOOT_PART}" /boot/ && { - for DISK in ${DISKS} - do - /root/install_grub ${DEFAULT_BOOT} "${DISK}" - done - /root/update_imgs "$(ip r | grep default | awk '{ print $3 }')" "${META_PART}" - } || - /bin/ash - - umount /boot && - sync - - echo "System is ready. You can now reboot." - /bin/ash - mode: "0755" - - - path: root/update_imgs - source: configs/update_imgs.sh - mode: "0755" - - - path: root/install_syslinux - contents: | - #!/bin/sh - mkdir -p /boot/EFI/boot /boot/imgs - - [ $1 == "0" ] && ONTIMEOUT="backend" || ONTIMEOUT="frontend" - - cd /usr/share/syslinux/efi64 - cp ldlinux.e64 menu.c32 libcom32.c32 libutil.c32 vesamenu.c32 poweroff.c32 /boot/EFI/boot - cp syslinux.efi /boot/EFI/boot/bootx64.efi - cat < /boot/syslinux.cfg - TIMEOUT 30 - ONTIMEOUT ${ONTIMEOUT} - - MENU background #00000000 * * - MENU color title * #FF22BBCC * - MENU color sel * #FFFFFFFF #FF22BBCC * - MENU color hotsel 1;7;37;40 #ffffffff #76a1d0ff * - - UI vesamenu.c32 - MENU TITLE Server FIC Challenge - - LABEL backend - MENU LABEL FIC Backend - LINUX /imgs/fickit-boot-kernel - INITRD /imgs/fickit-boot-initrd.img - APPEND console=ttyS0 console=tty0 root=fickit-backend-squashfs.img - LABEL frontend - MENU LABEL FIC Frontend - LINUX /imgs/fickit-boot-kernel - INITRD /imgs/fickit-boot-initrd.img - APPEND console=ttyS0 console=tty0 root=fickit-frontend-squashfs.img - LABEL update - MENU LABEL Update images - LINUX /imgs/fickit-boot-kernel - INITRD /imgs/fickit-update-initrd.img - APPEND console=ttyS0 console=tty0 - MENU SEPARATOR - LABEL poweroff - MENU LABEL ^Shutdown - KERNEL poweroff.c32 - EOF - - cp /usr/share/syslinux/libcom32.c32 /usr/share/syslinux/libutil.c32 /usr/share/syslinux/poweroff.c32 /usr/share/syslinux/vesamenu.c32 /boot/ - - shift - for p - do - # BIOS part - dd bs=440 conv=notrunc count=1 if=/usr/share/syslinux/mbr.bin of=${p} - syslinux --install ${p} - done - mode: "0550" - - path: root/install_grub - contents: | - #!/bin/sh - mkdir -p /boot/EFI/boot /boot/grub /boot/imgs - - cat < /boot/grub/grub.cfg - set timeout=3 - set default=$1 - - menuentry 'FIC Backend' { - set root=(hd0,1) - linux /imgs/fickit-boot-kernel console=ttyS0 console=tty0 quiet root=fickit-backend-squashfs.img - initrd /imgs/fickit-boot-initrd.img - } - - menuentry 'FIC Frontend' { - set root=(hd0,1) - linux /imgs/fickit-boot-kernel console=ttyS0 console=tty0 quiet root=fickit-frontend-squashfs.img - initrd /imgs/fickit-boot-initrd.img - } - - menuentry 'Update images' { - set root=(hd0,1) - linux /imgs/fickit-boot-kernel console=ttyS0 console=tty0 quiet - initrd /imgs/fickit-update-initrd.img - } - EOF - cp "/boot/grub/grub.cfg" "/boot/EFI/boot/grub.cfg" - - shift - for p - do - grub-mkimage -o "/boot/EFI/boot/bootx64.efi" -p /efi/boot -O x86_64-efi fat iso9660 part_gpt part_msdos normal boot linux configfile loopback chain efifwsetup efi_gop efi_uga ls search search_label search_fs_uuid search_fs_file gfxterm gfxterm_background gfxterm_menu test all_video loadenv exfat ext2 - grub-install --boot-directory="/boot/" --target=i386-pc "${p}" - done - mode: "0550" - - path: etc/sfdisk_schema - contents: | - ,750M,U,* - ,5M,L,- - ,4G,S,- - ,+,R,- - mode: "0440" - - path: etc/fdisk_cmd - contents: | - o - w - mode: "0440" diff --git a/fickit-update.yml b/fickit-update.yml deleted file mode 100644 index 3dc21b83..00000000 --- a/fickit-update.yml +++ /dev/null @@ -1,77 +0,0 @@ -kernel: - #image: nemunaire/kernel:5.10.62-0b705d955f5e283f62583c4e227d64a7924c138f-amd64 - image: linuxkit/kernel:6.6.71 - cmdline: "console=ttyS0 console=tty0" - - -init: - - nemunaire/mdadm:04814350d71ba9417e1f861be1685de26adf7a67 - - linuxkit/metadata:4f81c0c3a2b245567fd7d32d799018c9614a9907 - - alpine:latest - - -files: - - path: /init - contents: | - #!/bin/sh -x - modprobe xhci_pci - modprobe ahci - modprobe megaraid_sas - modprobe e1000e - modprobe tg3 - modprobe bnxt_en - - echo -n "Waiting module loading... " - sleep 3 - echo - - mount -t devtmpfs none /dev - mount -t proc none /proc - mount -t sysfs none /sys - - mdev -s - - if [ -b /dev/sdb ]; then - mdadm --auto-detect - mdadm --assemble /dev/md2 /dev/sd*1 - BOOT_PART=/dev/md2 - META_PART=/dev/md3 - else - BOOT_PART=/dev/sda1 - META_PART=/dev/sda2 - fi - - ip link set eth0 up - udhcpc -i eth0 - - mkdir -p /boot - - GW=$(ip r | grep default | awk '{ print $3 }') - - # Wait e1000e launched - if ! ping -W 2 -c 1 "${GW}" - then - sleep 4 - fi - - while ! ping -W 10 -c 1 "${GW}" - do - echo "Unable to contact ${GW}" - echo "Exit the shell when connection established." - /bin/ash - done - - mount "${BOOT_PART}" /boot/ && - /root/update_imgs "${GW}" "${META_PART}" || - /bin/ash - - umount /boot && - sync && - reboot -f - - /bin/ash - mode: "0755" - - - path: root/update_imgs - source: configs/update_imgs.sh - mode: "0755" diff --git a/fileexporter/.gitignore b/fileexporter/.gitignore deleted file mode 100644 index cbf49db1..00000000 --- a/fileexporter/.gitignore +++ /dev/null @@ -1 +0,0 @@ -fileexporter diff --git a/fileexporter/archive.go b/fileexporter/archive.go deleted file mode 100644 index d4fc1ceb..00000000 --- a/fileexporter/archive.go +++ /dev/null @@ -1,58 +0,0 @@ -package main - -import ( - "archive/zip" - "errors" - "io" - "log" - "os" - "path" -) - -type archiveFileCreator interface { - Create(name string) (io.Writer, error) - Close() error -} - -type filesCloser []io.Closer - -func (fds filesCloser) Close() error { - log.Println("Closing fd..") - for _, fd := range fds { - err := fd.Close() - if err != nil { - return err - } - } - - return nil -} - -func init() { - OutputFormats["archive"] = func(args ...string) (func(string) (io.WriteCloser, error), io.Closer, error) { - if len(args) != 1 { - return nil, nil, errors.New("archive has 1 required argument: [destination-file]") - } - - fd, err := os.Create(args[0]) - if err != nil { - return nil, nil, err - } - - var w archiveFileCreator - if path.Ext(args[0]) == ".zip" { - w = zip.NewWriter(fd) - } else { - return nil, nil, errors.New("destination file has to have .zip extension") - } - - return func(dest string) (io.WriteCloser, error) { - fw, err := w.Create(dest) - if err != nil { - return nil, err - } - - return NopCloser(fw), nil - }, filesCloser{w, fd}, nil - } -} diff --git a/fileexporter/copy.go b/fileexporter/copy.go deleted file mode 100644 index d4c7b96a..00000000 --- a/fileexporter/copy.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import ( - "errors" - "io" - - "srs.epita.fr/fic-server/libfic" -) - -func init() { - OutputFormats["copy"] = func(args ...string) (func(string) (io.WriteCloser, error), io.Closer, error) { - if len(args) > 1 { - return nil, nil, errors.New("copy can only take 1 argument: [destination-folder]") - } - - if len(args) == 1 { - fic.FilesDir = args[0] - } - - return nil, nil, nil - } -} diff --git a/fileexporter/main.go b/fileexporter/main.go deleted file mode 100644 index 5fea7f6d..00000000 --- a/fileexporter/main.go +++ /dev/null @@ -1,184 +0,0 @@ -package main - -import ( - "bytes" - "errors" - "flag" - "io" - "log" - "os" - "path" - "strings" - - "srs.epita.fr/fic-server/admin/sync" - "srs.epita.fr/fic-server/libfic" -) - -var OutputFormats = map[string]func(...string) (func(string) (io.WriteCloser, error), io.Closer, error){} - -func exportThemeFiles(tdir string) (errs error) { - theme, exceptions, err := sync.BuildTheme(sync.GlobalImporter, tdir) - errs = errors.Join(errs, err) - - err = sync.SyncThemeFiles(sync.GlobalImporter, theme) - if err != nil { - errs = errors.Join(errs, err) - } - - exercices, err := sync.GetExercices(sync.GlobalImporter, theme) - if err != nil { - log.Fatalf("Unable to list exercices for theme %q: %s", theme.Name, err) - } - - dmap := map[int64]*fic.Exercice{} - - for i, edir := range exercices { - log.Printf("In theme %s, doing exercice %d/%d: %s", tdir, i+1, len(exercices), edir) - err = exportExerciceFiles(theme, edir, &dmap, exceptions) - errs = errors.Join(errs, err) - } - - return -} - -func exportExerciceFiles(theme *fic.Theme, edir string, dmap *map[int64]*fic.Exercice, exceptions *sync.CheckExceptions) (errs error) { - exercice, _, eid, exceptions, _, berrs := sync.BuildExercice(sync.GlobalImporter, theme, path.Join(theme.Path, edir), dmap, nil) - errs = errors.Join(errs, berrs) - - if exercice != nil { - paramsFiles, err := sync.GetExerciceFilesParams(sync.GlobalImporter, exercice) - if err != nil { - errs = errors.Join(errs, sync.NewChallengeTxtError(exercice, 0, err)) - return - } - - _, err = sync.SyncExerciceFiles(sync.GlobalImporter, exercice, paramsFiles, func(fname string, digests map[string][]byte, filePath, origin string) (interface{}, error) { - return nil, nil - }) - errs = errors.Join(errs, err) - - if dmap != nil { - (*dmap)[int64(eid)] = exercice - } - } - return -} - -type nopCloser struct { - w io.Writer -} - -func (nc *nopCloser) Close() error { - return nil -} - -func (nc *nopCloser) Write(p []byte) (int, error) { - return nc.w.Write(p) -} - -func NopCloser(w io.Writer) *nopCloser { - return &nopCloser{w} -} - -func writeFileToTar(dest string) (io.WriteCloser, error) { - log.Println("import2Tar", dest) - return NopCloser(bytes.NewBuffer([]byte{})), nil -} - -func main() { - cloudDAVBase := "" - cloudUsername := "fic" - cloudPassword := "" - localImporterDirectory := "" - - // Read paremeters from environment - if v, exists := os.LookupEnv("FICCLOUD_URL"); exists { - cloudDAVBase = v - } - if v, exists := os.LookupEnv("FICCLOUD_USER"); exists { - cloudUsername = v - } - if v, exists := os.LookupEnv("FICCLOUD_PASS"); exists { - cloudPassword = v - } - - // Read parameters from command line - flag.StringVar(&localImporterDirectory, "localimport", localImporterDirectory, - "Base directory where to find challenges files to import, local part") - flag.StringVar(&cloudDAVBase, "clouddav", cloudDAVBase, - "Base directory where to find challenges files to import, cloud part") - flag.StringVar(&cloudUsername, "clouduser", cloudUsername, "Username used to sync") - flag.StringVar(&cloudPassword, "cloudpass", cloudPassword, "Password used to sync") - flag.BoolVar(&fic.OptionalDigest, "optionaldigest", fic.OptionalDigest, "Is the digest required when importing files?") - flag.BoolVar(&fic.StrongDigest, "strongdigest", fic.StrongDigest, "Are BLAKE2b digests required or is SHA-1 good enough?") - flag.Parse() - - // Do not display timestamp - log.SetFlags(0) - - // Instantiate importer - if localImporterDirectory != "" { - sync.GlobalImporter = sync.LocalImporter{Base: localImporterDirectory, Symlink: false} - } else if cloudDAVBase != "" { - sync.GlobalImporter, _ = sync.NewCloudImporter(cloudDAVBase, cloudUsername, cloudPassword) - } - - if sync.GlobalImporter == nil { - log.Fatal("No importer configured!") - } - - log.Println("Using", sync.GlobalImporter.Kind()) - - hasError := doExport() - if hasError { - os.Exit(1) - } -} - -func doExport() bool { - // Configure destination - if flag.NArg() < 1 { - var formats []string - - for k := range OutputFormats { - formats = append(formats, k) - } - - log.Fatal("Please define wanted output format between [" + strings.Join(formats, " ") + "]") - } else if outputFormat, ok := OutputFormats[flag.Arg(0)]; !ok { - var formats []string - - for k := range OutputFormats { - formats = append(formats, k) - } - - log.Fatal("Please define wanted output format between [" + strings.Join(formats, " ") + "]") - } else { - fw, closer, err := outputFormat(flag.Args()[1:]...) - if closer != nil { - defer closer.Close() - } - if err != nil { - log.Fatal(err) - } else if fw != nil { - sync.SetWriteFileFunc(fw) - } - } - - themes, err := sync.GetThemesExtended(sync.GlobalImporter) - if err != nil { - log.Fatal(err) - } - - hasError := false - for i, tdir := range themes { - log.Printf("Doing theme %d/%d: %s", i+1, len(themes), tdir) - err = exportThemeFiles(tdir) - if err != nil { - hasError = true - log.Println(err) - } - } - - return hasError -} diff --git a/flake.lock b/flake.lock deleted file mode 100644 index b5fc323d..00000000 --- a/flake.lock +++ /dev/null @@ -1,43 +0,0 @@ -{ - "nodes": { - "flake-compat": { - "flake": false, - "locked": { - "lastModified": 1650374568, - "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "b4a34015c698c7793d592d66adbab377907a2be8", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1653581809, - "narHash": "sha256-Uvka0V5MTGbeOfWte25+tfRL3moECDh1VwokWSZUdoY=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "83658b28fe638a170a19b8933aa008b30640fbd1", - "type": "github" - }, - "original": { - "id": "nixpkgs", - "ref": "nixos-unstable", - "type": "indirect" - } - }, - "root": { - "inputs": { - "flake-compat": "flake-compat", - "nixpkgs": "nixpkgs" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/flake.nix b/flake.nix deleted file mode 100644 index 03980c66..00000000 --- a/flake.nix +++ /dev/null @@ -1,129 +0,0 @@ -{ - description = "Submission server/infrastructure for the SRS challenge at FIC"; - - inputs.nixpkgs.url = "nixpkgs/nixos-unstable"; - - inputs.flake-compat = { - url = "github:edolstra/flake-compat"; - flake = false; - }; - - outputs = { self, nixpkgs, ... }: - let - - # Generate a version based on date - version = builtins.substring 0 12 self.lastModifiedDate; - vendorSha256 = "sha256-itCvN/Z8DkUUdtx6At+4DyeJK8PgFJ/5A3G03VT4I2k"; - overrideModAttrs = _ : { name = "fic-./.-${version}-go-modules"; }; - - # System types to support. - supportedSystems = - [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" "arm-linux" ]; - - # Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'. - forAllSystems = nixpkgs.lib.genAttrs supportedSystems; - - # Nixpkgs instantiated for supported system types. - nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; }); - - in { - - # Provide some binary packages for selected system types. - packages = forAllSystems (system: - let pkgs = nixpkgsFor.${system}; - in { - fic-admin = pkgs.buildGoModule { - pname = "admin"; - inherit version vendorSha256 overrideModAttrs; - src = ./.; - - subPackages = [ "admin" ]; - }; - - fic-checker = pkgs.buildGoModule { - pname = "checker"; - inherit version vendorSha256 overrideModAttrs; - src = ./.; - - subPackages = [ "checker" ]; - }; - - fic-dashboard = pkgs.buildGoModule { - pname = "dashboard"; - inherit version vendorSha256 overrideModAttrs; - src = ./.; - - subPackages = [ "dashboard" ]; - }; - - fic-generator = pkgs.buildGoModule { - pname = "generator"; - inherit version vendorSha256 overrideModAttrs; - src = ./.; - - subPackages = [ "generator" ]; - }; - - fic-synchro = pkgs.writeShellApplication { - name = "synchro"; - runtimeInputs = [ pkgs.rsync pkgs.openssh pkgs.coreutils ]; - text = '' - ${(builtins.readFile ./configs/synchro.sh)} - ''; - }; - - fic-configs = pkgs.stdenv.mkDerivation { - name = "configs"; - src = ./.; - installPhase = "mkdir -p $out/; cp -r configs/ $out/"; - }; - - fic-receiver = pkgs.buildGoModule { - pname = "receiver"; - inherit version vendorSha256 overrideModAttrs; - src = ./.; - - subPackages = [ "receiver" ]; - }; - - fic-qa = pkgs.buildGoModule { - pname = "qa"; - inherit version vendorSha256 overrideModAttrs; - src = ./.; - - subPackages = [ "qa" ]; - }; - - fic-remote-scores-sync-zqds = pkgs.buildGoModule { - pname = "scores-sync-zqds"; - inherit version vendorSha256 overrideModAttrs; - src = ./.; - - subPackages = [ "remote/scores-sync-zqds" ]; - }; - - fic-remote-challenge-sync-airbus = pkgs.buildGoModule { - pname = "challenge-sync-airbus"; - inherit version vendorSha256 overrideModAttrs; - src = ./.; - - subPackages = [ "remote/challenge-sync-airbus" ]; - }; - - fic-repochecker = pkgs.buildGoModule { - pname = "repochecker"; - inherit version vendorSha256 overrideModAttrs; - src = ./.; - - subPackages = [ "repochecker" ]; - }; - all = pkgs.linkFarmFromDrvs "fic-all" (builtins.attrValues (builtins.removeAttrs self.packages.x86_64-linux [ "all" ])); - }); - - devShell = forAllSystems (system: - let pkgs = nixpkgsFor.${system}; - in pkgs.mkShell { - buildInputs = with pkgs; [ go ]; - }); - }; -} diff --git a/frontend/fic/.eslintrc.cjs b/frontend/fic/.eslintrc.cjs deleted file mode 100644 index e496918e..00000000 --- a/frontend/fic/.eslintrc.cjs +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - root: true, - extends: ['eslint:recommended', 'prettier'], - plugins: ['svelte3'], - overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], - parserOptions: { - sourceType: 'module', - ecmaVersion: 2019 - }, - env: { - browser: true, - es2017: true, - node: true - } -}; diff --git a/frontend/fic/.gitignore b/frontend/fic/.gitignore deleted file mode 100644 index 60fb74fb..00000000 --- a/frontend/fic/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -.DS_Store -node_modules -/build -/.svelte-kit -/package - -static/issues.json -static/my.json -static/settings.json -static/teams.json -static/themes.json -static/wait.json \ No newline at end of file diff --git a/frontend/fic/.prettierrc b/frontend/fic/.prettierrc deleted file mode 100644 index ff2677ef..00000000 --- a/frontend/fic/.prettierrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "useTabs": true, - "singleQuote": true, - "trailingComma": "none", - "printWidth": 100 -} diff --git a/frontend/fic/jsconfig.json b/frontend/fic/jsconfig.json deleted file mode 100644 index 81ff9770..00000000 --- a/frontend/fic/jsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "./.svelte-kit/tsconfig.json" -} diff --git a/frontend/fic/package-lock.json b/frontend/fic/package-lock.json deleted file mode 100644 index d08b942c..00000000 --- a/frontend/fic/package-lock.json +++ /dev/null @@ -1,3305 +0,0 @@ -{ - "name": "fic-frontend", - "version": "0.0.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "fic-frontend", - "version": "0.0.1", - "dependencies": { - "@popperjs/core": "^2.11.5", - "bootstrap": "^5.1.3", - "bootstrap-icons": "^1.8.1", - "bootswatch": "^5.1.3", - "hash-wasm": "^4.9.0", - "seedrandom": "^3.0.5", - "svelte-bricks": "^0.2.1", - "vite": "^5.0.0", - "wordcloud": "^1.2.2" - }, - "devDependencies": { - "@sveltejs/adapter-static": "^3.0.0", - "@sveltejs/kit": "^2.0.0", - "@sveltejs/vite-plugin-svelte": "^3.0.0", - "@sveltestrap/sveltestrap": "^7.0.0", - "eslint": "^9.0.0", - "eslint-config-prettier": "^10.0.0", - "eslint-plugin-svelte": "^3.0.0", - "prettier": "^3.0.0", - "prettier-plugin-svelte": "^3.1.2", - "sass": "^1.51.0", - "sass-loader": "^16.0.0", - "svelte": "^4.0.0", - "vite": "^5.0.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", - "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", - "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.0.tgz", - "integrity": "sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "9.23.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz", - "integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", - "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.12.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@parcel/watcher": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", - "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "detect-libc": "^1.0.3", - "is-glob": "^4.0.3", - "micromatch": "^4.0.5", - "node-addon-api": "^7.0.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "@parcel/watcher-android-arm64": "2.5.1", - "@parcel/watcher-darwin-arm64": "2.5.1", - "@parcel/watcher-darwin-x64": "2.5.1", - "@parcel/watcher-freebsd-x64": "2.5.1", - "@parcel/watcher-linux-arm-glibc": "2.5.1", - "@parcel/watcher-linux-arm-musl": "2.5.1", - "@parcel/watcher-linux-arm64-glibc": "2.5.1", - "@parcel/watcher-linux-arm64-musl": "2.5.1", - "@parcel/watcher-linux-x64-glibc": "2.5.1", - "@parcel/watcher-linux-x64-musl": "2.5.1", - "@parcel/watcher-win32-arm64": "2.5.1", - "@parcel/watcher-win32-ia32": "2.5.1", - "@parcel/watcher-win32-x64": "2.5.1" - } - }, - "node_modules/@parcel/watcher-android-arm64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", - "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-darwin-arm64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", - "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-darwin-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", - "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-freebsd-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", - "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm-glibc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", - "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", - "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm64-glibc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", - "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm64-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", - "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", - "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", - "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-arm64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", - "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-ia32": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", - "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", - "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@polka/url": { - "version": "1.0.0-next.28", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", - "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.38.0.tgz", - "integrity": "sha512-ldomqc4/jDZu/xpYU+aRxo3V4mGCV9HeTgUBANI3oIQMOL+SsxB+S2lxMpkFp5UamSS3XuTMQVbsS24R4J4Qjg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.38.0.tgz", - "integrity": "sha512-VUsgcy4GhhT7rokwzYQP+aV9XnSLkkhlEJ0St8pbasuWO/vwphhZQxYEKUP3ayeCYLhk6gEtacRpYP/cj3GjyQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.38.0.tgz", - "integrity": "sha512-buA17AYXlW9Rn091sWMq1xGUvWQFOH4N1rqUxGJtEQzhChxWjldGCCup7r/wUnaI6Au8sKXpoh0xg58a7cgcpg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.38.0.tgz", - "integrity": "sha512-Mgcmc78AjunP1SKXl624vVBOF2bzwNWFPMP4fpOu05vS0amnLcX8gHIge7q/lDAHy3T2HeR0TqrriZDQS2Woeg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.38.0.tgz", - "integrity": "sha512-zzJACgjLbQTsscxWqvrEQAEh28hqhebpRz5q/uUd1T7VTwUNZ4VIXQt5hE7ncs0GrF+s7d3S4on4TiXUY8KoQA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.38.0.tgz", - "integrity": "sha512-hCY/KAeYMCyDpEE4pTETam0XZS4/5GXzlLgpi5f0IaPExw9kuB+PDTOTLuPtM10TlRG0U9OSmXJ+Wq9J39LvAg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.38.0.tgz", - "integrity": "sha512-mimPH43mHl4JdOTD7bUMFhBdrg6f9HzMTOEnzRmXbOZqjijCw8LA5z8uL6LCjxSa67H2xiLFvvO67PT05PRKGg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.38.0.tgz", - "integrity": "sha512-tPiJtiOoNuIH8XGG8sWoMMkAMm98PUwlriOFCCbZGc9WCax+GLeVRhmaxjJtz6WxrPKACgrwoZ5ia/uapq3ZVg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.38.0.tgz", - "integrity": "sha512-wZco59rIVuB0tjQS0CSHTTUcEde+pXQWugZVxWaQFdQQ1VYub/sTrNdY76D1MKdN2NB48JDuGABP6o6fqos8mA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.38.0.tgz", - "integrity": "sha512-fQgqwKmW0REM4LomQ+87PP8w8xvU9LZfeLBKybeli+0yHT7VKILINzFEuggvnV9M3x1Ed4gUBmGUzCo/ikmFbQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.38.0.tgz", - "integrity": "sha512-hz5oqQLXTB3SbXpfkKHKXLdIp02/w3M+ajp8p4yWOWwQRtHWiEOCKtc9U+YXahrwdk+3qHdFMDWR5k+4dIlddg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.38.0.tgz", - "integrity": "sha512-NXqygK/dTSibQ+0pzxsL3r4Xl8oPqVoWbZV9niqOnIHV/J92fe65pOir0xjkUZDRSPyFRvu+4YOpJF9BZHQImw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.38.0.tgz", - "integrity": "sha512-GEAIabR1uFyvf/jW/5jfu8gjM06/4kZ1W+j1nWTSSB3w6moZEBm7iBtzwQ3a1Pxos2F7Gz+58aVEnZHU295QTg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.38.0.tgz", - "integrity": "sha512-9EYTX+Gus2EGPbfs+fh7l95wVADtSQyYw4DfSBcYdUEAmP2lqSZY0Y17yX/3m5VKGGJ4UmIH5LHLkMJft3bYoA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.38.0.tgz", - "integrity": "sha512-Mpp6+Z5VhB9VDk7RwZXoG2qMdERm3Jw07RNlXHE0bOnEeX+l7Fy4bg+NxfyN15ruuY3/7Vrbpm75J9QHFqj5+Q==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.38.0.tgz", - "integrity": "sha512-vPvNgFlZRAgO7rwncMeE0+8c4Hmc+qixnp00/Uv3ht2x7KYrJ6ERVd3/R0nUtlE6/hu7/HiiNHJ/rP6knRFt1w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.38.0.tgz", - "integrity": "sha512-q5Zv+goWvQUGCaL7fU8NuTw8aydIL/C9abAVGCzRReuj5h30TPx4LumBtAidrVOtXnlB+RZkBtExMsfqkMfb8g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.38.0.tgz", - "integrity": "sha512-u/Jbm1BU89Vftqyqbmxdq14nBaQjQX1HhmsdBWqSdGClNaKwhjsg5TpW+5Ibs1mb8Es9wJiMdl86BcmtUVXNZg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.38.0.tgz", - "integrity": "sha512-mqu4PzTrlpNHHbu5qleGvXJoGgHpChBlrBx/mEhTPpnAL1ZAYFlvHD7rLK839LLKQzqEQMFJfGrrOHItN4ZQqA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.38.0.tgz", - "integrity": "sha512-jjqy3uWlecfB98Psxb5cD6Fny9Fupv9LrDSPTQZUROqjvZmcCqNu4UMl7qqhlUUGpwiAkotj6GYu4SZdcr/nLw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@sveltejs/adapter-static": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.8.tgz", - "integrity": "sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@sveltejs/kit": "^2.0.0" - } - }, - "node_modules/@sveltejs/kit": { - "version": "2.20.2", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.20.2.tgz", - "integrity": "sha512-Dv8TOAZC9vyfcAB9TMsvUEJsRbklRTeNfcYBPaeH6KnABJ99i3CvCB2eNx8fiiliIqe+9GIchBg4RodRH5p1BQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/cookie": "^0.6.0", - "cookie": "^0.6.0", - "devalue": "^5.1.0", - "esm-env": "^1.2.2", - "import-meta-resolve": "^4.1.0", - "kleur": "^4.1.5", - "magic-string": "^0.30.5", - "mrmime": "^2.0.0", - "sade": "^1.8.1", - "set-cookie-parser": "^2.6.0", - "sirv": "^3.0.0" - }, - "bin": { - "svelte-kit": "svelte-kit.js" - }, - "engines": { - "node": ">=18.13" - }, - "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0", - "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.3 || ^6.0.0" - } - }, - "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.2.tgz", - "integrity": "sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0", - "debug": "^4.3.4", - "deepmerge": "^4.3.1", - "kleur": "^4.1.5", - "magic-string": "^0.30.10", - "svelte-hmr": "^0.16.0", - "vitefu": "^0.2.5" - }, - "engines": { - "node": "^18.0.0 || >=20" - }, - "peerDependencies": { - "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.0" - } - }, - "node_modules/@sveltejs/vite-plugin-svelte-inspector": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.1.0.tgz", - "integrity": "sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.0.0 || >=20" - }, - "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0", - "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.0" - } - }, - "node_modules/@sveltestrap/sveltestrap": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@sveltestrap/sveltestrap/-/sveltestrap-7.1.0.tgz", - "integrity": "sha512-TpIx25kqLV+z+VD3yfqYayOI1IaCeWFbT0uqM6NfA4vQgDs9PjFwmjkU4YEAlV/ngs9e7xPmaRWE7lkrg4Miow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@popperjs/core": "^2.11.8" - }, - "peerDependencies": { - "svelte": "^4.0.0 || ^5.0.0 || ^5.0.0-next.0" - } - }, - "node_modules/@types/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/axobject-query": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", - "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/bootstrap": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz", - "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/twbs" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/bootstrap" - } - ], - "license": "MIT", - "peerDependencies": { - "@popperjs/core": "^2.11.8" - } - }, - "node_modules/bootstrap-icons": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz", - "integrity": "sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/twbs" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/bootstrap" - } - ], - "license": "MIT" - }, - "node_modules/bootswatch": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/bootswatch/-/bootswatch-5.3.3.tgz", - "integrity": "sha512-cJLhobnZsVCelU7zdH/L7wpcXAyUoTX4/5l2dWQ0JXgaVK80BdTQNU/ImWwoyIGBeyms4iQDAdNtOfPQZf0Atg==", - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/code-red": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", - "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15", - "@types/estree": "^1.0.1", - "acorn": "^8.10.0", - "estree-walker": "^3.0.3", - "periscopic": "^3.1.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/devalue": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.1.1.tgz", - "integrity": "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==", - "dev": true, - "license": "MIT" - }, - "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.23.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz", - "integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.2", - "@eslint/config-helpers": "^0.2.0", - "@eslint/core": "^0.12.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.23.0", - "@eslint/plugin-kit": "^0.2.7", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.3.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-compat-utils": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.6.4.tgz", - "integrity": "sha512-/u+GQt8NMfXO8w17QendT4gvO5acfxQsAKirAt0LVxDnr2N8YLCVbregaNc/Yhp7NM128DwCaRvr8PLDfeNkQw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "eslint": ">=6.0.0" - } - }, - "node_modules/eslint-config-prettier": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.1.tgz", - "integrity": "sha512-4EQQr6wXwS+ZJSzaR5ZCrYgLxqvUjdXctaEtBqHcbkW944B1NQyO4qpdHQbXBONfwxXdkAY81HH4+LUfrg+zPw==", - "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-svelte": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.4.0.tgz", - "integrity": "sha512-L0eX0W6M0YhIUhWRlOAaornY1lIz6xRSVKVJuiRovMM5wHUBQZmefwJRR0y+sqR0CHtJpFmxYiQbw3UaO8h5KA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.1", - "@jridgewell/sourcemap-codec": "^1.5.0", - "eslint-compat-utils": "^0.6.4", - "esutils": "^2.0.3", - "known-css-properties": "^0.35.0", - "postcss": "^8.4.49", - "postcss-load-config": "^3.1.4", - "postcss-safe-parser": "^7.0.0", - "semver": "^7.6.3", - "svelte-eslint-parser": "^1.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" - }, - "peerDependencies": { - "eslint": "^8.57.1 || ^9.0.0", - "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "svelte": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", - "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esm-env": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", - "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.14.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/hash-wasm": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/hash-wasm/-/hash-wasm-4.12.0.tgz", - "integrity": "sha512-+/2B2rYLb48I/evdOIhP+K/DD2ca2fgBjp6O+GBEnCDk2e4rpeXIK8GvIyRPjTezgmWn9gmKwkQjjx6BtqDHVQ==", - "license": "MIT" - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/immutable": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.1.tgz", - "integrity": "sha512-3jatXi9ObIsPGr3N5hGw/vWWcTkq6hUYhpQz4k0wLC+owqWi/LiugIw9x0EdNZ2yGedKN/HzePiBvaJRXa0Ujg==", - "dev": true, - "license": "MIT" - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-meta-resolve": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", - "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-reference": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", - "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.6" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/known-css-properties": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.35.0.tgz", - "integrity": "sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==", - "dev": true, - "license": "MIT" - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/locate-character": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", - "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "license": "CC0-1.0" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/mrmime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", - "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/periscopic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", - "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^3.0.0", - "is-reference": "^3.0.0" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.8", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-load-config": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", - "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", - "dev": true, - "license": "MIT", - "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^1.10.2" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-safe-parser": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", - "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-scss": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.9.tgz", - "integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss-scss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.4.29" - } - }, - "node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", - "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-plugin-svelte": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.3.3.tgz", - "integrity": "sha512-yViK9zqQ+H2qZD1w/bH7W8i+bVfKrD8GIFjkFe4Thl6kCT9SlAsXVNmt3jCvQOCsnOhcvYgsoVlRV/Eu6x5nNw==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "prettier": "^3.0.0", - "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/rollup": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.38.0.tgz", - "integrity": "sha512-5SsIRtJy9bf1ErAOiFMFzl64Ex9X5V7bnJ+WlFMb+zmP459OSWCEG7b0ERZ+PEU7xPt4OG3RHbrp1LJlXxYTrw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.7" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.38.0", - "@rollup/rollup-android-arm64": "4.38.0", - "@rollup/rollup-darwin-arm64": "4.38.0", - "@rollup/rollup-darwin-x64": "4.38.0", - "@rollup/rollup-freebsd-arm64": "4.38.0", - "@rollup/rollup-freebsd-x64": "4.38.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.38.0", - "@rollup/rollup-linux-arm-musleabihf": "4.38.0", - "@rollup/rollup-linux-arm64-gnu": "4.38.0", - "@rollup/rollup-linux-arm64-musl": "4.38.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.38.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.38.0", - "@rollup/rollup-linux-riscv64-gnu": "4.38.0", - "@rollup/rollup-linux-riscv64-musl": "4.38.0", - "@rollup/rollup-linux-s390x-gnu": "4.38.0", - "@rollup/rollup-linux-x64-gnu": "4.38.0", - "@rollup/rollup-linux-x64-musl": "4.38.0", - "@rollup/rollup-win32-arm64-msvc": "4.38.0", - "@rollup/rollup-win32-ia32-msvc": "4.38.0", - "@rollup/rollup-win32-x64-msvc": "4.38.0", - "fsevents": "~2.3.2" - } - }, - "node_modules/sade": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", - "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", - "dev": true, - "license": "MIT", - "dependencies": { - "mri": "^1.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/sass": { - "version": "1.86.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.86.0.tgz", - "integrity": "sha512-zV8vGUld/+mP4KbMLJMX7TyGCuUp7hnkOScgCMsWuHtns8CWBoz+vmEhoGMXsaJrbUP8gj+F1dLvVe79sK8UdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": "^4.0.0", - "immutable": "^5.0.2", - "source-map-js": ">=0.6.2 <2.0.0" - }, - "bin": { - "sass": "sass.js" - }, - "engines": { - "node": ">=14.0.0" - }, - "optionalDependencies": { - "@parcel/watcher": "^2.4.1" - } - }, - "node_modules/sass-loader": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.5.tgz", - "integrity": "sha512-oL+CMBXrj6BZ/zOq4os+UECPL+bWqt6OAC6DWS8Ln8GZRcMDjlJ4JC3FBDuHJdYaFWIdKNIBYmtZtK2MaMkNIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "neo-async": "^2.6.2" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", - "sass": "^1.3.0", - "sass-embedded": "*", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "node-sass": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/seedrandom": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", - "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-cookie-parser": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", - "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/sirv": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.1.tgz", - "integrity": "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@polka/url": "^1.0.0-next.24", - "mrmime": "^2.0.0", - "totalist": "^3.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/svelte": { - "version": "4.2.19", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.19.tgz", - "integrity": "sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==", - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.1", - "@jridgewell/sourcemap-codec": "^1.4.15", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/estree": "^1.0.1", - "acorn": "^8.9.0", - "aria-query": "^5.3.0", - "axobject-query": "^4.0.0", - "code-red": "^1.0.3", - "css-tree": "^2.3.1", - "estree-walker": "^3.0.3", - "is-reference": "^3.0.1", - "locate-character": "^3.0.0", - "magic-string": "^0.30.4", - "periscopic": "^3.1.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/svelte-bricks": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/svelte-bricks/-/svelte-bricks-0.2.1.tgz", - "integrity": "sha512-cc3XK3j5ViPyZ3K183+Sr53B2e8mJaiV3POyoJtjmm1dYc/TBMy7jOUMt8MW/snJzodpACfqwFzokBQbrZ297w==", - "license": "MIT", - "dependencies": { - "svelte": "^4.2.1" - } - }, - "node_modules/svelte-eslint-parser": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.1.1.tgz", - "integrity": "sha512-QLVGPIMDettl30qRHXU2VrPvVJKG8GsGstye7n8rFbEiu3gEARksuQg9Xu4GzubNxhGNM8stfBZkhyMbBQmjFA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-scope": "^8.2.0", - "eslint-visitor-keys": "^4.0.0", - "espree": "^10.0.0", - "postcss": "^8.4.49", - "postcss-scss": "^4.0.9", - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" - }, - "peerDependencies": { - "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "svelte": { - "optional": true - } - } - }, - "node_modules/svelte-hmr": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz", - "integrity": "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^12.20 || ^14.13.1 || >= 16" - }, - "peerDependencies": { - "svelte": "^3.19.0 || ^4.0.0" - } - }, - "node_modules/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, - "license": "MIT", - "optional": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/totalist": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", - "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/vite": { - "version": "5.4.15", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.15.tgz", - "integrity": "sha512-6ANcZRivqL/4WtwPGTKNaosuNJr5tWiftOC7liM7G9+rMb8+oeJeyzymDu4rTN93seySBmbjSfsS3Vzr19KNtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vitefu": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", - "integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordcloud": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/wordcloud/-/wordcloud-1.2.3.tgz", - "integrity": "sha512-9by77b7Sd9e1K75kSmVeAD+JnGpiLR1Z4EX1mYQL91jKrU1/4bHw4h4DExQ+dzfT+PvihDcH7OS7V4Y5UkbF2w==", - "license": "MIT" - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/frontend/fic/package.json b/frontend/fic/package.json deleted file mode 100644 index 8a4515e6..00000000 --- a/frontend/fic/package.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "fic-frontend", - "version": "0.0.1", - "scripts": { - "dev": "vite dev", - "build": "vite build", - "preview": "vite preview", - "lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .", - "format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ." - }, - "devDependencies": { - "@sveltejs/adapter-static": "^3.0.0", - "@sveltejs/kit": "^2.0.0", - "@sveltejs/vite-plugin-svelte": "^3.0.0", - "@sveltestrap/sveltestrap": "^7.0.0", - "eslint": "^9.0.0", - "eslint-config-prettier": "^10.0.0", - "eslint-plugin-svelte": "^3.0.0", - "prettier": "^3.0.0", - "prettier-plugin-svelte": "^3.1.2", - "sass": "^1.51.0", - "sass-loader": "^16.0.0", - "svelte": "^4.0.0", - "vite": "^5.0.0" - }, - "type": "module", - "dependencies": { - "@popperjs/core": "^2.11.5", - "bootstrap": "^5.1.3", - "bootstrap-icons": "^1.8.1", - "bootswatch": "^5.1.3", - "hash-wasm": "^4.9.0", - "seedrandom": "^3.0.5", - "svelte-bricks": "^0.2.1", - "vite": "^5.0.0", - "wordcloud": "^1.2.2" - } -} diff --git a/frontend/fic/src/app.html b/frontend/fic/src/app.html deleted file mode 100644 index f23bf00a..00000000 --- a/frontend/fic/src/app.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - %sveltekit.head% - - -
    %sveltekit.body%
    - - diff --git a/frontend/fic/src/fic.scss b/frontend/fic/src/fic.scss deleted file mode 100644 index ab622c9d..00000000 --- a/frontend/fic/src/fic.scss +++ /dev/null @@ -1,50 +0,0 @@ -@import "bootswatch/dist/slate/_variables"; - -$body-bg: $gray-100; -$old-body-bg: $gray-900; - -$dropdown-link-hover-bg: $old-body-bg; -$card-bg: lighten($old-body-bg, 5%); -$popover-bg: lighten($old-body-bg, 5%); -$toast-background-color: lighten($old-body-bg, 5%); -$modal-content-bg: lighten($old-body-bg, 5%); -$table-bg: lighten($old-body-bg, 5%); -$list-group-bg: lighten($old-body-bg, 5%); -$list-group-hover-bg: lighten($old-body-bg, 10%); -$list-group-action-hover-color: $gray-100; - -$list-group-border-color: rgba($gray-500, .6); - -$enable-print-styles: false; - -@import "bootstrap/scss/bootstrap"; -@import "bootswatch/dist/slate/_bootswatch"; - -.text-indent p { - text-indent: 1em; -} - -p img { - margin: auto; - max-width: 100%; - max-height: 100vh; -} -p:has(img) { - text-indent: 0 !important; - text-align: center; -} - -.level .level-item { - text-align: center; -} -.level .level-item .heading { - font-variant: small-caps; - text-transform: lowercase; -} -.level .level-item .value { - font-size: 1.3rem; - font-weight: bolder; -} -label { - margin-bottom: 0.25rem; -} diff --git a/frontend/fic/src/global.d.ts b/frontend/fic/src/global.d.ts deleted file mode 100644 index 63908c66..00000000 --- a/frontend/fic/src/global.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/frontend/fic/src/lib/components/CardTheme.svelte b/frontend/fic/src/lib/components/CardTheme.svelte deleted file mode 100644 index d32c51b9..00000000 --- a/frontend/fic/src/lib/components/CardTheme.svelte +++ /dev/null @@ -1,113 +0,0 @@ - - -
    - - {#if exercice && exercice.image} -
    - {:else if theme.image} -
    - {/if} - - {#if exercice} - {#if $exercices_idx[exercice.id].tags.includes("Reverse") || $exercices_idx[exercice.id].tags.includes("Reverse Engineering")} - #Reverse - {/if} - {/if} - - {#if $my && $my.team_id} -
    - {#if exercice && $my && !$my.exercices[exercice.id]} - - - - {/if} - {#if theme.locked} - - - - {:else} - {#if $max_solved > 1 && theme.solved == $max_solved} - - - - {/if} - {#if theme.exercice_coeff_max > 1 || (exercice && exercice.curcoeff > 1)} - - - - {/if} - {/if} - {#if exercice && $my && $my.exercices[exercice.id] && $my.exercices[exercice.id].solved_rank} - - - - {/if} - {#if !exercice && $myThemes[theme.id].exercice_solved > 0} - - {$myThemes[theme.id].exercice_solved}/{theme.exercice_count} - - {/if} -
    - {/if} - {#if exercice}{exercice.title}{:else}{theme.name}{/if} -
    -

    {#if exercice}{@html exercice.headline}{:else}{@html theme.headline}{/if}

    -
    -
    -
    - - diff --git a/frontend/fic/src/lib/components/Clock.svelte b/frontend/fic/src/lib/components/Clock.svelte deleted file mode 100644 index 61463ed9..00000000 --- a/frontend/fic/src/lib/components/Clock.svelte +++ /dev/null @@ -1,74 +0,0 @@ - - -
    - {#if $time.seconds} - - {$time.hours} - - - : - - - {$time.minutes} - - - : - - - {$time.seconds} - - {:else} - Chargement - {/if} -
    - - diff --git a/frontend/fic/src/lib/components/DateFormat.svelte b/frontend/fic/src/lib/components/DateFormat.svelte deleted file mode 100644 index 563a9934..00000000 --- a/frontend/fic/src/lib/components/DateFormat.svelte +++ /dev/null @@ -1,17 +0,0 @@ - - -{formatDate(date, dateStyle, timeStyle)} diff --git a/frontend/fic/src/lib/components/ExerciceDownloads.svelte b/frontend/fic/src/lib/components/ExerciceDownloads.svelte deleted file mode 100644 index 6da12ad5..00000000 --- a/frontend/fic/src/lib/components/ExerciceDownloads.svelte +++ /dev/null @@ -1,71 +0,0 @@ - - -{#if files.length} - - - - Téléchargements - - - - Attention : puisqu'il s'agit de captures effectuées dans le but de découvrir si des actes malveillants ont été commis, les contenus qui sont téléchargeables peuvent contenir du contenu malveillant ! - - - - {#each files as file, index} - hasDownloaded.update((u) => {u[file.path] = true; return u;})}> -

    - -

    -
    -

    {file.name}

    - {#if file.disclaimer} -
    - {file.disclaimer} -
    - {/if} - - Taille : - {#if file.compressed} - - environ - - - {:else} - - {/if} - - - b2sum : - {file.checksum} - -
    -
    - {/each} -
    -
    -{/if} - - diff --git a/frontend/fic/src/lib/components/ExerciceFlags.svelte b/frontend/fic/src/lib/components/ExerciceFlags.svelte deleted file mode 100644 index 260734a9..00000000 --- a/frontend/fic/src/lib/components/ExerciceFlags.svelte +++ /dev/null @@ -1,259 +0,0 @@ - - - - - - Faire son rapport - - {#if exercice.flags.length != exercice.nb_flags} - f.type !== undefined).length} - max={exercice.nb_flags} - class="rounded-0" - barClassName="text-light" - > - {exercice.flags.filter((f) => f.type !== undefined).length}/{exercice.nb_flags} - - {/if} - {#if exercice.tries || exercice.solved_time || exercice.submitted || sberr || $timeouted} - - {#if exercice.solved_time || exercice.tries} -
    - - {#if exercice.tries > 0}{exercice.tries} {exercice.tries==1?"tentative effectuée":"tentatives effectuées"}.{/if} - Dernière solution envoyée le . - -
    - {/if} - {#if exercice.solve_dist} - - {exercice.solve_dist} {exercice.solve_dist == 1?"réponse erronée":"réponses erronées"}. - - {/if} - {#if exercice.submitted || sberr} - - {#if !sberr} - Votre solution a bien été envoyée ! - {:else} - {sberr} {message} - {/if} - - {/if} - {#if $timeouted} - - Oops - La requête a dépassé le délai d'attente. Vous devriez réessayer dans quelques instant… - - {/if} -
    - {/if} - {#if !exercice.submitted || sberr} - -
    - {#each flags as flag, index ((flag.type?flag.type:"i") + (flag.id?flag.id:index))} - {#if !flag.type && !flag.id} -
    - {@html flag.label} -
    - {:else if flag.type == "mcq"} - - {:else} - - {/if} - {/each} - -
    - - {#if $settings.disablesubmitbutton} - {$settings.disablesubmitbutton} - {:else if readonly} - Ce défi est désactivé - {/if} -
    - -
    - {/if} -
    diff --git a/frontend/fic/src/lib/components/ExerciceHints.svelte b/frontend/fic/src/lib/components/ExerciceHints.svelte deleted file mode 100644 index a31337c8..00000000 --- a/frontend/fic/src/lib/components/ExerciceHints.svelte +++ /dev/null @@ -1,146 +0,0 @@ - - -{#if hints.length} - - - - Indices - - {#if hinterror} - - - {hinterror} - - - {/if} - - {#each hints as hint (hint.id)} - - {#if hint.file} -

    - -

    - {/if} -
    -

    {hint.title}

    - {#if hint.file} -

    - Cliquez ici pour télécharger l'indice.
    - b2sum : - {hint.content} -

    - {:else if hint.content && !hint.hidden} -

    {@html hint.content}

    - {:else} -

    - Débloquer cet indice vous fera perdre {hint.cost * $settings.hintCurrentCoefficient} {hint.cost * $settings.hintCurrentCoefficient == 1 ? "point" : "points"}. -

    - {/if} -
    - {#if !(hint.content || hint.file) || (!hint.file && hint.hidden)} -
    - {#if !(hint.content || hint.file)} - - {/if} - {#if !hint.file && hint.hidden} - - {/if} -
    - {/if} -
    - {/each} -
    -
    -{/if} diff --git a/frontend/fic/src/lib/components/ExerciceSolved.svelte b/frontend/fic/src/lib/components/ExerciceSolved.svelte deleted file mode 100644 index 81fc3a42..00000000 --- a/frontend/fic/src/lib/components/ExerciceSolved.svelte +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - Défi réussi ! - - - - {#if exercice.solved_rank} - Vous êtes la {exercice.solved_rank}{exercice.solved_rank==1?"re":"e"} équipe à avoir résolu ce défi à . - {:else} - Bravo, vous avez résolu ce défi à {exercice.solved_time}. - {/if} - Vous avez marqué {exercice.gain} {exercice.gain==1?"point":"points"} ! - - {#if exercice.finished} -
    - {@html exercice.finished} - {#if exercice.next} -
    - {/if} - {/if} - {#if next} - Passer au défi suivant - {/if} -
    -
    diff --git a/frontend/fic/src/lib/components/ExerciceVideo.svelte b/frontend/fic/src/lib/components/ExerciceVideo.svelte deleted file mode 100644 index 522312dd..00000000 --- a/frontend/fic/src/lib/components/ExerciceVideo.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - - - {#if uri.length > 0 && uri[0] === '/'} - - diff --git a/frontend/fic/src/lib/components/FileSize.svelte b/frontend/fic/src/lib/components/FileSize.svelte deleted file mode 100644 index f0338c5b..00000000 --- a/frontend/fic/src/lib/components/FileSize.svelte +++ /dev/null @@ -1,28 +0,0 @@ - - - - {formatSize(size)} - diff --git a/frontend/fic/src/lib/components/FlagKey.svelte b/frontend/fic/src/lib/components/FlagKey.svelte deleted file mode 100644 index 411aa26b..00000000 --- a/frontend/fic/src/lib/components/FlagKey.svelte +++ /dev/null @@ -1,268 +0,0 @@ - - -
    - {#if flag.bonus_gain} -
    - optionnel | {#if flag.bonus_gain > 0}+{/if}{flag.bonus_gain} pts -
    - {/if} - {#if !no_label} - - {/if} - {#if !flag.found && !$settings.hideCaseSensitivity && !flag.ignore_case} - - aA - - {/if} - {#if !flag.found} - {#each values as v, index} - {#if !flag.choices} -
    - {#if flag.type == 'number'} - - {:else if !flag.multiline} - {if (flag.separator && e.keyCode === 13) { e.preventDefault(); addItem(); tick().then(() => { document.getElementById('sol_' + flag.type + '' + flag.id + '_' + (values.length - 1)).focus(); }); return false;}}} - > - {:else} - - {/if} - {#if flag.unit} - {flag.unit} - {/if} - {#if flag.choices_cost > 0} - - {:else if flag.separator && !flag.nb_lines && index == values.length - 1} - - {/if} -
    - {:else if flag.type == 'radio'} - {#each Object.keys(flag.choices) as l, i} -
    - - -
    - {/each} - {:else} - - {/if} - {/each} - {#if flag.help} - {@html flag.help} - {/if} - {:else if value} - - {:else if $submissions && $submissions.flags && $submissions.flags[flag.id]} - - {:else} -
    diff --git a/frontend/fic/src/lib/components/FlagMCQ.svelte b/frontend/fic/src/lib/components/FlagMCQ.svelte deleted file mode 100644 index 0b437746..00000000 --- a/frontend/fic/src/lib/components/FlagMCQ.svelte +++ /dev/null @@ -1,58 +0,0 @@ - - -{#if flag.label} -

    - {flag.label} : - {#if flag.found} -

    -{/if} -{#if !flag.found || flag.justify} - {#each Object.keys(flag.choices) as cid, index} -
    - {#if typeof flag.choices[cid] != "object"} - acc + (previous_values.mcqs[Number(cur)] !== undefined ? 1 : 0), 0) > 0 && Object.keys(flag.choices).reduce((acc, cur) => acc && previous_values.mcqs[Number(cur)] == values[Number(cur)], true)} type="checkbox" id="mcq_{flag.id}_{cid}" bind:checked={values[Number(cid)]} disabled={flag.found || flag.part_solved}> - - {#if values[Number(cid)] && flag.justify} - - {/if} - {:else} - - - {/if} - {#if flag.choices[cid].justification && flag.choices[cid].justification.solved} -
    - {/each} -{/if} -
    diff --git a/frontend/fic/src/lib/components/FormIssue.svelte b/frontend/fic/src/lib/components/FormIssue.svelte deleted file mode 100644 index f2859d23..00000000 --- a/frontend/fic/src/lib/components/FormIssue.svelte +++ /dev/null @@ -1,54 +0,0 @@ - - -
    - {#if exercice || issue.id_exercice} -
    - -
    - {#if exercice.id} - - {:else} - - {/if} -
    -
    - {/if} -
    - -
    - {#if issue.id && $issues_idx[issue.id]} - - {:else} - - {/if} -
    -
    - -
    - -
    - -
    -
    - - -
    diff --git a/frontend/fic/src/lib/components/Header.svelte b/frontend/fic/src/lib/components/Header.svelte deleted file mode 100644 index dd3cf472..00000000 --- a/frontend/fic/src/lib/components/Header.svelte +++ /dev/null @@ -1,132 +0,0 @@ - - -{#if !$settings.hide_header} - -{/if} -
    - - (isOpen = !isOpen)} /> - - - {#if $settings.hide_header && $settings.end - $settings.start > 0} - - {/if} - - - - -
    - - diff --git a/frontend/fic/src/lib/components/HeaderClock.svelte b/frontend/fic/src/lib/components/HeaderClock.svelte deleted file mode 100644 index 87f2c672..00000000 --- a/frontend/fic/src/lib/components/HeaderClock.svelte +++ /dev/null @@ -1,53 +0,0 @@ - - -{#if $settings && $settings.end} - {#if $settings.end - $settings.start > 0} - - {:else} - - {/if} -{:else} -
    -

    - {$challengeInfo.title} -

    -
    -{/if} diff --git a/frontend/fic/src/lib/components/HeaderIssues.svelte b/frontend/fic/src/lib/components/HeaderIssues.svelte deleted file mode 100644 index 94d6a5af..00000000 --- a/frontend/fic/src/lib/components/HeaderIssues.svelte +++ /dev/null @@ -1,36 +0,0 @@ - - -{#if $issues.length} - - - - Problèmes - {$issues_nb_responses} - - -{/if} diff --git a/frontend/fic/src/lib/components/HeaderPartners.svelte b/frontend/fic/src/lib/components/HeaderPartners.svelte deleted file mode 100644 index fa50cf77..00000000 --- a/frontend/fic/src/lib/components/HeaderPartners.svelte +++ /dev/null @@ -1,30 +0,0 @@ - - -{#if $challengeInfo && $challengeInfo.partners} - - - -{/if} diff --git a/frontend/fic/src/lib/components/NavTags.svelte b/frontend/fic/src/lib/components/NavTags.svelte deleted file mode 100644 index 0bb644ba..00000000 --- a/frontend/fic/src/lib/components/NavTags.svelte +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - Tags - - - -
    - {#each Object.keys($tags).sort(function (a, b) { return a.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().localeCompare(b.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase()); }) as itag, index} - {#if (filter === "" && $tags[itag].count > 1) || (filter !== "" && itag.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().indexOf(filter.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase()) >= 0)} - - #{itag} - - {#if $my && $my.team_id}{$tags[itag].solved}/{/if}{$tags[itag].count} - - - {/if} - {/each} -
    -
    -
    - - diff --git a/frontend/fic/src/lib/components/NavThemes.svelte b/frontend/fic/src/lib/components/NavThemes.svelte deleted file mode 100644 index d006dc8c..00000000 --- a/frontend/fic/src/lib/components/NavThemes.svelte +++ /dev/null @@ -1,97 +0,0 @@ - - -{#if $themes.length > 0 && ($themes[0].id != 0 || $themes.length > 1)} - - - - Scenarii - - -
    - {#each $themes as th, index} - {#if th.id != 0} - - {th.name} - {#if $my && $my.team_id && th.exercice_count && $myThemes[th.id].exercice_solved >= th.exercice_count} - - - - {/if} - {#if $max_solved > 1 && th.solved == $max_solved} - - - - {/if} - {#if th.exercice_coeff_max > 1} - - - - {/if} - {#if th.locked} - - - - {/if} - - {#if $my && $my.team_id}{$myThemes[th.id].exercice_solved}/{/if}{th.exercice_count} - - - {/if} - {/each} -
    -
    -
    -{/if} -{#if $themesStore && $themesStore["0"] && $themesStore["0"].exercices} - - - - Défis - - -
    - {#each $themesStore["0"].exercices as exercice, index} - - {exercice.title} - {#if $my && $my.id_team && exercice.solved} - - - - {/if} - {#if exercice.curcoeff > 1} - - - - {/if} - {#if $themesStore["0"].locked || exercice.disabled || ($my && !$my.exercices[exercice.id])} - - - - {/if} - - {/each} -
    -
    -
    -{/if} - - diff --git a/frontend/fic/src/lib/components/RegistrationFormCreateTeam.svelte b/frontend/fic/src/lib/components/RegistrationFormCreateTeam.svelte deleted file mode 100644 index 9e3bb945..00000000 --- a/frontend/fic/src/lib/components/RegistrationFormCreateTeam.svelte +++ /dev/null @@ -1,113 +0,0 @@ - - -
    - - -
    -
    - - - -
    - Veuillez indiquer un nom d'équipe valide. -
    -
    -
    -
    - - {#if partR} -

    - {#if !$settings.canJoinTeam} - Membres d'équipe - - {:else} - Chef d'équipe - {/if} -

    - {#if message} -

    {message}

    - {/if} - - {#each value.members as member, mid} - 1} - bind:member={member} - on:delete={RemoveMember} - /> - {/each} - - - - - - - {/if} - diff --git a/frontend/fic/src/lib/components/RegistrationFormJoinTeam.svelte b/frontend/fic/src/lib/components/RegistrationFormJoinTeam.svelte deleted file mode 100644 index 81f43702..00000000 --- a/frontend/fic/src/lib/components/RegistrationFormJoinTeam.svelte +++ /dev/null @@ -1,109 +0,0 @@ - - -{#if Object.keys($teams).length} -
    - - -
    -
    - - {#if partJ} - - {:else} - - {/if} -
    - Veuillez indiquer une équipe valide. -
    -
    -
    -
    - - {#if partJ} -

    - Vos informations -

    - {#if message} -

    {message}

    - {/if} - - - - - - - - - {/if} - -{:else} -

    - Aucune équipe enregistrée pour l'instant. -

    -{/if} diff --git a/frontend/fic/src/lib/components/RegistrationRowMember.svelte b/frontend/fic/src/lib/components/RegistrationRowMember.svelte deleted file mode 100644 index 3f5de459..00000000 --- a/frontend/fic/src/lib/components/RegistrationRowMember.svelte +++ /dev/null @@ -1,37 +0,0 @@ - - - -
    - - -
    -
    - -
    -
    - -
    -
    - -
    - {#if canDelete} -
    - -
    - {/if} -
    diff --git a/frontend/fic/src/lib/components/ResolutionModal.svelte b/frontend/fic/src/lib/components/ResolutionModal.svelte deleted file mode 100644 index 058d02db..00000000 --- a/frontend/fic/src/lib/components/ResolutionModal.svelte +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - Solution du défi - {#if exercice} - : {exercice.title} - {/if} - - - {@html resolution} - - diff --git a/frontend/fic/src/lib/components/ScoreGrid.svelte b/frontend/fic/src/lib/components/ScoreGrid.svelte deleted file mode 100644 index 1ed3909b..00000000 --- a/frontend/fic/src/lib/components/ScoreGrid.svelte +++ /dev/null @@ -1,95 +0,0 @@ - - -{#await req} - - Veuillez patienter &hellips; - -{:then scores} - {#if scores} - - - - - - {#if row.reason == "Validation"} - - {#if !$exercices_idx[row.id_exercice].id_theme == 0} - Défi validé - {:else} - Étape validée - {/if} - {:else if row.reason == "First blood"} - - Bonus premier sang - {:else if row.reason == "Bonus flag"} - - Flag bonus complété - {:else if row.reason == "Tries"} - - Malus nombre de tentatives - {:else if row.reason == "Hint"} - - Indice dévoilé - {:else if row.reason == "Display choices"} - - Échange champ de texte contre liste de choix - {:else if row.reason.startsWith("Response ")} - {@const fields = row.reason.split(" ")} - - Validation {fields[1]} no {fields[3]} - {:else} - - {row.reason} - {/if} - {#if row.id_exercice && $exercices_idx[row.id_exercice]} - : - {$exercices_idx[row.id_exercice].title} - - {/if} - - - {Math.trunc(10*row.points)/10} × {#if row.reason.startsWith("Response ")}{Math.trunc($settings.questionGainRatio * 1000)/10} % ÷ {$settings.questionGainRatio / row.coeff}{:else if row.reason == "Validation" && $settings.questionGainRatio != 0}({row.coeff + $settings.questionGainRatio}{Math.trunc($settings.questionGainRatio * 1000)/10} %){:else}{row.coeff}{/if} - - - {Math.trunc(10*row.points * row.coeff)/10} - -
    - {:else} - - Vous n'avez fait aucune action vous faisant gagner ou perdre des points. - - {/if} - -{:catch error} - - Une erreur s'est produite: {JSON.stringify(error)} - - -{/await} diff --git a/frontend/fic/src/lib/components/TeamChangeName.svelte b/frontend/fic/src/lib/components/TeamChangeName.svelte deleted file mode 100644 index e8d8d13f..00000000 --- a/frontend/fic/src/lib/components/TeamChangeName.svelte +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - Changer de nom d'équipe - - - {#if sberr || message} -

    - {#if !sberr} - Votre demande a bien été envoyée ! - {:else} - {sberr} - {/if} - {message} -

    - {/if} -
    -
    - -
    -
    - - -
    -
    -
    -
    -
    -
    diff --git a/frontend/fic/src/lib/components/TeamChangePassword.svelte b/frontend/fic/src/lib/components/TeamChangePassword.svelte deleted file mode 100644 index cccc818f..00000000 --- a/frontend/fic/src/lib/components/TeamChangePassword.svelte +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - Changer de mot de passe - - -

    - Si vous souhaitez changer de mot de passe : -

    - -
    -
    diff --git a/frontend/fic/src/lib/components/TeamMembers.svelte b/frontend/fic/src/lib/components/TeamMembers.svelte deleted file mode 100644 index 33960f54..00000000 --- a/frontend/fic/src/lib/components/TeamMembers.svelte +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - Membres de l'équipe - - {#if members && members.length} - - {#each members as member (member.id)} - - {member.firstname} - {#if member.nickname} - {member.nickname} - {/if} - {member.lastname} - {#if member.company}– {member.company}{/if} - - {/each} - - {:else} - - Passez voir l'équipe d'organisation pour compléter ces informations. - - {/if} - diff --git a/frontend/fic/src/lib/components/ThemeNav.svelte b/frontend/fic/src/lib/components/ThemeNav.svelte deleted file mode 100644 index 8bd14b8f..00000000 --- a/frontend/fic/src/lib/components/ThemeNav.svelte +++ /dev/null @@ -1,57 +0,0 @@ - - - - {#each theme.exercices as ex, index} - - {#if ex.id == exercice.id} - - {ex.title} - {#if ex.curcoeff > 1.0} - - {:else if $my && $my.exercices[ex.id]} - - {ex.title} - {#if ex.curcoeff > 1.0} - - {:else} - - {ex.title} - {#if ex.curcoeff > 1.0} - - {/if} - - {/each} - - - diff --git a/frontend/fic/src/lib/stores/challengeinfo.js b/frontend/fic/src/lib/stores/challengeinfo.js deleted file mode 100644 index 734fdcd6..00000000 --- a/frontend/fic/src/lib/stores/challengeinfo.js +++ /dev/null @@ -1,27 +0,0 @@ -import { readable, writable } from 'svelte/store'; - -function createChallengeStore() { - const { subscribe, set, update } = writable({}); - - return { - subscribe, - - refresh: async (cb) => { - challengeInfo.update(await fetch('challenge.json', {headers: {'Accept': 'application/json'}}), cb); - }, - - update: (res_challenge, cb) => { - if (res_challenge.status === 200) { - res_challenge.json().then((challenge) => { - update((s) => (Object.assign({}, challenge))); - - if (cb) { - cb(challenge); - } - }); - } - }, - } -} - -export const challengeInfo = createChallengeStore(); diff --git a/frontend/fic/src/lib/stores/common.js b/frontend/fic/src/lib/stores/common.js deleted file mode 100644 index 73a83efc..00000000 --- a/frontend/fic/src/lib/stores/common.js +++ /dev/null @@ -1 +0,0 @@ -export let stop_refresh = { state: false }; diff --git a/frontend/fic/src/lib/stores/downloaded.js b/frontend/fic/src/lib/stores/downloaded.js deleted file mode 100644 index f9170135..00000000 --- a/frontend/fic/src/lib/stores/downloaded.js +++ /dev/null @@ -1,25 +0,0 @@ -import { writable } from 'svelte/store'; - -function createDownloadedStore() { - let init = { }; - try { - if (window.localStorage && window.localStorage.getItem("downloadedStore")) { - init = JSON.parse(window.localStorage.getItem("downloadedStore")); - } - } catch { - init = { }; - } - - const { subscribe, set, update } = writable(init); - - return { - subscribe, - - update: (u) => { - update(u); - if (window.localStorage) localStorage.setItem("downloadedStore", JSON.stringify(init)); - }, - } -} - -export const hasDownloaded = createDownloadedStore(); diff --git a/frontend/fic/src/lib/stores/exercices.js b/frontend/fic/src/lib/stores/exercices.js deleted file mode 100644 index 139a50a5..00000000 --- a/frontend/fic/src/lib/stores/exercices.js +++ /dev/null @@ -1,19 +0,0 @@ -import { derived, writable } from 'svelte/store'; - -import { exercices_idx_urlid } from './themes'; - -export const set_current_exercice = writable(null) - -export const current_exercice = derived( - [set_current_exercice, exercices_idx_urlid], - ([$set_current_exercice, $exercices_idx_urlid]) => { - if ($exercices_idx_urlid === null || Object.keys($exercices_idx_urlid).length == 0) { - return null; - } - - if ($exercices_idx_urlid[$set_current_exercice]) - return $exercices_idx_urlid[$set_current_exercice]; - - return undefined; - } -) diff --git a/frontend/fic/src/lib/stores/issues.js b/frontend/fic/src/lib/stores/issues.js deleted file mode 100644 index 09309915..00000000 --- a/frontend/fic/src/lib/stores/issues.js +++ /dev/null @@ -1,98 +0,0 @@ -import { derived, writable } from 'svelte/store'; - -import { stop_refresh } from './common'; - -let refresh_interval_issues = null; - -function createIssuesStore() { - const { subscribe, set, update } = writable([]); - - function updateFunc (res_issues, cb=null) { - if (res_issues.status === 200) { - res_issues.json().then((issues) => { - update((i) => issues); - - if (cb) { - cb(issues); - } - }); - } else if (res_issues.status === 404) { - update((i) => ([])); - } - } - - async function refreshFunc(cb=null, interval=null) { - if (refresh_interval_issues) - clearInterval(refresh_interval_issues); - if (interval === null) { - interval = Math.floor(Math.random() * 24000) + 32000; - } - if (stop_refresh.state) { - return; - } - refresh_interval_issues = setInterval(refreshFunc, interval); - - updateFunc(await fetch('issues.json', {headers: {'Accept': 'application/json'}}), cb); - } - - return { - subscribe, - - refresh: refreshFunc, - - update: updateFunc, - }; -} - -export const issuesStore = createIssuesStore(); - -export const issues = derived( - issuesStore, - ($issuesStore) => { - $issuesStore.forEach(function(issue, k) { - $issuesStore[k].texts.reverse(); - }) - return $issuesStore; - }, -); - -export const issues_idx = derived( - issues, - ($issues) => { - const issues_idx = {}; - - $issues.forEach(function(issue, k) { - issues_idx[issue.id] = issue; - }) - - return issues_idx; - }, -); - -export const issues_nb_responses = derived( - issuesStore, - ($issuesStore) => { - let issues_nb_responses = 0; - - $issuesStore.forEach(function(issue, k) { - issues_nb_responses += issue.texts.length; - }) - - return issues_nb_responses; - }, -); - -export const issues_need_info = derived( - issuesStore, - ($issuesStore) => { - let issues_need_info = 0; - - $issuesStore.forEach(function(issue, k) { - if (issue.state == 'need-info') issues_need_info++; - }) - - return issues_need_info; - }, -); - -export const issues_known_responses = writable(0); diff --git a/frontend/fic/src/lib/stores/my.js b/frontend/fic/src/lib/stores/my.js deleted file mode 100644 index 3dfe3ac9..00000000 --- a/frontend/fic/src/lib/stores/my.js +++ /dev/null @@ -1,69 +0,0 @@ -import { writable } from 'svelte/store'; - -import { stop_refresh } from './common'; - -let refresh_interval_my = null; - -function createMyStore() { - const { subscribe, set, update } = writable(null); - - function updateFunc(res_my, cb=null) { - if (res_my.status === 200) { - res_my.json().then((my) => { - for (let k in my.exercices) { - my.exercices[k].id = k; - - if (my.exercices[k].flags) { - let nb = 0; - for (let j in my.exercices[k].flags) { - if (my.exercices[k].flags[j].type && !my.exercices[k].flags[j].found) - nb += 1; - } - my.exercices[k].non_found_flags = nb; - } - - if (my.team_id === 0 && my.exercices[k].hints) { - for (let j in my.exercices[k].hints) { - my.exercices[k].hints[j].hidden = true; - } - } - } - - update((m) => (Object.assign(m?m:{}, my))); - - if (cb) { - cb(my); - } - }); - } else if (res_my.status === 404) { - update((m) => (null)); - if (cb) { - cb(null); - } - } - } - - async function refreshFunc(cb=null, interval=null) { - if (refresh_interval_my) - clearInterval(refresh_interval_my); - if (interval === null) { - interval = Math.floor(Math.random() * 24000) + 24000; - } - if (stop_refresh.state) { - return; - } - refresh_interval_my = setInterval(refreshFunc, interval); - - updateFunc(await fetch('my.json', {headers: {'Accept': 'application/json'}}), cb); - } - - return { - subscribe, - - refresh: refreshFunc, - - update: updateFunc, - }; -} - -export const my = createMyStore(); diff --git a/frontend/fic/src/lib/stores/mythemes.js b/frontend/fic/src/lib/stores/mythemes.js deleted file mode 100644 index e1744866..00000000 --- a/frontend/fic/src/lib/stores/mythemes.js +++ /dev/null @@ -1,75 +0,0 @@ -import { derived } from 'svelte/store'; - -import seedrandom from 'seedrandom'; - -import { my } from './my.js'; -import { themes as themesStore } from './themes.js'; - -export const myThemes = derived([my, themesStore], ([$my, $themesStore]) => { - const mythemes = {}; - - for (let key in $themesStore) { - mythemes[key] = {exercice_solved: 0}; - - if ($my && $my.exercices) { - for (const exercice of $themesStore[key].exercices) { - if ($my.exercices[exercice.id] && $my.exercices[exercice.id].solved_rank) { - mythemes[key].exercice_solved++; - } - } - } - } - - return mythemes; -}); - -export const themes = derived( - [my, themesStore], - ([$my, $themesStore]) => { - const arr = []; - for (let th in $themesStore) { - $themesStore[th].id = th - arr.push($themesStore[th]); - } - const size = arr.length; - const rng = new seedrandom($my && $my.team_id ? $my.team_id : 0); - const respD = []; - const respE = []; - const keys = []; - - for(let i=0;i { - const tags = {}; - - for (const key in $themesStore) { - for (const exercice of $themesStore[key].exercices) { - exercice.tags.forEach((tag) => { - tag = tag.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase(); - if (!tags[tag]) - tags[tag] = {count: 1, solved: 0}; - else - tags[tag].count += 1; - - if ($my && $my.exercices && $my.exercices[exercice.id] && $my.exercices[exercice.id].solved_rank) - tags[tag].solved += 1; - }); - } - } - - return tags; -}); diff --git a/frontend/fic/src/lib/stores/settings.js b/frontend/fic/src/lib/stores/settings.js deleted file mode 100644 index cc8db08a..00000000 --- a/frontend/fic/src/lib/stores/settings.js +++ /dev/null @@ -1,165 +0,0 @@ -import { readable, writable } from 'svelte/store'; - -import { stop_refresh } from './common'; -import { my } from './my'; - -let refresh_interval_settings = null; -let refresh_timeout_settings = null; - -function createSettingsStore() { - const { subscribe, set, update } = writable({}); - - function updateFunc(res_settings, cb) { - const recvTime = (new Date()).getTime(); - - if (res_settings.status === 200) { - res_settings.json().then((settings) => { - if (settings.start) - settings.start = new Date(settings.start); - if (settings.end) - settings.end = new Date(settings.end); - if (settings.generation) - settings.generation = new Date(settings.generation); - if (settings.nextchangetime) - settings.nextchangetime = new Date(settings.nextchangetime); - if (!settings.disablesubmitbutton) - settings.disablesubmitbutton = null; - - settings.recvTime = recvTime; - const x_fic_time = res_settings.headers.get("x-fic-time"); - if (x_fic_time) { - settings.currentTime = Math.floor(x_fic_time * 1000); - } else { - settings.currentTime = settings.recvTime; - } - - update((s) => (Object.assign({}, settings))); - - if (cb) { - cb(settings); - } - }); - } - } - - async function refreshFunc(cb=null, interval=null) { - if (refresh_interval_settings) - clearInterval(refresh_interval_settings); - if (interval === null) { - interval = Math.floor(Math.random() * 24000) + 32000; - } - if (stop_refresh.state) { - return; - } - refresh_interval_settings = setInterval(refreshFunc, interval); - - if (!cb) { - // Before we start, update settings more frequently. - cb = function(stgs) { - const srv_cur = new Date(Date.now() + (stgs.currentTime - stgs.recvTime)); - - if (stgs.start > srv_cur) { - const startIn = stgs.start - srv_cur; - if (refresh_timeout_settings) - clearTimeout(refresh_timeout_settings); - - if (startIn > 15000) { - refresh_timeout_settings = setTimeout(refreshFunc, Math.floor(Math.random() * 3500) + 10000); - } else if (startIn > 1500) { - refresh_timeout_settings = setTimeout(refreshFunc, startIn - 1000 - Math.floor(Math.random() * 500)) - } else { - // On scheduled start time, refresh my.json file - refresh_timeout_settings = setTimeout(my.refresh, startIn + Math.floor(Math.random() * 200)) - } - } else if (stgs.nextchangetime > srv_cur) { - if (refresh_timeout_settings) - clearTimeout(refresh_timeout_settings); - refresh_timeout_settings = setTimeout(() => { - refreshFunc(); - setTimeout(my.refresh, 4500 + Math.floor(Math.random() * 2000)); - }, stgs.nextchangetime - srv_cur + 500 + Math.floor(Math.random() * 500)); - } - }; - } - - updateFunc(await fetch('settings.json', {headers: {'Accept': 'application/json'}}), cb); - }; - - return { - subscribe, - - refresh: refreshFunc, - - update: updateFunc, - } -} - -export const settings = createSettingsStore(); - -function updateTime(settings) { - const time = {}; - - const srv_cur = new Date(Date.now() + (settings.currentTime - settings.recvTime)); - - let remain = 0; - if (settings.start === undefined || settings.start == 0) { - return time; - } else if (settings.start > srv_cur) { - time.startIn = Math.floor((settings.start - srv_cur) / 1000); - remain = settings.end - settings.start; - } else if (settings.end > srv_cur) { - time.startIn = 0; - remain = settings.end - srv_cur; - } - - time.progression = 1 - remain / (settings.end - settings.start); - - remain = remain / 1000; - - if (remain < 0) { - remain = 0; - time.end = true; - time.expired = true; - } else if (remain < 60) { - time.end = false; - time.expired = true; - } else { - time.end = false; - time.expired = false; - } - - time.remaining = remain; - time.hours = Math.floor(remain / 3600); - time.minutes = Math.floor((remain % 3600) / 60); - time.seconds = Math.floor(remain % 60); - - if (time.hours <= 9) { - time.hours = "0" + time.hours; - } - if (time.minutes <= 9) { - time.minutes = "0" + time.minutes; - } - if (time.seconds <= 9) { - time.seconds = "0" + time.seconds; - } - - return time; -} - -export const time = readable({}, function start(set) { - let _settings = {}; - - const unsubscribe = settings.subscribe((settings) => { - _settings = settings; - }); - - set(updateTime(_settings)); - const interval = setInterval(() => { - set(updateTime(_settings)); - }, 1000); - - return function stop() { - clearInterval(interval); - unsubscribe(); - } -}); diff --git a/frontend/fic/src/lib/stores/submissions.js b/frontend/fic/src/lib/stores/submissions.js deleted file mode 100644 index c4b5abd8..00000000 --- a/frontend/fic/src/lib/stores/submissions.js +++ /dev/null @@ -1,33 +0,0 @@ -import { writable } from 'svelte/store'; - -function createSubmissionsStore() { - let init = { - flags: { }, - mcqs: { }, - justifications: { }, - }; - try { - if (window.localStorage && window.localStorage.getItem("submissionsStore")) { - init = JSON.parse(window.localStorage.getItem("submissionsStore")); - } - } catch { - init = { - flags: { }, - mcqs: { }, - justifications: { }, - }; - } - - const { subscribe, set, update } = writable(init); - - return { - subscribe, - - update: (u) => { - update(u); - if (window.localStorage) localStorage.setItem("submissionsStore", JSON.stringify(init)); - }, - } -} - -export const submissions = createSubmissionsStore(); diff --git a/frontend/fic/src/lib/stores/teams.js b/frontend/fic/src/lib/stores/teams.js deleted file mode 100644 index a583f60d..00000000 --- a/frontend/fic/src/lib/stores/teams.js +++ /dev/null @@ -1,78 +0,0 @@ -import { derived, writable } from 'svelte/store'; - -import { stop_refresh } from './common'; - -let refresh_interval_teams = null; - -function createTeamsStore() { - const { subscribe, set, update } = writable({}); - - function updateFunc(res_teams, cb=null) { - if (res_teams.status === 200) { - res_teams.json().then((teams) => { - update((t) => teams); - - if (cb) { - cb(teams); - } - }); - } - } - - async function refreshFunc(cb=null, interval=null) { - if (refresh_interval_teams) - clearInterval(refresh_interval_teams); - if (interval === null) { - interval = Math.floor(Math.random() * 24000) + 32000; - } - if (stop_refresh.state) { - return; - } - refresh_interval_teams = setInterval(refreshFunc, interval); - - updateFunc(await fetch('teams.json', {headers: {'Accept': 'application/json'}}), cb); - } - - return { - subscribe, - - refresh: refreshFunc, - - update: updateFunc, - }; -} - -export const teamsStore = createTeamsStore(); - -export const teams = derived( - teamsStore, - ($teamsStore) => { - const teams = {}; - - for (const tid in $teamsStore) { - teams[tid] = $teamsStore[tid]; - teams[tid].id = Number(tid); - } - - return teams; - } -); - -export const teams_count = derived( - teamsStore, - ($teamsStore) => Object.keys(teams).length -); - -export const rank = derived( - teams, - ($teams) => { - const rank = []; - - for (const tid in $teams) { - rank.push($teams[tid]); - } - rank.sort((a, b) => (a.rank > b.rank ? 1 : (a.rank == b.rank ? 0 : -1))); - - return rank; - } -); diff --git a/frontend/fic/src/lib/stores/themes.js b/frontend/fic/src/lib/stores/themes.js deleted file mode 100644 index 25c5ea83..00000000 --- a/frontend/fic/src/lib/stores/themes.js +++ /dev/null @@ -1,157 +0,0 @@ -import { derived, writable } from 'svelte/store'; - -import { stop_refresh } from './common'; - -let refresh_interval_themes = null; - -function createThemesStore() { - const { subscribe, set, update } = writable({}); - - async function updateFunc (res_themes, cb=null) { - if (res_themes.status === 200) { - const themes = await res_themes.json(); - - update((t) => themes); - - if (cb) { - cb(themes); - } - } - } - - async function refreshFunc(cb=null, interval=null) { - if (refresh_interval_themes) - clearInterval(refresh_interval_themes); - if (interval === null) { - interval = Math.floor(Math.random() * 24000) + 32000; - } - if (stop_refresh.state) { - return; - } - refresh_interval_themes = setInterval(refreshFunc, interval); - - await updateFunc(await fetch('themes.json', {headers: {'Accept': 'application/json'}}), cb); - } - - return { - subscribe, - - refresh: refreshFunc, - - update: updateFunc, - }; -} - -export const themesStore = createThemesStore(); - -export const themes = derived( - themesStore, - ($themesStore) => { - const themes = {}; - - for (const key in $themesStore) { - const theme = $themesStore[key]; - - themes[key] = theme - themes[key].exercice_count = theme.exercices.length; - themes[key].exercice_coeff_max = 0; - themes[key].max_gain = 0; - - for (const k in theme.exercices) { - const exercice = theme.exercices[k]; - - themes[key].max_gain += exercice.gain; - if (themes[key].exercice_coeff_max < exercice.curcoeff) { - themes[key].exercice_coeff_max = exercice.curcoeff; - } - - if (k > 0) - themes[key].exercices[k-1].next = k; - } - } - - return themes; - }, -); - -export const themes_idx = derived( - themes, - ($themes) => { - const ret = {}; - - for (const key in $themes) { - const theme = $themes[key]; - - ret[theme.urlid] = theme; - } - - return ret; - }, - null, -); - -export const exercices_idx = derived( - themesStore, - ($themesStore) => { - const ret = {}; - - for (const key in $themesStore) { - const theme = $themesStore[key]; - for (let exercice of theme.exercices) { - ret[exercice.id] = exercice; - ret[exercice.id].id_theme = key; - } - } - - return ret; - }, -); - -export const exercices_idx_urlid = derived( - themesStore, - ($themesStore) => { - const ret = {}; - - for (const key in $themesStore) { - const theme = $themesStore[key]; - for (let exercice of theme.exercices) { - ret[exercice.urlid] = exercice; - } - } - - return ret; - }, - null -); - -export const max_solved = derived( - themesStore, - ($themesStore) => { - let ret = 0; - - for (const key in $themesStore) { - const theme = $themesStore[key]; - if (theme.solved > ret) { - ret = theme.solved; - } - } - - return ret; - }, -); - -export const set_current_theme = writable(null) - -export const current_theme = derived( - [set_current_theme, themes_idx], - ([$set_current_theme, $themes_idx]) => { - if ($themes_idx === null || Object.keys($themes_idx).length == 0) { - return null; - } - - if ($themes_idx[$set_current_theme]) - return $themes_idx[$set_current_theme]; - - return undefined; - } -) diff --git a/frontend/fic/src/lib/wait.js b/frontend/fic/src/lib/wait.js deleted file mode 100644 index 9b165a98..00000000 --- a/frontend/fic/src/lib/wait.js +++ /dev/null @@ -1,22 +0,0 @@ -import { writable } from 'svelte/store'; - -import { my } from '$lib/stores/my.js'; -import { teamsStore } from '$lib/stores/teams.js'; - -export let waitInProgress = writable(false); -export let timeouted = writable(false); - -export function waitDiff(i, exercice) { - timeouted.set(false); - my.refresh((my) => { - if (my && (my.exercices[exercice.id].tries != exercice.tries || my.exercices[exercice.id].solved_rank != exercice.solved_rank || my.exercices[exercice.id].solved_time != exercice.solved_time)) { - waitInProgress.set(false); - teamsStore.refresh(); - } else if (i > 0) { - setTimeout(waitDiff, (12-i)*50+440, i-1, exercice); - } else { - timeouted.set(true); - waitInProgress.set(false); - } - }) -} diff --git a/frontend/fic/src/routes/+error.svelte b/frontend/fic/src/routes/+error.svelte deleted file mode 100644 index c284ab7b..00000000 --- a/frontend/fic/src/routes/+error.svelte +++ /dev/null @@ -1,5 +0,0 @@ - - -

    {$page.status} : {$page.error.message}

    diff --git a/frontend/fic/src/routes/+layout.js b/frontend/fic/src/routes/+layout.js deleted file mode 100644 index 2b7d9506..00000000 --- a/frontend/fic/src/routes/+layout.js +++ /dev/null @@ -1,22 +0,0 @@ -import { challengeInfo } from '$lib/stores/challengeinfo.js'; -import { stop_refresh } from '$lib/stores/common'; -import { issuesStore } from '$lib/stores/issues.js'; -import { my } from '$lib/stores/my.js'; -import { teamsStore } from '$lib/stores/teams.js'; -import { themesStore } from '$lib/stores/themes.js'; -import { settings, time } from '$lib/stores/settings.js'; - -export const ssr = false; - -export async function load() { - await challengeInfo.refresh(); - await settings.refresh(); - await themesStore.refresh(); - teamsStore.refresh(); - my.refresh((my) => { - if (my && my.team_id === 0) { - stop_refresh.state = true; - } - }); - issuesStore.refresh(); -} diff --git a/frontend/fic/src/routes/+layout.svelte b/frontend/fic/src/routes/+layout.svelte deleted file mode 100644 index bec2495b..00000000 --- a/frontend/fic/src/routes/+layout.svelte +++ /dev/null @@ -1,70 +0,0 @@ - - - - {#if $challengeInfo} - {$challengeInfo.title} - - {#if $challengeInfo.main_logo && $challengeInfo.main_logo.length} - - {/if} - {/if} - - - - -{#if $settings.globaltopmessage} -
    - {$settings.globaltopmessage} -
    -{/if} -
    - -{#if !$my && $page.route.id != "/register"} - - {#if $settings.allowRegistration} - - Votre équipe n'est pas encore enregistrée. Rendez-vous sur cette page pour procéder à votre inscription. - - {:else} - - Il semblerait qu'il y ait eu un problème lors de l'attribution de votre certificat. Veuillez vous signaler auprès de notre équipe afin de corriger ce problème. - - {/if} - -{/if} - - - - diff --git a/frontend/fic/src/routes/+page.js b/frontend/fic/src/routes/+page.js deleted file mode 100644 index afe2aa7f..00000000 --- a/frontend/fic/src/routes/+page.js +++ /dev/null @@ -1,5 +0,0 @@ -import { set_current_theme } from '$lib/stores/themes'; - -export function load() { - set_current_theme.set(null); -} diff --git a/frontend/fic/src/routes/+page.svelte b/frontend/fic/src/routes/+page.svelte deleted file mode 100644 index 5b4a0bac..00000000 --- a/frontend/fic/src/routes/+page.svelte +++ /dev/null @@ -1,123 +0,0 @@ - - - - {#if $my} - {#if !($my.team_id)} - - Attention : puisqu'il s'agit de captures effectuées dans le but de découvrir si des actes malveillants ont été commis sur différents systèmes d'information, les contenus qui sont téléchargeables peuvent contenir du contenu malveillant ! - - {:else if $teams[$my.team_id]} - - Félicitations {#if $my.members}{#each $my.members as member, index (member.id)}{#if member.id !== $my.members[0].id}{#if member.id === $my.members[$my.members.length - 1].id} et {:else}, {/if}{/if}{member.firstname} {member.lastname}{/each} {/if}! vous êtes maintenant connecté à l'espace de votre équipe {$teams[$my.team_id].name}. - {#if !$settings.denyNameChange}Vous pouvez changer ce nom dès maintenant en vous rendant sur la page de votre équipe.{/if} - - - {#if !$settings.ignoreTeamMembers && $my.team_id && (!$my.members || !$my.members.length)} - - Les membres de votre équipe ne sont pas encore enregistrés. Passez voir l'équipe serveur pour corriger cela. - - {/if} - {/if} - {/if} - - - {#if item.exercice} - {@const theme = item.theme} - {@const exercice = item.exercice} - - {:else} - {@const th = item.theme} - - {/if} - - diff --git a/frontend/fic/src/routes/[theme]/+layout.js b/frontend/fic/src/routes/[theme]/+layout.js deleted file mode 100644 index c5264093..00000000 --- a/frontend/fic/src/routes/[theme]/+layout.js +++ /dev/null @@ -1,5 +0,0 @@ -import { set_current_theme } from '$lib/stores/themes'; - -export function load({ params }) { - set_current_theme.set(params.theme); -} diff --git a/frontend/fic/src/routes/[theme]/+layout.svelte b/frontend/fic/src/routes/[theme]/+layout.svelte deleted file mode 100644 index bbb71a88..00000000 --- a/frontend/fic/src/routes/[theme]/+layout.svelte +++ /dev/null @@ -1,121 +0,0 @@ - - - - {$current_theme?($current_theme.name + " - "):""}{$challengeInfo.title} - - -{#if $current_theme === null} - - - Chargement en cours… - -{:else if !$current_theme} - - - - Ce scénario n'existe pas. - - -{:else} - -{/if} - - diff --git a/frontend/fic/src/routes/[theme]/+page.svelte b/frontend/fic/src/routes/[theme]/+page.svelte deleted file mode 100644 index 32903d10..00000000 --- a/frontend/fic/src/routes/[theme]/+page.svelte +++ /dev/null @@ -1,165 +0,0 @@ - - -{#if $current_theme.id == 0} - - - -{:else} - - - - - -
    - {#if $current_theme.locked} -
    -
    -
    - CONFIDENTIEL -
    -
    -
    - {/if} - - -

    {@html $current_theme.headline}

    -

    {@html $current_theme.intro}

    - - {#if $current_theme.partner_txt || $current_theme.partner_img || $current_theme.partner_href} - - - {#if $current_theme.partner_img} - En-tête du scénario - {/if} - {#if $current_theme.partner_txt || $current_theme.partner_href} - - {#if $current_theme.partner_txt} - {@html $current_theme.partner_txt} - {/if} - {#if $current_theme.partner_href} - - {/if} - - {/if} - - - {/if} -
    -
    -
    - - - {#if $current_theme.exercices && $current_theme.exercices.length} -
      - {#each $current_theme.exercices as exercice, index} -
    • -
      - {#if index + 1 == $current_theme.exercices.length} -
      - {:else} -
      - {/if} -
      -
      - - - - -
      -
      -
      - - {#if $my && $my.exercices[exercice.id] && $my.exercices[exercice.id].wip} - - {#if exercice.curcoeff > 1.0} -
      -
      - {#each exercice.tags as tag, idx} - #{tag} - {/each} -
      -
      -

      {@html exercice.headline}

      -
      -
      - {#if $my && $my.exercices[exercice.id]} - - - {:else} - - - {/if} -
      -
      -
    • - {/each} -
    - {:else} -

    - Aucun contenu disponible actuellement. -

    - {/if} - -
    - -
    -{/if} - - diff --git a/frontend/fic/src/routes/[theme]/[exercice]/+layout.js b/frontend/fic/src/routes/[theme]/[exercice]/+layout.js deleted file mode 100644 index 998cbe53..00000000 --- a/frontend/fic/src/routes/[theme]/[exercice]/+layout.js +++ /dev/null @@ -1,5 +0,0 @@ -import { set_current_exercice } from '$lib/stores/exercices'; - -export function load({ params }) { - set_current_exercice.set(params.exercice); -} diff --git a/frontend/fic/src/routes/[theme]/[exercice]/+layout.svelte b/frontend/fic/src/routes/[theme]/[exercice]/+layout.svelte deleted file mode 100644 index 4d40077c..00000000 --- a/frontend/fic/src/routes/[theme]/[exercice]/+layout.svelte +++ /dev/null @@ -1,34 +0,0 @@ - - - - {$current_exercice?$current_exercice.title+" - ":""}{$challengeInfo.title} - - -{#if $current_exercice === null} -
    - - Chargement en cours… -
    -{:else if !$current_exercice} - - - Vous n'avez pas encore accès à ce défi. - -{:else} - {#if $current_theme.id != 0} - - {/if} - -{/if} diff --git a/frontend/fic/src/routes/[theme]/[exercice]/+page.svelte b/frontend/fic/src/routes/[theme]/[exercice]/+page.svelte deleted file mode 100644 index 42ad7b06..00000000 --- a/frontend/fic/src/routes/[theme]/[exercice]/+page.svelte +++ /dev/null @@ -1,293 +0,0 @@ - - -{#if $current_exercice} - - - {#if $current_theme.locked} -
    -
    -
    - CONFIDENTIEL -
    -
    -
    - {/if} -

    {$current_exercice.title}

    -
    - {#each $current_exercice.tags as tag, index} - #{tag} - {/each} -
    - {#if !$my || !$my.exercices[$current_exercice.id]} -

    {@html $current_exercice.headline}

    - {:else} - {#if $my.exercices[$current_exercice.id].wip} - - - - Cette étape est marquée comme étant en cours d'élaboration. - - Elle n'est pas prête à être tentée. Vous devriez directement passer à l'étape suivante. - - {/if} -

    {@html $my.exercices[$current_exercice.id].statement}

    - {#if $my.exercices[$current_exercice.id].issue} - - {@html $my.exercices[$current_exercice.id].issue} - - {/if} - {/if} -
    - - - - - -
    - {#if $settings.discountedFactor > 0 && $my && $my.exercices[$current_exercice.id]} -
    - Cote -
    - {:else} -
    - Gain -
    - {/if} -
    - {#if $settings.discountedFactor && $current_exercice.solved} - - {Math.trunc($current_exercice.gain * (1-$settings.discountedFactor*$current_exercice.solved)*10)/10} {$current_exercice.gain==1?"point":"points"} - {:else} - {$current_exercice.gain} {$current_exercice.gain==1?"point":"points"} - {/if} -
    - {#if $settings.firstBlood && $current_exercice.solved < 1} -
    - +{$settings.firstBlood * 100}% (prem's) -
    - {:else if $settings.discountedFactor > 0 && $my && $my.exercices[$current_exercice.id]} -
    - initialement {$current_exercice.gain} {$current_exercice.gain==1?"point":"points"} -
    - {/if} - {#if $current_exercice.curcoeff != 1.0 || $settings.exerciceCurrentCoefficient != 1.0} -
    - {#if $current_exercice.curcoeff * $settings.exerciceCurrentCoefficient > 1}+{Math.round(($current_exercice.curcoeff * $settings.exerciceCurrentCoefficient - 1) * 100)}{:else}-{Math.round((1-($current_exercice.curcoeff * $settings.exerciceCurrentCoefficient)) * 100)}{/if}% (bonus) -
    - {/if} -
    - - -
    -
    - Tenté par -
    -
    - {#if !$current_exercice.tried} - aucune équipe - {:else} - {$current_exercice.tried} {$current_exercice.tried == 1?"équipe":"équipes"} - {#if $my && $my.exercices[$current_exercice.id] && $my.exercices[$current_exercice.id].total_tries} - (cumulant {$my.exercices[$current_exercice.id].total_tries} {$my.exercices[$current_exercice.id].total_tries == 1?"tentative":"tentatives"}) - {/if} - {/if} -
    -
    - - -
    -
    - Résolu par -
    -
    - {#if !$current_exercice.solved} - aucune équipe - {:else} - {$current_exercice.solved} {$current_exercice.solved == 1?"équipe":"équipes"} - {/if} -
    -
    - - -
    -
    - {#if !$current_exercice.solved} - Tenté par - {:else} - Résolu par - {/if} -
    -
    - {#if !$current_exercice.solved} - {#if !$current_exercice.tried} - aucune équipe - {:else} - {$current_exercice.tried} {$current_exercice.tried == 1?"équipe":"équipes"} - {#if $my && $my.exercices[$current_exercice.id] && $my.exercices[$current_exercice.id].total_tries} - (cumulant {$my.exercices[$current_exercice.id].total_tries} {$my.exercices[$current_exercice.id].total_tries == 1?"tentative":"tentatives"}) - {/if} - {/if} - {:else} - {$current_exercice.solved} {$current_exercice.solved == 1?"équipe":"équipes"} - {/if} -
    -
    - -
    - - {#if $my && $my.team_id} - - {#if $settings.acceptNewIssue} - - - Rapporter une anomalie sur ce défi - - {/if} - {#if $settings.QAenabled} - - - Voir les éléments QA sur ce défi - - {/if} - {#if $settings.wip && $settings.canResetProgress} - - {/if} - - {/if} -
    -
    -
    - - {#if $my && $my.exercices[$current_exercice.id]} - - - {#if $my.exercices[$current_exercice.id].files} - - {/if} - {#if $my.exercices[$current_exercice.id].hints} - - {/if} - - - {#if $my.exercices[$current_exercice.id].flags && ($my.exercices[$current_exercice.id].non_found_flags > 0 || !$my.exercices[$current_exercice.id].solved_rank) && !solved[$current_exercice.id]} - {#if $current_theme.locked} - - - - Faire son rapport - - -

    - Ce scénario n'est pas accessible ! -

    -

    - Vous ne pouvez pas compléter son rapport. -

    -
    -
    - {:else} - - {/if} - {/if} - {#if $my.exercices[$current_exercice.id].solved_rank || solved[$current_exercice.id]} - - {/if} - {#if $my.exercices[$current_exercice.id].resolution || $my.exercices[$current_exercice.id].video_uri} - - -
    - - Solution du défi -
    - {#if $my.exercices[$current_exercice.id].resolution} - - - {/if} -
    - {#if $my.exercices[$current_exercice.id].resolution} - - {@html $my.exercices[$current_exercice.id].resolution} - - {/if} - {#if $my.exercices[$current_exercice.id].video_uri} - - {/if} -
    - {/if} - -
    - {/if} -{/if} diff --git a/frontend/fic/src/routes/edit/+page.js b/frontend/fic/src/routes/edit/+page.js deleted file mode 100644 index afe2aa7f..00000000 --- a/frontend/fic/src/routes/edit/+page.js +++ /dev/null @@ -1,5 +0,0 @@ -import { set_current_theme } from '$lib/stores/themes'; - -export function load() { - set_current_theme.set(null); -} diff --git a/frontend/fic/src/routes/edit/+page.svelte b/frontend/fic/src/routes/edit/+page.svelte deleted file mode 100644 index e5689bc8..00000000 --- a/frontend/fic/src/routes/edit/+page.svelte +++ /dev/null @@ -1,61 +0,0 @@ - - - -

    - Votre équipe - {#if $my} - {$my.name} - {/if} -

    - - {#if $my} - - - - {#if !$settings.denyNameChange} - - {/if} - {#if $settings.acceptNewIssue} - - {/if} - - - - - - Détail du score - - - - - - - {:else} - - Vous n'avez pas encore d'équipe ! - Rendez-vous sur la page d'inscription pour plus d'information. - - {/if} -
    diff --git a/frontend/fic/src/routes/issues/+page.js b/frontend/fic/src/routes/issues/+page.js deleted file mode 100644 index caae4fd7..00000000 --- a/frontend/fic/src/routes/issues/+page.js +++ /dev/null @@ -1,14 +0,0 @@ -import { get_store_value } from 'svelte/internal'; - -import { exercices_idx } from '$lib/stores/themes.js'; - -export async function load({ url }) { - const eidx = get_store_value(exercices_idx); - - const exercice = eidx[url.searchParams.get("eid")]?eidx[url.searchParams.get("eid")]:null; - - return { - exercice: exercice, - fillIssue: exercice !== null || url.searchParams.get("fill-issue") !== null, - }; -} diff --git a/frontend/fic/src/routes/issues/+page.svelte b/frontend/fic/src/routes/issues/+page.svelte deleted file mode 100644 index ee33b539..00000000 --- a/frontend/fic/src/routes/issues/+page.svelte +++ /dev/null @@ -1,184 +0,0 @@ - - - -{#if message || sberr} - - {#if !sberr} - Votre rapport a bien été envoyé ! - {:else} - {sberr} - {/if} - {message} - -{/if} - -{#if data.fillIssue} - - - - {#if issue.id} - Répondre à un message - {:else} - Rapporter une anomalie sur un défi - {/if} - - - {#if !$settings.acceptNewIssue} -

    Rapprochez-vous d'un membre de l'équipe afin d'obtenir de l'aide.

    - {:else} - - {/if} -
    -
    -{/if} - - - - - - - - - - - - - - {#each $issues as issue (issue.id)} - - - - - - - - {:else} - - - - {/each} - -
    ObjetÉtat / PrioritéGéré parMessages - {#if !data.fillIssue} - - {/if} -
    - {issue.subject} - {#if issue.exercice} (défi {issue.exercice}){/if} - {issue.state} / {issue.priority}{#if issue.assignee}{issue.assignee}{:else}En attente d'attribution{/if} - {#each issue.texts as text, index} -

    - {#if !text.assignee || text.assignee == '$team'}Vous{:else}{text.assignee}{/if} - le  : - {text.cnt} -

    - {/each} -
    - -
    - Aucune anomalie remontée pour l'instant.
    - Vous souhaitez nous faire remonter un problème ? -
    -
    -
    diff --git a/frontend/fic/src/routes/rank/+page.js b/frontend/fic/src/routes/rank/+page.js deleted file mode 100644 index afe2aa7f..00000000 --- a/frontend/fic/src/routes/rank/+page.js +++ /dev/null @@ -1,5 +0,0 @@ -import { set_current_theme } from '$lib/stores/themes'; - -export function load() { - set_current_theme.set(null); -} diff --git a/frontend/fic/src/routes/rank/+page.svelte b/frontend/fic/src/routes/rank/+page.svelte deleted file mode 100644 index 5b5d3e63..00000000 --- a/frontend/fic/src/routes/rank/+page.svelte +++ /dev/null @@ -1,55 +0,0 @@ - - - -

    - {$challengeInfo.title} - Classement -

    -
    -
    - -
    - - - - - - - - - - {#each $rank as team (team.id)} - {#if team.rank != 0 || ($my && $my.team_id == team.id)} - = 0}> - - - - - {/if} - {/each} - -
    RangÉquipePoints
    {team.rank}{team.name}{Math.round(team.score*100)/100}
    -
    -
    diff --git a/frontend/fic/src/routes/register/+page.js b/frontend/fic/src/routes/register/+page.js deleted file mode 100644 index afe2aa7f..00000000 --- a/frontend/fic/src/routes/register/+page.js +++ /dev/null @@ -1,5 +0,0 @@ -import { set_current_theme } from '$lib/stores/themes'; - -export function load() { - set_current_theme.set(null); -} diff --git a/frontend/fic/src/routes/register/+page.svelte b/frontend/fic/src/routes/register/+page.svelte deleted file mode 100644 index 66f16581..00000000 --- a/frontend/fic/src/routes/register/+page.svelte +++ /dev/null @@ -1,147 +0,0 @@ - - - - - - Félicitations ! vous êtes maintenant authentifié auprès de notre serveur ! - - {#if !$my} - {#if message} - - {message} - - {/if} - {#if !$settings.allowRegistration} - - Oups, il semblerait qu'il y ait eu un problème lors de l'attribution de votre certificat. - Veuillez vous signaler auprès de notre équipe afin de corriger ce problème. - - {:else if registrationInProgress} -
    - Inscription en cours… -
    - {:else} - {#if !$settings.denyTeamCreation && !partJ} - -

    - Votre équipe n'est pas encore enregistrée sur notre serveur. Afin de - pouvoir participer au challenge, nous vous remercions de bien vouloir - remplir le formulaire d'inscription suivant : -

    - -
    - {/if} - {#if $settings.canJoinTeam && !partR} - -

    - {#if !$settings.denyTeamCreation} - Si votre équipe est déjà créée, rejoignez-là ! - {:else} - Vous n'êtes pas encore enregistré·e sur notre serveur. Afin de - pouvoir participer au challenge, nous vous remercions de bien vouloir - rejoindre votre équipe : - {/if} -

    - -
    - {/if} - {/if} - {/if} -
    diff --git a/frontend/fic/src/routes/rules/+page.js b/frontend/fic/src/routes/rules/+page.js deleted file mode 100644 index afe2aa7f..00000000 --- a/frontend/fic/src/routes/rules/+page.js +++ /dev/null @@ -1,5 +0,0 @@ -import { set_current_theme } from '$lib/stores/themes'; - -export function load() { - set_current_theme.set(null); -} diff --git a/frontend/fic/src/routes/rules/+page.svelte b/frontend/fic/src/routes/rules/+page.svelte deleted file mode 100644 index 23224da3..00000000 --- a/frontend/fic/src/routes/rules/+page.svelte +++ /dev/null @@ -1,231 +0,0 @@ - - - -

    - {$challengeInfo.title} - Règles générales -

    - -
    -
    -
    -

    Débloquage des challenges

    -

    - Au début, seul le premier défi de chaque scénario est - accessible. Les défis de niveau supérieur sont débloqués en - validant celui du niveau qui le précéde. -

    - {#if $settings.unlockedStandaloneExercicesByThemeStepValidation > 0 || $settings.unlockedStandaloneExercicesByStandaloneExerciceValidation > 0} -

    - Vous avez également accès à {$settings.unlockedStandaloneExercices} défis indépendants. - D'autres défis sont débloqués - {#if $settings.unlockedStandaloneExercicesByThemeStepValidation > 0}{#if $settings.unlockedStandaloneExercicesByThemeStepValidation < 1} toutes les {1/$settings.unlockedStandaloneExercicesByThemeStepValidation} étape{#if 1/$settings.unlockedStandaloneExercicesByThemeStepValidation > 1}s{/if} de scénario que vous validez{:else}par {$settings.unlockedStandaloneExercicesByThemeStepValidation} défis pour chaque étape de scénario validée{/if}{/if} - {#if $settings.unlockedStandaloneExercicesByStandaloneExerciceValidation > 0}{#if $settings.unlockedStandaloneExercicesByStandaloneExerciceValidation < 1} tous les {1/$settings.unlockedStandaloneExercicesByStandaloneExerciceValidation} défi{#if 1/$settings.unlockedStandaloneExercicesByStandaloneExerciceValidation > 1}s{/if} indépendant que vous validez{:else}par {$settings.unlockedStandaloneExercicesByStandaloneExerciceValidation} exercice indépendant validé{/if}{/if} -

    - {/if} -
    - -

    Le classement

    -

    - Pour figurer dans le classement, il faut avoir réalisé au moins une - action : qu'elle ajoute ou retire des points. -

    -

    - En cas d'égalité au score, les équipes sont départagées selon leur - ordre d'arrivée à ce score. -

    - -
    -

    Calcul des points

    -

    - Pour gagner des points, vous devez résoudre les défis qui vous sont - proposés. Plus le challenge est compliqué, plus il rapporte de points. -

    - - {#if $settings.questionGainRatio != 0} -

    - Même si vous n'arrivez pas à valider un défi, toutes les questions validées augmentent votre score. - {Math.trunc($settings.questionGainRatio * 1000)/10} % des points du défi sont répartis à parts égales entre toutes les questions. -

    -

    - Par exemple, pour un défi de 5 questions valant 20 points, en ayant répondu à 3 questions sur les 5, votre score sera augmenté de :
    - 20 × {Math.trunc($settings.questionGainRatio * 1000)/10} % ÷ 5 × 3 = {Math.trunc(20 * $settings.questionGainRatio / 5 * 3 * 100)/100} points. -

    -

    - Les {Math.trunc(1000 - $settings.questionGainRatio * 1000)/10} % restants sont obtenus à la validation complète du défi. -

    - {/if} - - {#if $settings.submissionCostBase != 0} -

    Coût des tentatives

    -

    - Vous disposez de 10 tentatives pour trouver la/les solutions d'un - challenge. Au delà, chaque tentative vous fait perdre une petite quantité - de points comme suit : -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Nombre de tentativesCoût par tentative
    0 à 100 point
    11 à 20{Math.round($settings.submissionCostBase * 10) / 10} {$settings.submissionCostBase < 2?"point":"points"}
    21 à 30{Math.round($settings.submissionCostBase * 20) / 10} {$settings.submissionCostBase * 2 < 2?"point":"points"}
    31 à 40{Math.round($settings.submissionCostBase * 30) / 10} {$settings.submissionCostBase * 3 < 2?"point":"points"}
    41 à 50{Math.round($settings.submissionCostBase * 40) / 10} {$settings.submissionCostBase * 4 < 2?"point":"points"}
    ......
    -

    - Par exemple : -

    -
      -
    • À 10 tentatives, vous aurez perdu {$settings.submissionCostBase * 0} {$settings.submissionCostBase * 0 < 2?"point":"points"}.
    • -
    • À 15 tentatives, vous aurez perdu en tout {$settings.submissionCostBase * 5} {$settings.submissionCostBase * 5 < 2?"point":"points"} : {$settings.submissionCostBase} × 5.
    • -
    • 25 tentatives vous coûteront en tout {$settings.submissionCostBase * 20} {$settings.submissionCostBase * 20 < 2?"point":"points"} : {$settings.submissionCostBase} × 10 + {$settings.submissionCostBase} × 2 × 5.
    • -
    • 50 tentatives vous coûteront en tout {$settings.submissionCostBase * 100} {$settings.submissionCostBase * 100 < 2?"point":"points"} : {$settings.submissionCostBase} × 10 + {$settings.submissionCostBase} × 2 × 10 + {$settings.submissionCostBase} × 3 × 10 + {$settings.submissionCostBase} × 4 × 10.
    • -
    - {#if $settings.countOnlyNotGoodTries} -

    - Seules les tentatives sans aucune bonne réponse sont prises en compte dans ce calcul. Lorsque vous complétez un formulaire avec un champ valide et un/des champs invalides, ceci n'est pas pris en compte dans le nombre de tentatives. -

    - {:else} -

    - La dernière tentative (lorsque tous les flags sont bons) est comptabilisée - parmi ce nombre de tentatives. -

    - {/if} - {/if} -
    -
    -
    -
    - {#if $settings.discountedFactor > 0} -

    Décote des gains

    -

    - Une validation d'étape ne vous garantit pas un solde de points fixe. -

    -

    - Selon le nombre d'équipes qui valident un challenge donné, sa cote diminue et vous rapporte alors moins de points. Le gain final est donc indépendant du fait que vous ayez validé l'étape avant une autre équipe : le gain affiché est un gain maximum que vous obtiendriez si aucune autre équipe ne valide cette étape. -

    -

    - Chaque validation réduit de {$settings.discountedFactor*100} % la cote de l'exercice. -

    -

    - Ainsi, pour un exercice d'une valeur initiale de {10*$settings.globalScoreCoefficient} points : -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Nombre d'équipes validant l'étape
    à la fin de la compétition
    Gain réel
    1{10*$settings.globalScoreCoefficient} points
    2{Math.round(100*$settings.globalScoreCoefficient*(1-$settings.discountedFactor))/10} points
    5{Math.round(100*$settings.globalScoreCoefficient*(1-$settings.discountedFactor*5))/10} points
    10{Math.round(100*$settings.globalScoreCoefficient*(1-$settings.discountedFactor*10))/10} points
    20{Math.round(100*$settings.globalScoreCoefficient*(1-$settings.discountedFactor*20))/10} points
    ......
    -
    - {/if} - -

    Coût des indices

    -

    - Pour vous aider, certains défis vous proposent un ou - plusieurs indices. Ces indices vous font perdre des - points, la valeur de points perdus est indiquée pour chaque indice. -

    -

    - Ces points sont perdus, que vous réussissiez ou non le défi. -

    -

    - Vous pouvez débloquer des indices même si vous ne disposez pas de - suffisamment de points (ou même si vous n'en avez pas encore) ; dans ce - cas, votre score sera négatif. -

    -
    - -

    Bonus

    -

    - Plusieurs bonus peuvent s'appliquer en même temps, dans ce cas, le calcul - du bonus est toujours effectué à partir du nombre de points initiaux du - défi. -

    - - {#if $settings.firstBlood} -

    Prem's

    -

    - Un bonus de +{$settings.firstBlood * 100} % est attribué à la première équipe qui résout un défi. -

    - {/if} - -

    Bonus temporaires

    -

    - Au cours du challenge, afin de booster les équipes ou certains challenges, - un bonus peut-être attribué si une tentative valide est envoyée durant la - période d'activité du bonus. Restez à l'écoute et observez les challenges - portant cette icône :

    -
    -
    -
    -
    diff --git a/frontend/fic/src/routes/tags/+page.js b/frontend/fic/src/routes/tags/+page.js deleted file mode 100644 index afe2aa7f..00000000 --- a/frontend/fic/src/routes/tags/+page.js +++ /dev/null @@ -1,5 +0,0 @@ -import { set_current_theme } from '$lib/stores/themes'; - -export function load() { - set_current_theme.set(null); -} diff --git a/frontend/fic/src/routes/tags/+page.svelte b/frontend/fic/src/routes/tags/+page.svelte deleted file mode 100644 index 90f5b20d..00000000 --- a/frontend/fic/src/routes/tags/+page.svelte +++ /dev/null @@ -1,60 +0,0 @@ - - - - - diff --git a/frontend/fic/src/routes/tags/[tag]/+page.js b/frontend/fic/src/routes/tags/[tag]/+page.js deleted file mode 100644 index b3c0d650..00000000 --- a/frontend/fic/src/routes/tags/[tag]/+page.js +++ /dev/null @@ -1,9 +0,0 @@ -import { set_current_theme } from '$lib/stores/themes'; - -export async function load({ params }) { - set_current_theme.set(null); - - return { - tag: params.tag, - }; -} diff --git a/frontend/fic/src/routes/tags/[tag]/+page.svelte b/frontend/fic/src/routes/tags/[tag]/+page.svelte deleted file mode 100644 index a7f0fd53..00000000 --- a/frontend/fic/src/routes/tags/[tag]/+page.svelte +++ /dev/null @@ -1,65 +0,0 @@ - - - -

    - Challenges {data.tag} -

    - -{#if exercices.length} - - {#key exercices} - {#each exercices as {theme, exercice, index} (index)} - - - - {/each} - {/key} - -{:else} -

    - Il n'y a aucun défi sur ce thème. -

    -{/if} -
    diff --git a/frontend/fic/static/e404.html b/frontend/fic/static/e404.html deleted file mode 100644 index 9cf59734..00000000 --- a/frontend/fic/static/e404.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - Challenge Forensic - - - - - - - - - - - -
    -
    - -
    -
    - -
    -
    - -
    -
    -

    Page introuvable Erreur 404

    -
    -

    - La page à laquelle vous tentez d'accéder n'existe pas ou l'adresse que vous avez tapée est incorrecte. -

    -

    - Si le problème persiste, contactez un administrateur. -

    -
    -
    - - - diff --git a/frontend/fic/static/e404.json b/frontend/fic/static/e404.json deleted file mode 100644 index e92fea36..00000000 --- a/frontend/fic/static/e404.json +++ /dev/null @@ -1 +0,0 @@ -{"errmsg": "La page à laquelle vous tentez d'accéder n'existe pas ou l'adresse que vous avez tapée est incorrecte."} diff --git a/frontend/fic/static/e413.html b/frontend/fic/static/e413.html deleted file mode 100644 index 330f2c69..00000000 --- a/frontend/fic/static/e413.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - - Challenge Forensic - - - - - - - - - - - -
    -
    - -
    -
    - -
    -
    - -
    -
    -

    Requête trop grosse Erreur 413

    -
    -

    - La quantité de données que vous souhaitez envoyer au serveur est trop importante pour qu'il accepte de la traiter. -

    -
    -
    - - - diff --git a/frontend/fic/static/e413.json b/frontend/fic/static/e413.json deleted file mode 100644 index 194d473c..00000000 --- a/frontend/fic/static/e413.json +++ /dev/null @@ -1 +0,0 @@ -{"errmsg": "La quantité de données que vous souhaitez envoyer au serveur est trop importante pour qu'il accepte de la traiter."} diff --git a/frontend/fic/static/e500.html b/frontend/fic/static/e500.html deleted file mode 100644 index 62e4c887..00000000 --- a/frontend/fic/static/e500.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - Challenge Forensic - - - - - - - - - - - -
    -
    - -
    -
    - -
    -
    - -
    -
    -

    Erreur interne Erreur 500

    -
    -

    - Notre serveur est actuellement dans l'incapacité de répondre à votre requête.
    Veuillez recommencer dans quelques instants. -

    -

    - Si le problème persiste, contactez un administrateur. -

    -
    -
    - - - diff --git a/frontend/fic/static/e500.json b/frontend/fic/static/e500.json deleted file mode 100644 index 43ee1302..00000000 --- a/frontend/fic/static/e500.json +++ /dev/null @@ -1 +0,0 @@ -{"errmsg": "Notre serveur est actuellement dans l'incapacité de répondre à votre requête. \nVeuillez recommencer dans quelques instants."} diff --git a/frontend/fic/svelte.config.js b/frontend/fic/svelte.config.js deleted file mode 100644 index f83b5ae2..00000000 --- a/frontend/fic/svelte.config.js +++ /dev/null @@ -1,15 +0,0 @@ -/** @type {import('@sveltejs/kit').Config} */ -import adapt from '@sveltejs/adapter-static'; - -const config = { - kit: { - adapter: adapt({ - fallback: 'index.html' - }), - paths: { - relative: false - }, - } -}; - -export default config; diff --git a/frontend/fic/vite.config.js b/frontend/fic/vite.config.js deleted file mode 100644 index af1b952c..00000000 --- a/frontend/fic/vite.config.js +++ /dev/null @@ -1,14 +0,0 @@ -import { sveltekit } from '@sveltejs/kit/vite'; - -/** @type {import('vite').UserConfig} */ -const config = { - server: { - hmr: { - port: 10000 - } - }, - - plugins: [sveltekit()] -}; - -export default config; diff --git a/gen_hash_link_files.sh b/gen_hash_link_files.sh new file mode 100755 index 00000000..2cb4873a --- /dev/null +++ b/gen_hash_link_files.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +FROM=`realpath $1`; shift +TO=`realpath $1`; shift + +if [ -z "$FROM" ] || [ -z "$TO" ] +then + echo "Usage: $0 from to" + exit 1 +elif ! [ -d "$FROM" ] +then + echo "$FROM not found" + exit 2 +fi + +mkdir -p "$TO" || exit 3 +rm -rf "$TO" || exit 3 + +for i in `find "$FROM" -mindepth 1 -type f` +do + FILE=`echo $i | sed "s!^$FROM/!!"` + HASH=`echo -n $FILE | sha384sum | cut -d " " -f 1` + + mkdir -p "$TO/$HASH/" + ln -s "$i" "$TO/$HASH/" +done diff --git a/gen_site.pl b/gen_site.pl new file mode 100755 index 00000000..5f980509 --- /dev/null +++ b/gen_site.pl @@ -0,0 +1,726 @@ +#!/usr/bin/env perl + +use v5.10.1; +use strict; +use warnings; +use threads; +use threads::shared; + +use Cwd 'abs_path'; +use File::Basename; +use File::Copy; +use File::Find; +use File::Path qw/make_path remove_tree/; +use File::Temp "tempdir"; +use Getopt::Long; +use IO::Socket; +use Thread::Queue; + +our $outdir = "outest"; +our $outteams = "/teams/"; +our $outerrors = "/errors/"; +our $outhome = "/htdocs/"; +our $tmpdir = tempdir(); + +our $baseurl = "http://localhost"; +our $baseadmin = "/admin/"; +our $basehome = "/"; +our $baseerrors = "/errors/"; +our $baseteams = "/connected/"; +our $threads = 6; + +sub genErrors(;$) +{ + my $m = shift // Mirror->new(); + + $m->add_url($baseerrors . "400"); + $m->add_url($baseerrors . "403"); + $m->add_url($baseerrors . "404"); + $m->add_url($baseerrors . "413"); + $m->add_url($baseerrors . "500"); + $m->add_url($baseerrors . "502"); + + return $m; +} + +sub genHome(;$) +{ + my $m = shift // Mirror->new(); + $m->add_url($basehome . "index.html"); + $m->add_url($basehome); + + genErrors($m); + + return $m; +} + +sub genFull(;$) +{ + my $m = shift // Mirror->new(); + $m->add_url($baseteams); + + return $m; +} + +sub genTeam($;$) +{ + my $team_id = shift; + my $m = shift // Mirror->new(); + + $m->add_url($baseteams . $team_id); + + return $m; +} + +sub genTeamTheme($$;$) +{ + my $team_id = shift; + my $theme_id = shift; + my $m = shift // Mirror->new(); + + $m->add_url($baseteams . $team_id . "/" . $theme_id); + + return $m; +} + + +my $queue :shared = Thread::Queue->new(); +my $main_thread; + +sub manage +{ + my $m = shift // Mirror->new(); + + while (1) + { + if ($queue->pending() <= 0) + { + lock($queue); + cond_wait($queue) while $queue->pending() <= 0; + } + + my $cmd; + { + lock($queue); + $cmd = $queue->peek(); + } + + $m->{end} = 0 if ($m->{end}); + + for ($cmd) + { + if (/^all$/) + { + say "Generate all teams"; + genFull($m); + } + elsif (/^HOME/) + { + say "Generate full public part"; + genHome($m); + } + elsif (/^ERRORS?/) + { + say "Generate errors pages"; + genErrors($m); + } + elsif (/^TEAM([0-9]+)$/) + { + say "Generate team: $1"; + genTeam($1, $m); + } + elsif (/^TEAM([0-9]+),([0-9]+)$/) + { + say "Generate team theme: $1/$2"; + genTeamTheme($1, $2, $m); + } + elsif (/^(reset)*r(e(s(e(t)?)?)?)?/) + { + say "Performing RESET ..."; + remove_tree($main::tmpdir); + mkdir($main::tmpdir); + $m->reset(); + } + elsif (/^(D)?(SYNC)*S(Y(N(C)?)?)?/) + { + sync($1); + } + elsif (/^LS$/) + { + system("ls '$main::tmpdir'"); + } + elsif (/^J(O(I(N)?)?)?$/) + { + say "JOIN receive, stopping all threads..."; + $m->stop(); + return 1; + } + elsif (/^help/i) + { + say "TODO, sorry :("; + } + else { + say "$cmd is not a valid command"; + } + } + + next if ! $m->join(); + say ">>> $cmd done"; + { + lock($queue); + + my $dq = $queue->dequeue(); + if ($cmd ne $dq) + { + $queue->insert(0, $dq); + + for (my $i = 0; $i < $queue->pending(); $i++) + { + if ($queue->peek($i) eq $cmd) + { + $queue->extract($i); + last; + } + } + } + } + } +} + +sub sync +{ + if (shift) + { + say "Full synchronization to $main::outdir"; + + my $tmpcopy = tempdir(); + + find( + sub + { + if (-f) + { + my $todir = $File::Find::dir."/"; + if ($todir =~ /^\Q$main::tmpdir\E\/?(\Q$main::baseadmin\E|\Q$main::baseteams\E|\Q$main::baseerrors\E|\Q$main::basehome\E)(.*)$/) + { + $todir = $tmpcopy; + + return if ($1 eq $main::baseadmin); + $todir .= $main::outteams if ($1 eq $main::baseteams); + $todir .= $main::outerrors if ($1 eq $main::baseerrors); + $todir .= $main::outhome if ($1 eq $main::basehome); + + $todir .= $2; + } + make_path($todir, { mode => 0751 }) if (! -d $todir ); + + copy($File::Find::name, $todir) or warn(q{copy failed:} . $!); + } + }, + $tmpdir + ); + + abs_path($main::outdir); + abs_path($tmpcopy); + + remove_tree($main::outdir); + mkdir($main::outdir); + + system("mv '$tmpcopy'/* '$main::outdir/'"); + } + else + { + say "Incremental synchronization to $main::outdir"; + + find( + sub + { + if (-f) + { + my $todir = $File::Find::dir."/"; + if ($todir =~ /^\Q$main::tmpdir\E\/?(\Q$main::baseadmin\E|\Q$main::baseteams\E|\Q$main::basehome\E)(.*)$/) + { + $todir = $main::outdir; + + return if ($1 eq $main::baseadmin); + $todir .= $main::outteams if ($1 eq $main::baseteams); + $todir .= $main::outerrors if ($1 eq $main::baseerrors); + $todir .= $main::outhome if ($1 eq $main::basehome); + + $todir .= $2; + } + make_path($todir, { mode => 0751 }) if (! -d $todir ); + + say "$File::Find::name -> $todir"; + + copy($File::Find::name, $todir) or warn(q{copy failed:} . $!); + } + }, + $tmpdir + ); + } +} + +sub parse($$;$) +{ + my $m = shift; + my $change_current = 0; + my $cmds = shift; + my $chan_output = shift // \*STDOUT; + + for my $cmd ($cmds =~ /([^:]+)/g) + { + my $len = length($cmd); + + # Search the right position + my $i; + for ($i = 0; $i < $queue->pending(); $i++) + { + last if ($len > length($queue->peek($i))); + } + say $chan_output "Inserting $cmd at position $i/".$queue->pending(); + $queue->insert($i, $cmd); + $change_current = 1 if $i == 0 && $queue->pending() != 1; + } + + if ($change_current) + { + say "Priority item have changed, stoping running threads"; + $m->end(); + } + + { + lock($queue); + cond_broadcast($queue); + } + + #print Dumper($queue); +} + + +# Parse arguments +my $help; my $deamon; my $socket; +GetOptions ("threads|thread|t=i" => \$threads, + "baseadmin|ba=s" => \$baseadmin, + "basehome|bh=s" => \$basehome, + "baseteams|bt=s" => \$baseteams, + "outdir|out|o=s" => \$outdir, + "deamon|d" => \$deamon, + "socket|s=s" => \$socket, + "help|h|?" => \$help); + +$outdir = abs_path($outdir); + +sub create_socket +{ + my $m = shift; + my $socket_path = abs_path( shift ); + + unlink($socket_path) if -e $socket_path; + + my $socket = IO::Socket::UNIX->new( + Local => $socket_path, + Type => SOCK_STREAM, + Listen => SOMAXCONN, + ); + say "Socket listening on $socket_path; waiting for connections..."; + + while(my $connection = $socket->accept) + { + say "New connexion, new thread ready for parsing actions!"; + threads->create(\&socket_run, $m, $connection); + } +} + +sub socket_run +{ + my $m = shift; + my $connection = shift; + + $connection->autoflush(1); + say $connection "You are connected to gen_site.pl, please enter command:"; + while (<$connection>) + { + chomp $_; + parse($m, $_, $connection); + } + say "Closing socket connection; stopping thread."; + close $connection; +} + +if ($deamon) +{ + my $m :shared = Mirror->new(); + + $main_thread = threads->create(\&manage, $m); + + threads->create(\&create_socket, $m, $socket) if ($socket); + + while(<>) + { + chomp $_; + parse($m, $_); + } + + parse($m, "J"); + $main_thread->join(); +} +elsif (@ARGV) +{ + my $m :shared = Mirror->new(); + + $main_thread = threads->create(\&manage, $m); + + threads->create(\&create_socket, $m, $socket) if ($socket); + + while ($_ = shift) { + parse($m, $_); + } + + parse($m, "J"); + $main_thread->join(); +} +else +{ + my $m = genFull(); + genHome($m); + + $m->join(); + + sync(1); +} + +remove_tree($main::tmpdir); + + +package Mirror; + +use v5.10.1; +use strict; +use warnings; +use threads; +use threads::shared; + +use Thread::Queue; + +sub new($) +{ + my $class = shift; + my $self :shared = shared_clone({ + threads => [], + stop => 0, + }); + + bless $self, $class; + + $self->start(); + + return $self; +} + +sub start($) +{ + my $self = shift; + + $self->{add} = Thread::Queue->new("#RESET"); + $self->{todo} = Thread::Queue->new(); + $self->{waiting} = 0; + $self->{end} = 0; + + push @{ $self->{threads} }, threads->create(\&run_add, $self)->tid(); + + for (my $i = 0; $i < $main::threads; $i++) { + push @{ $self->{threads} }, threads->create(\&run, $self, $i + 1)->tid(); + } +} + +sub reset($) +{ + my $self = shift; + + $self->{add}->enqueue("#RESET"); +} + +sub end($) +{ + my $self = shift; + + $self->{end} = 1; + + while($self->{add}->dequeue_nb()) { } + while($self->{todo}->dequeue_nb()) { } + + $self->reset(); + + { + lock($self); + + cond_broadcast($self); + } + + return $self->join(); +} + +sub stop($) +{ + my $self = shift; + + $self->{stop} = 1; + + return $self->end(); +} + +sub add_url($@) +{ + my $self = shift; + + while (my $link = shift) + { + $self->{add}->enqueue($link); + } +} + +sub run_add +{ + my $self = shift; + my $id = shift // 0; + + my %urlseen; + + while (! $self->{stop}) + { + my $link = $self->{add}->dequeue_nb(); + + if (! defined $link) + { + { + lock($self); + $self->{waiting} += 1; + cond_broadcast($self); + } + $link = $self->{add}->dequeue(); + last if (! defined $link); + { + lock($self); + $self->{waiting} -= 1; + } + } + + next if (exists $urlseen{$link}); + + if ($link eq "#RESET") + { + %urlseen = (); + } + else + { + $self->{todo}->enqueue($link); + $urlseen{$link} = 1; + } + } +} + +sub run +{ + my $self = shift; + my $id = shift // 0; + + while (! $self->{stop}) + { + my $url = $self->{todo}->dequeue_nb(); + + if (! defined $url) + { + { + lock($self); + $self->{waiting} += 1; + cond_broadcast($self); + } + $url = $self->{todo}->dequeue(); + last if (! defined $url); + { + lock($self); + $self->{waiting} -= 1; + } + } + + say "[$id] $baseurl$url"; + + my $p = FicPage->new($baseurl . $url); + $p->fetch(); + + $p->toRightURL() if ($url !~ /\/$/); + + my @links = $p->getNearLinks() if ! $self->{end}; + + $p->treatLinks(); + $p->save(); + + for my $link (@links) + { + $self->add_url($link) if ($link ne $url && ($link =~ /^?\Q$url\E/) || $p->{url} =~ /\.css$/); + } + } +} + +sub join($) +{ + my $self = shift; + + sleep 1 if ($self->{waiting} == $main::threads + 1); + + return ($self->{waiting} > 0 && ! $self->{end}) if ($self->{waiting} == $main::threads + 1); + + { + lock($self); + cond_wait($self) until ($self->{waiting} >= $main::threads + 1 || $self->{waiting} < 0 || $self->{stop}); + } + + return ($self->{waiting} > 0 && ! $self->{end}); +} + + +package FicPage; + +use v5.10.1; +use strict; +use warnings; + +use File::Basename; +use File::Path qw(make_path); +use HTTP::Request::Common qw(GET POST); +use LWP::UserAgent; + +sub new +{ + my $class = shift; + my $self = { + url => shift, + }; + + bless $self, $class; + + return $self; +} + +sub toRightURL($) +{ + my $self = shift; + + my $search = $self->{url}; + $search =~ s!/$!!; + + for my $url ($self->getLinks()) + { + $url = $baseurl . $url; + if ($url =~ /^\Q$search\E/) + { + $url =~ s!^(\Q$search\E[^/]*).*$!$1/!; + + $self->{url} = $url; + return $url; + } + } + + return $self->{url}; +} + +sub getLinks($) +{ + my $self = shift; + + return $self->{content} =~ /(?:src|href|action)="([^"]+)"/g; +} + +sub getNearLinks($) +{ + my $self = shift; + + if ($self->{url} =~ /\.css$/) + { + return $self->{content} =~ /url\(["']?(?:\Q$main::baseurl\E|(?:\/?\.\.)+)?([^:)"'?#]+)/g; + } + else + { + my @links = $self->{content} =~ /(?:src|href)="(?:\Q$main::baseurl\E|(?:\/?\.\.)+)?([^:"]+)"/g; + + for my $action ($self->{content} =~ /action="(?:\Q$main::baseurl\E|(?:\/?\.\.)+)?([^:"]+)"/g) + { + push @links, $action; + push @links, "$action/gerr"; + push @links, "$action/serr"; + } + + return @links; + } +} + +sub treatLinks($) +{ + my $self = shift; + + $self->{content} =~ s!(src|href|action)="( \Q$baseteams\E[^/]+/ | \Q$baseadmin\E | \Q$basehome\E)([^"]*)"!$1="/$3"!gx; +} + +sub fetch($) +{ + my $self = shift; + my $ua = LWP::UserAgent->new; + + my $res = $ua->request(GET $self->{url}); + + $self->{content} = $res->content; +} + +sub alreadySaved($;$) +{ + my $self = shift; + + my $path = $self->getSavePath(@_); + return -f $path || ( -d $path && -f "$path/index.html" ); +} + +sub getSavePath($;$) +{ + my $self = shift; + my $basedir = shift // $main::tmpdir; + + # Convert URL to real directory path + my $path = $self->{url}; + $path =~ s/^\Q$main::baseurl\E//; + + return "$basedir$path"; +} + +sub save($;$) +{ + my $self = shift; + + # Convert URL to real directory path + my $path = $self->getSavePath(@_); + + eval + { + if ($path =~ /\.[a-z0-9]{2,4}$/) + { + eval { + make_path( dirname("$path") , { mode => 0751 }) if (! -d dirname("$path") ); + }; + + open my $fd, ">", $path or die "$path: $!"; + print $fd $self->{content}; + close $fd; + } + else + { + eval { + make_path "$path", { mode => 0751 } if (! -d "$path"); + }; + + open my $fd, ">", "$path/index.html" or die "$path: $!"; + print $fd $self->{content}; + close $fd; + } + }; + print $@ if ($@); +} diff --git a/gen_site.sh b/gen_site.sh new file mode 100755 index 00000000..c859ed3d --- /dev/null +++ b/gen_site.sh @@ -0,0 +1,155 @@ +#!/bin/sh + +BASEURL="localhost" +SALT_TEAM="connected" +OUT_TEAM="./teams" +OUT_HTDOCS="./htdocs" + +MAX_PARAL=10 + +DEBUG=0 + + +cd `dirname "$0"` + +if [ "$UID" = "0" ] +then + SCRIPT=`pwd`/`basename "$0"` + su -c "sh -c '$SCRIPT $@'" synchro + exit $? +fi + +if [ -f "/tmp/generate_site" ] +then + echo "This script is already running" 1>&2 + echo "Remove the file /tmp/generate_site if you are sure this is not true" 1>&2 + exit 1 +fi + +touch /tmp/generate_site + +WGET_OPT="--no-check-certificate -c" + +if [ $DEBUG -ne 1 ] +then + WGET_OPT="-q" +fi + +./clear_cache.sh top + +mkdir -p out + +ORIG_DIR=`pwd` +MYTMPDIR=`mktemp -d` +cd "$MYTMPDIR" + +# First, remove existing version if any +rm -rf "$BASEURL" "$OUT_TEAM" + +wget $WGET_OPT -m -b "http://$BASEURL:8080/" -o /dev/null + +mkdir -p "$BASEURL" +ln -sf "$ORIG_DIR/files/" "$BASEURL/files" + +# Get list of teams +TEAMS= +if [ $# -gt 0 ] +then + while [ $# -gt 0 ] + do + TEAMS="$TEAMS /$SALT_TEAM/$1/" + shift + done + FULLSYNC=0 +else + for l in $(curl -k "http://$BASEURL:8080/$SALT_TEAM/" 2> /dev/null | grep -oE "/[^/]+/[0-9]+/") + do + TEAMS="$TEAMS $l" + done + FULLSYNC=1 +fi + +echo "Team list to generate: $TEAMS" + +NB=0 +PIDLIST= +# Fetch them in parallel +for l in $TEAMS +do + ( + if ! wget $WGET_OPT -m "http://$BASEURL:8080/$l" + then + exit 1 + fi + + for m in $(grep -R "
    &2 + exit $ERR + +else + MOREOPT= + if [ "$FULL" = "1" ] + then + MOREOPT="--delete" + fi + # Ok, now, sync files with prod + rsync -av $MOREOPT * "$ORIG_DIR/out" + + cd "$ORIG_DIR" + rm -rf "$MYTMPDIR" +fi diff --git a/generator/.gitignore b/generator/.gitignore deleted file mode 100644 index a8a6bda7..00000000 --- a/generator/.gitignore +++ /dev/null @@ -1 +0,0 @@ -generator \ No newline at end of file diff --git a/generator/generation.go b/generator/generation.go deleted file mode 100644 index 9f128197..00000000 --- a/generator/generation.go +++ /dev/null @@ -1,366 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "io" - "io/ioutil" - "log" - "net/http" - "os" - "path" - "runtime" - "sync" - "time" - - "srs.epita.fr/fic-server/libfic" -) - -var parallelJobs = runtime.NumCPU() -var genQueue chan *fic.GenStruct -var inQueueMutex sync.RWMutex -var inGenQueue map[fic.GenerateType]bool - -func init() { - genQueue = make(chan *fic.GenStruct) - inGenQueue = map[fic.GenerateType]bool{} -} - -func launchWorkers() { - log.Println("Running with", parallelJobs, "worker(s)") - for i := parallelJobs; i > 0; i-- { - go consumer() - } -} - -func enqueueHandler(w http.ResponseWriter, r *http.Request) { - var gs fic.GenStruct - - dec := json.NewDecoder(r.Body) - err := dec.Decode(&gs) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - log.Printf("POST /enqueue | %v", gs) - - appendGenQueue(gs) - http.Error(w, "OK", http.StatusOK) -} - -func performHandler(w http.ResponseWriter, r *http.Request) { - var gs fic.GenStruct - - dec := json.NewDecoder(r.Body) - err := dec.Decode(&gs) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - log.Printf("POST /perform | %v", gs) - - err = <-appendGenQueue(gs).GenEnded() - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } else { - http.Error(w, "OK", http.StatusOK) - } -} - -func performFullResyncHandler(w http.ResponseWriter, r *http.Request) { - log.Printf("POST /full-resync started") - - waitList := genAll() - var errs string - for _, e := range waitList { - err := <-e - if err != nil { - errs += err.Error() + "\n" - } - } - - if errs != "" { - lastRegeneration = time.Now() - http.Error(w, errs, http.StatusInternalServerError) - } else { - http.Error(w, "done", http.StatusOK) - } - - log.Printf("POST /full-resync done") -} - -func appendGenQueue(gs fic.GenStruct) *fic.GenStruct { - if gs.Type == fic.GenTeam || gs.Type == fic.GenTeamIssues { - genQueue <- &gs - return &gs - } - - // Append only if not already in queue - inQueueMutex.RLock() - if v, ok := inGenQueue[gs.Type]; !ok || !v { - inQueueMutex.RUnlock() - - inQueueMutex.Lock() - inGenQueue[gs.Type] = true - inQueueMutex.Unlock() - - genQueue <- &gs - } else { - inQueueMutex.RUnlock() - } - - return &gs -} - -func consumer() { - var id string - var err error - - for { - gs := <-genQueue - id = gs.Id - - inQueueMutex.Lock() - inGenQueue[gs.Type] = false - inQueueMutex.Unlock() - - switch gs.Type { - case fic.GenPublic: - err = genMyPublicFile() - case fic.GenEvents: - err = genEventsFile() - case fic.GenTeam: - err = genTeamMyFile(gs.TeamId) - case fic.GenTeams: - err = genTeamsFile() - case fic.GenThemes: - err = genThemesFile() - case fic.GenTeamIssues: - err = genTeamIssuesFile(gs.TeamId) - } - - if err != nil { - log.Println(id, "[ERR] Unable to generate:", err) - } - gs.End(err) - } -} - -// Generate issues.json for a given team -func genTeamIssuesFile(teamid int64) error { - team, err := fic.GetTeam(teamid) - if err != nil { - return fmt.Errorf("Unable to GetTeam: %w", err) - } - - dirPath := path.Join(TeamsDir, fmt.Sprintf("%d", team.Id)) - - my, err := team.MyIssueFile() - if err != nil { - return err - } - - if my == nil { - if _, err := os.Stat(path.Join(dirPath, "issues.json")); !os.IsNotExist(err) { - err = os.Remove(path.Join(dirPath, "issues.json")) - if err != nil { - log.Printf("Unable to remove empty issues file: %s", path.Join(dirPath, "issues.json")) - } - } - return nil - } - - if s, err := os.Stat(dirPath); os.IsNotExist(err) { - os.MkdirAll(dirPath, 0751) - } else if !s.IsDir() { - return fmt.Errorf("%s is not a directory", dirPath) - } - - if j, err := json.Marshal(my); err != nil { - return err - } else if err = ioutil.WriteFile(path.Join(dirPath, "issues.json"), j, 0644); err != nil { - return err - } - - return nil -} - -// Generate my.json, wait.json and scores.json for a given team -func genTeamMyFile(teamid int64) error { - team, err := fic.GetTeam(teamid) - if err != nil { - return fmt.Errorf("Unable to GetTeam: %w", err) - } - - dirPath := path.Join(TeamsDir, fmt.Sprintf("%d", team.Id)) - - if s, err := os.Stat(dirPath); os.IsNotExist(err) { - os.MkdirAll(dirPath, 0751) - } else if !s.IsDir() { - return fmt.Errorf("%s is not a directory", dirPath) - } - - if my, err := fic.MyJSONTeam(team, true); err != nil { - return err - } else if j, err := json.Marshal(my); err != nil { - return err - } else if err = ioutil.WriteFile(path.Join(dirPath, "my.json"), j, 0666); err != nil { - return err - } - - // Speed up generation when challenge is started - if !ChStarted { - if my, err := fic.MyJSONTeam(team, false); err != nil { - return err - } else if j, err := json.Marshal(my); err != nil { - return err - } else if err = ioutil.WriteFile(path.Join(dirPath, "wait.json"), j, 0666); err != nil { - return err - } - } else { - if scores, err := team.ScoreGrid(); err != nil { - return err - } else if j, err := json.Marshal(scores); err != nil { - return err - } else if err = ioutil.WriteFile(path.Join(dirPath, "scores.json"), j, 0666); err != nil { - return err - } - } - - return nil -} - -// Generate public my.json file -func genMyPublicFile() error { - dirPath := path.Join(TeamsDir, "public") - - if s, err := os.Stat(dirPath); os.IsNotExist(err) { - os.MkdirAll(dirPath, 0751) - } else if !s.IsDir() { - return fmt.Errorf("%s is not a directory", dirPath) - } - - if my, err := fic.MyJSONTeam(nil, true); err != nil { - return err - } else if j, err := json.Marshal(my); err != nil { - return err - } else if err = ioutil.WriteFile(path.Join(dirPath, "my.json"), j, 0666); err != nil { - return err - } - - os.Symlink("my.json", path.Join(dirPath, "wait.json")) - - if teams, err := fic.ExportTeams(true); err != nil { - return err - } else if j, err := json.Marshal(teams); err != nil { - return err - } else if err = ioutil.WriteFile(path.Join(dirPath, "teams.json"), j, 0666); err != nil { - return err - } - - return nil -} - -// Generate general evemts.json file -func genEventsFile() error { - if evts, err := fic.GetLastEvents(); err != nil { - return err - } else if j, err := json.Marshal(evts); err != nil { - return err - } else if err = ioutil.WriteFile(path.Join(TeamsDir, "events.json"), j, 0666); err != nil { - return err - } - - return nil -} - -// Generate general teams.json file -func genTeamsFile() error { - if teams, err := fic.ExportTeams(false); err != nil { - return err - } else if j, err := json.Marshal(teams); err != nil { - return err - } else if err = ioutil.WriteFile(path.Join(TeamsDir, "teams.json"), j, 0666); err != nil { - return err - } - - if teams, err := fic.ExportTeams(true); err != nil { - return err - } else if j, err := json.Marshal(teams); err != nil { - return err - } else if err = ioutil.WriteFile(path.Join(TeamsDir, "public", "teams.json"), j, 0666); err != nil { - return err - } - - return nil -} - -// Generate general themes.json file -func genThemesFile() error { - themes, err := fic.ExportThemes() - if err != nil { - return fmt.Errorf("unable to generate themes: %w", err) - } - - var wr io.Writer - - themesfd, err := os.Create(path.Join(TeamsDir, "themes.json")) - if err != nil { - return fmt.Errorf("unable to re-create themes.json: %w", err) - } - defer themesfd.Close() - - themeswait, err := os.Create(path.Join(TeamsDir, "themes-wait.json")) - if err != nil { - return fmt.Errorf("unable to re-create themes-wait.json: %w", err) - } - defer themeswait.Close() - - if allowRegistration { - wr = io.MultiWriter(themesfd, themeswait) - } else { - wr = themesfd - } - - enc := json.NewEncoder(wr) - err = enc.Encode(themes) - if err != nil { - return fmt.Errorf("unable to encode themes.json: %w", err) - } - - if !allowRegistration { - enc = json.NewEncoder(themeswait) - err = enc.Encode(map[string]string{}) - if err != nil { - return fmt.Errorf("unable to encode themes-wait.json: %w", err) - } - } - - return nil -} - -func genAll() (waitList []chan error) { - waitList = append( - waitList, - appendGenQueue(fic.GenStruct{Type: fic.GenThemes}).GenEnded(), - appendGenQueue(fic.GenStruct{Type: fic.GenTeams}).GenEnded(), - appendGenQueue(fic.GenStruct{Type: fic.GenEvents}).GenEnded(), - appendGenQueue(fic.GenStruct{Type: fic.GenPublic}).GenEnded(), - ) - - if teams, err := fic.GetActiveTeams(); err != nil { - log.Println("Team retrieval error: ", err) - } else { - for _, team := range teams { - waitList = append( - waitList, - appendGenQueue(fic.GenStruct{Type: fic.GenTeam, TeamId: team.Id}).GenEnded(), - appendGenQueue(fic.GenStruct{Type: fic.GenTeamIssues, TeamId: team.Id}).GenEnded(), - ) - } - } - - return -} diff --git a/generator/main.go b/generator/main.go deleted file mode 100644 index de5ddba5..00000000 --- a/generator/main.go +++ /dev/null @@ -1,168 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - "log" - "net" - "net/http" - "os" - "os/signal" - "path" - "strings" - "syscall" - "time" - - "srs.epita.fr/fic-server/libfic" - "srs.epita.fr/fic-server/settings" -) - -var TeamsDir string - -var ChStarted = false -var lastRegeneration time.Time -var skipInitialGeneration = false -var allowRegistration bool - -func reloadSettings(config *settings.Settings) { - fic.HintCoefficient = config.HintCurCoefficient - fic.WChoiceCoefficient = config.WChoiceCurCoefficient - fic.ExerciceCurrentCoefficient = config.ExerciceCurCoefficient - ChStarted = config.Start.Unix() > 0 && time.Since(config.Start) >= 0 - if allowRegistration != config.AllowRegistration || fic.PartialValidation != config.PartialValidation || fic.UnlockedChallengeDepth != config.UnlockedChallengeDepth || fic.UnlockedStandaloneExercices != config.UnlockedStandaloneExercices || fic.UnlockedStandaloneExercicesByThemeStepValidation != config.UnlockedStandaloneExercicesByThemeStepValidation || fic.UnlockedStandaloneExercicesByStandaloneExerciceValidation != config.UnlockedStandaloneExercicesByStandaloneExerciceValidation || fic.UnlockedChallengeUpTo != config.UnlockedChallengeUpTo || fic.DisplayAllFlags != config.DisplayAllFlags || fic.FirstBlood != config.FirstBlood || fic.SubmissionCostBase != config.SubmissionCostBase || fic.SubmissionUniqueness != config.SubmissionUniqueness || fic.DiscountedFactor != config.DiscountedFactor || fic.QuestionGainRatio != config.QuestionGainRatio || fic.HideCaseSensitivity != config.HideCaseSensitivity { - allowRegistration = config.AllowRegistration - - fic.PartialValidation = config.PartialValidation - fic.UnlockedChallengeDepth = config.UnlockedChallengeDepth - fic.UnlockedChallengeUpTo = config.UnlockedChallengeUpTo - fic.UnlockedStandaloneExercices = config.UnlockedStandaloneExercices - fic.UnlockedStandaloneExercicesByThemeStepValidation = config.UnlockedStandaloneExercicesByThemeStepValidation - fic.UnlockedStandaloneExercicesByStandaloneExerciceValidation = config.UnlockedStandaloneExercicesByStandaloneExerciceValidation - fic.DisplayAllFlags = config.DisplayAllFlags - - fic.FirstBlood = config.FirstBlood - fic.SubmissionCostBase = config.SubmissionCostBase - fic.SubmissionUniqueness = config.SubmissionUniqueness - fic.GlobalScoreCoefficient = config.GlobalScoreCoefficient - fic.CountOnlyNotGoodTries = config.CountOnlyNotGoodTries - fic.HideCaseSensitivity = config.HideCaseSensitivity - fic.DiscountedFactor = config.DiscountedFactor - fic.QuestionGainRatio = config.QuestionGainRatio - - if !skipInitialGeneration { - log.Println("Generating files...") - go func() { - waitList := genAll() - for _, e := range waitList { - <-e - } - log.Println("Full generation done") - }() - lastRegeneration = time.Now() - } else { - skipInitialGeneration = false - log.Println("Regeneration skipped by option.") - } - } else { - log.Println("No change found. Skipping regeneration.") - } -} - -func main() { - if v, exists := os.LookupEnv("FIC_BASEURL"); exists { - fic.FilesDir = v + "files" - } else { - fic.FilesDir = "/files" - } - - var bind = flag.String("bind", "./GENERATOR/generator.socket", "Bind path or port/socket") - var dsn = flag.String("dsn", fic.DSNGenerator(), "DSN to connect to the MySQL server") - flag.StringVar(&settings.SettingsDir, "settings", "./SETTINGSDIST", "Base directory where load and save settings") - flag.StringVar(&TeamsDir, "teams", "./TEAMS", "Base directory where save teams JSON files") - flag.StringVar(&fic.FilesDir, "files", fic.FilesDir, "Request path prefix to reach files") - flag.BoolVar(&skipInitialGeneration, "skipfullgeneration", skipInitialGeneration, "Skip the initial regeneration") - flag.IntVar(¶llelJobs, "jobs", parallelJobs, "Number of generation workers") - flag.Parse() - - log.SetPrefix("[generator] ") - - settings.SettingsDir = path.Clean(settings.SettingsDir) - TeamsDir = path.Clean(TeamsDir) - - launchWorkers() - - log.Println("Opening DB...") - if err := fic.DBInit(*dsn); err != nil { - log.Fatal("Cannot open the database: ", err) - } - defer fic.DBClose() - - // Load configuration - settings.LoadAndWatchSettings(path.Join(settings.SettingsDir, settings.SettingsFile), reloadSettings) - - // Register SIGUSR1, SIGUSR2 - interrupt1 := make(chan os.Signal, 1) - signal.Notify(interrupt1, syscall.SIGUSR1) - interrupt2 := make(chan os.Signal, 1) - signal.Notify(interrupt2, syscall.SIGUSR2) - - // Prepare graceful shutdown - interrupt := make(chan os.Signal, 1) - signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM) - - srv := &http.Server{ - Addr: *bind, - ReadHeaderTimeout: 15 * time.Second, - ReadTimeout: 15 * time.Second, - WriteTimeout: 10 * time.Second, - IdleTimeout: 30 * time.Second, - } - - http.HandleFunc("/enqueue", enqueueHandler) - http.HandleFunc("/perform", performHandler) - http.HandleFunc("/full", performFullResyncHandler) - - // Serve pages - go func() { - if !strings.Contains(*bind, ":") { - if _, err := os.Stat(*bind); !os.IsNotExist(err) { - if err := os.Remove(*bind); err != nil { - log.Fatal(err) - } - } - - os.MkdirAll(path.Dir(*bind), 0751) - - unixListener, err := net.Listen("unix", *bind) - if err != nil { - log.Fatal(err) - } - log.Fatal(srv.Serve(unixListener)) - } else if err := srv.ListenAndServe(); err != nil { - log.Fatal(err) - } - }() - log.Println(fmt.Sprintf("Ready, listening on %s", *bind)) - - // Wait shutdown signal -loop: - for { - select { - case <-interrupt: - break loop - case <-interrupt1: - log.Println("SIGUSR1 received, regenerating all files...") - genAll() - log.Println("SIGUSR1 treated.") - case <-interrupt2: - inQueueMutex.Lock() - log.Printf("SIGUSR2 received, dumping statistics:\n parallelJobs: %d\n genQueue: %d\n Teams in queue: %v\n Challenge started: %v\n Last regeneration: %v\n", parallelJobs, len(genQueue), inGenQueue, ChStarted, lastRegeneration) - inQueueMutex.Unlock() - } - } - - log.Print("The service is shutting down...") - srv.Shutdown(context.Background()) - log.Println("done") -} diff --git a/go.mod b/go.mod deleted file mode 100644 index 9ace7117..00000000 --- a/go.mod +++ /dev/null @@ -1,92 +0,0 @@ -module srs.epita.fr/fic-server - -go 1.23.0 - -toolchain go1.24.1 - -require ( - github.com/BurntSushi/toml v1.5.0 - github.com/asticode/go-astisub v0.34.0 - github.com/cenkalti/dominantcolor v1.0.3 - github.com/gin-contrib/sessions v1.0.2 - github.com/gin-gonic/gin v1.10.0 - github.com/go-git/go-git/v5 v5.14.0 - github.com/go-sql-driver/mysql v1.9.1 - github.com/google/gopacket v1.1.19 - github.com/studio-b12/gowebdav v0.10.0 - github.com/u2takey/ffmpeg-go v0.5.0 - github.com/yuin/goldmark v1.7.8 - gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b - go.uber.org/multierr v1.11.0 - golang.org/x/crypto v0.36.0 - golang.org/x/image v0.25.0 - golang.org/x/oauth2 v0.28.0 - gopkg.in/fsnotify.v1 v1.4.7 -) - -require ( - dario.cat/mergo v1.0.0 // indirect - filippo.io/edwards25519 v1.1.0 // indirect - github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/ProtonMail/go-crypto v1.1.5 // indirect - github.com/acomagu/bufpipe v1.0.4 // indirect - github.com/asticode/go-astikit v0.20.0 // indirect - github.com/asticode/go-astits v1.8.0 // indirect - github.com/aws/aws-sdk-go v1.38.20 // indirect - github.com/bytedance/sonic v1.11.6 // indirect - github.com/bytedance/sonic/loader v0.1.1 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect - github.com/chenzhuoyu/iasm v0.9.1 // indirect - github.com/cloudflare/circl v1.6.0 // indirect - github.com/cloudwego/base64x v0.1.4 // indirect - github.com/cloudwego/iasm v0.2.0 // indirect - github.com/cyphar/filepath-securejoin v0.4.1 // indirect - github.com/disintegration/imaging v1.6.2 // indirect - github.com/emirpasic/gods v1.18.1 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.3 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect - github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.6.2 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.20.0 // indirect - github.com/goccy/go-json v0.10.2 // indirect - github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect - github.com/golang/protobuf v1.5.4 // indirect - github.com/gorilla/context v1.1.2 // indirect - github.com/gorilla/securecookie v1.1.2 // indirect - github.com/gorilla/sessions v1.2.2 // indirect - github.com/imdario/mergo v0.3.15 // indirect - github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.7 // indirect - github.com/leodido/go-urn v1.4.0 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect - github.com/pjbgf/sha1cd v0.3.2 // indirect - github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b // indirect - github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect - github.com/skeema/knownhosts v1.3.1 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/u2takey/go-utils v0.3.1 // indirect - github.com/ugorji/go/codec v1.2.12 // indirect - github.com/xanzy/ssh-agent v0.3.3 // indirect - golang.org/x/arch v0.8.0 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.35.0 // indirect - golang.org/x/sync v0.12.0 // indirect - golang.org/x/sys v0.31.0 // indirect - golang.org/x/text v0.23.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.34.1 // indirect - gopkg.in/warnings.v0 v0.1.2 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/go.sum b/go.sum deleted file mode 100644 index 1fd08549..00000000 --- a/go.sum +++ /dev/null @@ -1,967 +0,0 @@ -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= -filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.3.0 h1:Ws8e5YmnrGEHzZEzg0YvK/7COGYtTC5PbaH9oSSbgfA= -github.com/BurntSushi/toml v1.3.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= -github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= -github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= -github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= -github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= -github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0AE5csawV4YXMNGNQQXvLRps3z2Z59OPO+I= -github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= -github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= -github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= -github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 h1:ZK3C5DtzV2nVAQTx5S5jQvMeDqWtD1By5mOoyY/xJek= -github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE= -github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 h1:KLq8BE0KwCL+mmXnjLWEAOYO+2l2AE4YMmqG1ZpZHBs= -github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= -github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= -github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk= -github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= -github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4= -github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= -github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= -github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= -github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= -github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/asticode/go-astikit v0.20.0 h1:+7N+J4E4lWx2QOkRdOf6DafWJMv6O4RRfgClwQokrH8= -github.com/asticode/go-astikit v0.20.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0= -github.com/asticode/go-astisub v0.21.0 h1:xaCx7SnqblsR7ZqFbo9wq/JYwen7IAG2AVIcfecLxNI= -github.com/asticode/go-astisub v0.21.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8= -github.com/asticode/go-astisub v0.22.0 h1:1oHRHnrm5AWpzTKnjQnxkroFbQuVbkBysbyKUF2y2jY= -github.com/asticode/go-astisub v0.22.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8= -github.com/asticode/go-astisub v0.23.0 h1:WzkWty0Phy9rGrG6r0FjShBy9f1Wn7sMLvjdYj5hki4= -github.com/asticode/go-astisub v0.23.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8= -github.com/asticode/go-astisub v0.24.0 h1:Y3eDWeDyt+QlydjLrBuK91RZBkUenFH3EhUWoHqHdMo= -github.com/asticode/go-astisub v0.24.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8= -github.com/asticode/go-astisub v0.25.1 h1:RZMGfZPp7CXOkI6g+zCU7DRLuciGPGup921uKZnMXPI= -github.com/asticode/go-astisub v0.25.1/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8= -github.com/asticode/go-astisub v0.26.0 h1:Ka1oUyWzo/lIx7RX97GI1QdbClqYVxI0ExKuZRN/cDk= -github.com/asticode/go-astisub v0.26.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8= -github.com/asticode/go-astisub v0.26.1 h1:cL53FKU52cDPJzDkvy8CQvofwMaAOkR4Wc4B1gkUPWs= -github.com/asticode/go-astisub v0.26.1/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8= -github.com/asticode/go-astisub v0.26.2 h1:cdEXcm+SUSmYCEPTQYbbfCECnmQoIFfH6pF8wDJhfVo= -github.com/asticode/go-astisub v0.26.2/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8= -github.com/asticode/go-astisub v0.27.0 h1:MLm+v38QhPxggPdnESjsGb/JTmD9916wpd2jfWakhug= -github.com/asticode/go-astisub v0.27.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8= -github.com/asticode/go-astisub v0.29.0 h1:1Pjz+TkaAwjPPoH88bQ6nnoXjin+WO2zZV95C/COGrs= -github.com/asticode/go-astisub v0.29.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8= -github.com/asticode/go-astisub v0.30.0 h1:z4k2Y+V+rlCE8qk3uw/nie56KXxJaL1/GwTP+9F2GMM= -github.com/asticode/go-astisub v0.30.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8= -github.com/asticode/go-astisub v0.32.0 h1:i1RHVQyTxSAuX0X3YC5zIyWruVZorS3cDXxqxYa0qss= -github.com/asticode/go-astisub v0.32.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8= -github.com/asticode/go-astisub v0.34.0 h1:owKNj0A9pc7YVW/rNy2MJZ1mf0L8DTdklZVfyZDhTWI= -github.com/asticode/go-astisub v0.34.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8= -github.com/asticode/go-astits v1.8.0 h1:rf6aiiGn/QhlFjNON1n5plqF3Fs025XLUwiQ0NB6oZg= -github.com/asticode/go-astits v1.8.0/go.mod h1:DkOWmBNQpnr9mv24KfZjq4JawCFX1FCqjLVGvO0DygQ= -github.com/aws/aws-sdk-go v1.38.20 h1:QbzNx/tdfATbdKfubBpkt84OM6oBkxQZRw6+bW2GyeA= -github.com/aws/aws-sdk-go v1.38.20/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA= -github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= -github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= -github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= -github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= -github.com/bytedance/sonic v1.11.3 h1:jRN+yEjakWh8aK5FzrciUHG8OFXK+4/KrAX/ysEtHAA= -github.com/bytedance/sonic v1.11.3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= -github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= -github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= -github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= -github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/cenkalti/dominantcolor v1.0.2 h1:nP1qLG2sD4vu+mGjvEcp3zMaiT7OvcRDtp+wE0YEtfg= -github.com/cenkalti/dominantcolor v1.0.2/go.mod h1:HvN7ziRLPAes3UkUrLDDRADCPTFsKUzZx5ZAQx8KECc= -github.com/cenkalti/dominantcolor v1.0.3 h1:Pt0vfRZ8enkZh1n22RvoboA53SMM/v2aEwNQTZKSqww= -github.com/cenkalti/dominantcolor v1.0.3/go.mod h1:mGpFMbWUnyXaGN48Zbf9bU9HJP1eCCD7dnsscb4lyR4= -github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= -github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= -github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= -github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= -github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= -github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= -github.com/cloudflare/circl v1.1.0 h1:bZgT/A+cikZnKIwn7xL2OBj012Bmvho/o6RpRvv3GKY= -github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= -github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= -github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= -github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= -github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= -github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= -github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= -github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= -github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= -github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= -github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= -github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM= -github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= -github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= -github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= -github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= -github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= -github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= -github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= -github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= -github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= -github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= -github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= -github.com/gin-contrib/sessions v0.0.5 h1:CATtfHmLMQrMNpJRgzjWXD7worTh7g7ritsQfmF+0jE= -github.com/gin-contrib/sessions v0.0.5/go.mod h1:vYAuaUPqie3WUSsft6HUlCjlwwoJQs97miaG2+7neKY= -github.com/gin-contrib/sessions v1.0.0 h1:r5GLta4Oy5xo9rAwMHx8B4wLpeRGHMdz9NafzJAdP8Y= -github.com/gin-contrib/sessions v1.0.0/go.mod h1:DN0f4bvpqMQElDdi+gNGScrP2QEI04IErRyMFyorUOI= -github.com/gin-contrib/sessions v1.0.1 h1:3hsJyNs7v7N8OtelFmYXFrulAf6zSR7nW/putcPEHxI= -github.com/gin-contrib/sessions v1.0.1/go.mod h1:ouxSFM24/OgIud5MJYQJLpy6AwxQ5EYO9yLhbtObGkM= -github.com/gin-contrib/sessions v1.0.2 h1:UaIjUvTH1cMeOdj3in6dl+Xb6It8RiKRF9Z1anbUyCA= -github.com/gin-contrib/sessions v1.0.2/go.mod h1:KxKxWqWP5LJVDCInulOl4WbLzK2KSPlLesfZ66wRvMs= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= -github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= -github.com/gin-gonic/gin v1.8.2 h1:UzKToD9/PoFj/V4rvlKqTRKnQYyz8Sc1MJlv4JHPtvY= -github.com/gin-gonic/gin v1.8.2/go.mod h1:qw5AYuDrzRTnhvusDsrov+fDIxp9Dleuu12h8nfB398= -github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= -github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= -github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= -github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= -github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= -github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= -github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= -github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= -github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= -github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= -github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= -github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-billy/v5 v5.4.0 h1:Vaw7LaSTRJOUric7pe4vnzBSgyuf2KrLsu2Y4ZpQBDE= -github.com/go-git/go-billy/v5 v5.4.0/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= -github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= -github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= -github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= -github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= -github.com/go-git/go-billy/v5 v5.6.1 h1:u+dcrgaguSSkbjzHwelEjc0Yj300NUevrrPphk/SoRA= -github.com/go-git/go-billy/v5 v5.6.1/go.mod h1:0AsLr1z2+Uksi4NlElmMblP5rPcDZNRCD8ujZCRR2BE= -github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= -github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= -github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8= -github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= -github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= -github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= -github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= -github.com/go-git/go-git/v5 v5.5.1 h1:5vtv2TB5PM/gPM+EvsHJ16hJh4uAkdGcKilcwY7FYwo= -github.com/go-git/go-git/v5 v5.5.1/go.mod h1:uz5PQ3d0gz7mSgzZhSJToM6ALPaKCdSnl58/Xb5hzr8= -github.com/go-git/go-git/v5 v5.5.2 h1:v8lgZa5k9ylUw+OR/roJHTxR4QItsNFI5nKtAXFuynw= -github.com/go-git/go-git/v5 v5.5.2/go.mod h1:BE5hUJ5yaV2YMxhmaP4l6RBQ08kMxKSPD4BlxtH7OjI= -github.com/go-git/go-git/v5 v5.6.0 h1:JvBdYfcttd+0kdpuWO7KTu0FYgCf5W0t5VwkWGobaa4= -github.com/go-git/go-git/v5 v5.6.0/go.mod h1:6nmJ0tJ3N4noMV1Omv7rC5FG3/o8Cm51TB4CJp7mRmE= -github.com/go-git/go-git/v5 v5.6.1 h1:q4ZRqQl4pR/ZJHc1L5CFjGA1a10u76aV1iC+nh+bHsk= -github.com/go-git/go-git/v5 v5.6.1/go.mod h1:mvyoL6Unz0PiTQrGQfSfiLFhBH1c1e84ylC2MDs4ee8= -github.com/go-git/go-git/v5 v5.7.0 h1:t9AudWVLmqzlo+4bqdf7GY+46SUuRsx59SboFxkq2aE= -github.com/go-git/go-git/v5 v5.7.0/go.mod h1:coJHKEOk5kUClpsNlXrUvPrDxY3w3gjHvhcZd8Fodw8= -github.com/go-git/go-git/v5 v5.8.0 h1:Rc543s6Tyq+YcyPwZRvU4jzZGM8rB/wWu94TnTIYALQ= -github.com/go-git/go-git/v5 v5.8.0/go.mod h1:coJHKEOk5kUClpsNlXrUvPrDxY3w3gjHvhcZd8Fodw8= -github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+A= -github.com/go-git/go-git/v5 v5.8.1/go.mod h1:FHFuoD6yGz5OSKEBK+aWN9Oah0q54Jxl0abmj6GnqAo= -github.com/go-git/go-git/v5 v5.9.0 h1:cD9SFA7sHVRdJ7AYck1ZaAa/yeuBvGPxwXDL8cxrObY= -github.com/go-git/go-git/v5 v5.9.0/go.mod h1:RKIqga24sWdMGZF+1Ekv9kylsDz6LzdTSI2s/OsZWE0= -github.com/go-git/go-git/v5 v5.10.0 h1:F0x3xXrAWmhwtzoCokU4IMPcBdncG+HAAqi9FcOOjbQ= -github.com/go-git/go-git/v5 v5.10.0/go.mod h1:1FOZ/pQnqw24ghP2n7cunVl0ON55BsjPYvhWHvZGhoo= -github.com/go-git/go-git/v5 v5.10.1 h1:tu8/D8i+TWxgKpzQ3Vc43e+kkhXqtsZCKI/egajKnxk= -github.com/go-git/go-git/v5 v5.10.1/go.mod h1:uEuHjxkHap8kAl//V5F/nNWwqIYtP/402ddd05mp0wg= -github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= -github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= -github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= -github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= -github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M= -github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc= -github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0= -github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A= -github.com/go-git/go-git/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi60= -github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj9ItW3Wk5k= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= -github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= -github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= -github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= -github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= -github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= -github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= -github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= -github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= -github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= -github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= -github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= -github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= -github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4= -github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= -github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= -github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= -github.com/go-sql-driver/mysql v1.9.1 h1:FrjNGn/BsJQjVRuSa8CBrM5BWA9BWoXXat3KrtSb/iI= -github.com/go-sql-driver/mysql v1.9.1/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= -github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM= -github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= -github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= -github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= -github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= -github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o= -github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM= -github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= -github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= -github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= -github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= -github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= -github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= -github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= -github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= -github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck= -github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= -github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= -github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= -github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/panjf2000/ants/v2 v2.4.2/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A= -github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= -github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= -github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= -github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= -github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo= -github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= -github.com/pjbgf/sha1cd v0.2.3 h1:uKQP/7QOzNtKYH7UTohZLcjF5/55EnTw0jO/Ru4jZwI= -github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M= -github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= -github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= -github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= -github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b h1:aUNXCGgukb4gtY99imuIeoh8Vr0GSwAlYxPAhqZrpFc= -github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= -github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0= -github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag= -github.com/skeema/knownhosts v1.1.1 h1:MTk78x9FPgDFVFkDLTrsnnfCJl7g1C/nnKvePgrIngE= -github.com/skeema/knownhosts v1.1.1/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo= -github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM= -github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo= -github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= -github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= -github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A= -github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= -github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= -github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= -github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= -github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/studio-b12/gowebdav v0.0.0-20221102155456-200a600c0272 h1:dXbdJSdxf0EnR4SkcsfRNuGCvoEk9lavXbSCFXN2gJc= -github.com/studio-b12/gowebdav v0.0.0-20221102155456-200a600c0272/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE= -github.com/studio-b12/gowebdav v0.0.0-20221109171924-60ec5ad56012 h1:ZC+dlnsjxqrcB68nEFbIEfo4iXsog3Sg8FlXKytAjhY= -github.com/studio-b12/gowebdav v0.0.0-20221109171924-60ec5ad56012/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE= -github.com/studio-b12/gowebdav v0.0.0-20230203175954-cd21842fb675 h1:zgn46xCgyM8IOOzWhM+/Wa657aeO95JhGd1/iTtl4X8= -github.com/studio-b12/gowebdav v0.0.0-20230203175954-cd21842fb675/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE= -github.com/studio-b12/gowebdav v0.0.0-20230203202212-3282f94193f2 h1:VsBj3UD2xyAOu7kJw6O/2jjG2UXLFoBzihqDU9Ofg9M= -github.com/studio-b12/gowebdav v0.0.0-20230203202212-3282f94193f2/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE= -github.com/studio-b12/gowebdav v0.9.0 h1:1j1sc9gQnNxbXXM4M/CebPOX4aXYtr7MojAVcN4dHjU= -github.com/studio-b12/gowebdav v0.9.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE= -github.com/studio-b12/gowebdav v0.10.0 h1:Yewz8FFiadcGEu4hxS/AAJQlHelndqln1bns3hcJIYc= -github.com/studio-b12/gowebdav v0.10.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/u2takey/ffmpeg-go v0.4.1 h1:l5ClIwL3N2LaH1zF3xivb3kP2HW95eyG5xhHE1JdZ9Y= -github.com/u2takey/ffmpeg-go v0.4.1/go.mod h1:ruZWkvC1FEiUNjmROowOAps3ZcWxEiOpFoHCvk97kGc= -github.com/u2takey/ffmpeg-go v0.5.0 h1:r7d86XuL7uLWJ5mzSeQ03uvjfIhiJYvsRAJFCW4uklU= -github.com/u2takey/ffmpeg-go v0.5.0/go.mod h1:ruZWkvC1FEiUNjmROowOAps3ZcWxEiOpFoHCvk97kGc= -github.com/u2takey/go-utils v0.3.1 h1:TaQTgmEZZeDHQFYfd+AdUT1cT4QJgJn/XVPELhHw4ys= -github.com/u2takey/go-utils v0.3.1/go.mod h1:6e+v5vEZ/6gu12w/DC2ixZdZtCrNokVxD0JUklcqdCs= -github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= -github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= -github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU= -github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= -github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= -github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= -github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= -github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= -github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark v1.5.2 h1:ALmeCk/px5FSm1MAcFBAsVKZjDuMVj8Tm7FFIlMJnqU= -github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark v1.5.3 h1:3HUJmBFbQW9fhQOzMgseU134xfi6hU+mjWywx5Ty+/M= -github.com/yuin/goldmark v1.5.3/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU= -github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark v1.5.5 h1:IJznPe8wOzfIKETmMkd06F8nXkmlhaHqFRM9l1hAGsU= -github.com/yuin/goldmark v1.5.5/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark v1.5.6 h1:COmQAWTCcGetChm3Ig7G/t8AFAN00t+o8Mt4cf7JpwA= -github.com/yuin/goldmark v1.5.6/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68= -github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA= -github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= -github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U= -github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= -github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg= -github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= -github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= -github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= -gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b h1:7gd+rd8P3bqcn/96gOZa3F5dpJr/vEiDQYlNb/y2uNs= -gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -gocv.io/x/gocv v0.25.0/go.mod h1:Rar2PS6DV+T4FL+PM535EImD/h13hGVaHhnCu1xarBs= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.1.0 h1:oMxhUYsO9VsR1dcoVUjJjIGhx1LXol3989T/yZ59Xsw= -golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= -golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= -golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= -golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE= -golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= -golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= -golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= -golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= -golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= -golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= -golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= -golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= -golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= -golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg= -golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= -golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= -golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= -golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= -golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= -golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.1.0 h1:r8Oj8ZA2Xy12/b5KZYj3tuv7NG/fBz3TwQVvpJ9l8Rk= -golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c= -golang.org/x/image v0.2.0 h1:/DcQ0w3VHKCC5p0/P2B0JpAZ9Z++V2KOo2fyU89CXBQ= -golang.org/x/image v0.2.0/go.mod h1:la7oBXb9w3YFjBqaAwtynVioc1ZvOnNteUNrifGNmAI= -golang.org/x/image v0.3.0 h1:HTDXbdK9bjfSWkPzDJIw89W8CAtfFGduujWs33NLLsg= -golang.org/x/image v0.3.0/go.mod h1:fXd9211C/0VTlYuAcOhW8dY/RtEJqODXOWBDpmYBf+A= -golang.org/x/image v0.4.0 h1:x1RWAiZIvERqkltrFjtQP1ycmiR5pmhjtCfVOtdURuQ= -golang.org/x/image v0.4.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4= -golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI= -golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4= -golang.org/x/image v0.6.0 h1:bR8b5okrPI3g/gyZakLZHeWxAR8Dn5CyxXv1hLH5g/4= -golang.org/x/image v0.6.0/go.mod h1:MXLdDR43H7cDJq5GEGXEVeeNhPgi+YYEQ2pC1byI1x0= -golang.org/x/image v0.7.0 h1:gzS29xtG1J5ybQlv0PuyfE3nmc6R4qB73m6LUUmvFuw= -golang.org/x/image v0.7.0/go.mod h1:nd/q4ef1AKKYl/4kft7g+6UyGbdiqWqTP1ZAbRoV7Rg= -golang.org/x/image v0.8.0 h1:agUcRXV/+w6L9ryntYYsF2x9fQTMd4T8fiiYXAVW6Jg= -golang.org/x/image v0.8.0/go.mod h1:PwLxp3opCYg4WR2WO9P0L6ESnsD6bLTWcw8zanLMVFM= -golang.org/x/image v0.9.0 h1:QrzfX26snvCM20hIhBwuHI/ThTg18b/+kcKdXHvnR+g= -golang.org/x/image v0.9.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0= -golang.org/x/image v0.10.0 h1:gXjUUtwtx5yOE0VKWq1CH4IJAClq4UGgUA3i+rpON9M= -golang.org/x/image v0.10.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0= -golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo= -golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8= -golang.org/x/image v0.12.0 h1:w13vZbU4o5rKOFFR8y7M+c4A5jXDC0uXTdHYRP8X2DQ= -golang.org/x/image v0.12.0/go.mod h1:Lu90jvHG7GfemOIcldsh9A2hS01ocl6oNO7ype5mEnk= -golang.org/x/image v0.13.0 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg= -golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk= -golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4= -golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= -golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8= -golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= -golang.org/x/image v0.16.0 h1:9kloLAKhUufZhA12l5fwnx2NZW39/we1UhBesW433jw= -golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs= -golang.org/x/image v0.17.0 h1:nTRVVdajgB8zCMZVsViyzhnMKPwYeroEERRC64JuLco= -golang.org/x/image v0.17.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= -golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= -golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= -golang.org/x/image v0.19.0 h1:D9FX4QWkLfkeqaC62SonffIIuYdOk/UE2XKUBgRIBIQ= -golang.org/x/image v0.19.0/go.mod h1:y0zrRqlQRWQ5PXaYCOMLTW2fpsxZ8Qh9I/ohnInJEys= -golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw= -golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM= -golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s= -golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78= -golang.org/x/image v0.22.0 h1:UtK5yLUzilVrkjMAZAZ34DXGpASN8i8pj8g+O+yd10g= -golang.org/x/image v0.22.0/go.mod h1:9hPFhljd4zZ1GNSIZJ49sqbp45GKK9t6w+iXvGqZUz4= -golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= -golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= -golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ= -golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8= -golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ= -golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.3.0 h1:VWL6FNY2bEEmsGVKabSlHu5Irp34xmMRoqb/9lF9lxk= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= -golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= -golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= -golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= -golang.org/x/oauth2 v0.1.0 h1:isLCZuhj4v+tYv7eskaN4v/TM+A1begWWgyVJDdl1+Y= -golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= -golang.org/x/oauth2 v0.2.0 h1:GtQkldQ9m7yvzCL1V+LrYow3Khe0eJH0w7RbX/VbaIU= -golang.org/x/oauth2 v0.2.0/go.mod h1:Cwn6afJ8jrQwYMxQDTpISoXmXW9I6qF6vDeuuoX3Ibs= -golang.org/x/oauth2 v0.3.0 h1:6l90koy8/LaBLmLu8jpHeHexzMwEita0zFfYlggy2F8= -golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk= -golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M= -golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= -golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= -golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= -golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= -golang.org/x/oauth2 v0.9.0 h1:BPpt2kU7oMRq3kCHAA1tbSEshXRw1LpG2ztgDwrzuAs= -golang.org/x/oauth2 v0.9.0/go.mod h1:qYgFZaFiu6Wg24azG8bdV52QJXJGbZzIIsRCdVKzbLw= -golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= -golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= -golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= -golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= -golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= -golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= -golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= -golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= -golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0= -golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM= -golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= -golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= -golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= -golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= -golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= -golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= -golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= -golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= -golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= -golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= -golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= -golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= -golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= -golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= -golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= -golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/htdocs-admin b/htdocs-admin deleted file mode 120000 index 4e69c177..00000000 --- a/htdocs-admin +++ /dev/null @@ -1 +0,0 @@ -admin/static \ No newline at end of file diff --git a/htdocs-dashboard b/htdocs-dashboard deleted file mode 120000 index 4f97701f..00000000 --- a/htdocs-dashboard +++ /dev/null @@ -1 +0,0 @@ -dashboard/static \ No newline at end of file diff --git a/htdocs/.onyx b/htdocs/.onyx new file mode 100644 index 00000000..9e6e10a7 --- /dev/null +++ b/htdocs/.onyx @@ -0,0 +1 @@ +../onyx/load.php diff --git a/htdocs/assets/js/html5shiv.js/index.html b/htdocs/assets/js/html5shiv.js/index.html new file mode 100644 index 00000000..f488e849 --- /dev/null +++ b/htdocs/assets/js/html5shiv.js/index.html @@ -0,0 +1,7 @@ + +400 Bad Request + +

    400 Bad Request

    +
    nginx/1.4.4
    + + diff --git a/configs/nginx/auth/none.conf b/htdocs/assets/js/respond.min.js/index.html similarity index 100% rename from configs/nginx/auth/none.conf rename to htdocs/assets/js/respond.min.js/index.html diff --git a/htdocs/css/bootstrap-theme.css b/htdocs/css/bootstrap-theme.css new file mode 100644 index 00000000..df2d3d96 --- /dev/null +++ b/htdocs/css/bootstrap-theme.css @@ -0,0 +1,397 @@ +/*! + * Bootstrap v3.0.3 (http://getbootstrap.com) + * Copyright 2013 Twitter, Inc. + * Licensed under http://www.apache.org/licenses/LICENSE-2.0 + */ + +.btn-default, +.btn-primary, +.btn-success, +.btn-info, +.btn-warning, +.btn-danger { + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.btn-default:active, +.btn-primary:active, +.btn-success:active, +.btn-info:active, +.btn-warning:active, +.btn-danger:active, +.btn-default.active, +.btn-primary.active, +.btn-success.active, +.btn-info.active, +.btn-warning.active, +.btn-danger.active { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +} + +.btn:active, +.btn.active { + background-image: none; +} + +.btn-default { + text-shadow: 0 1px 0 #fff; + background-image: -webkit-linear-gradient(top, #ffffff 0%, #e0e0e0 100%); + background-image: linear-gradient(to bottom, #ffffff 0%, #e0e0e0 100%); + background-repeat: repeat-x; + border-color: #dbdbdb; + border-color: #ccc; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-default:hover, +.btn-default:focus { + background-color: #e0e0e0; + background-position: 0 -15px; +} + +.btn-default:active, +.btn-default.active { + background-color: #e0e0e0; + border-color: #dbdbdb; +} + +.btn-primary { + background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%); + background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%); + background-repeat: repeat-x; + border-color: #2b669a; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-primary:hover, +.btn-primary:focus { + background-color: #2d6ca2; + background-position: 0 -15px; +} + +.btn-primary:active, +.btn-primary.active { + background-color: #2d6ca2; + border-color: #2b669a; +} + +.btn-success { + background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); + background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); + background-repeat: repeat-x; + border-color: #3e8f3e; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-success:hover, +.btn-success:focus { + background-color: #419641; + background-position: 0 -15px; +} + +.btn-success:active, +.btn-success.active { + background-color: #419641; + border-color: #3e8f3e; +} + +.btn-warning { + background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); + background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); + background-repeat: repeat-x; + border-color: #e38d13; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-warning:hover, +.btn-warning:focus { + background-color: #eb9316; + background-position: 0 -15px; +} + +.btn-warning:active, +.btn-warning.active { + background-color: #eb9316; + border-color: #e38d13; +} + +.btn-danger { + background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); + background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); + background-repeat: repeat-x; + border-color: #b92c28; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-danger:hover, +.btn-danger:focus { + background-color: #c12e2a; + background-position: 0 -15px; +} + +.btn-danger:active, +.btn-danger.active { + background-color: #c12e2a; + border-color: #b92c28; +} + +.btn-info { + background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); + background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); + background-repeat: repeat-x; + border-color: #28a4c9; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-info:hover, +.btn-info:focus { + background-color: #2aabd2; + background-position: 0 -15px; +} + +.btn-info:active, +.btn-info.active { + background-color: #2aabd2; + border-color: #28a4c9; +} + +.thumbnail, +.img-thumbnail { + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); +} + +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + background-color: #e8e8e8; + background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); +} + +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + background-color: #357ebd; + background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); + background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); +} + +.navbar-default { + background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%); + background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%); + background-repeat: repeat-x; + border-radius: 4px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075); +} + +.navbar-default .navbar-nav > .active > a { + background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%); + background-image: linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0); + -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075); +} + +.navbar-brand, +.navbar-nav > li > a { + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25); +} + +.navbar-inverse { + background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222222 100%); + background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.navbar-inverse .navbar-nav > .active > a { + background-image: -webkit-linear-gradient(top, #222222 0%, #282828 100%); + background-image: linear-gradient(to bottom, #222222 0%, #282828 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0); + -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25); + box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25); +} + +.navbar-inverse .navbar-brand, +.navbar-inverse .navbar-nav > li > a { + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} + +.navbar-static-top, +.navbar-fixed-top, +.navbar-fixed-bottom { + border-radius: 0; +} + +.alert { + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.alert-success { + background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); + background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); + background-repeat: repeat-x; + border-color: #b2dba1; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); +} + +.alert-info { + background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); + background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); + background-repeat: repeat-x; + border-color: #9acfea; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); +} + +.alert-warning { + background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); + background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); + background-repeat: repeat-x; + border-color: #f5e79e; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); +} + +.alert-danger { + background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); + background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); + background-repeat: repeat-x; + border-color: #dca7a7; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); +} + +.progress { + background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); + background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); +} + +.progress-bar { + background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%); + background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0); +} + +.progress-bar-success { + background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); + background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); +} + +.progress-bar-info { + background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); + background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); +} + +.progress-bar-warning { + background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); + background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); +} + +.progress-bar-danger { + background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); + background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); +} + +.list-group { + border-radius: 4px; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); +} + +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + text-shadow: 0 -1px 0 #3071a9; + background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%); + background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%); + background-repeat: repeat-x; + border-color: #3278b3; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0); +} + +.panel { + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.panel-default > .panel-heading { + background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); +} + +.panel-primary > .panel-heading { + background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); + background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); +} + +.panel-success > .panel-heading { + background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); + background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); +} + +.panel-info > .panel-heading { + background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); + background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); +} + +.panel-warning > .panel-heading { + background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); + background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); +} + +.panel-danger > .panel-heading { + background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); + background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); +} + +.well { + background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); + background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); + background-repeat: repeat-x; + border-color: #dcdcdc; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); + -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1); +} \ No newline at end of file diff --git a/htdocs/css/bootstrap-theme.min.css b/htdocs/css/bootstrap-theme.min.css new file mode 100644 index 00000000..c7b6d39b --- /dev/null +++ b/htdocs/css/bootstrap-theme.min.css @@ -0,0 +1,7 @@ +/*! + * Bootstrap v3.0.3 (http://getbootstrap.com) + * Copyright 2013 Twitter, Inc. + * Licensed under http://www.apache.org/licenses/LICENSE-2.0 + */ + +.btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn:active,.btn.active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe0e0e0',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-default:hover,.btn-default:focus{background-color:#e0e0e0;background-position:0 -15px}.btn-default:active,.btn-default.active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-primary{background-image:-webkit-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:linear-gradient(to bottom,#428bca 0,#2d6ca2 100%);background-repeat:repeat-x;border-color:#2b669a;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff2d6ca2',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:focus{background-color:#2d6ca2;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#2d6ca2;border-color:#2b669a}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);background-repeat:repeat-x;border-color:#3e8f3e;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff419641',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:focus{background-color:#419641;background-position:0 -15px}.btn-success:active,.btn-success.active{background-color:#419641;border-color:#3e8f3e}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);background-repeat:repeat-x;border-color:#e38d13;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffeb9316',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:focus{background-color:#eb9316;background-position:0 -15px}.btn-warning:active,.btn-warning.active{background-color:#eb9316;border-color:#e38d13}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);background-repeat:repeat-x;border-color:#b92c28;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc12e2a',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:focus{background-color:#c12e2a;background-position:0 -15px}.btn-danger:active,.btn-danger.active{background-color:#c12e2a;border-color:#b92c28}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);background-repeat:repeat-x;border-color:#28a4c9;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2aabd2',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:focus{background-color:#2aabd2;background-position:0 -15px}.btn-info:active,.btn-info.active{background-color:#2aabd2;border-color:#28a4c9}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#ffe8e8e8',GradientType=0)}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-color:#357ebd;background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);background-repeat:repeat-x;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff8f8f8',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075)}.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f3f3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb',endColorstr='#fff3f3f3',GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.075);box-shadow:inset 0 3px 9px rgba(0,0,0,0.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,0.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#222 0,#282828 100%);background-image:linear-gradient(to bottom,#222 0,#282828 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff282828',GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.25);box-shadow:inset 0 3px 9px rgba(0,0,0,0.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);background-repeat:repeat-x;border-color:#b2dba1;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffc8e5bc',GradientType=0)}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);background-repeat:repeat-x;border-color:#9acfea;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffb9def0',GradientType=0)}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);background-repeat:repeat-x;border-color:#f5e79e;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fff8efc0',GradientType=0)}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);background-repeat:repeat-x;border-color:#dca7a7;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffe7c3c3',GradientType=0)}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb',endColorstr='#fff5f5f5',GradientType=0)}.progress-bar{background-image:-webkit-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3071a9',GradientType=0)}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff449d44',GradientType=0)}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff31b0d5',GradientType=0)}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffec971f',GradientType=0)}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc9302c',GradientType=0)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);background-repeat:repeat-x;border-color:#3278b3;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3278b3',GradientType=0)}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#ffe8e8e8',GradientType=0)}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffd0e9c6',GradientType=0)}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffc4e3f3',GradientType=0)}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fffaf2cc',GradientType=0)}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffebcccc',GradientType=0)}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);background-repeat:repeat-x;border-color:#dcdcdc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8',endColorstr='#fff5f5f5',GradientType=0);-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1)} \ No newline at end of file diff --git a/htdocs/css/bootstrap.css b/htdocs/css/bootstrap.css new file mode 100644 index 00000000..377dff30 --- /dev/null +++ b/htdocs/css/bootstrap.css @@ -0,0 +1,7118 @@ +/*! + * Bootstrap v3.0.3 (http://getbootstrap.com) + * Copyright 2013 Twitter, Inc. + * Licensed under http://www.apache.org/licenses/LICENSE-2.0 + */ + +/*! normalize.css v2.1.3 | MIT License | git.io/normalize */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section, +summary { + display: block; +} + +audio, +canvas, +video { + display: inline-block; +} + +audio:not([controls]) { + display: none; + height: 0; +} + +[hidden], +template { + display: none; +} + +html { + font-family: sans-serif; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} + +body { + margin: 0; +} + +a { + background: transparent; +} + +a:focus { + outline: thin dotted; +} + +a:active, +a:hover { + outline: 0; +} + +h1 { + margin: 0.67em 0; + font-size: 2em; +} + +abbr[title] { + border-bottom: 1px dotted; +} + +b, +strong { + font-weight: bold; +} + +dfn { + font-style: italic; +} + +hr { + height: 0; + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +mark { + color: #000; + background: #ff0; +} + +code, +kbd, +pre, +samp { + font-family: monospace, serif; + font-size: 1em; +} + +pre { + white-space: pre-wrap; +} + +q { + quotes: "\201C" "\201D" "\2018" "\2019"; +} + +small { + font-size: 80%; +} + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +img { + border: 0; +} + +svg:not(:root) { + overflow: hidden; +} + +figure { + margin: 0; +} + +fieldset { + padding: 0.35em 0.625em 0.75em; + margin: 0 2px; + border: 1px solid #c0c0c0; +} + +legend { + padding: 0; + border: 0; +} + +button, +input, +select, +textarea { + margin: 0; + font-family: inherit; + font-size: 100%; +} + +button, +input { + line-height: normal; +} + +button, +select { + text-transform: none; +} + +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + cursor: pointer; + -webkit-appearance: button; +} + +button[disabled], +html input[disabled] { + cursor: default; +} + +input[type="checkbox"], +input[type="radio"] { + padding: 0; + box-sizing: border-box; +} + +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} + +textarea { + overflow: auto; + vertical-align: top; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} + +@media print { + * { + color: #000 !important; + text-shadow: none !important; + background: transparent !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + a[href^="javascript:"]:after, + a[href^="#"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + @page { + margin: 2cm .5cm; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + select { + background: #fff !important; + } + .navbar { + display: none; + } + .table td, + .table th { + background-color: #fff !important; + } + .btn > .caret, + .dropup > .btn > .caret { + border-top-color: #000 !important; + } + .label { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #ddd !important; + } +} + +*, +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +html { + font-size: 62.5%; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.428571429; + color: #333333; + background-color: #ffffff; +} + +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +a { + color: #428bca; + text-decoration: none; +} + +a:hover, +a:focus { + color: #2a6496; + text-decoration: underline; +} + +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +img { + vertical-align: middle; +} + +.img-responsive { + display: block; + height: auto; + max-width: 100%; +} + +.img-rounded { + border-radius: 6px; +} + +.img-thumbnail { + display: inline-block; + height: auto; + max-width: 100%; + padding: 4px; + line-height: 1.428571429; + background-color: #ffffff; + border: 1px solid #dddddd; + border-radius: 4px; + -webkit-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} + +.img-circle { + border-radius: 50%; +} + +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eeeeee; +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} + +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 500; + line-height: 1.1; + color: inherit; +} + +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small, +h1 .small, +h2 .small, +h3 .small, +h4 .small, +h5 .small, +h6 .small, +.h1 .small, +.h2 .small, +.h3 .small, +.h4 .small, +.h5 .small, +.h6 .small { + font-weight: normal; + line-height: 1; + color: #999999; +} + +h1, +h2, +h3 { + margin-top: 20px; + margin-bottom: 10px; +} + +h1 small, +h2 small, +h3 small, +h1 .small, +h2 .small, +h3 .small { + font-size: 65%; +} + +h4, +h5, +h6 { + margin-top: 10px; + margin-bottom: 10px; +} + +h4 small, +h5 small, +h6 small, +h4 .small, +h5 .small, +h6 .small { + font-size: 75%; +} + +h1, +.h1 { + font-size: 36px; +} + +h2, +.h2 { + font-size: 30px; +} + +h3, +.h3 { + font-size: 24px; +} + +h4, +.h4 { + font-size: 18px; +} + +h5, +.h5 { + font-size: 14px; +} + +h6, +.h6 { + font-size: 12px; +} + +p { + margin: 0 0 10px; +} + +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 200; + line-height: 1.4; +} + +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} + +small, +.small { + font-size: 85%; +} + +cite { + font-style: normal; +} + +.text-muted { + color: #999999; +} + +.text-primary { + color: #428bca; +} + +.text-primary:hover { + color: #3071a9; +} + +.text-warning { + color: #8a6d3b; +} + +.text-warning:hover { + color: #66512c; +} + +.text-danger { + color: #a94442; +} + +.text-danger:hover { + color: #843534; +} + +.text-success { + color: #3c763d; +} + +.text-success:hover { + color: #2b542c; +} + +.text-info { + color: #31708f; +} + +.text-info:hover { + color: #245269; +} + +.text-left { + text-align: left; +} + +.text-right { + text-align: right; +} + +.text-center { + text-align: center; +} + +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eeeeee; +} + +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} + +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} + +.list-unstyled { + padding-left: 0; + list-style: none; +} + +.list-inline { + padding-left: 0; + list-style: none; +} + +.list-inline > li { + display: inline-block; + padding-right: 5px; + padding-left: 5px; +} + +.list-inline > li:first-child { + padding-left: 0; +} + +dl { + margin-top: 0; + margin-bottom: 20px; +} + +dt, +dd { + line-height: 1.428571429; +} + +dt { + font-weight: bold; +} + +dd { + margin-left: 0; +} + +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; + } + .dl-horizontal dd { + margin-left: 180px; + } + .dl-horizontal dd:before, + .dl-horizontal dd:after { + display: table; + content: " "; + } + .dl-horizontal dd:after { + clear: both; + } + .dl-horizontal dd:before, + .dl-horizontal dd:after { + display: table; + content: " "; + } + .dl-horizontal dd:after { + clear: both; + } +} + +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #999999; +} + +.initialism { + font-size: 90%; + text-transform: uppercase; +} + +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + border-left: 5px solid #eeeeee; +} + +blockquote p { + font-size: 17.5px; + font-weight: 300; + line-height: 1.25; +} + +blockquote p:last-child { + margin-bottom: 0; +} + +blockquote small, +blockquote .small { + display: block; + line-height: 1.428571429; + color: #999999; +} + +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} + +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + border-right: 5px solid #eeeeee; + border-left: 0; +} + +blockquote.pull-right p, +blockquote.pull-right small, +blockquote.pull-right .small { + text-align: right; +} + +blockquote.pull-right small:before, +blockquote.pull-right .small:before { + content: ''; +} + +blockquote.pull-right small:after, +blockquote.pull-right .small:after { + content: '\00A0 \2014'; +} + +blockquote:before, +blockquote:after { + content: ""; +} + +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.428571429; +} + +code, +kbd, +pre, +samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; +} + +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + white-space: nowrap; + background-color: #f9f2f4; + border-radius: 4px; +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.428571429; + color: #333333; + word-break: break-all; + word-wrap: break-word; + background-color: #f5f5f5; + border: 1px solid #cccccc; + border-radius: 4px; +} + +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; +} + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} + +.container { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} + +.container:before, +.container:after { + display: table; + content: " "; +} + +.container:after { + clear: both; +} + +.container:before, +.container:after { + display: table; + content: " "; +} + +.container:after { + clear: both; +} + +@media (min-width: 768px) { + .container { + width: 750px; + } +} + +@media (min-width: 992px) { + .container { + width: 970px; + } +} + +@media (min-width: 1200px) { + .container { + width: 1170px; + } +} + +.row { + margin-right: -15px; + margin-left: -15px; +} + +.row:before, +.row:after { + display: table; + content: " "; +} + +.row:after { + clear: both; +} + +.row:before, +.row:after { + display: table; + content: " "; +} + +.row:after { + clear: both; +} + +.col-xs-1, +.col-sm-1, +.col-md-1, +.col-lg-1, +.col-xs-2, +.col-sm-2, +.col-md-2, +.col-lg-2, +.col-xs-3, +.col-sm-3, +.col-md-3, +.col-lg-3, +.col-xs-4, +.col-sm-4, +.col-md-4, +.col-lg-4, +.col-xs-5, +.col-sm-5, +.col-md-5, +.col-lg-5, +.col-xs-6, +.col-sm-6, +.col-md-6, +.col-lg-6, +.col-xs-7, +.col-sm-7, +.col-md-7, +.col-lg-7, +.col-xs-8, +.col-sm-8, +.col-md-8, +.col-lg-8, +.col-xs-9, +.col-sm-9, +.col-md-9, +.col-lg-9, +.col-xs-10, +.col-sm-10, +.col-md-10, +.col-lg-10, +.col-xs-11, +.col-sm-11, +.col-md-11, +.col-lg-11, +.col-xs-12, +.col-sm-12, +.col-md-12, +.col-lg-12 { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} + +.col-xs-1, +.col-xs-2, +.col-xs-3, +.col-xs-4, +.col-xs-5, +.col-xs-6, +.col-xs-7, +.col-xs-8, +.col-xs-9, +.col-xs-10, +.col-xs-11, +.col-xs-12 { + float: left; +} + +.col-xs-12 { + width: 100%; +} + +.col-xs-11 { + width: 91.66666666666666%; +} + +.col-xs-10 { + width: 83.33333333333334%; +} + +.col-xs-9 { + width: 75%; +} + +.col-xs-8 { + width: 66.66666666666666%; +} + +.col-xs-7 { + width: 58.333333333333336%; +} + +.col-xs-6 { + width: 50%; +} + +.col-xs-5 { + width: 41.66666666666667%; +} + +.col-xs-4 { + width: 33.33333333333333%; +} + +.col-xs-3 { + width: 25%; +} + +.col-xs-2 { + width: 16.666666666666664%; +} + +.col-xs-1 { + width: 8.333333333333332%; +} + +.col-xs-pull-12 { + right: 100%; +} + +.col-xs-pull-11 { + right: 91.66666666666666%; +} + +.col-xs-pull-10 { + right: 83.33333333333334%; +} + +.col-xs-pull-9 { + right: 75%; +} + +.col-xs-pull-8 { + right: 66.66666666666666%; +} + +.col-xs-pull-7 { + right: 58.333333333333336%; +} + +.col-xs-pull-6 { + right: 50%; +} + +.col-xs-pull-5 { + right: 41.66666666666667%; +} + +.col-xs-pull-4 { + right: 33.33333333333333%; +} + +.col-xs-pull-3 { + right: 25%; +} + +.col-xs-pull-2 { + right: 16.666666666666664%; +} + +.col-xs-pull-1 { + right: 8.333333333333332%; +} + +.col-xs-pull-0 { + right: 0; +} + +.col-xs-push-12 { + left: 100%; +} + +.col-xs-push-11 { + left: 91.66666666666666%; +} + +.col-xs-push-10 { + left: 83.33333333333334%; +} + +.col-xs-push-9 { + left: 75%; +} + +.col-xs-push-8 { + left: 66.66666666666666%; +} + +.col-xs-push-7 { + left: 58.333333333333336%; +} + +.col-xs-push-6 { + left: 50%; +} + +.col-xs-push-5 { + left: 41.66666666666667%; +} + +.col-xs-push-4 { + left: 33.33333333333333%; +} + +.col-xs-push-3 { + left: 25%; +} + +.col-xs-push-2 { + left: 16.666666666666664%; +} + +.col-xs-push-1 { + left: 8.333333333333332%; +} + +.col-xs-push-0 { + left: 0; +} + +.col-xs-offset-12 { + margin-left: 100%; +} + +.col-xs-offset-11 { + margin-left: 91.66666666666666%; +} + +.col-xs-offset-10 { + margin-left: 83.33333333333334%; +} + +.col-xs-offset-9 { + margin-left: 75%; +} + +.col-xs-offset-8 { + margin-left: 66.66666666666666%; +} + +.col-xs-offset-7 { + margin-left: 58.333333333333336%; +} + +.col-xs-offset-6 { + margin-left: 50%; +} + +.col-xs-offset-5 { + margin-left: 41.66666666666667%; +} + +.col-xs-offset-4 { + margin-left: 33.33333333333333%; +} + +.col-xs-offset-3 { + margin-left: 25%; +} + +.col-xs-offset-2 { + margin-left: 16.666666666666664%; +} + +.col-xs-offset-1 { + margin-left: 8.333333333333332%; +} + +.col-xs-offset-0 { + margin-left: 0; +} + +@media (min-width: 768px) { + .col-sm-1, + .col-sm-2, + .col-sm-3, + .col-sm-4, + .col-sm-5, + .col-sm-6, + .col-sm-7, + .col-sm-8, + .col-sm-9, + .col-sm-10, + .col-sm-11, + .col-sm-12 { + float: left; + } + .col-sm-12 { + width: 100%; + } + .col-sm-11 { + width: 91.66666666666666%; + } + .col-sm-10 { + width: 83.33333333333334%; + } + .col-sm-9 { + width: 75%; + } + .col-sm-8 { + width: 66.66666666666666%; + } + .col-sm-7 { + width: 58.333333333333336%; + } + .col-sm-6 { + width: 50%; + } + .col-sm-5 { + width: 41.66666666666667%; + } + .col-sm-4 { + width: 33.33333333333333%; + } + .col-sm-3 { + width: 25%; + } + .col-sm-2 { + width: 16.666666666666664%; + } + .col-sm-1 { + width: 8.333333333333332%; + } + .col-sm-pull-12 { + right: 100%; + } + .col-sm-pull-11 { + right: 91.66666666666666%; + } + .col-sm-pull-10 { + right: 83.33333333333334%; + } + .col-sm-pull-9 { + right: 75%; + } + .col-sm-pull-8 { + right: 66.66666666666666%; + } + .col-sm-pull-7 { + right: 58.333333333333336%; + } + .col-sm-pull-6 { + right: 50%; + } + .col-sm-pull-5 { + right: 41.66666666666667%; + } + .col-sm-pull-4 { + right: 33.33333333333333%; + } + .col-sm-pull-3 { + right: 25%; + } + .col-sm-pull-2 { + right: 16.666666666666664%; + } + .col-sm-pull-1 { + right: 8.333333333333332%; + } + .col-sm-pull-0 { + right: 0; + } + .col-sm-push-12 { + left: 100%; + } + .col-sm-push-11 { + left: 91.66666666666666%; + } + .col-sm-push-10 { + left: 83.33333333333334%; + } + .col-sm-push-9 { + left: 75%; + } + .col-sm-push-8 { + left: 66.66666666666666%; + } + .col-sm-push-7 { + left: 58.333333333333336%; + } + .col-sm-push-6 { + left: 50%; + } + .col-sm-push-5 { + left: 41.66666666666667%; + } + .col-sm-push-4 { + left: 33.33333333333333%; + } + .col-sm-push-3 { + left: 25%; + } + .col-sm-push-2 { + left: 16.666666666666664%; + } + .col-sm-push-1 { + left: 8.333333333333332%; + } + .col-sm-push-0 { + left: 0; + } + .col-sm-offset-12 { + margin-left: 100%; + } + .col-sm-offset-11 { + margin-left: 91.66666666666666%; + } + .col-sm-offset-10 { + margin-left: 83.33333333333334%; + } + .col-sm-offset-9 { + margin-left: 75%; + } + .col-sm-offset-8 { + margin-left: 66.66666666666666%; + } + .col-sm-offset-7 { + margin-left: 58.333333333333336%; + } + .col-sm-offset-6 { + margin-left: 50%; + } + .col-sm-offset-5 { + margin-left: 41.66666666666667%; + } + .col-sm-offset-4 { + margin-left: 33.33333333333333%; + } + .col-sm-offset-3 { + margin-left: 25%; + } + .col-sm-offset-2 { + margin-left: 16.666666666666664%; + } + .col-sm-offset-1 { + margin-left: 8.333333333333332%; + } + .col-sm-offset-0 { + margin-left: 0; + } +} + +@media (min-width: 992px) { + .col-md-1, + .col-md-2, + .col-md-3, + .col-md-4, + .col-md-5, + .col-md-6, + .col-md-7, + .col-md-8, + .col-md-9, + .col-md-10, + .col-md-11, + .col-md-12 { + float: left; + } + .col-md-12 { + width: 100%; + } + .col-md-11 { + width: 91.66666666666666%; + } + .col-md-10 { + width: 83.33333333333334%; + } + .col-md-9 { + width: 75%; + } + .col-md-8 { + width: 66.66666666666666%; + } + .col-md-7 { + width: 58.333333333333336%; + } + .col-md-6 { + width: 50%; + } + .col-md-5 { + width: 41.66666666666667%; + } + .col-md-4 { + width: 33.33333333333333%; + } + .col-md-3 { + width: 25%; + } + .col-md-2 { + width: 16.666666666666664%; + } + .col-md-1 { + width: 8.333333333333332%; + } + .col-md-pull-12 { + right: 100%; + } + .col-md-pull-11 { + right: 91.66666666666666%; + } + .col-md-pull-10 { + right: 83.33333333333334%; + } + .col-md-pull-9 { + right: 75%; + } + .col-md-pull-8 { + right: 66.66666666666666%; + } + .col-md-pull-7 { + right: 58.333333333333336%; + } + .col-md-pull-6 { + right: 50%; + } + .col-md-pull-5 { + right: 41.66666666666667%; + } + .col-md-pull-4 { + right: 33.33333333333333%; + } + .col-md-pull-3 { + right: 25%; + } + .col-md-pull-2 { + right: 16.666666666666664%; + } + .col-md-pull-1 { + right: 8.333333333333332%; + } + .col-md-pull-0 { + right: 0; + } + .col-md-push-12 { + left: 100%; + } + .col-md-push-11 { + left: 91.66666666666666%; + } + .col-md-push-10 { + left: 83.33333333333334%; + } + .col-md-push-9 { + left: 75%; + } + .col-md-push-8 { + left: 66.66666666666666%; + } + .col-md-push-7 { + left: 58.333333333333336%; + } + .col-md-push-6 { + left: 50%; + } + .col-md-push-5 { + left: 41.66666666666667%; + } + .col-md-push-4 { + left: 33.33333333333333%; + } + .col-md-push-3 { + left: 25%; + } + .col-md-push-2 { + left: 16.666666666666664%; + } + .col-md-push-1 { + left: 8.333333333333332%; + } + .col-md-push-0 { + left: 0; + } + .col-md-offset-12 { + margin-left: 100%; + } + .col-md-offset-11 { + margin-left: 91.66666666666666%; + } + .col-md-offset-10 { + margin-left: 83.33333333333334%; + } + .col-md-offset-9 { + margin-left: 75%; + } + .col-md-offset-8 { + margin-left: 66.66666666666666%; + } + .col-md-offset-7 { + margin-left: 58.333333333333336%; + } + .col-md-offset-6 { + margin-left: 50%; + } + .col-md-offset-5 { + margin-left: 41.66666666666667%; + } + .col-md-offset-4 { + margin-left: 33.33333333333333%; + } + .col-md-offset-3 { + margin-left: 25%; + } + .col-md-offset-2 { + margin-left: 16.666666666666664%; + } + .col-md-offset-1 { + margin-left: 8.333333333333332%; + } + .col-md-offset-0 { + margin-left: 0; + } +} + +@media (min-width: 1200px) { + .col-lg-1, + .col-lg-2, + .col-lg-3, + .col-lg-4, + .col-lg-5, + .col-lg-6, + .col-lg-7, + .col-lg-8, + .col-lg-9, + .col-lg-10, + .col-lg-11, + .col-lg-12 { + float: left; + } + .col-lg-12 { + width: 100%; + } + .col-lg-11 { + width: 91.66666666666666%; + } + .col-lg-10 { + width: 83.33333333333334%; + } + .col-lg-9 { + width: 75%; + } + .col-lg-8 { + width: 66.66666666666666%; + } + .col-lg-7 { + width: 58.333333333333336%; + } + .col-lg-6 { + width: 50%; + } + .col-lg-5 { + width: 41.66666666666667%; + } + .col-lg-4 { + width: 33.33333333333333%; + } + .col-lg-3 { + width: 25%; + } + .col-lg-2 { + width: 16.666666666666664%; + } + .col-lg-1 { + width: 8.333333333333332%; + } + .col-lg-pull-12 { + right: 100%; + } + .col-lg-pull-11 { + right: 91.66666666666666%; + } + .col-lg-pull-10 { + right: 83.33333333333334%; + } + .col-lg-pull-9 { + right: 75%; + } + .col-lg-pull-8 { + right: 66.66666666666666%; + } + .col-lg-pull-7 { + right: 58.333333333333336%; + } + .col-lg-pull-6 { + right: 50%; + } + .col-lg-pull-5 { + right: 41.66666666666667%; + } + .col-lg-pull-4 { + right: 33.33333333333333%; + } + .col-lg-pull-3 { + right: 25%; + } + .col-lg-pull-2 { + right: 16.666666666666664%; + } + .col-lg-pull-1 { + right: 8.333333333333332%; + } + .col-lg-pull-0 { + right: 0; + } + .col-lg-push-12 { + left: 100%; + } + .col-lg-push-11 { + left: 91.66666666666666%; + } + .col-lg-push-10 { + left: 83.33333333333334%; + } + .col-lg-push-9 { + left: 75%; + } + .col-lg-push-8 { + left: 66.66666666666666%; + } + .col-lg-push-7 { + left: 58.333333333333336%; + } + .col-lg-push-6 { + left: 50%; + } + .col-lg-push-5 { + left: 41.66666666666667%; + } + .col-lg-push-4 { + left: 33.33333333333333%; + } + .col-lg-push-3 { + left: 25%; + } + .col-lg-push-2 { + left: 16.666666666666664%; + } + .col-lg-push-1 { + left: 8.333333333333332%; + } + .col-lg-push-0 { + left: 0; + } + .col-lg-offset-12 { + margin-left: 100%; + } + .col-lg-offset-11 { + margin-left: 91.66666666666666%; + } + .col-lg-offset-10 { + margin-left: 83.33333333333334%; + } + .col-lg-offset-9 { + margin-left: 75%; + } + .col-lg-offset-8 { + margin-left: 66.66666666666666%; + } + .col-lg-offset-7 { + margin-left: 58.333333333333336%; + } + .col-lg-offset-6 { + margin-left: 50%; + } + .col-lg-offset-5 { + margin-left: 41.66666666666667%; + } + .col-lg-offset-4 { + margin-left: 33.33333333333333%; + } + .col-lg-offset-3 { + margin-left: 25%; + } + .col-lg-offset-2 { + margin-left: 16.666666666666664%; + } + .col-lg-offset-1 { + margin-left: 8.333333333333332%; + } + .col-lg-offset-0 { + margin-left: 0; + } +} + +table { + max-width: 100%; + background-color: transparent; +} + +th { + text-align: left; +} + +.table { + width: 100%; + margin-bottom: 20px; +} + +.table > thead > tr > th, +.table > tbody > tr > th, +.table > tfoot > tr > th, +.table > thead > tr > td, +.table > tbody > tr > td, +.table > tfoot > tr > td { + padding: 8px; + line-height: 1.428571429; + vertical-align: top; + border-top: 1px solid #dddddd; +} + +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #dddddd; +} + +.table > caption + thead > tr:first-child > th, +.table > colgroup + thead > tr:first-child > th, +.table > thead:first-child > tr:first-child > th, +.table > caption + thead > tr:first-child > td, +.table > colgroup + thead > tr:first-child > td, +.table > thead:first-child > tr:first-child > td { + border-top: 0; +} + +.table > tbody + tbody { + border-top: 2px solid #dddddd; +} + +.table .table { + background-color: #ffffff; +} + +.table-condensed > thead > tr > th, +.table-condensed > tbody > tr > th, +.table-condensed > tfoot > tr > th, +.table-condensed > thead > tr > td, +.table-condensed > tbody > tr > td, +.table-condensed > tfoot > tr > td { + padding: 5px; +} + +.table-bordered { + border: 1px solid #dddddd; +} + +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #dddddd; +} + +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} + +.table-striped > tbody > tr:nth-child(odd) > td, +.table-striped > tbody > tr:nth-child(odd) > th { + background-color: #f9f9f9; +} + +.table-hover > tbody > tr:hover > td, +.table-hover > tbody > tr:hover > th { + background-color: #f5f5f5; +} + +table col[class*="col-"] { + position: static; + display: table-column; + float: none; +} + +table td[class*="col-"], +table th[class*="col-"] { + display: table-cell; + float: none; +} + +.table > thead > tr > .active, +.table > tbody > tr > .active, +.table > tfoot > tr > .active, +.table > thead > .active > td, +.table > tbody > .active > td, +.table > tfoot > .active > td, +.table > thead > .active > th, +.table > tbody > .active > th, +.table > tfoot > .active > th { + background-color: #f5f5f5; +} + +.table-hover > tbody > tr > .active:hover, +.table-hover > tbody > .active:hover > td, +.table-hover > tbody > .active:hover > th { + background-color: #e8e8e8; +} + +.table > thead > tr > .success, +.table > tbody > tr > .success, +.table > tfoot > tr > .success, +.table > thead > .success > td, +.table > tbody > .success > td, +.table > tfoot > .success > td, +.table > thead > .success > th, +.table > tbody > .success > th, +.table > tfoot > .success > th { + background-color: #dff0d8; +} + +.table-hover > tbody > tr > .success:hover, +.table-hover > tbody > .success:hover > td, +.table-hover > tbody > .success:hover > th { + background-color: #d0e9c6; +} + +.table > thead > tr > .danger, +.table > tbody > tr > .danger, +.table > tfoot > tr > .danger, +.table > thead > .danger > td, +.table > tbody > .danger > td, +.table > tfoot > .danger > td, +.table > thead > .danger > th, +.table > tbody > .danger > th, +.table > tfoot > .danger > th { + background-color: #f2dede; +} + +.table-hover > tbody > tr > .danger:hover, +.table-hover > tbody > .danger:hover > td, +.table-hover > tbody > .danger:hover > th { + background-color: #ebcccc; +} + +.table > thead > tr > .warning, +.table > tbody > tr > .warning, +.table > tfoot > tr > .warning, +.table > thead > .warning > td, +.table > tbody > .warning > td, +.table > tfoot > .warning > td, +.table > thead > .warning > th, +.table > tbody > .warning > th, +.table > tfoot > .warning > th { + background-color: #fcf8e3; +} + +.table-hover > tbody > tr > .warning:hover, +.table-hover > tbody > .warning:hover > td, +.table-hover > tbody > .warning:hover > th { + background-color: #faf2cc; +} + +@media (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-x: scroll; + overflow-y: hidden; + border: 1px solid #dddddd; + -ms-overflow-style: -ms-autohiding-scrollbar; + -webkit-overflow-scrolling: touch; + } + .table-responsive > .table { + margin-bottom: 0; + } + .table-responsive > .table > thead > tr > th, + .table-responsive > .table > tbody > tr > th, + .table-responsive > .table > tfoot > tr > th, + .table-responsive > .table > thead > tr > td, + .table-responsive > .table > tbody > tr > td, + .table-responsive > .table > tfoot > tr > td { + white-space: nowrap; + } + .table-responsive > .table-bordered { + border: 0; + } + .table-responsive > .table-bordered > thead > tr > th:first-child, + .table-responsive > .table-bordered > tbody > tr > th:first-child, + .table-responsive > .table-bordered > tfoot > tr > th:first-child, + .table-responsive > .table-bordered > thead > tr > td:first-child, + .table-responsive > .table-bordered > tbody > tr > td:first-child, + .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; + } + .table-responsive > .table-bordered > thead > tr > th:last-child, + .table-responsive > .table-bordered > tbody > tr > th:last-child, + .table-responsive > .table-bordered > tfoot > tr > th:last-child, + .table-responsive > .table-bordered > thead > tr > td:last-child, + .table-responsive > .table-bordered > tbody > tr > td:last-child, + .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; + } + .table-responsive > .table-bordered > tbody > tr:last-child > th, + .table-responsive > .table-bordered > tfoot > tr:last-child > th, + .table-responsive > .table-bordered > tbody > tr:last-child > td, + .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; + } +} + +fieldset { + padding: 0; + margin: 0; + border: 0; +} + +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} + +label { + display: inline-block; + margin-bottom: 5px; + font-weight: bold; +} + +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + /* IE8-9 */ + + line-height: normal; +} + +input[type="file"] { + display: block; +} + +select[multiple], +select[size] { + height: auto; +} + +select optgroup { + font-family: inherit; + font-size: inherit; + font-style: inherit; +} + +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +input[type="number"]::-webkit-outer-spin-button, +input[type="number"]::-webkit-inner-spin-button { + height: auto; +} + +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.428571429; + color: #555555; + vertical-align: middle; +} + +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.428571429; + color: #555555; + vertical-align: middle; + background-color: #ffffff; + background-image: none; + border: 1px solid #cccccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; + transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; +} + +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); +} + +.form-control:-moz-placeholder { + color: #999999; +} + +.form-control::-moz-placeholder { + color: #999999; + opacity: 1; +} + +.form-control:-ms-input-placeholder { + color: #999999; +} + +.form-control::-webkit-input-placeholder { + color: #999999; +} + +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + cursor: not-allowed; + background-color: #eeeeee; +} + +textarea.form-control { + height: auto; +} + +.form-group { + margin-bottom: 15px; +} + +.radio, +.checkbox { + display: block; + min-height: 20px; + padding-left: 20px; + margin-top: 10px; + margin-bottom: 10px; + vertical-align: middle; +} + +.radio label, +.checkbox label { + display: inline; + margin-bottom: 0; + font-weight: normal; + cursor: pointer; +} + +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + float: left; + margin-left: -20px; +} + +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} + +.radio-inline, +.checkbox-inline { + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + vertical-align: middle; + cursor: pointer; +} + +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} + +input[type="radio"][disabled], +input[type="checkbox"][disabled], +.radio[disabled], +.radio-inline[disabled], +.checkbox[disabled], +.checkbox-inline[disabled], +fieldset[disabled] input[type="radio"], +fieldset[disabled] input[type="checkbox"], +fieldset[disabled] .radio, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} + +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +select.input-sm { + height: 30px; + line-height: 30px; +} + +textarea.input-sm { + height: auto; +} + +.input-lg { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} + +select.input-lg { + height: 46px; + line-height: 46px; +} + +textarea.input-lg { + height: auto; +} + +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline { + color: #8a6d3b; +} + +.has-warning .form-control { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.has-warning .form-control:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; +} + +.has-warning .input-group-addon { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #8a6d3b; +} + +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline { + color: #a94442; +} + +.has-error .form-control { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.has-error .form-control:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; +} + +.has-error .input-group-addon { + color: #a94442; + background-color: #f2dede; + border-color: #a94442; +} + +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline { + color: #3c763d; +} + +.has-success .form-control { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.has-success .form-control:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; +} + +.has-success .input-group-addon { + color: #3c763d; + background-color: #dff0d8; + border-color: #3c763d; +} + +.form-control-static { + margin-bottom: 0; +} + +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373; +} + +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .form-control { + display: inline-block; + } + .form-inline select.form-control { + width: auto; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + padding-left: 0; + margin-top: 0; + margin-bottom: 0; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + float: none; + margin-left: 0; + } +} + +.form-horizontal .control-label, +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + padding-top: 7px; + margin-top: 0; + margin-bottom: 0; +} + +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 27px; +} + +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px; +} + +.form-horizontal .form-group:before, +.form-horizontal .form-group:after { + display: table; + content: " "; +} + +.form-horizontal .form-group:after { + clear: both; +} + +.form-horizontal .form-group:before, +.form-horizontal .form-group:after { + display: table; + content: " "; +} + +.form-horizontal .form-group:after { + clear: both; +} + +.form-horizontal .form-control-static { + padding-top: 7px; +} + +@media (min-width: 768px) { + .form-horizontal .control-label { + text-align: right; + } +} + +.btn { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: normal; + line-height: 1.428571429; + text-align: center; + white-space: nowrap; + vertical-align: middle; + cursor: pointer; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + -o-user-select: none; + user-select: none; +} + +.btn:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.btn:hover, +.btn:focus { + color: #333333; + text-decoration: none; +} + +.btn:active, +.btn.active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +} + +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + pointer-events: none; + cursor: not-allowed; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; +} + +.btn-default { + color: #333333; + background-color: #ffffff; + border-color: #cccccc; +} + +.btn-default:hover, +.btn-default:focus, +.btn-default:active, +.btn-default.active, +.open .dropdown-toggle.btn-default { + color: #333333; + background-color: #ebebeb; + border-color: #adadad; +} + +.btn-default:active, +.btn-default.active, +.open .dropdown-toggle.btn-default { + background-image: none; +} + +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + background-color: #ffffff; + border-color: #cccccc; +} + +.btn-default .badge { + color: #ffffff; + background-color: #fff; +} + +.btn-primary { + color: #ffffff; + background-color: #428bca; + border-color: #357ebd; +} + +.btn-primary:hover, +.btn-primary:focus, +.btn-primary:active, +.btn-primary.active, +.open .dropdown-toggle.btn-primary { + color: #ffffff; + background-color: #3276b1; + border-color: #285e8e; +} + +.btn-primary:active, +.btn-primary.active, +.open .dropdown-toggle.btn-primary { + background-image: none; +} + +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + background-color: #428bca; + border-color: #357ebd; +} + +.btn-primary .badge { + color: #428bca; + background-color: #fff; +} + +.btn-warning { + color: #ffffff; + background-color: #f0ad4e; + border-color: #eea236; +} + +.btn-warning:hover, +.btn-warning:focus, +.btn-warning:active, +.btn-warning.active, +.open .dropdown-toggle.btn-warning { + color: #ffffff; + background-color: #ed9c28; + border-color: #d58512; +} + +.btn-warning:active, +.btn-warning.active, +.open .dropdown-toggle.btn-warning { + background-image: none; +} + +.btn-warning.disabled, +.btn-warning[disabled], +fieldset[disabled] .btn-warning, +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled:active, +.btn-warning[disabled]:active, +fieldset[disabled] .btn-warning:active, +.btn-warning.disabled.active, +.btn-warning[disabled].active, +fieldset[disabled] .btn-warning.active { + background-color: #f0ad4e; + border-color: #eea236; +} + +.btn-warning .badge { + color: #f0ad4e; + background-color: #fff; +} + +.btn-danger { + color: #ffffff; + background-color: #d9534f; + border-color: #d43f3a; +} + +.btn-danger:hover, +.btn-danger:focus, +.btn-danger:active, +.btn-danger.active, +.open .dropdown-toggle.btn-danger { + color: #ffffff; + background-color: #d2322d; + border-color: #ac2925; +} + +.btn-danger:active, +.btn-danger.active, +.open .dropdown-toggle.btn-danger { + background-image: none; +} + +.btn-danger.disabled, +.btn-danger[disabled], +fieldset[disabled] .btn-danger, +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled:active, +.btn-danger[disabled]:active, +fieldset[disabled] .btn-danger:active, +.btn-danger.disabled.active, +.btn-danger[disabled].active, +fieldset[disabled] .btn-danger.active { + background-color: #d9534f; + border-color: #d43f3a; +} + +.btn-danger .badge { + color: #d9534f; + background-color: #fff; +} + +.btn-success { + color: #ffffff; + background-color: #5cb85c; + border-color: #4cae4c; +} + +.btn-success:hover, +.btn-success:focus, +.btn-success:active, +.btn-success.active, +.open .dropdown-toggle.btn-success { + color: #ffffff; + background-color: #47a447; + border-color: #398439; +} + +.btn-success:active, +.btn-success.active, +.open .dropdown-toggle.btn-success { + background-image: none; +} + +.btn-success.disabled, +.btn-success[disabled], +fieldset[disabled] .btn-success, +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled:active, +.btn-success[disabled]:active, +fieldset[disabled] .btn-success:active, +.btn-success.disabled.active, +.btn-success[disabled].active, +fieldset[disabled] .btn-success.active { + background-color: #5cb85c; + border-color: #4cae4c; +} + +.btn-success .badge { + color: #5cb85c; + background-color: #fff; +} + +.btn-info { + color: #ffffff; + background-color: #5bc0de; + border-color: #46b8da; +} + +.btn-info:hover, +.btn-info:focus, +.btn-info:active, +.btn-info.active, +.open .dropdown-toggle.btn-info { + color: #ffffff; + background-color: #39b3d7; + border-color: #269abc; +} + +.btn-info:active, +.btn-info.active, +.open .dropdown-toggle.btn-info { + background-image: none; +} + +.btn-info.disabled, +.btn-info[disabled], +fieldset[disabled] .btn-info, +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled:active, +.btn-info[disabled]:active, +fieldset[disabled] .btn-info:active, +.btn-info.disabled.active, +.btn-info[disabled].active, +fieldset[disabled] .btn-info.active { + background-color: #5bc0de; + border-color: #46b8da; +} + +.btn-info .badge { + color: #5bc0de; + background-color: #fff; +} + +.btn-link { + font-weight: normal; + color: #428bca; + cursor: pointer; + border-radius: 0; +} + +.btn-link, +.btn-link:active, +.btn-link[disabled], +fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} + +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; +} + +.btn-link:hover, +.btn-link:focus { + color: #2a6496; + text-decoration: underline; + background-color: transparent; +} + +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #999999; + text-decoration: none; +} + +.btn-lg { + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} + +.btn-sm { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +.btn-xs { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +.btn-block { + display: block; + width: 100%; + padding-right: 0; + padding-left: 0; +} + +.btn-block + .btn-block { + margin-top: 5px; +} + +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} + +.fade { + opacity: 0; + -webkit-transition: opacity 0.15s linear; + transition: opacity 0.15s linear; +} + +.fade.in { + opacity: 1; +} + +.collapse { + display: none; +} + +.collapse.in { + display: block; +} + +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition: height 0.35s ease; + transition: height 0.35s ease; +} + +@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.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg'); +} + +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + -webkit-font-smoothing: antialiased; + font-style: normal; + font-weight: normal; + line-height: 1; + -moz-osx-font-smoothing: grayscale; +} + +.glyphicon:empty { + width: 1em; +} + +.glyphicon-asterisk:before { + content: "\2a"; +} + +.glyphicon-plus:before { + content: "\2b"; +} + +.glyphicon-euro:before { + content: "\20ac"; +} + +.glyphicon-minus:before { + content: "\2212"; +} + +.glyphicon-cloud:before { + content: "\2601"; +} + +.glyphicon-envelope:before { + content: "\2709"; +} + +.glyphicon-pencil:before { + content: "\270f"; +} + +.glyphicon-glass:before { + content: "\e001"; +} + +.glyphicon-music:before { + content: "\e002"; +} + +.glyphicon-search:before { + content: "\e003"; +} + +.glyphicon-heart:before { + content: "\e005"; +} + +.glyphicon-star:before { + content: "\e006"; +} + +.glyphicon-star-empty:before { + content: "\e007"; +} + +.glyphicon-user:before { + content: "\e008"; +} + +.glyphicon-film:before { + content: "\e009"; +} + +.glyphicon-th-large:before { + content: "\e010"; +} + +.glyphicon-th:before { + content: "\e011"; +} + +.glyphicon-th-list:before { + content: "\e012"; +} + +.glyphicon-ok:before { + content: "\e013"; +} + +.glyphicon-remove:before { + content: "\e014"; +} + +.glyphicon-zoom-in:before { + content: "\e015"; +} + +.glyphicon-zoom-out:before { + content: "\e016"; +} + +.glyphicon-off:before { + content: "\e017"; +} + +.glyphicon-signal:before { + content: "\e018"; +} + +.glyphicon-cog:before { + content: "\e019"; +} + +.glyphicon-trash:before { + content: "\e020"; +} + +.glyphicon-home:before { + content: "\e021"; +} + +.glyphicon-file:before { + content: "\e022"; +} + +.glyphicon-time:before { + content: "\e023"; +} + +.glyphicon-road:before { + content: "\e024"; +} + +.glyphicon-download-alt:before { + content: "\e025"; +} + +.glyphicon-download:before { + content: "\e026"; +} + +.glyphicon-upload:before { + content: "\e027"; +} + +.glyphicon-inbox:before { + content: "\e028"; +} + +.glyphicon-play-circle:before { + content: "\e029"; +} + +.glyphicon-repeat:before { + content: "\e030"; +} + +.glyphicon-refresh:before { + content: "\e031"; +} + +.glyphicon-list-alt:before { + content: "\e032"; +} + +.glyphicon-lock:before { + content: "\e033"; +} + +.glyphicon-flag:before { + content: "\e034"; +} + +.glyphicon-headphones:before { + content: "\e035"; +} + +.glyphicon-volume-off:before { + content: "\e036"; +} + +.glyphicon-volume-down:before { + content: "\e037"; +} + +.glyphicon-volume-up:before { + content: "\e038"; +} + +.glyphicon-qrcode:before { + content: "\e039"; +} + +.glyphicon-barcode:before { + content: "\e040"; +} + +.glyphicon-tag:before { + content: "\e041"; +} + +.glyphicon-tags:before { + content: "\e042"; +} + +.glyphicon-book:before { + content: "\e043"; +} + +.glyphicon-bookmark:before { + content: "\e044"; +} + +.glyphicon-print:before { + content: "\e045"; +} + +.glyphicon-camera:before { + content: "\e046"; +} + +.glyphicon-font:before { + content: "\e047"; +} + +.glyphicon-bold:before { + content: "\e048"; +} + +.glyphicon-italic:before { + content: "\e049"; +} + +.glyphicon-text-height:before { + content: "\e050"; +} + +.glyphicon-text-width:before { + content: "\e051"; +} + +.glyphicon-align-left:before { + content: "\e052"; +} + +.glyphicon-align-center:before { + content: "\e053"; +} + +.glyphicon-align-right:before { + content: "\e054"; +} + +.glyphicon-align-justify:before { + content: "\e055"; +} + +.glyphicon-list:before { + content: "\e056"; +} + +.glyphicon-indent-left:before { + content: "\e057"; +} + +.glyphicon-indent-right:before { + content: "\e058"; +} + +.glyphicon-facetime-video:before { + content: "\e059"; +} + +.glyphicon-picture:before { + content: "\e060"; +} + +.glyphicon-map-marker:before { + content: "\e062"; +} + +.glyphicon-adjust:before { + content: "\e063"; +} + +.glyphicon-tint:before { + content: "\e064"; +} + +.glyphicon-edit:before { + content: "\e065"; +} + +.glyphicon-share:before { + content: "\e066"; +} + +.glyphicon-check:before { + content: "\e067"; +} + +.glyphicon-move:before { + content: "\e068"; +} + +.glyphicon-step-backward:before { + content: "\e069"; +} + +.glyphicon-fast-backward:before { + content: "\e070"; +} + +.glyphicon-backward:before { + content: "\e071"; +} + +.glyphicon-play:before { + content: "\e072"; +} + +.glyphicon-pause:before { + content: "\e073"; +} + +.glyphicon-stop:before { + content: "\e074"; +} + +.glyphicon-forward:before { + content: "\e075"; +} + +.glyphicon-fast-forward:before { + content: "\e076"; +} + +.glyphicon-step-forward:before { + content: "\e077"; +} + +.glyphicon-eject:before { + content: "\e078"; +} + +.glyphicon-chevron-left:before { + content: "\e079"; +} + +.glyphicon-chevron-right:before { + content: "\e080"; +} + +.glyphicon-plus-sign:before { + content: "\e081"; +} + +.glyphicon-minus-sign:before { + content: "\e082"; +} + +.glyphicon-remove-sign:before { + content: "\e083"; +} + +.glyphicon-ok-sign:before { + content: "\e084"; +} + +.glyphicon-question-sign:before { + content: "\e085"; +} + +.glyphicon-info-sign:before { + content: "\e086"; +} + +.glyphicon-screenshot:before { + content: "\e087"; +} + +.glyphicon-remove-circle:before { + content: "\e088"; +} + +.glyphicon-ok-circle:before { + content: "\e089"; +} + +.glyphicon-ban-circle:before { + content: "\e090"; +} + +.glyphicon-arrow-left:before { + content: "\e091"; +} + +.glyphicon-arrow-right:before { + content: "\e092"; +} + +.glyphicon-arrow-up:before { + content: "\e093"; +} + +.glyphicon-arrow-down:before { + content: "\e094"; +} + +.glyphicon-share-alt:before { + content: "\e095"; +} + +.glyphicon-resize-full:before { + content: "\e096"; +} + +.glyphicon-resize-small:before { + content: "\e097"; +} + +.glyphicon-exclamation-sign:before { + content: "\e101"; +} + +.glyphicon-gift:before { + content: "\e102"; +} + +.glyphicon-leaf:before { + content: "\e103"; +} + +.glyphicon-fire:before { + content: "\e104"; +} + +.glyphicon-eye-open:before { + content: "\e105"; +} + +.glyphicon-eye-close:before { + content: "\e106"; +} + +.glyphicon-warning-sign:before { + content: "\e107"; +} + +.glyphicon-plane:before { + content: "\e108"; +} + +.glyphicon-calendar:before { + content: "\e109"; +} + +.glyphicon-random:before { + content: "\e110"; +} + +.glyphicon-comment:before { + content: "\e111"; +} + +.glyphicon-magnet:before { + content: "\e112"; +} + +.glyphicon-chevron-up:before { + content: "\e113"; +} + +.glyphicon-chevron-down:before { + content: "\e114"; +} + +.glyphicon-retweet:before { + content: "\e115"; +} + +.glyphicon-shopping-cart:before { + content: "\e116"; +} + +.glyphicon-folder-close:before { + content: "\e117"; +} + +.glyphicon-folder-open:before { + content: "\e118"; +} + +.glyphicon-resize-vertical:before { + content: "\e119"; +} + +.glyphicon-resize-horizontal:before { + content: "\e120"; +} + +.glyphicon-hdd:before { + content: "\e121"; +} + +.glyphicon-bullhorn:before { + content: "\e122"; +} + +.glyphicon-bell:before { + content: "\e123"; +} + +.glyphicon-certificate:before { + content: "\e124"; +} + +.glyphicon-thumbs-up:before { + content: "\e125"; +} + +.glyphicon-thumbs-down:before { + content: "\e126"; +} + +.glyphicon-hand-right:before { + content: "\e127"; +} + +.glyphicon-hand-left:before { + content: "\e128"; +} + +.glyphicon-hand-up:before { + content: "\e129"; +} + +.glyphicon-hand-down:before { + content: "\e130"; +} + +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} + +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} + +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} + +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} + +.glyphicon-globe:before { + content: "\e135"; +} + +.glyphicon-wrench:before { + content: "\e136"; +} + +.glyphicon-tasks:before { + content: "\e137"; +} + +.glyphicon-filter:before { + content: "\e138"; +} + +.glyphicon-briefcase:before { + content: "\e139"; +} + +.glyphicon-fullscreen:before { + content: "\e140"; +} + +.glyphicon-dashboard:before { + content: "\e141"; +} + +.glyphicon-paperclip:before { + content: "\e142"; +} + +.glyphicon-heart-empty:before { + content: "\e143"; +} + +.glyphicon-link:before { + content: "\e144"; +} + +.glyphicon-phone:before { + content: "\e145"; +} + +.glyphicon-pushpin:before { + content: "\e146"; +} + +.glyphicon-usd:before { + content: "\e148"; +} + +.glyphicon-gbp:before { + content: "\e149"; +} + +.glyphicon-sort:before { + content: "\e150"; +} + +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} + +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} + +.glyphicon-sort-by-order:before { + content: "\e153"; +} + +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} + +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} + +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} + +.glyphicon-unchecked:before { + content: "\e157"; +} + +.glyphicon-expand:before { + content: "\e158"; +} + +.glyphicon-collapse-down:before { + content: "\e159"; +} + +.glyphicon-collapse-up:before { + content: "\e160"; +} + +.glyphicon-log-in:before { + content: "\e161"; +} + +.glyphicon-flash:before { + content: "\e162"; +} + +.glyphicon-log-out:before { + content: "\e163"; +} + +.glyphicon-new-window:before { + content: "\e164"; +} + +.glyphicon-record:before { + content: "\e165"; +} + +.glyphicon-save:before { + content: "\e166"; +} + +.glyphicon-open:before { + content: "\e167"; +} + +.glyphicon-saved:before { + content: "\e168"; +} + +.glyphicon-import:before { + content: "\e169"; +} + +.glyphicon-export:before { + content: "\e170"; +} + +.glyphicon-send:before { + content: "\e171"; +} + +.glyphicon-floppy-disk:before { + content: "\e172"; +} + +.glyphicon-floppy-saved:before { + content: "\e173"; +} + +.glyphicon-floppy-remove:before { + content: "\e174"; +} + +.glyphicon-floppy-save:before { + content: "\e175"; +} + +.glyphicon-floppy-open:before { + content: "\e176"; +} + +.glyphicon-credit-card:before { + content: "\e177"; +} + +.glyphicon-transfer:before { + content: "\e178"; +} + +.glyphicon-cutlery:before { + content: "\e179"; +} + +.glyphicon-header:before { + content: "\e180"; +} + +.glyphicon-compressed:before { + content: "\e181"; +} + +.glyphicon-earphone:before { + content: "\e182"; +} + +.glyphicon-phone-alt:before { + content: "\e183"; +} + +.glyphicon-tower:before { + content: "\e184"; +} + +.glyphicon-stats:before { + content: "\e185"; +} + +.glyphicon-sd-video:before { + content: "\e186"; +} + +.glyphicon-hd-video:before { + content: "\e187"; +} + +.glyphicon-subtitles:before { + content: "\e188"; +} + +.glyphicon-sound-stereo:before { + content: "\e189"; +} + +.glyphicon-sound-dolby:before { + content: "\e190"; +} + +.glyphicon-sound-5-1:before { + content: "\e191"; +} + +.glyphicon-sound-6-1:before { + content: "\e192"; +} + +.glyphicon-sound-7-1:before { + content: "\e193"; +} + +.glyphicon-copyright-mark:before { + content: "\e194"; +} + +.glyphicon-registration-mark:before { + content: "\e195"; +} + +.glyphicon-cloud-download:before { + content: "\e197"; +} + +.glyphicon-cloud-upload:before { + content: "\e198"; +} + +.glyphicon-tree-conifer:before { + content: "\e199"; +} + +.glyphicon-tree-deciduous:before { + content: "\e200"; +} + +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px solid; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} + +.dropdown { + position: relative; +} + +.dropdown-toggle:focus { + outline: 0; +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + list-style: none; + background-color: #ffffff; + border: 1px solid #cccccc; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + background-clip: padding-box; +} + +.dropdown-menu.pull-right { + right: 0; + left: auto; +} + +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} + +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.428571429; + color: #333333; + white-space: nowrap; +} + +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + color: #262626; + text-decoration: none; + background-color: #f5f5f5; +} + +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #ffffff; + text-decoration: none; + background-color: #428bca; + outline: 0; +} + +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #999999; +} + +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + cursor: not-allowed; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.open > .dropdown-menu { + display: block; +} + +.open > a { + outline: 0; +} + +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.428571429; + color: #999999; +} + +.dropdown-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 990; +} + +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} + +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + border-top: 0; + border-bottom: 4px solid; + content: ""; +} + +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; +} + +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + right: 0; + left: auto; + } +} + +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} + +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + float: left; +} + +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover, +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus, +.btn-group > .btn:active, +.btn-group-vertical > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn.active { + z-index: 2; +} + +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus { + outline: none; +} + +.btn-group .btn + .btn, +.btn-group .btn + .btn-group, +.btn-group .btn-group + .btn, +.btn-group .btn-group + .btn-group { + margin-left: -1px; +} + +.btn-toolbar:before, +.btn-toolbar:after { + display: table; + content: " "; +} + +.btn-toolbar:after { + clear: both; +} + +.btn-toolbar:before, +.btn-toolbar:after { + display: table; + content: " "; +} + +.btn-toolbar:after { + clear: both; +} + +.btn-toolbar .btn-group { + float: left; +} + +.btn-toolbar > .btn + .btn, +.btn-toolbar > .btn-group + .btn, +.btn-toolbar > .btn + .btn-group, +.btn-toolbar > .btn-group + .btn-group { + margin-left: 5px; +} + +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} + +.btn-group > .btn:first-child { + margin-left: 0; +} + +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} + +.btn-group > .btn-group { + float: left; +} + +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} + +.btn-group > .btn-group:first-child > .btn:last-child, +.btn-group > .btn-group:first-child > .dropdown-toggle { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.btn-group > .btn-group:last-child > .btn:first-child { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} + +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} + +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +.btn-group-lg > .btn { + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} + +.btn-group > .btn + .dropdown-toggle { + padding-right: 8px; + padding-left: 8px; +} + +.btn-group > .btn-lg + .dropdown-toggle { + padding-right: 12px; + padding-left: 12px; +} + +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +} + +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none; +} + +.btn .caret { + margin-left: 0; +} + +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0; +} + +.dropup .btn-lg .caret { + border-width: 0 5px 5px; +} + +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group, +.btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; +} + +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after { + display: table; + content: " "; +} + +.btn-group-vertical > .btn-group:after { + clear: both; +} + +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after { + display: table; + content: " "; +} + +.btn-group-vertical > .btn-group:after { + clear: both; +} + +.btn-group-vertical > .btn-group > .btn { + float: none; +} + +.btn-group-vertical > .btn + .btn, +.btn-group-vertical > .btn + .btn-group, +.btn-group-vertical > .btn-group + .btn, +.btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; +} + +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0; +} + +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-right-radius: 0; + border-bottom-left-radius: 4px; + border-top-left-radius: 0; +} + +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} + +.btn-group-vertical > .btn-group:first-child > .btn:last-child, +.btn-group-vertical > .btn-group:first-child > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.btn-group-vertical > .btn-group:last-child > .btn:first-child { + border-top-right-radius: 0; + border-top-left-radius: 0; +} + +.btn-group-justified { + display: table; + width: 100%; + border-collapse: separate; + table-layout: fixed; +} + +.btn-group-justified > .btn, +.btn-group-justified > .btn-group { + display: table-cell; + float: none; + width: 1%; +} + +.btn-group-justified > .btn-group .btn { + width: 100%; +} + +[data-toggle="buttons"] > .btn > input[type="radio"], +[data-toggle="buttons"] > .btn > input[type="checkbox"] { + display: none; +} + +.input-group { + position: relative; + display: table; + border-collapse: separate; +} + +.input-group[class*="col-"] { + float: none; + padding-right: 0; + padding-left: 0; +} + +.input-group .form-control { + width: 100%; + margin-bottom: 0; +} + +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} + +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn { + height: 46px; + line-height: 46px; +} + +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn { + height: auto; +} + +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px; +} + +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn { + height: auto; +} + +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; +} + +.input-group-addon:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child), +.input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} + +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} + +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: normal; + line-height: 1; + color: #555555; + text-align: center; + background-color: #eeeeee; + border: 1px solid #cccccc; + border-radius: 4px; +} + +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px; +} + +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 6px; +} + +.input-group-addon input[type="radio"], +.input-group-addon input[type="checkbox"] { + margin-top: 0; +} + +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group-addon:first-child { + border-right: 0; +} + +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child) { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} + +.input-group-addon:last-child { + border-left: 0; +} + +.input-group-btn { + position: relative; + white-space: nowrap; +} + +.input-group-btn:first-child > .btn { + margin-right: -1px; +} + +.input-group-btn:last-child > .btn { + margin-left: -1px; +} + +.input-group-btn > .btn { + position: relative; +} + +.input-group-btn > .btn + .btn { + margin-left: -4px; +} + +.input-group-btn > .btn:hover, +.input-group-btn > .btn:active { + z-index: 2; +} + +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: none; +} + +.nav:before, +.nav:after { + display: table; + content: " "; +} + +.nav:after { + clear: both; +} + +.nav:before, +.nav:after { + display: table; + content: " "; +} + +.nav:after { + clear: both; +} + +.nav > li { + position: relative; + display: block; +} + +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} + +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eeeeee; +} + +.nav > li.disabled > a { + color: #999999; +} + +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #999999; + text-decoration: none; + cursor: not-allowed; + background-color: transparent; +} + +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eeeeee; + border-color: #428bca; +} + +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} + +.nav > li > a > img { + max-width: none; +} + +.nav-tabs { + border-bottom: 1px solid #dddddd; +} + +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} + +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.428571429; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} + +.nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee #dddddd; +} + +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555555; + cursor: default; + background-color: #ffffff; + border: 1px solid #dddddd; + border-bottom-color: transparent; +} + +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} + +.nav-tabs.nav-justified > li { + float: none; +} + +.nav-tabs.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} + +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} + +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} + +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px; +} + +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #dddddd; +} + +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #dddddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} + +.nav-pills > li { + float: left; +} + +.nav-pills > li > a { + border-radius: 4px; +} + +.nav-pills > li + li { + margin-left: 2px; +} + +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #ffffff; + background-color: #428bca; +} + +.nav-stacked > li { + float: none; +} + +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} + +.nav-justified { + width: 100%; +} + +.nav-justified > li { + float: none; +} + +.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} + +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} + +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-justified > li > a { + margin-bottom: 0; + } +} + +.nav-tabs-justified { + border-bottom: 0; +} + +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px; +} + +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #dddddd; +} + +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #dddddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} + +.tab-content > .tab-pane { + display: none; +} + +.tab-content > .active { + display: block; +} + +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-right-radius: 0; + border-top-left-radius: 0; +} + +.navbar { + position: relative; + min-height: 50px; + margin-bottom: 20px; + border: 1px solid transparent; +} + +.navbar:before, +.navbar:after { + display: table; + content: " "; +} + +.navbar:after { + clear: both; +} + +.navbar:before, +.navbar:after { + display: table; + content: " "; +} + +.navbar:after { + clear: both; +} + +@media (min-width: 768px) { + .navbar { + border-radius: 4px; + } +} + +.navbar-header:before, +.navbar-header:after { + display: table; + content: " "; +} + +.navbar-header:after { + clear: both; +} + +.navbar-header:before, +.navbar-header:after { + display: table; + content: " "; +} + +.navbar-header:after { + clear: both; +} + +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} + +.navbar-collapse { + max-height: 340px; + padding-right: 15px; + padding-left: 15px; + overflow-x: visible; + border-top: 1px solid transparent; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); + -webkit-overflow-scrolling: touch; +} + +.navbar-collapse:before, +.navbar-collapse:after { + display: table; + content: " "; +} + +.navbar-collapse:after { + clear: both; +} + +.navbar-collapse:before, +.navbar-collapse:after { + display: table; + content: " "; +} + +.navbar-collapse:after { + clear: both; +} + +.navbar-collapse.in { + overflow-y: auto; +} + +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-right: 0; + padding-left: 0; + } +} + +.container > .navbar-header, +.container > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} + +@media (min-width: 768px) { + .container > .navbar-header, + .container > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} + +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} + +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} + +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; +} + +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} + +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} + +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} + +.navbar-brand { + float: left; + padding: 15px 15px; + font-size: 18px; + line-height: 20px; +} + +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} + +@media (min-width: 768px) { + .navbar > .container .navbar-brand { + margin-left: -15px; + } +} + +.navbar-toggle { + position: relative; + float: right; + padding: 9px 10px; + margin-top: 8px; + margin-right: 15px; + margin-bottom: 8px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} + +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; +} + +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px; +} + +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } +} + +.navbar-nav { + margin: 7.5px -15px; +} + +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} + +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + box-shadow: none; + } + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} + +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 15px; + padding-bottom: 15px; + } + .navbar-nav.navbar-right:last-child { + margin-right: -15px; + } +} + +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + } +} + +.navbar-form { + padding: 10px 15px; + margin-top: 8px; + margin-right: -15px; + margin-bottom: 8px; + margin-left: -15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); +} + +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .form-control { + display: inline-block; + } + .navbar-form select.form-control { + width: auto; + } + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + padding-left: 0; + margin-top: 0; + margin-bottom: 0; + } + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + float: none; + margin-left: 0; + } +} + +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } +} + +@media (min-width: 768px) { + .navbar-form { + width: auto; + padding-top: 0; + padding-bottom: 0; + margin-right: 0; + margin-left: 0; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-form.navbar-right:last-child { + margin-right: -15px; + } +} + +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-right-radius: 0; + border-top-left-radius: 0; +} + +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.navbar-nav.pull-right > li > .dropdown-menu, +.navbar-nav > li > .dropdown-menu.pull-right { + right: 0; + left: auto; +} + +.navbar-btn { + margin-top: 8px; + margin-bottom: 8px; +} + +.navbar-btn.btn-sm { + margin-top: 10px; + margin-bottom: 10px; +} + +.navbar-btn.btn-xs { + margin-top: 14px; + margin-bottom: 14px; +} + +.navbar-text { + margin-top: 15px; + margin-bottom: 15px; +} + +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-right: 15px; + margin-left: 15px; + } + .navbar-text.navbar-right:last-child { + margin-right: 0; + } +} + +.navbar-default { + background-color: #f8f8f8; + border-color: #e7e7e7; +} + +.navbar-default .navbar-brand { + color: #777777; +} + +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #5e5e5e; + background-color: transparent; +} + +.navbar-default .navbar-text { + color: #777777; +} + +.navbar-default .navbar-nav > li > a { + color: #777777; +} + +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #333333; + background-color: transparent; +} + +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #555555; + background-color: #e7e7e7; +} + +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #cccccc; + background-color: transparent; +} + +.navbar-default .navbar-toggle { + border-color: #dddddd; +} + +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #dddddd; +} + +.navbar-default .navbar-toggle .icon-bar { + background-color: #cccccc; +} + +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #e7e7e7; +} + +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + color: #555555; + background-color: #e7e7e7; +} + +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #777777; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #333333; + background-color: transparent; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #555555; + background-color: #e7e7e7; + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #cccccc; + background-color: transparent; + } +} + +.navbar-default .navbar-link { + color: #777777; +} + +.navbar-default .navbar-link:hover { + color: #333333; +} + +.navbar-inverse { + background-color: #222222; + border-color: #080808; +} + +.navbar-inverse .navbar-brand { + color: #999999; +} + +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #ffffff; + background-color: transparent; +} + +.navbar-inverse .navbar-text { + color: #999999; +} + +.navbar-inverse .navbar-nav > li > a { + color: #999999; +} + +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #ffffff; + background-color: transparent; +} + +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #ffffff; + background-color: #080808; +} + +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444444; + background-color: transparent; +} + +.navbar-inverse .navbar-toggle { + border-color: #333333; +} + +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #333333; +} + +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #ffffff; +} + +.navbar-inverse .navbar-collapse, +.navbar-inverse .navbar-form { + border-color: #101010; +} + +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + color: #ffffff; + background-color: #080808; +} + +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #999999; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #ffffff; + background-color: transparent; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #ffffff; + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #444444; + background-color: transparent; + } +} + +.navbar-inverse .navbar-link { + color: #999999; +} + +.navbar-inverse .navbar-link:hover { + color: #ffffff; +} + +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f5f5f5; + border-radius: 4px; +} + +.breadcrumb > li { + display: inline-block; +} + +.breadcrumb > li + li:before { + padding: 0 5px; + color: #cccccc; + content: "/\00a0"; +} + +.breadcrumb > .active { + color: #999999; +} + +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} + +.pagination > li { + display: inline; +} + +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + padding: 6px 12px; + margin-left: -1px; + line-height: 1.428571429; + text-decoration: none; + background-color: #ffffff; + border: 1px solid #dddddd; +} + +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-bottom-left-radius: 4px; + border-top-left-radius: 4px; +} + +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} + +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + background-color: #eeeeee; +} + +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 2; + color: #ffffff; + cursor: default; + background-color: #428bca; + border-color: #428bca; +} + +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #999999; + cursor: not-allowed; + background-color: #ffffff; + border-color: #dddddd; +} + +.pagination-lg > li > a, +.pagination-lg > li > span { + padding: 10px 16px; + font-size: 18px; +} + +.pagination-lg > li:first-child > a, +.pagination-lg > li:first-child > span { + border-bottom-left-radius: 6px; + border-top-left-radius: 6px; +} + +.pagination-lg > li:last-child > a, +.pagination-lg > li:last-child > span { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; +} + +.pagination-sm > li > a, +.pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; +} + +.pagination-sm > li:first-child > a, +.pagination-sm > li:first-child > span { + border-bottom-left-radius: 3px; + border-top-left-radius: 3px; +} + +.pagination-sm > li:last-child > a, +.pagination-sm > li:last-child > span { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} + +.pager { + padding-left: 0; + margin: 20px 0; + text-align: center; + list-style: none; +} + +.pager:before, +.pager:after { + display: table; + content: " "; +} + +.pager:after { + clear: both; +} + +.pager:before, +.pager:after { + display: table; + content: " "; +} + +.pager:after { + clear: both; +} + +.pager li { + display: inline; +} + +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #ffffff; + border: 1px solid #dddddd; + border-radius: 15px; +} + +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #eeeeee; +} + +.pager .next > a, +.pager .next > span { + float: right; +} + +.pager .previous > a, +.pager .previous > span { + float: left; +} + +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #999999; + cursor: not-allowed; + background-color: #ffffff; +} + +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #ffffff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} + +.label[href]:hover, +.label[href]:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} + +.label:empty { + display: none; +} + +.btn .label { + position: relative; + top: -1px; +} + +.label-default { + background-color: #999999; +} + +.label-default[href]:hover, +.label-default[href]:focus { + background-color: #808080; +} + +.label-primary { + background-color: #428bca; +} + +.label-primary[href]:hover, +.label-primary[href]:focus { + background-color: #3071a9; +} + +.label-success { + background-color: #5cb85c; +} + +.label-success[href]:hover, +.label-success[href]:focus { + background-color: #449d44; +} + +.label-info { + background-color: #5bc0de; +} + +.label-info[href]:hover, +.label-info[href]:focus { + background-color: #31b0d5; +} + +.label-warning { + background-color: #f0ad4e; +} + +.label-warning[href]:hover, +.label-warning[href]:focus { + background-color: #ec971f; +} + +.label-danger { + background-color: #d9534f; +} + +.label-danger[href]:hover, +.label-danger[href]:focus { + background-color: #c9302c; +} + +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + line-height: 1; + color: #ffffff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + background-color: #999999; + border-radius: 10px; +} + +.badge:empty { + display: none; +} + +.btn .badge { + position: relative; + top: -1px; +} + +a.badge:hover, +a.badge:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} + +a.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #428bca; + background-color: #ffffff; +} + +.nav-pills > li > a > .badge { + margin-left: 3px; +} + +.jumbotron { + padding: 30px; + margin-bottom: 30px; + font-size: 21px; + font-weight: 200; + line-height: 2.1428571435; + color: inherit; + background-color: #eeeeee; +} + +.jumbotron h1, +.jumbotron .h1 { + line-height: 1; + color: inherit; +} + +.jumbotron p { + line-height: 1.4; +} + +.container .jumbotron { + border-radius: 6px; +} + +.jumbotron .container { + max-width: 100%; +} + +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron { + padding-right: 60px; + padding-left: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 63px; + } +} + +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.428571429; + background-color: #ffffff; + border: 1px solid #dddddd; + border-radius: 4px; + -webkit-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} + +.thumbnail > img, +.thumbnail a > img { + display: block; + height: auto; + max-width: 100%; + margin-right: auto; + margin-left: auto; +} + +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #428bca; +} + +.thumbnail .caption { + padding: 9px; + color: #333333; +} + +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} + +.alert h4 { + margin-top: 0; + color: inherit; +} + +.alert .alert-link { + font-weight: bold; +} + +.alert > p, +.alert > ul { + margin-bottom: 0; +} + +.alert > p + p { + margin-top: 5px; +} + +.alert-dismissable { + padding-right: 35px; +} + +.alert-dismissable .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} + +.alert-success { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} + +.alert-success hr { + border-top-color: #c9e2b3; +} + +.alert-success .alert-link { + color: #2b542c; +} + +.alert-info { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} + +.alert-info hr { + border-top-color: #a6e1ec; +} + +.alert-info .alert-link { + color: #245269; +} + +.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} + +.alert-warning hr { + border-top-color: #f7e1b5; +} + +.alert-warning .alert-link { + color: #66512c; +} + +.alert-danger { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} + +.alert-danger hr { + border-top-color: #e4b9c0; +} + +.alert-danger .alert-link { + color: #843534; +} + +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f5f5f5; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); +} + +.progress-bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #ffffff; + text-align: center; + background-color: #428bca; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-transition: width 0.6s ease; + transition: width 0.6s ease; +} + +.progress-striped .progress-bar { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-size: 40px 40px; +} + +.progress.active .progress-bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} + +.progress-bar-success { + background-color: #5cb85c; +} + +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-bar-info { + background-color: #5bc0de; +} + +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-bar-warning { + background-color: #f0ad4e; +} + +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-bar-danger { + background-color: #d9534f; +} + +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.media, +.media-body { + overflow: hidden; + zoom: 1; +} + +.media, +.media .media { + margin-top: 15px; +} + +.media:first-child { + margin-top: 0; +} + +.media-object { + display: block; +} + +.media-heading { + margin: 0 0 5px; +} + +.media > .pull-left { + margin-right: 10px; +} + +.media > .pull-right { + margin-left: 10px; +} + +.media-list { + padding-left: 0; + list-style: none; +} + +.list-group { + padding-left: 0; + margin-bottom: 20px; +} + +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #ffffff; + border: 1px solid #dddddd; +} + +.list-group-item:first-child { + border-top-right-radius: 4px; + border-top-left-radius: 4px; +} + +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} + +.list-group-item > .badge { + float: right; +} + +.list-group-item > .badge + .badge { + margin-right: 5px; +} + +a.list-group-item { + color: #555555; +} + +a.list-group-item .list-group-item-heading { + color: #333333; +} + +a.list-group-item:hover, +a.list-group-item:focus { + text-decoration: none; + background-color: #f5f5f5; +} + +a.list-group-item.active, +a.list-group-item.active:hover, +a.list-group-item.active:focus { + z-index: 2; + color: #ffffff; + background-color: #428bca; + border-color: #428bca; +} + +a.list-group-item.active .list-group-item-heading, +a.list-group-item.active:hover .list-group-item-heading, +a.list-group-item.active:focus .list-group-item-heading { + color: inherit; +} + +a.list-group-item.active .list-group-item-text, +a.list-group-item.active:hover .list-group-item-text, +a.list-group-item.active:focus .list-group-item-text { + color: #e1edf7; +} + +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} + +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} + +.panel { + margin-bottom: 20px; + background-color: #ffffff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); +} + +.panel-body { + padding: 15px; +} + +.panel-body:before, +.panel-body:after { + display: table; + content: " "; +} + +.panel-body:after { + clear: both; +} + +.panel-body:before, +.panel-body:after { + display: table; + content: " "; +} + +.panel-body:after { + clear: both; +} + +.panel > .list-group { + margin-bottom: 0; +} + +.panel > .list-group .list-group-item { + border-width: 1px 0; +} + +.panel > .list-group .list-group-item:first-child { + border-top-right-radius: 0; + border-top-left-radius: 0; +} + +.panel > .list-group .list-group-item:last-child { + border-bottom: 0; +} + +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} + +.panel > .table, +.panel > .table-responsive > .table { + margin-bottom: 0; +} + +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive { + border-top: 1px solid #dddddd; +} + +.panel > .table > tbody:first-child th, +.panel > .table > tbody:first-child td { + border-top: 0; +} + +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} + +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} + +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} + +.panel > .table-bordered > thead > tr:last-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:last-child > th, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-bordered > thead > tr:last-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; +} + +.panel > .table-responsive { + margin-bottom: 0; + border: 0; +} + +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} + +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} + +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} + +.panel-title > a { + color: inherit; +} + +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #dddddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} + +.panel-group .panel { + margin-bottom: 0; + overflow: hidden; + border-radius: 4px; +} + +.panel-group .panel + .panel { + margin-top: 5px; +} + +.panel-group .panel-heading { + border-bottom: 0; +} + +.panel-group .panel-heading + .panel-collapse .panel-body { + border-top: 1px solid #dddddd; +} + +.panel-group .panel-footer { + border-top: 0; +} + +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #dddddd; +} + +.panel-default { + border-color: #dddddd; +} + +.panel-default > .panel-heading { + color: #333333; + background-color: #f5f5f5; + border-color: #dddddd; +} + +.panel-default > .panel-heading + .panel-collapse .panel-body { + border-top-color: #dddddd; +} + +.panel-default > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #dddddd; +} + +.panel-primary { + border-color: #428bca; +} + +.panel-primary > .panel-heading { + color: #ffffff; + background-color: #428bca; + border-color: #428bca; +} + +.panel-primary > .panel-heading + .panel-collapse .panel-body { + border-top-color: #428bca; +} + +.panel-primary > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #428bca; +} + +.panel-success { + border-color: #d6e9c6; +} + +.panel-success > .panel-heading { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} + +.panel-success > .panel-heading + .panel-collapse .panel-body { + border-top-color: #d6e9c6; +} + +.panel-success > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #d6e9c6; +} + +.panel-warning { + border-color: #faebcc; +} + +.panel-warning > .panel-heading { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} + +.panel-warning > .panel-heading + .panel-collapse .panel-body { + border-top-color: #faebcc; +} + +.panel-warning > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #faebcc; +} + +.panel-danger { + border-color: #ebccd1; +} + +.panel-danger > .panel-heading { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} + +.panel-danger > .panel-heading + .panel-collapse .panel-body { + border-top-color: #ebccd1; +} + +.panel-danger > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #ebccd1; +} + +.panel-info { + border-color: #bce8f1; +} + +.panel-info > .panel-heading { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} + +.panel-info > .panel-heading + .panel-collapse .panel-body { + border-top-color: #bce8f1; +} + +.panel-info > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #bce8f1; +} + +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} + +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15); +} + +.well-lg { + padding: 24px; + border-radius: 6px; +} + +.well-sm { + padding: 9px; + border-radius: 3px; +} + +.close { + float: right; + font-size: 21px; + font-weight: bold; + line-height: 1; + color: #000000; + text-shadow: 0 1px 0 #ffffff; + opacity: 0.2; + filter: alpha(opacity=20); +} + +.close:hover, +.close:focus { + color: #000000; + text-decoration: none; + cursor: pointer; + opacity: 0.5; + filter: alpha(opacity=50); +} + +button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; +} + +.modal-open { + overflow: hidden; +} + +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + display: none; + overflow: auto; + overflow-y: scroll; +} + +.modal.fade .modal-dialog { + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + transform: translate(0, -25%); + -webkit-transition: -webkit-transform 0.3s ease-out; + -moz-transition: -moz-transform 0.3s ease-out; + -o-transition: -o-transform 0.3s ease-out; + transition: transform 0.3s ease-out; +} + +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + transform: translate(0, 0); +} + +.modal-dialog { + position: relative; + z-index: 1050; + width: auto; + margin: 10px; +} + +.modal-content { + position: relative; + background-color: #ffffff; + border: 1px solid #999999; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 6px; + outline: none; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); + box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); + background-clip: padding-box; +} + +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1030; + background-color: #000000; +} + +.modal-backdrop.fade { + opacity: 0; + filter: alpha(opacity=0); +} + +.modal-backdrop.in { + opacity: 0.5; + filter: alpha(opacity=50); +} + +.modal-header { + min-height: 16.428571429px; + padding: 15px; + border-bottom: 1px solid #e5e5e5; +} + +.modal-header .close { + margin-top: -2px; +} + +.modal-title { + margin: 0; + line-height: 1.428571429; +} + +.modal-body { + position: relative; + padding: 20px; +} + +.modal-footer { + padding: 19px 20px 20px; + margin-top: 15px; + text-align: right; + border-top: 1px solid #e5e5e5; +} + +.modal-footer:before, +.modal-footer:after { + display: table; + content: " "; +} + +.modal-footer:after { + clear: both; +} + +.modal-footer:before, +.modal-footer:after { + display: table; + content: " "; +} + +.modal-footer:after { + clear: both; +} + +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} + +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} + +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} + +@media screen and (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + } +} + +.tooltip { + position: absolute; + z-index: 1030; + display: block; + font-size: 12px; + line-height: 1.4; + opacity: 0; + filter: alpha(opacity=0); + visibility: visible; +} + +.tooltip.in { + opacity: 0.9; + filter: alpha(opacity=90); +} + +.tooltip.top { + padding: 5px 0; + margin-top: -3px; +} + +.tooltip.right { + padding: 0 5px; + margin-left: 3px; +} + +.tooltip.bottom { + padding: 5px 0; + margin-top: 3px; +} + +.tooltip.left { + padding: 0 5px; + margin-left: -3px; +} + +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #ffffff; + text-align: center; + text-decoration: none; + background-color: #000000; + border-radius: 4px; +} + +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} + +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-top-color: #000000; + border-width: 5px 5px 0; +} + +.tooltip.top-left .tooltip-arrow { + bottom: 0; + left: 5px; + border-top-color: #000000; + border-width: 5px 5px 0; +} + +.tooltip.top-right .tooltip-arrow { + right: 5px; + bottom: 0; + border-top-color: #000000; + border-width: 5px 5px 0; +} + +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-right-color: #000000; + border-width: 5px 5px 5px 0; +} + +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-left-color: #000000; + border-width: 5px 0 5px 5px; +} + +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-bottom-color: #000000; + border-width: 0 5px 5px; +} + +.tooltip.bottom-left .tooltip-arrow { + top: 0; + left: 5px; + border-bottom-color: #000000; + border-width: 0 5px 5px; +} + +.tooltip.bottom-right .tooltip-arrow { + top: 0; + right: 5px; + border-bottom-color: #000000; + border-width: 0 5px 5px; +} + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1010; + display: none; + max-width: 276px; + padding: 1px; + text-align: left; + white-space: normal; + background-color: #ffffff; + border: 1px solid #cccccc; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + background-clip: padding-box; +} + +.popover.top { + margin-top: -10px; +} + +.popover.right { + margin-left: 10px; +} + +.popover.bottom { + margin-top: 10px; +} + +.popover.left { + margin-left: -10px; +} + +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + font-weight: normal; + line-height: 18px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} + +.popover-content { + padding: 9px 14px; +} + +.popover .arrow, +.popover .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} + +.popover .arrow { + border-width: 11px; +} + +.popover .arrow:after { + border-width: 10px; + content: ""; +} + +.popover.top .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999999; + border-top-color: rgba(0, 0, 0, 0.25); + border-bottom-width: 0; +} + +.popover.top .arrow:after { + bottom: 1px; + margin-left: -10px; + border-top-color: #ffffff; + border-bottom-width: 0; + content: " "; +} + +.popover.right .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999999; + border-right-color: rgba(0, 0, 0, 0.25); + border-left-width: 0; +} + +.popover.right .arrow:after { + bottom: -10px; + left: 1px; + border-right-color: #ffffff; + border-left-width: 0; + content: " "; +} + +.popover.bottom .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-bottom-color: #999999; + border-bottom-color: rgba(0, 0, 0, 0.25); + border-top-width: 0; +} + +.popover.bottom .arrow:after { + top: 1px; + margin-left: -10px; + border-bottom-color: #ffffff; + border-top-width: 0; + content: " "; +} + +.popover.left .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-left-color: #999999; + border-left-color: rgba(0, 0, 0, 0.25); + border-right-width: 0; +} + +.popover.left .arrow:after { + right: 1px; + bottom: -10px; + border-left-color: #ffffff; + border-right-width: 0; + content: " "; +} + +.carousel { + position: relative; +} + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} + +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: 0.6s ease-in-out left; + transition: 0.6s ease-in-out left; +} + +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + height: auto; + max-width: 100%; + line-height: 1; +} + +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} + +.carousel-inner > .active { + left: 0; +} + +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} + +.carousel-inner > .next { + left: 100%; +} + +.carousel-inner > .prev { + left: -100%; +} + +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} + +.carousel-inner > .active.left { + left: -100%; +} + +.carousel-inner > .active.right { + left: 100%; +} + +.carousel-control { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 15%; + font-size: 20px; + color: #ffffff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); + opacity: 0.5; + filter: alpha(opacity=50); +} + +.carousel-control.left { + background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.5) 0), color-stop(rgba(0, 0, 0, 0.0001) 100%)); + background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); +} + +.carousel-control.right { + right: 0; + left: auto; + background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.0001) 0), color-stop(rgba(0, 0, 0, 0.5) 100%)); + background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.5) 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); +} + +.carousel-control:hover, +.carousel-control:focus { + color: #ffffff; + text-decoration: none; + outline: none; + opacity: 0.9; + filter: alpha(opacity=90); +} + +.carousel-control .icon-prev, +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-left, +.carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + z-index: 5; + display: inline-block; +} + +.carousel-control .icon-prev, +.carousel-control .glyphicon-chevron-left { + left: 50%; +} + +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-right { + right: 50%; +} + +.carousel-control .icon-prev, +.carousel-control .icon-next { + width: 20px; + height: 20px; + margin-top: -10px; + margin-left: -10px; + font-family: serif; +} + +.carousel-control .icon-prev:before { + content: '\2039'; +} + +.carousel-control .icon-next:before { + content: '\203a'; +} + +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + padding-left: 0; + margin-left: -30%; + text-align: center; + list-style: none; +} + +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); + border: 1px solid #ffffff; + border-radius: 10px; +} + +.carousel-indicators .active { + width: 12px; + height: 12px; + margin: 0; + background-color: #ffffff; +} + +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #ffffff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); +} + +.carousel-caption .btn { + text-shadow: none; +} + +@media screen and (min-width: 768px) { + .carousel-control .glyphicons-chevron-left, + .carousel-control .glyphicons-chevron-right, + .carousel-control .icon-prev, + .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -15px; + margin-left: -15px; + font-size: 30px; + } + .carousel-caption { + right: 20%; + left: 20%; + padding-bottom: 30px; + } + .carousel-indicators { + bottom: 20px; + } +} + +.clearfix:before, +.clearfix:after { + display: table; + content: " "; +} + +.clearfix:after { + clear: both; +} + +.center-block { + display: block; + margin-right: auto; + margin-left: auto; +} + +.pull-right { + float: right !important; +} + +.pull-left { + float: left !important; +} + +.hide { + display: none !important; +} + +.show { + display: block !important; +} + +.invisible { + visibility: hidden; +} + +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +.hidden { + display: none !important; + visibility: hidden !important; +} + +.affix { + position: fixed; +} + +@-ms-viewport { + width: device-width; +} + +.visible-xs, +tr.visible-xs, +th.visible-xs, +td.visible-xs { + display: none !important; +} + +@media (max-width: 767px) { + .visible-xs { + display: block !important; + } + table.visible-xs { + display: table; + } + tr.visible-xs { + display: table-row !important; + } + th.visible-xs, + td.visible-xs { + display: table-cell !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-xs.visible-sm { + display: block !important; + } + table.visible-xs.visible-sm { + display: table; + } + tr.visible-xs.visible-sm { + display: table-row !important; + } + th.visible-xs.visible-sm, + td.visible-xs.visible-sm { + display: table-cell !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-xs.visible-md { + display: block !important; + } + table.visible-xs.visible-md { + display: table; + } + tr.visible-xs.visible-md { + display: table-row !important; + } + th.visible-xs.visible-md, + td.visible-xs.visible-md { + display: table-cell !important; + } +} + +@media (min-width: 1200px) { + .visible-xs.visible-lg { + display: block !important; + } + table.visible-xs.visible-lg { + display: table; + } + tr.visible-xs.visible-lg { + display: table-row !important; + } + th.visible-xs.visible-lg, + td.visible-xs.visible-lg { + display: table-cell !important; + } +} + +.visible-sm, +tr.visible-sm, +th.visible-sm, +td.visible-sm { + display: none !important; +} + +@media (max-width: 767px) { + .visible-sm.visible-xs { + display: block !important; + } + table.visible-sm.visible-xs { + display: table; + } + tr.visible-sm.visible-xs { + display: table-row !important; + } + th.visible-sm.visible-xs, + td.visible-sm.visible-xs { + display: table-cell !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + table.visible-sm { + display: table; + } + tr.visible-sm { + display: table-row !important; + } + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-sm.visible-md { + display: block !important; + } + table.visible-sm.visible-md { + display: table; + } + tr.visible-sm.visible-md { + display: table-row !important; + } + th.visible-sm.visible-md, + td.visible-sm.visible-md { + display: table-cell !important; + } +} + +@media (min-width: 1200px) { + .visible-sm.visible-lg { + display: block !important; + } + table.visible-sm.visible-lg { + display: table; + } + tr.visible-sm.visible-lg { + display: table-row !important; + } + th.visible-sm.visible-lg, + td.visible-sm.visible-lg { + display: table-cell !important; + } +} + +.visible-md, +tr.visible-md, +th.visible-md, +td.visible-md { + display: none !important; +} + +@media (max-width: 767px) { + .visible-md.visible-xs { + display: block !important; + } + table.visible-md.visible-xs { + display: table; + } + tr.visible-md.visible-xs { + display: table-row !important; + } + th.visible-md.visible-xs, + td.visible-md.visible-xs { + display: table-cell !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-md.visible-sm { + display: block !important; + } + table.visible-md.visible-sm { + display: table; + } + tr.visible-md.visible-sm { + display: table-row !important; + } + th.visible-md.visible-sm, + td.visible-md.visible-sm { + display: table-cell !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important; + } + table.visible-md { + display: table; + } + tr.visible-md { + display: table-row !important; + } + th.visible-md, + td.visible-md { + display: table-cell !important; + } +} + +@media (min-width: 1200px) { + .visible-md.visible-lg { + display: block !important; + } + table.visible-md.visible-lg { + display: table; + } + tr.visible-md.visible-lg { + display: table-row !important; + } + th.visible-md.visible-lg, + td.visible-md.visible-lg { + display: table-cell !important; + } +} + +.visible-lg, +tr.visible-lg, +th.visible-lg, +td.visible-lg { + display: none !important; +} + +@media (max-width: 767px) { + .visible-lg.visible-xs { + display: block !important; + } + table.visible-lg.visible-xs { + display: table; + } + tr.visible-lg.visible-xs { + display: table-row !important; + } + th.visible-lg.visible-xs, + td.visible-lg.visible-xs { + display: table-cell !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-lg.visible-sm { + display: block !important; + } + table.visible-lg.visible-sm { + display: table; + } + tr.visible-lg.visible-sm { + display: table-row !important; + } + th.visible-lg.visible-sm, + td.visible-lg.visible-sm { + display: table-cell !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-lg.visible-md { + display: block !important; + } + table.visible-lg.visible-md { + display: table; + } + tr.visible-lg.visible-md { + display: table-row !important; + } + th.visible-lg.visible-md, + td.visible-lg.visible-md { + display: table-cell !important; + } +} + +@media (min-width: 1200px) { + .visible-lg { + display: block !important; + } + table.visible-lg { + display: table; + } + tr.visible-lg { + display: table-row !important; + } + th.visible-lg, + td.visible-lg { + display: table-cell !important; + } +} + +.hidden-xs { + display: block !important; +} + +table.hidden-xs { + display: table; +} + +tr.hidden-xs { + display: table-row !important; +} + +th.hidden-xs, +td.hidden-xs { + display: table-cell !important; +} + +@media (max-width: 767px) { + .hidden-xs, + tr.hidden-xs, + th.hidden-xs, + td.hidden-xs { + display: none !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .hidden-xs.hidden-sm, + tr.hidden-xs.hidden-sm, + th.hidden-xs.hidden-sm, + td.hidden-xs.hidden-sm { + display: none !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-xs.hidden-md, + tr.hidden-xs.hidden-md, + th.hidden-xs.hidden-md, + td.hidden-xs.hidden-md { + display: none !important; + } +} + +@media (min-width: 1200px) { + .hidden-xs.hidden-lg, + tr.hidden-xs.hidden-lg, + th.hidden-xs.hidden-lg, + td.hidden-xs.hidden-lg { + display: none !important; + } +} + +.hidden-sm { + display: block !important; +} + +table.hidden-sm { + display: table; +} + +tr.hidden-sm { + display: table-row !important; +} + +th.hidden-sm, +td.hidden-sm { + display: table-cell !important; +} + +@media (max-width: 767px) { + .hidden-sm.hidden-xs, + tr.hidden-sm.hidden-xs, + th.hidden-sm.hidden-xs, + td.hidden-sm.hidden-xs { + display: none !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm, + tr.hidden-sm, + th.hidden-sm, + td.hidden-sm { + display: none !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-sm.hidden-md, + tr.hidden-sm.hidden-md, + th.hidden-sm.hidden-md, + td.hidden-sm.hidden-md { + display: none !important; + } +} + +@media (min-width: 1200px) { + .hidden-sm.hidden-lg, + tr.hidden-sm.hidden-lg, + th.hidden-sm.hidden-lg, + td.hidden-sm.hidden-lg { + display: none !important; + } +} + +.hidden-md { + display: block !important; +} + +table.hidden-md { + display: table; +} + +tr.hidden-md { + display: table-row !important; +} + +th.hidden-md, +td.hidden-md { + display: table-cell !important; +} + +@media (max-width: 767px) { + .hidden-md.hidden-xs, + tr.hidden-md.hidden-xs, + th.hidden-md.hidden-xs, + td.hidden-md.hidden-xs { + display: none !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .hidden-md.hidden-sm, + tr.hidden-md.hidden-sm, + th.hidden-md.hidden-sm, + td.hidden-md.hidden-sm { + display: none !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md, + tr.hidden-md, + th.hidden-md, + td.hidden-md { + display: none !important; + } +} + +@media (min-width: 1200px) { + .hidden-md.hidden-lg, + tr.hidden-md.hidden-lg, + th.hidden-md.hidden-lg, + td.hidden-md.hidden-lg { + display: none !important; + } +} + +.hidden-lg { + display: block !important; +} + +table.hidden-lg { + display: table; +} + +tr.hidden-lg { + display: table-row !important; +} + +th.hidden-lg, +td.hidden-lg { + display: table-cell !important; +} + +@media (max-width: 767px) { + .hidden-lg.hidden-xs, + tr.hidden-lg.hidden-xs, + th.hidden-lg.hidden-xs, + td.hidden-lg.hidden-xs { + display: none !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .hidden-lg.hidden-sm, + tr.hidden-lg.hidden-sm, + th.hidden-lg.hidden-sm, + td.hidden-lg.hidden-sm { + display: none !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-lg.hidden-md, + tr.hidden-lg.hidden-md, + th.hidden-lg.hidden-md, + td.hidden-lg.hidden-md { + display: none !important; + } +} + +@media (min-width: 1200px) { + .hidden-lg, + tr.hidden-lg, + th.hidden-lg, + td.hidden-lg { + display: none !important; + } +} + +.visible-print, +tr.visible-print, +th.visible-print, +td.visible-print { + display: none !important; +} + +@media print { + .visible-print { + display: block !important; + } + table.visible-print { + display: table; + } + tr.visible-print { + display: table-row !important; + } + th.visible-print, + td.visible-print { + display: table-cell !important; + } + .hidden-print, + tr.hidden-print, + th.hidden-print, + td.hidden-print { + display: none !important; + } +} \ No newline at end of file diff --git a/htdocs/css/bootstrap.min.css b/htdocs/css/bootstrap.min.css new file mode 100644 index 00000000..c547283b --- /dev/null +++ b/htdocs/css/bootstrap.min.css @@ -0,0 +1,7 @@ +/*! + * Bootstrap v3.0.3 (http://getbootstrap.com) + * Copyright 2013 Twitter, Inc. + * Licensed under http://www.apache.org/licenses/LICENSE-2.0 + */ + +/*! normalize.css v2.1.3 | MIT License | git.io/normalize */article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden],template{display:none}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a{background:transparent}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{margin:.67em 0;font-size:2em}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}hr{height:0;-moz-box-sizing:content-box;box-sizing:content-box}mark{color:#000;background:#ff0}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:"\201C" "\201D" "\2018" "\2019"}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid #c0c0c0}legend{padding:0;border:0}button,input,select,textarea{margin:0;font-family:inherit;font-size:100%}button,input{line-height:normal}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}button[disabled],html input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{padding:0;box-sizing:border-box}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}@media print{*{color:#000!important;text-shadow:none!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}@page{margin:2cm .5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff!important}.navbar{display:none}.table td,.table th{background-color:#fff!important}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table-bordered th,.table-bordered td{border:1px solid #ddd!important}}*,*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:62.5%;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.428571429;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}img{vertical-align:middle}.img-responsive{display:block;height:auto;max-width:100%}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;height:auto;max-width:100%;padding:4px;line-height:1.428571429;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:normal;line-height:1;color:#999}h1,h2,h3{margin-top:20px;margin-bottom:10px}h1 small,h2 small,h3 small,h1 .small,h2 .small,h3 .small{font-size:65%}h4,h5,h6{margin-top:10px;margin-bottom:10px}h4 small,h5 small,h6 small,h4 .small,h5 .small,h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:200;line-height:1.4}@media(min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}cite{font-style:normal}.text-muted{color:#999}.text-primary{color:#428bca}.text-primary:hover{color:#3071a9}.text-warning{color:#8a6d3b}.text-warning:hover{color:#66512c}.text-danger{color:#a94442}.text-danger:hover{color:#843534}.text-success{color:#3c763d}.text-success:hover{color:#2b542c}.text-info{color:#31708f}.text-info:hover{color:#245269}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}.list-inline>li:first-child{padding-left:0}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.428571429}dt{font-weight:bold}dd{margin-left:0}@media(min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}.dl-horizontal dd:before,.dl-horizontal dd:after{display:table;content:" "}.dl-horizontal dd:after{clear:both}.dl-horizontal dd:before,.dl-horizontal dd:after{display:table;content:" "}.dl-horizontal dd:after{clear:both}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;border-left:5px solid #eee}blockquote p{font-size:17.5px;font-weight:300;line-height:1.25}blockquote p:last-child{margin-bottom:0}blockquote small,blockquote .small{display:block;line-height:1.428571429;color:#999}blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small,blockquote.pull-right .small{text-align:right}blockquote.pull-right small:before,blockquote.pull-right .small:before{content:''}blockquote.pull-right small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}blockquote:before,blockquote:after{content:""}address{margin-bottom:20px;font-style:normal;line-height:1.428571429}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;white-space:nowrap;background-color:#f9f2f4;border-radius:4px}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.428571429;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.container:before,.container:after{display:table;content:" "}.container:after{clear:both}.container:before,.container:after{display:table;content:" "}.container:after{clear:both}@media(min-width:768px){.container{width:750px}}@media(min-width:992px){.container{width:970px}}@media(min-width:1200px){.container{width:1170px}}.row{margin-right:-15px;margin-left:-15px}.row:before,.row:after{display:table;content:" "}.row:after{clear:both}.row:before,.row:after{display:table;content:" "}.row:after{clear:both}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666666666666%}.col-xs-10{width:83.33333333333334%}.col-xs-9{width:75%}.col-xs-8{width:66.66666666666666%}.col-xs-7{width:58.333333333333336%}.col-xs-6{width:50%}.col-xs-5{width:41.66666666666667%}.col-xs-4{width:33.33333333333333%}.col-xs-3{width:25%}.col-xs-2{width:16.666666666666664%}.col-xs-1{width:8.333333333333332%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666666666666%}.col-xs-pull-10{right:83.33333333333334%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666666666666%}.col-xs-pull-7{right:58.333333333333336%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666666666667%}.col-xs-pull-4{right:33.33333333333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.666666666666664%}.col-xs-pull-1{right:8.333333333333332%}.col-xs-pull-0{right:0}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666666666666%}.col-xs-push-10{left:83.33333333333334%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666666666666%}.col-xs-push-7{left:58.333333333333336%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666666666667%}.col-xs-push-4{left:33.33333333333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.666666666666664%}.col-xs-push-1{left:8.333333333333332%}.col-xs-push-0{left:0}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666666666666%}.col-xs-offset-10{margin-left:83.33333333333334%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666666666666%}.col-xs-offset-7{margin-left:58.333333333333336%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666666666667%}.col-xs-offset-4{margin-left:33.33333333333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.666666666666664%}.col-xs-offset-1{margin-left:8.333333333333332%}.col-xs-offset-0{margin-left:0}@media(min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666666666666%}.col-sm-10{width:83.33333333333334%}.col-sm-9{width:75%}.col-sm-8{width:66.66666666666666%}.col-sm-7{width:58.333333333333336%}.col-sm-6{width:50%}.col-sm-5{width:41.66666666666667%}.col-sm-4{width:33.33333333333333%}.col-sm-3{width:25%}.col-sm-2{width:16.666666666666664%}.col-sm-1{width:8.333333333333332%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666666666666%}.col-sm-pull-10{right:83.33333333333334%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666666666666%}.col-sm-pull-7{right:58.333333333333336%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666666666667%}.col-sm-pull-4{right:33.33333333333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.666666666666664%}.col-sm-pull-1{right:8.333333333333332%}.col-sm-pull-0{right:0}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666666666666%}.col-sm-push-10{left:83.33333333333334%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666666666666%}.col-sm-push-7{left:58.333333333333336%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666666666667%}.col-sm-push-4{left:33.33333333333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.666666666666664%}.col-sm-push-1{left:8.333333333333332%}.col-sm-push-0{left:0}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666666666666%}.col-sm-offset-10{margin-left:83.33333333333334%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666666666666%}.col-sm-offset-7{margin-left:58.333333333333336%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666666666667%}.col-sm-offset-4{margin-left:33.33333333333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.666666666666664%}.col-sm-offset-1{margin-left:8.333333333333332%}.col-sm-offset-0{margin-left:0}}@media(min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666666666666%}.col-md-10{width:83.33333333333334%}.col-md-9{width:75%}.col-md-8{width:66.66666666666666%}.col-md-7{width:58.333333333333336%}.col-md-6{width:50%}.col-md-5{width:41.66666666666667%}.col-md-4{width:33.33333333333333%}.col-md-3{width:25%}.col-md-2{width:16.666666666666664%}.col-md-1{width:8.333333333333332%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666666666666%}.col-md-pull-10{right:83.33333333333334%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666666666666%}.col-md-pull-7{right:58.333333333333336%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666666666667%}.col-md-pull-4{right:33.33333333333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.666666666666664%}.col-md-pull-1{right:8.333333333333332%}.col-md-pull-0{right:0}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666666666666%}.col-md-push-10{left:83.33333333333334%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666666666666%}.col-md-push-7{left:58.333333333333336%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666666666667%}.col-md-push-4{left:33.33333333333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.666666666666664%}.col-md-push-1{left:8.333333333333332%}.col-md-push-0{left:0}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666666666666%}.col-md-offset-10{margin-left:83.33333333333334%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666666666666%}.col-md-offset-7{margin-left:58.333333333333336%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666666666667%}.col-md-offset-4{margin-left:33.33333333333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.666666666666664%}.col-md-offset-1{margin-left:8.333333333333332%}.col-md-offset-0{margin-left:0}}@media(min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666666666666%}.col-lg-10{width:83.33333333333334%}.col-lg-9{width:75%}.col-lg-8{width:66.66666666666666%}.col-lg-7{width:58.333333333333336%}.col-lg-6{width:50%}.col-lg-5{width:41.66666666666667%}.col-lg-4{width:33.33333333333333%}.col-lg-3{width:25%}.col-lg-2{width:16.666666666666664%}.col-lg-1{width:8.333333333333332%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666666666666%}.col-lg-pull-10{right:83.33333333333334%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666666666666%}.col-lg-pull-7{right:58.333333333333336%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666666666667%}.col-lg-pull-4{right:33.33333333333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.666666666666664%}.col-lg-pull-1{right:8.333333333333332%}.col-lg-pull-0{right:0}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666666666666%}.col-lg-push-10{left:83.33333333333334%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666666666666%}.col-lg-push-7{left:58.333333333333336%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666666666667%}.col-lg-push-4{left:33.33333333333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.666666666666664%}.col-lg-push-1{left:8.333333333333332%}.col-lg-push-0{left:0}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666666666666%}.col-lg-offset-10{margin-left:83.33333333333334%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666666666666%}.col-lg-offset-7{margin-left:58.333333333333336%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666666666667%}.col-lg-offset-4{margin-left:33.33333333333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.666666666666664%}.col-lg-offset-1{margin-left:8.333333333333332%}.col-lg-offset-0{margin-left:0}}table{max-width:100%;background-color:transparent}th{text-align:left}.table{width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.428571429;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#f5f5f5}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{display:table-cell;float:none}.table>thead>tr>.active,.table>tbody>tr>.active,.table>tfoot>tr>.active,.table>thead>.active>td,.table>tbody>.active>td,.table>tfoot>.active>td,.table>thead>.active>th,.table>tbody>.active>th,.table>tfoot>.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>.active:hover,.table-hover>tbody>.active:hover>td,.table-hover>tbody>.active:hover>th{background-color:#e8e8e8}.table>thead>tr>.success,.table>tbody>tr>.success,.table>tfoot>tr>.success,.table>thead>.success>td,.table>tbody>.success>td,.table>tfoot>.success>td,.table>thead>.success>th,.table>tbody>.success>th,.table>tfoot>.success>th{background-color:#dff0d8}.table-hover>tbody>tr>.success:hover,.table-hover>tbody>.success:hover>td,.table-hover>tbody>.success:hover>th{background-color:#d0e9c6}.table>thead>tr>.danger,.table>tbody>tr>.danger,.table>tfoot>tr>.danger,.table>thead>.danger>td,.table>tbody>.danger>td,.table>tfoot>.danger>td,.table>thead>.danger>th,.table>tbody>.danger>th,.table>tfoot>.danger>th{background-color:#f2dede}.table-hover>tbody>tr>.danger:hover,.table-hover>tbody>.danger:hover>td,.table-hover>tbody>.danger:hover>th{background-color:#ebcccc}.table>thead>tr>.warning,.table>tbody>tr>.warning,.table>tfoot>tr>.warning,.table>thead>.warning>td,.table>tbody>.warning>td,.table>tfoot>.warning>td,.table>thead>.warning>th,.table>tbody>.warning>th,.table>tfoot>.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>.warning:hover,.table-hover>tbody>.warning:hover>td,.table-hover>tbody>.warning:hover>th{background-color:#faf2cc}@media(max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-x:scroll;overflow-y:hidden;border:1px solid #ddd;-ms-overflow-style:-ms-autohiding-scrollbar;-webkit-overflow-scrolling:touch}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}select[multiple],select[size]{height:auto}select optgroup{font-family:inherit;font-size:inherit;font-style:inherit}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}input[type="number"]::-webkit-outer-spin-button,input[type="number"]::-webkit-inner-spin-button{height:auto}output{display:block;padding-top:7px;font-size:14px;line-height:1.428571429;color:#555;vertical-align:middle}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.428571429;color:#555;vertical-align:middle;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control:-moz-placeholder{color:#999}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee}textarea.form-control{height:auto}.form-group{margin-bottom:15px}.radio,.checkbox{display:block;min-height:20px;padding-left:20px;margin-top:10px;margin-bottom:10px;vertical-align:middle}.radio label,.checkbox label{display:inline;margin-bottom:0;font-weight:normal;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{float:left;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;font-weight:normal;vertical-align:middle;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],input[type="checkbox"][disabled],.radio[disabled],.radio-inline[disabled],.checkbox[disabled],.checkbox-inline[disabled],fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"],fieldset[disabled] .radio,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm{height:auto}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg{height:46px;line-height:46px}textarea.input-lg{height:auto}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.form-control-static{margin-bottom:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media(min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block}.form-inline select.form-control{width:auto}.form-inline .radio,.form-inline .checkbox{display:inline-block;padding-left:0;margin-top:0;margin-bottom:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:none;margin-left:0}}.form-horizontal .control-label,.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}.form-horizontal .form-group:before,.form-horizontal .form-group:after{display:table;content:" "}.form-horizontal .form-group:after{clear:both}.form-horizontal .form-group:before,.form-horizontal .form-group:after{display:table;content:" "}.form-horizontal .form-group:after{clear:both}.form-horizontal .form-control-static{padding-top:7px}@media(min-width:768px){.form-horizontal .control-label{text-align:right}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:normal;line-height:1.428571429;text-align:center;white-space:nowrap;vertical-align:middle;cursor:pointer;background-image:none;border:1px solid transparent;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus{color:#333;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{pointer-events:none;cursor:not-allowed;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{color:#333;background-color:#ebebeb;border-color:#adadad}.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#fff}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{color:#fff;background-color:#3276b1;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#357ebd}.btn-primary .badge{color:#428bca;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{color:#fff;background-color:#ed9c28;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{color:#fff;background-color:#d2322d;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{color:#fff;background-color:#47a447;border-color:#398439}.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{color:#fff;background-color:#39b3d7;border-color:#269abc}.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-link{font-weight:normal;color:#428bca;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#999;text-decoration:none}.btn-lg{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%;padding-right:0;padding-left:0}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;transition:height .35s ease}@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.woff') format('woff'),url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'),url('../fonts/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';-webkit-font-smoothing:antialiased;font-style:normal;font-weight:normal;line-height:1;-moz-osx-font-smoothing:grayscale}.glyphicon:empty{width:1em}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.428571429;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#428bca;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.428571429;color:#999}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media(min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:focus,.btn-group-vertical>.btn:focus{outline:0}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar:before,.btn-toolbar:after{display:table;content:" "}.btn-toolbar:after{clear:both}.btn-toolbar:before,.btn-toolbar:after{display:table;content:" "}.btn-toolbar:after{clear:both}.btn-toolbar .btn-group{float:left}.btn-toolbar>.btn+.btn,.btn-toolbar>.btn-group+.btn,.btn-toolbar>.btn+.btn-group,.btn-toolbar>.btn-group+.btn-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after{display:table;content:" "}.btn-group-vertical>.btn-group:after{clear:both}.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after{display:table;content:" "}.btn-group-vertical>.btn-group:after{clear:both}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-right-radius:0;border-bottom-left-radius:4px;border-top-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child>.btn:last-child,.btn-group-vertical>.btn-group:first-child>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;border-collapse:separate;table-layout:fixed}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}[data-toggle="buttons"]>.btn>input[type="radio"],[data-toggle="buttons"]>.btn>input[type="checkbox"]{display:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:normal;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;white-space:nowrap}.input-group-btn:first-child>.btn{margin-right:-1px}.input-group-btn:last-child>.btn{margin-left:-1px}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-4px}.input-group-btn>.btn:hover,.input-group-btn>.btn:active{z-index:2}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav:before,.nav:after{display:table;content:" "}.nav:after{clear:both}.nav:before,.nav:after{display:table;content:" "}.nav:after{clear:both}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#428bca}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.428571429;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media(min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media(min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media(min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media(min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}.navbar:before,.navbar:after{display:table;content:" "}.navbar:after{clear:both}.navbar:before,.navbar:after{display:table;content:" "}.navbar:after{clear:both}@media(min-width:768px){.navbar{border-radius:4px}}.navbar-header:before,.navbar-header:after{display:table;content:" "}.navbar-header:after{clear:both}.navbar-header:before,.navbar-header:after{display:table;content:" "}.navbar-header:after{clear:both}@media(min-width:768px){.navbar-header{float:left}}.navbar-collapse{max-height:340px;padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse:before,.navbar-collapse:after{display:table;content:" "}.navbar-collapse:after{clear:both}.navbar-collapse:before,.navbar-collapse:after{display:table;content:" "}.navbar-collapse:after{clear:both}.navbar-collapse.in{overflow-y:auto}@media(min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.container>.navbar-header,.container>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media(min-width:768px){.container>.navbar-header,.container>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media(min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media(min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}@media(min-width:768px){.navbar>.container .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media(min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media(max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media(min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}.navbar-nav.navbar-right:last-child{margin-right:-15px}}@media(min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}@media(min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block}.navbar-form select.form-control{width:auto}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;padding-left:0;margin-top:0;margin-bottom:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{float:none;margin-left:0}}@media(max-width:767px){.navbar-form .form-group{margin-bottom:5px}}@media(min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-form.navbar-right:last-child{margin-right:-15px}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-nav.pull-right>li>.dropdown-menu,.navbar-nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media(min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}.navbar-text.navbar-right:last-child{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#ccc}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#555;background-color:#e7e7e7}@media(max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#999}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .navbar-nav>li>a{color:#999}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#fff;background-color:#080808}@media(max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#999}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#fff}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#999}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.428571429;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{background-color:#eee}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:2;color:#fff;cursor:default;background-color:#428bca;border-color:#428bca}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager:before,.pager:after{display:table;content:" "}.pager:after{clear:both}.pager:before,.pager:after{display:table;content:" "}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.label[href]:hover,.label[href]:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#999}.label-default[href]:hover,.label-default[href]:focus{background-color:#808080}.label-primary{background-color:#428bca}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#3071a9}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;background-color:#999;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px;margin-bottom:30px;font-size:21px;font-weight:200;line-height:2.1428571435;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{line-height:1;color:inherit}.jumbotron p{line-height:1.4}.container .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.428571429;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.thumbnail>img,.thumbnail a>img{display:block;height:auto;max-width:100%;margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#428bca}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable{padding-right:35px}.alert-dismissable .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-size:40px 40px}.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.media,.media-body{overflow:hidden;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{text-decoration:none;background-color:#f5f5f5}a.list-group-item.active,a.list-group-item.active:hover,a.list-group-item.active:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}a.list-group-item.active .list-group-item-heading,a.list-group-item.active:hover .list-group-item-heading,a.list-group-item.active:focus .list-group-item-heading{color:inherit}a.list-group-item.active .list-group-item-text,a.list-group-item.active:hover .list-group-item-text,a.list-group-item.active:focus .list-group-item-text{color:#e1edf7}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-body:before,.panel-body:after{display:table;content:" "}.panel-body:after{clear:both}.panel-body:before,.panel-body:after{display:table;content:" "}.panel-body:after{clear:both}.panel>.list-group{margin-bottom:0}.panel>.list-group .list-group-item{border-width:1px 0}.panel>.list-group .list-group-item:first-child{border-top-right-radius:0;border-top-left-radius:0}.panel>.list-group .list-group-item:last-child{border-bottom:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive{border-top:1px solid #ddd}.panel>.table>tbody:first-child th,.panel>.table>tbody:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:last-child>th,.panel>.table-responsive>.table-bordered>thead>tr:last-child>th,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th,.panel>.table-bordered>thead>tr:last-child>td,.panel>.table-responsive>.table-bordered>thead>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-group .panel{margin-bottom:0;overflow:hidden;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse .panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse .panel-body{border-top-color:#ddd}.panel-default>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#428bca}.panel-primary>.panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-primary>.panel-heading+.panel-collapse .panel-body{border-top-color:#428bca}.panel-primary>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse .panel-body{border-top-color:#d6e9c6}.panel-success>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#d6e9c6}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse .panel-body{border-top-color:#faebcc}.panel-warning>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse .panel-body{border-top-color:#ebccd1}.panel-danger>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#ebccd1}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse .panel-body{border-top-color:#bce8f1}.panel-info>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#bce8f1}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;display:none;overflow:auto;overflow-y:scroll}.modal.fade .modal-dialog{-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);transform:translate(0,-25%);-webkit-transition:-webkit-transform .3s ease-out;-moz-transition:-moz-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}.modal-dialog{position:relative;z-index:1050;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);background-clip:padding-box}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1030;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.5;filter:alpha(opacity=50)}.modal-header{min-height:16.428571429px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.428571429}.modal-body{position:relative;padding:20px}.modal-footer{padding:19px 20px 20px;margin-top:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer:before,.modal-footer:after{display:table;content:" "}.modal-footer:after{clear:both}.modal-footer:before,.modal-footer:after{display:table;content:" "}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}@media screen and (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}}.tooltip{position:absolute;z-index:1030;display:block;font-size:12px;line-height:1.4;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.9;filter:alpha(opacity=90)}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:#000;border-width:5px 5px 0}.tooltip.top-left .tooltip-arrow{bottom:0;left:5px;border-top-color:#000;border-width:5px 5px 0}.tooltip.top-right .tooltip-arrow{right:5px;bottom:0;border-top-color:#000;border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#000;border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:#000;border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:#000;border-width:0 5px 5px}.tooltip.bottom-left .tooltip-arrow{top:0;left:5px;border-bottom-color:#000;border-width:0 5px 5px}.tooltip.bottom-right .tooltip-arrow{top:0;right:5px;border-bottom-color:#000;border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;white-space:normal;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);background-clip:padding-box}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover .arrow,.popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow{border-width:11px}.popover .arrow:after{border-width:10px;content:""}.popover.top .arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top .arrow:after{bottom:1px;margin-left:-10px;border-top-color:#fff;border-bottom-width:0;content:" "}.popover.right .arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right .arrow:after{bottom:-10px;left:1px;border-right-color:#fff;border-left-width:0;content:" "}.popover.bottom .arrow{top:-11px;left:50%;margin-left:-11px;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);border-top-width:0}.popover.bottom .arrow:after{top:1px;margin-left:-10px;border-bottom-color:#fff;border-top-width:0;content:" "}.popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-left-color:#999;border-left-color:rgba(0,0,0,0.25);border-right-width:0}.popover.left .arrow:after{right:1px;bottom:-10px;border-left-color:#fff;border-right-width:0;content:" "}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;height:auto;max-width:100%;line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);opacity:.5;filter:alpha(opacity=50)}.carousel-control.left{background-image:-webkit-linear-gradient(left,color-stop(rgba(0,0,0,0.5) 0),color-stop(rgba(0,0,0,0.0001) 100%));background-image:linear-gradient(to right,rgba(0,0,0,0.5) 0,rgba(0,0,0,0.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000',endColorstr='#00000000',GradientType=1)}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,color-stop(rgba(0,0,0,0.0001) 0),color-stop(rgba(0,0,0,0.5) 100%));background-image:linear-gradient(to right,rgba(0,0,0,0.0001) 0,rgba(0,0,0,0.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000',endColorstr='#80000000',GradientType=1)}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;outline:0;opacity:.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-10px;margin-left:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicons-chevron-left,.carousel-control .glyphicons-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;margin-left:-15px;font-size:30px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after{display:table;content:" "}.clearfix:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important;visibility:hidden!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,tr.visible-xs,th.visible-xs,td.visible-xs{display:none!important}@media(max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table}tr.visible-xs{display:table-row!important}th.visible-xs,td.visible-xs{display:table-cell!important}}@media(min-width:768px) and (max-width:991px){.visible-xs.visible-sm{display:block!important}table.visible-xs.visible-sm{display:table}tr.visible-xs.visible-sm{display:table-row!important}th.visible-xs.visible-sm,td.visible-xs.visible-sm{display:table-cell!important}}@media(min-width:992px) and (max-width:1199px){.visible-xs.visible-md{display:block!important}table.visible-xs.visible-md{display:table}tr.visible-xs.visible-md{display:table-row!important}th.visible-xs.visible-md,td.visible-xs.visible-md{display:table-cell!important}}@media(min-width:1200px){.visible-xs.visible-lg{display:block!important}table.visible-xs.visible-lg{display:table}tr.visible-xs.visible-lg{display:table-row!important}th.visible-xs.visible-lg,td.visible-xs.visible-lg{display:table-cell!important}}.visible-sm,tr.visible-sm,th.visible-sm,td.visible-sm{display:none!important}@media(max-width:767px){.visible-sm.visible-xs{display:block!important}table.visible-sm.visible-xs{display:table}tr.visible-sm.visible-xs{display:table-row!important}th.visible-sm.visible-xs,td.visible-sm.visible-xs{display:table-cell!important}}@media(min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table}tr.visible-sm{display:table-row!important}th.visible-sm,td.visible-sm{display:table-cell!important}}@media(min-width:992px) and (max-width:1199px){.visible-sm.visible-md{display:block!important}table.visible-sm.visible-md{display:table}tr.visible-sm.visible-md{display:table-row!important}th.visible-sm.visible-md,td.visible-sm.visible-md{display:table-cell!important}}@media(min-width:1200px){.visible-sm.visible-lg{display:block!important}table.visible-sm.visible-lg{display:table}tr.visible-sm.visible-lg{display:table-row!important}th.visible-sm.visible-lg,td.visible-sm.visible-lg{display:table-cell!important}}.visible-md,tr.visible-md,th.visible-md,td.visible-md{display:none!important}@media(max-width:767px){.visible-md.visible-xs{display:block!important}table.visible-md.visible-xs{display:table}tr.visible-md.visible-xs{display:table-row!important}th.visible-md.visible-xs,td.visible-md.visible-xs{display:table-cell!important}}@media(min-width:768px) and (max-width:991px){.visible-md.visible-sm{display:block!important}table.visible-md.visible-sm{display:table}tr.visible-md.visible-sm{display:table-row!important}th.visible-md.visible-sm,td.visible-md.visible-sm{display:table-cell!important}}@media(min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table}tr.visible-md{display:table-row!important}th.visible-md,td.visible-md{display:table-cell!important}}@media(min-width:1200px){.visible-md.visible-lg{display:block!important}table.visible-md.visible-lg{display:table}tr.visible-md.visible-lg{display:table-row!important}th.visible-md.visible-lg,td.visible-md.visible-lg{display:table-cell!important}}.visible-lg,tr.visible-lg,th.visible-lg,td.visible-lg{display:none!important}@media(max-width:767px){.visible-lg.visible-xs{display:block!important}table.visible-lg.visible-xs{display:table}tr.visible-lg.visible-xs{display:table-row!important}th.visible-lg.visible-xs,td.visible-lg.visible-xs{display:table-cell!important}}@media(min-width:768px) and (max-width:991px){.visible-lg.visible-sm{display:block!important}table.visible-lg.visible-sm{display:table}tr.visible-lg.visible-sm{display:table-row!important}th.visible-lg.visible-sm,td.visible-lg.visible-sm{display:table-cell!important}}@media(min-width:992px) and (max-width:1199px){.visible-lg.visible-md{display:block!important}table.visible-lg.visible-md{display:table}tr.visible-lg.visible-md{display:table-row!important}th.visible-lg.visible-md,td.visible-lg.visible-md{display:table-cell!important}}@media(min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table}tr.visible-lg{display:table-row!important}th.visible-lg,td.visible-lg{display:table-cell!important}}.hidden-xs{display:block!important}table.hidden-xs{display:table}tr.hidden-xs{display:table-row!important}th.hidden-xs,td.hidden-xs{display:table-cell!important}@media(max-width:767px){.hidden-xs,tr.hidden-xs,th.hidden-xs,td.hidden-xs{display:none!important}}@media(min-width:768px) and (max-width:991px){.hidden-xs.hidden-sm,tr.hidden-xs.hidden-sm,th.hidden-xs.hidden-sm,td.hidden-xs.hidden-sm{display:none!important}}@media(min-width:992px) and (max-width:1199px){.hidden-xs.hidden-md,tr.hidden-xs.hidden-md,th.hidden-xs.hidden-md,td.hidden-xs.hidden-md{display:none!important}}@media(min-width:1200px){.hidden-xs.hidden-lg,tr.hidden-xs.hidden-lg,th.hidden-xs.hidden-lg,td.hidden-xs.hidden-lg{display:none!important}}.hidden-sm{display:block!important}table.hidden-sm{display:table}tr.hidden-sm{display:table-row!important}th.hidden-sm,td.hidden-sm{display:table-cell!important}@media(max-width:767px){.hidden-sm.hidden-xs,tr.hidden-sm.hidden-xs,th.hidden-sm.hidden-xs,td.hidden-sm.hidden-xs{display:none!important}}@media(min-width:768px) and (max-width:991px){.hidden-sm,tr.hidden-sm,th.hidden-sm,td.hidden-sm{display:none!important}}@media(min-width:992px) and (max-width:1199px){.hidden-sm.hidden-md,tr.hidden-sm.hidden-md,th.hidden-sm.hidden-md,td.hidden-sm.hidden-md{display:none!important}}@media(min-width:1200px){.hidden-sm.hidden-lg,tr.hidden-sm.hidden-lg,th.hidden-sm.hidden-lg,td.hidden-sm.hidden-lg{display:none!important}}.hidden-md{display:block!important}table.hidden-md{display:table}tr.hidden-md{display:table-row!important}th.hidden-md,td.hidden-md{display:table-cell!important}@media(max-width:767px){.hidden-md.hidden-xs,tr.hidden-md.hidden-xs,th.hidden-md.hidden-xs,td.hidden-md.hidden-xs{display:none!important}}@media(min-width:768px) and (max-width:991px){.hidden-md.hidden-sm,tr.hidden-md.hidden-sm,th.hidden-md.hidden-sm,td.hidden-md.hidden-sm{display:none!important}}@media(min-width:992px) and (max-width:1199px){.hidden-md,tr.hidden-md,th.hidden-md,td.hidden-md{display:none!important}}@media(min-width:1200px){.hidden-md.hidden-lg,tr.hidden-md.hidden-lg,th.hidden-md.hidden-lg,td.hidden-md.hidden-lg{display:none!important}}.hidden-lg{display:block!important}table.hidden-lg{display:table}tr.hidden-lg{display:table-row!important}th.hidden-lg,td.hidden-lg{display:table-cell!important}@media(max-width:767px){.hidden-lg.hidden-xs,tr.hidden-lg.hidden-xs,th.hidden-lg.hidden-xs,td.hidden-lg.hidden-xs{display:none!important}}@media(min-width:768px) and (max-width:991px){.hidden-lg.hidden-sm,tr.hidden-lg.hidden-sm,th.hidden-lg.hidden-sm,td.hidden-lg.hidden-sm{display:none!important}}@media(min-width:992px) and (max-width:1199px){.hidden-lg.hidden-md,tr.hidden-lg.hidden-md,th.hidden-lg.hidden-md,td.hidden-lg.hidden-md{display:none!important}}@media(min-width:1200px){.hidden-lg,tr.hidden-lg,th.hidden-lg,td.hidden-lg{display:none!important}}.visible-print,tr.visible-print,th.visible-print,td.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table}tr.visible-print{display:table-row!important}th.visible-print,td.visible-print{display:table-cell!important}.hidden-print,tr.hidden-print,th.hidden-print,td.hidden-print{display:none!important}} \ No newline at end of file diff --git a/htdocs/css/home.css b/htdocs/css/home.css new file mode 100644 index 00000000..343be509 --- /dev/null +++ b/htdocs/css/home.css @@ -0,0 +1,19 @@ +.clock #ficlogo { + float: left; + height: 100px; + margin-left: 25px; + width: 12%; +} +.clock #epitalogo { + float: right; + height: 100px; + width: 14%; +} +.clock #ficlogo img { + max-height: 170px; + max-width: 100%; +} +.clock #epitalogo img { + max-height: 170px; + max-width: 100%; +} \ No newline at end of file diff --git a/htdocs/css/score.css b/htdocs/css/score.css new file mode 100644 index 00000000..2a5870ff --- /dev/null +++ b/htdocs/css/score.css @@ -0,0 +1,85 @@ + +.clock { + background:#202020; + margin: 0 auto; + padding: 30px; + border: 1px solid #333; + color: #fff; +} + +#Date { + background:#202020; + font-family: bold, sans-serif; + text-align:center; + text-shadow:0 0 5px #00c6ff; +} + +#Date + ul { + width: 800px; + margin: 0 auto; + padding: 0px; + list-style: none; + text-align: center; +} + +#Date + ul li { + display: inline; + font-size: 5em; + text-align: center; + font-family: sans-serif; + text-shadow: 0 0 5px #00c6ff; +} + +.samptest { + display: block !important; + overflow-x: scroll; + text-overflow: ellipsis; +} +samp { + display: block; + overflow-x: auto; + text-overflow: ellipsis; +} + +.point { + position: relative; + -moz-animation: mymove 1s ease infinite; + -webkit-animation: mymove 1s ease infinite; + padding-left: 10px; + padding-right: 10px; +} + +/* Simple Animation */ +@-webkit-keyframes mymove { + 0% { + opacity: 1.0; + text-shadow: 0 0 20px #00c6ff; + } + + 50% { + opacity: 0; + text-shadow: none; + } + + 100% { + opacity: 1.0; + text-shadow: 0 0 20px #00c6ff; + } +} + +@-moz-keyframes mymove { + 0% { + opacity: 1.0; + text-shadow: 0 0 20px #00c6ff; + } + + 50% { + opacity: 0; + text-shadow: none; + } + + 100% { + opacity: 1.0; + text-shadow: 0 0 20px #00c6ff; + }; +} diff --git a/htdocs/exos.dtd b/htdocs/exos.dtd new file mode 100644 index 00000000..1e7c160b --- /dev/null +++ b/htdocs/exos.dtd @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/htdocs/favicon.ico b/htdocs/favicon.ico new file mode 100644 index 00000000..42fa798f Binary files /dev/null and b/htdocs/favicon.ico differ diff --git a/htdocs/fonts/glyphicons-halflings-regular.eot b/htdocs/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 00000000..423bd5d3 Binary files /dev/null and b/htdocs/fonts/glyphicons-halflings-regular.eot differ diff --git a/htdocs/fonts/glyphicons-halflings-regular.svg b/htdocs/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 00000000..44694887 --- /dev/null +++ b/htdocs/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/htdocs/fonts/glyphicons-halflings-regular.ttf b/htdocs/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 00000000..a498ef4e Binary files /dev/null and b/htdocs/fonts/glyphicons-halflings-regular.ttf differ diff --git a/htdocs/fonts/glyphicons-halflings-regular.woff b/htdocs/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 00000000..d83c539b Binary files /dev/null and b/htdocs/fonts/glyphicons-halflings-regular.woff differ diff --git a/htdocs/img/challenge.jpg b/htdocs/img/challenge.jpg new file mode 100644 index 00000000..21ec23f4 Binary files /dev/null and b/htdocs/img/challenge.jpg differ diff --git a/htdocs/img/epita.png b/htdocs/img/epita.png new file mode 100644 index 00000000..f3110584 Binary files /dev/null and b/htdocs/img/epita.png differ diff --git a/htdocs/img/fic.png b/htdocs/img/fic.png new file mode 100644 index 00000000..9561de5e Binary files /dev/null and b/htdocs/img/fic.png differ diff --git a/htdocs/index.php b/htdocs/index.php new file mode 100644 index 00000000..d9b76dfb --- /dev/null +++ b/htdocs/index.php @@ -0,0 +1,265 @@ +assign("SALT_CDN",SALT_PUBLIC); +$template->assign("SALT_PUBLIC",SALT_PUBLIC); +$template->assign("SALT_USER",SALT_USER); +$template->assign("SALT_ADMIN",SALT_ADMIN); + +// Convert hidden URL to raw one +$urls = Cache::read("urls"); +$p = strtolower(gpc("p")); + +if (!empty($urls["/".$p])) + $p = $urls["/".$p]; + +$n = preg_match_all("#[^/]+#", $p, $out); +$p = $out[0]; + +// Admin part +if ($n && $p[0] == SALT_ADMIN) +{ + if (isset($p[1])) + $template->assign("menu_where", $p[1]); + else + $template->assign("menu_where", ""); + if (isset($p[2])) + $template->assign("submenu_where", $p[2]); + else + $template->assign("submenu_where", ""); + + $template->assign("themes", Theme::get_themes()); + + if ($n <= 1) + $page = require("admin/home.php"); + else + { + if (isset($p[2])) + $cmpstr = $p[1]."/".$p[2]; + else + $cmpstr = $p[1]; + + if (strpos($cmpstr, 'ex/') === 0) + $page = require("admin/exercice.php"); + else + switch($cmpstr) + { + case "certificate": + case "certificate/": + case "certificate/newca": + case "certificate/newca/": + case "certificate/newclient": + case "certificate/newclient/": + case "certificate/deleteca": + case "certificate/deleteca/": + case "certificate/revoke": + case "certificate/revoke/": + case "certificate/get": + case "certificate/get/": + $page = require("admin/certificate.php"); + break; + + case "chrono": + case "chrono/": + case "chrono/init": + case "chrono/init/": + case "chrono/start": + case "chrono/start/": + $page = require("admin/chrono.php"); + break; + + case "exercices/import": + case "exercices/import/": + $page = require("admin/import_exercices.php"); + break; + + // Theme + case "themes": + case "themes/": + $page = require("admin/list_themes.php"); + break; + + case "themes/import": + case "themes/import/": + $page = require("admin/import_themes.php"); + break; + + case "themes/export": + case "themes/export/": + $page = require("admin/export_theme.php"); + break; + + // Users + case "teams": + case "teams/": + $page = require("admin/list_users.php"); + break; + + case "teams/import": + case "teams/import/": + $page = require("admin/import_users.php"); + break; + + case "teams/export": + case "teams/export/": + $page = require("admin/export_users.php"); + break; + } + } +} + +// Known users +else if ($n && $p[0] == SALT_USER) +{ + $connected = true; + if ($n <= 1) + { + require("public/home.php"); + $page = "teams/list"; + } + + else if (!empty($p[1]) && empty($VAR["start_challenge"])) + { + $TEAM = new Team($p[1]); + $template->assign("my_team", $TEAM); + $template->assign("themes", Theme::get_themes()); + + require("team/me.php"); + $page = "teams/lobby"; + } + + else if (!empty($p[1]) && time() > $VAR["end_challenge"]) + { + $TEAM = new Team($p[1]); + $template->assign("my_team", $TEAM); + $template->assign("themes", Theme::get_themes()); + $template->assign("rank", Team::get_top()); + + require("team/summary.php"); + $page = "teams/end"; + } + + else + { + $TEAM = new Team($p[1]); + $template->assign("my_team", $TEAM); + $template->assign("themes", Theme::get_themes()); + + if ($n <= 2) + $page = require("team/summary.php"); + else + { + switch($p[2]) + { + case "me/": + case "me": + $page = require("team/me.php"); + break; + + case "rank/": + case "rank": + $page = require("team/rank.php"); + break; + + case "summary": + case "summary/": + $page = require("team/summary.php"); + break; + + case "confirmation": + $page = require("team/confirmation.php"); + break; + } + + // SALT/$team/$theme + if (empty($page)) + { + $tmp = explode("-", $p[2]); + $id = intval($tmp[0]); + + if ($id == 0) + $page = "404"; + else + { + try + { + $THEME = new Theme($id); + unset($tmp, $id); + $template->assign("cur_theme", $THEME); + + if ($n == 4 || ($n >= 5 && $p[4] == "submission")) + $id_exo = $p[3]; + else if ($n == 3) + { + foreach($THEME->get_exercices_ordered() as $exo) + { + if (! $exo->has_solved($TEAM)) + break; + } + $id_exo = $exo->id; + } + + $EXERCICE = new Exercice($id_exo, $THEME); + $template->assign("cur_exercice", $EXERCICE); + $page = require("team/exercice.php"); + } + catch(ExerciceNotFoundException $e) + { + $page = "404"; + } + catch(InvalidThemeException $e) + { + $page = "404"; + } + catch(ThemeNotFoundException $e) + { + $page = "404"; + } + } + } + } + } +} + +// Public part +else +{ + if ($n == 0) + $page = require("public/home.php"); + else if ($n && $p[0] == "errors") + { + $template->assign("err", intval($p[1])); + $page = "err"; + } + else if ($n == 1) + { + $TEAM = intval(substr($p[0], 0, strpos($p[0], "-"))); + + if (!empty($TEAM)) + $page = require("public/score.php"); + else + $page = require("public/rank.php"); + } +} + +// No page here...? +if (empty($page) || $page == "404" || ! file_exists(ONYX . "/tpl/bootstrap/".$page.".tpl")) +{ + $template->assign("err", 404); + $template->display("err.tpl"); +} +else + $template->display($page.".tpl"); diff --git a/htdocs/js/bootstrap.js b/htdocs/js/bootstrap.js new file mode 100644 index 00000000..850e6e53 --- /dev/null +++ b/htdocs/js/bootstrap.js @@ -0,0 +1,2006 @@ +/*! + * Bootstrap v3.0.3 (http://getbootstrap.com) + * Copyright 2013 Twitter, Inc. + * Licensed under http://www.apache.org/licenses/LICENSE-2.0 + */ + +if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery") } + +/* ======================================================================== + * Bootstrap: transition.js v3.0.3 + * http://getbootstrap.com/javascript/#transitions + * ======================================================================== + * Copyright 2013 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================================== */ + + ++function ($) { "use strict"; + + // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) + // ============================================================ + + function transitionEnd() { + var el = document.createElement('bootstrap') + + var transEndEventNames = { + 'WebkitTransition' : 'webkitTransitionEnd' + , 'MozTransition' : 'transitionend' + , 'OTransition' : 'oTransitionEnd otransitionend' + , 'transition' : 'transitionend' + } + + for (var name in transEndEventNames) { + if (el.style[name] !== undefined) { + return { end: transEndEventNames[name] } + } + } + } + + // http://blog.alexmaccaw.com/css-transitions + $.fn.emulateTransitionEnd = function (duration) { + var called = false, $el = this + $(this).one($.support.transition.end, function () { called = true }) + var callback = function () { if (!called) $($el).trigger($.support.transition.end) } + setTimeout(callback, duration) + return this + } + + $(function () { + $.support.transition = transitionEnd() + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: alert.js v3.0.3 + * http://getbootstrap.com/javascript/#alerts + * ======================================================================== + * Copyright 2013 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================================== */ + + ++function ($) { "use strict"; + + // ALERT CLASS DEFINITION + // ====================== + + var dismiss = '[data-dismiss="alert"]' + var Alert = function (el) { + $(el).on('click', dismiss, this.close) + } + + Alert.prototype.close = function (e) { + var $this = $(this) + var selector = $this.attr('data-target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + var $parent = $(selector) + + if (e) e.preventDefault() + + if (!$parent.length) { + $parent = $this.hasClass('alert') ? $this : $this.parent() + } + + $parent.trigger(e = $.Event('close.bs.alert')) + + if (e.isDefaultPrevented()) return + + $parent.removeClass('in') + + function removeElement() { + $parent.trigger('closed.bs.alert').remove() + } + + $.support.transition && $parent.hasClass('fade') ? + $parent + .one($.support.transition.end, removeElement) + .emulateTransitionEnd(150) : + removeElement() + } + + + // ALERT PLUGIN DEFINITION + // ======================= + + var old = $.fn.alert + + $.fn.alert = function (option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.alert') + + if (!data) $this.data('bs.alert', (data = new Alert(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + $.fn.alert.Constructor = Alert + + + // ALERT NO CONFLICT + // ================= + + $.fn.alert.noConflict = function () { + $.fn.alert = old + return this + } + + + // ALERT DATA-API + // ============== + + $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: button.js v3.0.3 + * http://getbootstrap.com/javascript/#buttons + * ======================================================================== + * Copyright 2013 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================================== */ + + ++function ($) { "use strict"; + + // BUTTON PUBLIC CLASS DEFINITION + // ============================== + + var Button = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Button.DEFAULTS, options) + } + + Button.DEFAULTS = { + loadingText: 'loading...' + } + + Button.prototype.setState = function (state) { + var d = 'disabled' + var $el = this.$element + var val = $el.is('input') ? 'val' : 'html' + var data = $el.data() + + state = state + 'Text' + + if (!data.resetText) $el.data('resetText', $el[val]()) + + $el[val](data[state] || this.options[state]) + + // push to event loop to allow forms to submit + setTimeout(function () { + state == 'loadingText' ? + $el.addClass(d).attr(d, d) : + $el.removeClass(d).removeAttr(d); + }, 0) + } + + Button.prototype.toggle = function () { + var $parent = this.$element.closest('[data-toggle="buttons"]') + var changed = true + + if ($parent.length) { + var $input = this.$element.find('input') + if ($input.prop('type') === 'radio') { + // see if clicking on current one + if ($input.prop('checked') && this.$element.hasClass('active')) + changed = false + else + $parent.find('.active').removeClass('active') + } + if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change') + } + + if (changed) this.$element.toggleClass('active') + } + + + // BUTTON PLUGIN DEFINITION + // ======================== + + var old = $.fn.button + + $.fn.button = function (option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.button') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.button', (data = new Button(this, options))) + + if (option == 'toggle') data.toggle() + else if (option) data.setState(option) + }) + } + + $.fn.button.Constructor = Button + + + // BUTTON NO CONFLICT + // ================== + + $.fn.button.noConflict = function () { + $.fn.button = old + return this + } + + + // BUTTON DATA-API + // =============== + + $(document).on('click.bs.button.data-api', '[data-toggle^=button]', function (e) { + var $btn = $(e.target) + if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') + $btn.button('toggle') + e.preventDefault() + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: carousel.js v3.0.3 + * http://getbootstrap.com/javascript/#carousel + * ======================================================================== + * Copyright 2013 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================================== */ + + ++function ($) { "use strict"; + + // CAROUSEL CLASS DEFINITION + // ========================= + + var Carousel = function (element, options) { + this.$element = $(element) + this.$indicators = this.$element.find('.carousel-indicators') + this.options = options + this.paused = + this.sliding = + this.interval = + this.$active = + this.$items = null + + this.options.pause == 'hover' && this.$element + .on('mouseenter', $.proxy(this.pause, this)) + .on('mouseleave', $.proxy(this.cycle, this)) + } + + Carousel.DEFAULTS = { + interval: 5000 + , pause: 'hover' + , wrap: true + } + + Carousel.prototype.cycle = function (e) { + e || (this.paused = false) + + this.interval && clearInterval(this.interval) + + this.options.interval + && !this.paused + && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) + + return this + } + + Carousel.prototype.getActiveIndex = function () { + this.$active = this.$element.find('.item.active') + this.$items = this.$active.parent().children() + + return this.$items.index(this.$active) + } + + Carousel.prototype.to = function (pos) { + var that = this + var activeIndex = this.getActiveIndex() + + if (pos > (this.$items.length - 1) || pos < 0) return + + if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) + if (activeIndex == pos) return this.pause().cycle() + + return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) + } + + Carousel.prototype.pause = function (e) { + e || (this.paused = true) + + if (this.$element.find('.next, .prev').length && $.support.transition.end) { + this.$element.trigger($.support.transition.end) + this.cycle(true) + } + + this.interval = clearInterval(this.interval) + + return this + } + + Carousel.prototype.next = function () { + if (this.sliding) return + return this.slide('next') + } + + Carousel.prototype.prev = function () { + if (this.sliding) return + return this.slide('prev') + } + + Carousel.prototype.slide = function (type, next) { + var $active = this.$element.find('.item.active') + var $next = next || $active[type]() + var isCycling = this.interval + var direction = type == 'next' ? 'left' : 'right' + var fallback = type == 'next' ? 'first' : 'last' + var that = this + + if (!$next.length) { + if (!this.options.wrap) return + $next = this.$element.find('.item')[fallback]() + } + + this.sliding = true + + isCycling && this.pause() + + var e = $.Event('slide.bs.carousel', { relatedTarget: $next[0], direction: direction }) + + if ($next.hasClass('active')) return + + if (this.$indicators.length) { + this.$indicators.find('.active').removeClass('active') + this.$element.one('slid.bs.carousel', function () { + var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()]) + $nextIndicator && $nextIndicator.addClass('active') + }) + } + + if ($.support.transition && this.$element.hasClass('slide')) { + this.$element.trigger(e) + if (e.isDefaultPrevented()) return + $next.addClass(type) + $next[0].offsetWidth // force reflow + $active.addClass(direction) + $next.addClass(direction) + $active + .one($.support.transition.end, function () { + $next.removeClass([type, direction].join(' ')).addClass('active') + $active.removeClass(['active', direction].join(' ')) + that.sliding = false + setTimeout(function () { that.$element.trigger('slid.bs.carousel') }, 0) + }) + .emulateTransitionEnd(600) + } else { + this.$element.trigger(e) + if (e.isDefaultPrevented()) return + $active.removeClass('active') + $next.addClass('active') + this.sliding = false + this.$element.trigger('slid.bs.carousel') + } + + isCycling && this.cycle() + + return this + } + + + // CAROUSEL PLUGIN DEFINITION + // ========================== + + var old = $.fn.carousel + + $.fn.carousel = function (option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.carousel') + var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) + var action = typeof option == 'string' ? option : options.slide + + if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) + if (typeof option == 'number') data.to(option) + else if (action) data[action]() + else if (options.interval) data.pause().cycle() + }) + } + + $.fn.carousel.Constructor = Carousel + + + // CAROUSEL NO CONFLICT + // ==================== + + $.fn.carousel.noConflict = function () { + $.fn.carousel = old + return this + } + + + // CAROUSEL DATA-API + // ================= + + $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) { + var $this = $(this), href + var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 + var options = $.extend({}, $target.data(), $this.data()) + var slideIndex = $this.attr('data-slide-to') + if (slideIndex) options.interval = false + + $target.carousel(options) + + if (slideIndex = $this.attr('data-slide-to')) { + $target.data('bs.carousel').to(slideIndex) + } + + e.preventDefault() + }) + + $(window).on('load', function () { + $('[data-ride="carousel"]').each(function () { + var $carousel = $(this) + $carousel.carousel($carousel.data()) + }) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: collapse.js v3.0.3 + * http://getbootstrap.com/javascript/#collapse + * ======================================================================== + * Copyright 2013 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================================== */ + + ++function ($) { "use strict"; + + // COLLAPSE PUBLIC CLASS DEFINITION + // ================================ + + var Collapse = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Collapse.DEFAULTS, options) + this.transitioning = null + + if (this.options.parent) this.$parent = $(this.options.parent) + if (this.options.toggle) this.toggle() + } + + Collapse.DEFAULTS = { + toggle: true + } + + Collapse.prototype.dimension = function () { + var hasWidth = this.$element.hasClass('width') + return hasWidth ? 'width' : 'height' + } + + Collapse.prototype.show = function () { + if (this.transitioning || this.$element.hasClass('in')) return + + var startEvent = $.Event('show.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var actives = this.$parent && this.$parent.find('> .panel > .in') + + if (actives && actives.length) { + var hasData = actives.data('bs.collapse') + if (hasData && hasData.transitioning) return + actives.collapse('hide') + hasData || actives.data('bs.collapse', null) + } + + var dimension = this.dimension() + + this.$element + .removeClass('collapse') + .addClass('collapsing') + [dimension](0) + + this.transitioning = 1 + + var complete = function () { + this.$element + .removeClass('collapsing') + .addClass('in') + [dimension]('auto') + this.transitioning = 0 + this.$element.trigger('shown.bs.collapse') + } + + if (!$.support.transition) return complete.call(this) + + var scrollSize = $.camelCase(['scroll', dimension].join('-')) + + this.$element + .one($.support.transition.end, $.proxy(complete, this)) + .emulateTransitionEnd(350) + [dimension](this.$element[0][scrollSize]) + } + + Collapse.prototype.hide = function () { + if (this.transitioning || !this.$element.hasClass('in')) return + + var startEvent = $.Event('hide.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var dimension = this.dimension() + + this.$element + [dimension](this.$element[dimension]()) + [0].offsetHeight + + this.$element + .addClass('collapsing') + .removeClass('collapse') + .removeClass('in') + + this.transitioning = 1 + + var complete = function () { + this.transitioning = 0 + this.$element + .trigger('hidden.bs.collapse') + .removeClass('collapsing') + .addClass('collapse') + } + + if (!$.support.transition) return complete.call(this) + + this.$element + [dimension](0) + .one($.support.transition.end, $.proxy(complete, this)) + .emulateTransitionEnd(350) + } + + Collapse.prototype.toggle = function () { + this[this.$element.hasClass('in') ? 'hide' : 'show']() + } + + + // COLLAPSE PLUGIN DEFINITION + // ========================== + + var old = $.fn.collapse + + $.fn.collapse = function (option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.collapse') + var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + $.fn.collapse.Constructor = Collapse + + + // COLLAPSE NO CONFLICT + // ==================== + + $.fn.collapse.noConflict = function () { + $.fn.collapse = old + return this + } + + + // COLLAPSE DATA-API + // ================= + + $(document).on('click.bs.collapse.data-api', '[data-toggle=collapse]', function (e) { + var $this = $(this), href + var target = $this.attr('data-target') + || e.preventDefault() + || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 + var $target = $(target) + var data = $target.data('bs.collapse') + var option = data ? 'toggle' : $this.data() + var parent = $this.attr('data-parent') + var $parent = parent && $(parent) + + if (!data || !data.transitioning) { + if ($parent) $parent.find('[data-toggle=collapse][data-parent="' + parent + '"]').not($this).addClass('collapsed') + $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed') + } + + $target.collapse(option) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: dropdown.js v3.0.3 + * http://getbootstrap.com/javascript/#dropdowns + * ======================================================================== + * Copyright 2013 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================================== */ + + ++function ($) { "use strict"; + + // DROPDOWN CLASS DEFINITION + // ========================= + + var backdrop = '.dropdown-backdrop' + var toggle = '[data-toggle=dropdown]' + var Dropdown = function (element) { + $(element).on('click.bs.dropdown', this.toggle) + } + + Dropdown.prototype.toggle = function (e) { + var $this = $(this) + + if ($this.is('.disabled, :disabled')) return + + var $parent = getParent($this) + var isActive = $parent.hasClass('open') + + clearMenus() + + if (!isActive) { + if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { + // if mobile we use a backdrop because click events don't delegate + $('