From c129b2e477773999f826d3dc248ef662bda9cd1d Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 25 Jan 2019 07:26:52 +0100 Subject: [PATCH 0001/1637] frontend: polish public version checks --- frontend/static/js/challenge.js | 31 +++++++++++++++++++++++++------ frontend/static/views/defi.html | 5 ++++- libfic/team_my.go | 4 ++++ 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/frontend/static/js/challenge.js b/frontend/static/js/challenge.js index bbf92bff..d80e96ff 100644 --- a/frontend/static/js/challenge.js +++ b/frontend/static/js/challenge.js @@ -19,12 +19,21 @@ function treatFlagKey(flag) { } if (flag.found == null && flag.soluce !== undefined) { - if (check === undefined) check = true; + if (flag.value && flag.soluce) { + if (flag.ignore_case) + flag.value = flag.value.toLowerCase(); + if (flag.validator_regexp) { + var re = new RegExp(flag.validator_regexp, flag.ignore_case?'ui':'u'); + var match = re.exec(flag.value); + match.shift(); + flag.value = match.join("+"); + } - if (flag.value && flag.soluce == b2sum(flag.value)) - flag.found = new Date(); - check &= flag.found; + if (flag.soluce == b2sum(flag.value)) + flag.found = new Date(); + } } + return flag.found !== undefined && flag.found !== false; } angular.module("FICApp", ["ngRoute", "ngSanitize"]) @@ -329,7 +338,11 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) angular.forEach(data.exercices, function(exercice, eid) { if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].submitted) data.exercices[eid].timeouted = true; + if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].solved !== undefined) + data.exercices[eid].solved = $scope.my.exercices[eid].solved; angular.forEach(exercice.flags, function(flag, fid) { + if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].flags && $scope.my.exercices[eid].flags[fid] && $scope.my.exercices[eid].flags[fid].found !== undefined) + data.exercices[eid].flags[fid].found = $scope.my.exercices[eid].flags[fid].found; if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].flags && $scope.my.exercices[eid].flags[fid] && $scope.my.exercices[eid].flags[fid].value !== undefined) data.exercices[eid].flags[fid].value = $scope.my.exercices[eid].flags[fid].value; if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].flags && $scope.my.exercices[eid].flags[fid] && $scope.my.exercices[eid].flags[fid].values !== undefined) @@ -337,6 +350,10 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) else data.exercices[eid].flags[fid].values = [""]; }); + angular.forEach(exercice.mcqs, function(mcq, mid) { + if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].mcqs && $scope.my.exercices[eid].mcqs[mid] && $scope.my.exercices[eid].mcqs[mid].solved !== undefined) + data.exercices[eid].mcqs[mid].solved = $scope.my.exercices[eid].mcqs[mid].solved; + }); }); angular.forEach(data.exercices, function(exercice, eid) { angular.forEach(exercice.mcqs, function(mcq, mid) { @@ -474,7 +491,9 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) { resp["flags"] = {}; angular.forEach($scope.my.exercices[$rootScope.current_exercice].flags, function(flag,kid) { - treatFlagKey(flag); + if (check === undefined) check = true; + + check &= treatFlagKey(flag) || flag.found; if (flag.found == null && flag.soluce === undefined) { resp["flags"][kid] = flag.value; } @@ -483,9 +502,9 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) if ($scope.my.exercices[$rootScope.current_exercice].mcqs && Object.keys($scope.my.exercices[$rootScope.current_exercice].mcqs).length) { - var soluce = ""; resp["mcqs"] = {}; angular.forEach($scope.my.exercices[$rootScope.current_exercice].mcqs, function(mcq) { + var soluce = ""; if (mcq.solved == null) { angular.forEach(mcq.choices, function(choice, cid) { if (mcq.soluce !== undefined) { diff --git a/frontend/static/views/defi.html b/frontend/static/views/defi.html index 48c88b7a..64e93bc7 100644 --- a/frontend/static/views/defi.html +++ b/frontend/static/views/defi.html @@ -119,9 +119,12 @@ Défi réussi !
-

+

Vous êtes la {{ my.exercices[current_exercice].solved_rank }} équipe à avoir résolu ce défi à {{ my.exercices[current_exercice].solved_time | date:"mediumTime" }}. Vous avez marqué !

+

+ Bravo, vous avez résolu ce défi à {{ my.exercices[current_exercice].solved_time | date:"mediumTime" }}. Vous marquez ! +



diff --git a/libfic/team_my.go b/libfic/team_my.go index a471652b..0f74415f 100644 --- a/libfic/team_my.go +++ b/libfic/team_my.go @@ -30,6 +30,8 @@ type myTeamFlag struct { Help string `json:"help,omitempty"` Separator string `json:"separator,omitempty"` IgnoreOrder bool `json:"ignore_order,omitempty"` + IgnoreCase bool `json:"ignore_case,omitempty"` + ValidatorRe *string `json:"validator_regexp,omitempty"` Solved *time.Time `json:"found,omitempty"` Soluce string `json:"soluce,omitempty"` Choices map[string]string `json:"choices,omitempty"` @@ -189,6 +191,8 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { // Retrieve solved state or solution for public iface if t == nil { + flag.IgnoreCase = k.IgnoreCase + flag.ValidatorRe = k.ValidatorRegexp flag.Soluce = hex.EncodeToString(k.Checksum) } else if PartialValidation { flag.Solved = t.HasPartiallySolved(k) From 04b42de06115f6f35c4e6762d541b1fd7744dee8 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 25 Jan 2019 07:54:27 +0100 Subject: [PATCH 0002/1637] libfic: add igncorecase flag to regexp related to ignorecase flag --- libfic/flag_key.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libfic/flag_key.go b/libfic/flag_key.go index c31c8ac7..065cb89f 100644 --- a/libfic/flag_key.go +++ b/libfic/flag_key.go @@ -73,7 +73,10 @@ func getHashedFlag(raw_value []byte) [blake2b.Size]byte { return hash } -func ExecValidatorRegexp(vre string, val []byte) ([]byte, error) { +func ExecValidatorRegexp(vre string, val []byte, ignorecase bool) ([]byte, error) { + if (ignorecase) { + vre = "(?i)" + vre + } if re, err := regexp.Compile(vre); err != nil { return val, err } else if res := re.FindSubmatch(val); res == nil { @@ -92,7 +95,7 @@ func (e Exercice) AddRawFlagKey(name string, help string, ignorecase bool, valid // Check that raw value passes through the regexp if validator_regexp != nil { var err error - if raw_value, err = ExecValidatorRegexp(*validator_regexp, raw_value); err != nil { + if raw_value, err = ExecValidatorRegexp(*validator_regexp, raw_value, ignorecase); err != nil { return FlagKey{}, err } } @@ -127,7 +130,7 @@ func (k FlagKey) ComputeChecksum(val []byte) (cksum []byte, err error) { // Check that raw value passes through the regexp if k.ValidatorRegexp != nil { - if val, err = ExecValidatorRegexp(*k.ValidatorRegexp, val); err != nil { + if val, err = ExecValidatorRegexp(*k.ValidatorRegexp, val, k.IgnoreCase); err != nil { return } } From 2b106df669362f6294ed2570c7082952c0327e3c Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 25 Jan 2019 08:29:39 +0100 Subject: [PATCH 0003/1637] frontend: avoid fetching events.json on public interface --- frontend/static/js/challenge.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/static/js/challenge.js b/frontend/static/js/challenge.js index d80e96ff..d56ce50b 100644 --- a/frontend/static/js/challenge.js +++ b/frontend/static/js/challenge.js @@ -250,6 +250,8 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) var refreshRate = 1200; if ($rootScope.notify_field == 0 && eventsLastTreated) refreshRate = 30000; + if ($scope.my && !$scope.my.team_id) + return; refreshEventsInterval = $interval(refreshEvents, Math.floor(Math.random() * refreshRate * 2) + refreshRate); if (!eventsLastTreated) { From 7587cd9140aa6263e5a8863a19a9729123cd0fdc Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 25 Jan 2019 12:39:27 +0100 Subject: [PATCH 0004/1637] Remove old unused files --- admin/fill_exercices.sh | 262 ---------------------------------------- admin/get_files.sh | 23 ---- 2 files changed, 285 deletions(-) delete mode 100755 admin/fill_exercices.sh delete mode 100755 admin/get_files.sh diff --git a/admin/fill_exercices.sh b/admin/fill_exercices.sh deleted file mode 100755 index e2ce2141..00000000 --- a/admin/fill_exercices.sh +++ /dev/null @@ -1,262 +0,0 @@ -#!/bin/bash - -BASEURL="http://localhost:8081" -BASEURI="https://owncloud.srs.epita.fr/remote.php/webdav/FIC 2018" -BASEFILE="/mnt/fic/" -CLOUDPASS="$CLOUD_USER:$CLOUD_PASS" - -new_theme() { - NAME=`echo $1 | sed 's/"/\\\\"/g'` - AUTHORS=`echo $2 | sed 's/"/\\\\"/g'` - curl -f -s -d "{\"name\": \"$NAME\", \"authors\": \"$AUTHORS\"}" "${BASEURL}/api/themes" | - grep -Eo '"id":[0-9]+,' | grep -Eo "[0-9]+" -} - -new_exercice() { - THEME="$1" - TITLE=`echo "$2" | sed 's/"/\\\\"/g'` - STATEMENT=`echo "$3" | sed 's/"/\\\\"/g' | sed ':a;N;$!ba;s/\n/
/g'` - DEPEND="$4" - GAIN="$5" - VIDEO="$6" - - curl -f -s -d "{\"title\": \"$TITLE\", \"statement\": \"$STATEMENT\", \"depend\": $DEPEND, \"gain\": $GAIN, \"videoURI\": \"$VIDEO\"}" "${BASEURL}/api/themes/$THEME/exercices" | - grep -Eo '"id":[0-9]+,' | grep -Eo "[0-9]+" -} - -new_file() ( - THEME="$1" - EXERCICE="$2" - URI="$3" - DIGEST="$4" - ARGS="$5" - - FIRST= - PARTS=$(echo "$ARGS" | while read arg - do - [ -n "$arg" ] && { - [ -z "${FIRST}" ] || echo -n "," - echo "\"$arg\"" - } - FIRST=1 - done) - - [ -n "${DIGEST}" ] && DIGEST=", \"digest\": \"${DIGEST}\"" - - cat <&2 -{"path": "${BASEFILE}${URI}"${DIGEST}, "parts": [${PARTS}]} -EOF - -# curl -f -s -d "{\"URI\": \"${BASEFILE}${URI}\"}" "${BASEURL}/api/themes/$THEME/$EXERCICE/files" | - curl -f -s -d @- "${BASEURL}/api/themes/$THEME/exercices/$EXERCICE/files" </g'` - COST="$5" - URI="$6" - - [ -n "${CONTENT}" ] && CONTENT=", \"content\": \"${CONTENT}\"" - [ -n "${URI}" ] && URI=", \"path\": \"${BASEFILE}${URI}\"" - - curl -f -s -d "{\"title\": \"$TITLE\"$CONTENT$URI, \"cost\": $COST}" "${BASEURL}/api/themes/$THEME/exercices/$EXERCICE/hints" | - grep -Eo '"id":[0-9]+,' | grep -Eo "[0-9]+" -} - -new_key() { - THEME="$1" - EXERCICE="$2" - TYPE="$3" - KEY=`echo $4 | sed 's#\\\\#\\\\\\\\#g' | sed 's/"/\\\\"/g'` - - curl -f -s -d "{\"type\": \"$TYPE\", \"key\": \"$KEY\"}" "${BASEURL}/api/themes/$THEME/exercices/$EXERCICE/keys" | - grep -Eo '"id":[0-9]+,' | grep -Eo "[0-9]+" -} - -get_dir_from_cloud() { - curl -f -s -X PROPFIND -u "${CLOUDPASS}" "${BASEURI}$1" | xmllint --format - | grep 'd:href' | sed -E 's/^.*>(.*)<.*$/\1/' -} -get_dir() { - ls "${BASEFILE}$1" 2> /dev/null -} -#alias get_dir=get_dir_from_cloud - -get_file_from_cloud() { - curl -f -s -u "${CLOUDPASS}" "${BASEURI}$1" | tr -d '\r' -} -get_file() { - cat "${BASEFILE}$1" 2> /dev/null | tr -d '\r' - echo -} -#alias get_file=get_file_from_cloud - -unhtmlentities() { - cat | sed -E 's/%20/ /g' | sed -E "s/%27/'/g" | sed -E 's/%c3%a9/é/g' | sed -E 's/%c3%a8/è/g' -} - -# Theme -{ - if [ $# -ge 1 ]; then - echo $1 - else - get_dir "" - fi -} | while read f; do basename "$f"; done | while read THEME_URI -do - THM_BASEURI="/${THEME_URI}/" - THEME_NAME=$(echo "${THEME_URI#*-}" | unhtmlentities) - THEME_AUTHORS=$(get_file "${THM_BASEURI}/AUTHORS.txt" | sed '/^$/d;s/$/, /' | tr -d '\n' | sed 's/, $//') - THEME_ID=`new_theme "$THEME_NAME" "$THEME_AUTHORS"` - if [ -z "$THEME_ID" ]; then - echo -e "\e[31;01m!!! An error occured during theme add\e[00m" - continue - else - echo -e "\e[33m>>> New theme created:\e[00m $THEME_ID - $THEME_NAME" - fi - - LAST=null - EXO_NUM=0 - { - if [ $# -ge 2 ]; then - echo "$2" - else - get_dir "${THM_BASEURI}" - fi - } | while read f; do basename "$f"; done | while read EXO_URI - do - case ${EXO_URI} in - [0-9]-*) - ;; - *) - continue;; - esac - - #EXO_NUM=$((EXO_NUM + 1)) - EXO_NUM=${EXO_URI%-*} - EXO_NAME=$(echo "${EXO_URI#*-}" | unhtmlentities) - echo - echo -e "\e[36m--- Filling exercice ${EXO_NUM} in theme ${THEME_NAME}\e[00m" - - EXO_BASEURI="${EXO_URI}/" - - EXO_VIDEO=$(get_dir "${THM_BASEURI}${EXO_BASEURI}/resolution/" | grep -E "\.(mov|mkv|mp4|avi|flv|ogv|webm)$" | while read f; do basename "$f"; done | tail -1) - [ -n "$EXO_VIDEO" ] && EXO_VIDEO="/resolution${THM_BASEURI}${EXO_BASEURI}resolution/${EXO_VIDEO}" - - if [ "${LAST}" = "null" ]; then - echo ">>> Assuming this exercice has no dependency" - else - echo ">>> Assuming this exercice depends on the last entry (id=${LAST})" - fi - - EXO_GAIN=$((3 * (2 ** $EXO_NUM) - 1)) - HINT_COST=$(($EXO_GAIN / 4)) - echo ">>> Using default gain: ${EXO_GAIN} points" - - EXO_SCENARIO=$(get_file "${THM_BASEURI}${EXO_BASEURI}/scenario.txt") - - EXO_ID=`new_exercice "${THEME_ID}" "${EXO_NAME}" "${EXO_SCENARIO}" "${LAST}" "${EXO_GAIN}" "${EXO_VIDEO}"` - if [ -z "$EXO_ID" ]; then - echo -e "\e[31;01m!!! An error occured during exercice add.\e[00m" - continue - else - echo -e "\e[32m>>> New exercice created:\e[00m $EXO_ID - $EXO_NAME" - fi - - - # Keys - get_file "${THM_BASEURI}${EXO_BASEURI}/flags.txt" | while read KEYLINE - do - [ -z "${KEYLINE}" ] && continue - - KEY_NAME=$(echo "$KEYLINE" | cut -d$'\t' -f 1) - KEY_RAW=$(echo "$KEYLINE" | cut -d$'\t' -f 2-) - - if [ -z "${KEY_RAW}" ] || [ "${KEY_NAME}" = "${KEY_RAW}" ]; then - KEY_NAME=$(echo "$KEYLINE" | cut -d : -f 1) - KEY_RAW=$(echo "$KEYLINE" | cut -d : -f 2-) - fi - - if [ -z "${KEY_NAME}" ]; then - KEY_NAME="Flag" - fi - - KEY_ID=`new_key "${THEME_ID}" "${EXO_ID}" "${KEY_NAME}" "${KEY_RAW}"` - if [ -z "$KEY_ID" ]; then - echo -e "\e[31;01m!!! An error occured during key import!\e[00m (name=${KEYNAME};raw=${KEY_RAW})" - else - echo -e "\e[32m>>> New key added:\e[00m $KEY_ID - $KEY_NAME" - fi - done - - - # Hints - HINTS=$(get_dir "${THM_BASEURI}${EXO_BASEURI}/hints/" | sed -E 's#(.*)#hints/\1#') - [ -z "${HINTS}" ] && HINTS=$(get_dir "${THM_BASEURI}${EXO_BASEURI}/" | grep ^hint.) - [ -z "${HINTS}" ] && HINTS="hint.txt" - HINT_COUNT=1 - echo "${HINTS}" | while read HINT - do - EXO_HINT=$(get_file "${THM_BASEURI}${EXO_BASEURI}/${HINT}") - if [ -n "$EXO_HINT" ]; then - EXO_HINT_TYPE=$(echo "${EXO_HINT}" | file --mime-type -b -) - if echo "${EXO_HINT_TYPE}" | grep text/ && [ $(echo "${EXO_HINT}" | wc -l) -lt 25 ]; then - HINT_ID=`new_hint "${THEME_ID}" "${EXO_ID}" "Astuce #${HINT_COUNT}" "${EXO_HINT}" "${HINT_COST}"` - else - HINT_ID=`new_hint "${THEME_ID}" "${EXO_ID}" "Astuce #${HINT_COUNT}" "" "${HINT_COST}" "${THM_BASEURI}${EXO_BASEURI}/${HINT}"` - fi - - if [ -z "$HINT_ID" ]; then - echo -e "\e[31;01m!!! An error occured during hint import!\e[00m (title=Astuce #${HINT_COUNT};content::${EXO_HINT_TYPE};cost=${HINT_COST})" - else - echo -e "\e[32m>>> New hint added:\e[00m $HINT_ID - Astuce #${HINT_COUNT}" - fi - fi - HINT_COUNT=$(($HINT_COUNT + 1)) - done - - - # Files: splited - get_dir "${THM_BASEURI}${EXO_BASEURI}files/" | grep -v DIGESTS.txt | grep '[0-9][0-9]$' | sed -E 's/\.?([0-9][0-9])$//' | sort | uniq | while read f; do basename "$f"; done | while read FILE_URI - do - DIGEST=$(get_file "${THM_BASEURI}${EXO_BASEURI}files/DIGESTS.txt" | grep "${FILE_URI}\$" | awk '{ print $1; }') - - PARTS= - for part in $(get_dir "${THM_BASEURI}${EXO_BASEURI}files/" | grep "${FILE_URI}" | sort) - do - PARTS="${PARTS}${BASEFILE}${THM_BASEURI}${EXO_BASEURI}files/${part} -" - done - echo -e "\e[35mImport splited file ${THM_BASEURI}${EXO_BASEURI}files/${FILE_URI} from\e[00m `echo ${PARTS} | tr '\n' ' '`" - - FILE_ID=`new_file "${THEME_ID}" "${EXO_ID}" "${THM_BASEURI}${EXO_BASEURI}files/${FILE_URI}" "${DIGEST}" "${PARTS}"` - if [ -z "$FILE_ID" ]; then - echo -e "\e[31;01m!!! An error occured during file import! Please check path.\e[00m" - else - echo -e "\e[32m>>> New file added:\e[00m $FILE_ID - $FILE_URI" - fi - done - - # Files: entire - get_dir "${THM_BASEURI}${EXO_BASEURI}files/" | grep -v DIGESTS.txt | grep -v '[0-9][0-9]$' | while read f; do basename "$f"; done | while read FILE_URI - do - DIGEST=$(get_file "${THM_BASEURI}${EXO_BASEURI}files/DIGESTS.txt" | grep "${FILE_URI}\$" | awk '{ print $1; }') - - echo "Import file ${THM_BASEURI}${EXO_BASEURI}files/${FILE_URI}" - FILE_ID=`new_file "${THEME_ID}" "${EXO_ID}" "${THM_BASEURI}${EXO_BASEURI}files/${FILE_URI}" "${DIGEST}"` - if [ -z "$FILE_ID" ]; then - echo -e "\e[31;01m!!! An error occured during file import! Please check path.\e[00m" - else - echo -e "\e[32m>>> New file added:\e[00m $FILE_ID - $FILE_URI" - fi - done - - - LAST=$EXO_ID - done - echo -done diff --git a/admin/get_files.sh b/admin/get_files.sh deleted file mode 100755 index 68c032cb..00000000 --- a/admin/get_files.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh - -BASEURL="http://localhost:8081" -BASEURI="https://srs.epita.fr/owncloud/remote.php/webdav/FIC 2016" -CLOUDUSER='fic' -CLOUDPASS='f>t\nV33R|(+?$i*' - -if [ $# -gt 0 ] -then - WHERE=$1 -else - WHERE="files" -fi - -curl -q -f ${BASEURL}/api/themes/files-bindings | while read l -do - FROM=$(echo "$l" | cut -d ";" -f 1) - DEST=$(echo "$l" | cut -d ";" -f 2) - - mkdir -p $(dirname "${WHERE}${DEST}") - - wget -O "${WHERE}${DEST}" --user "${CLOUDUSER}" --password "${CLOUDPASS}" "${BASEURI}${FROM}" -done From 81502ce2382f6d1fdcb95f83a6f03def3a8324c3 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 25 Jan 2019 13:29:51 +0100 Subject: [PATCH 0005/1637] frontend: prefer default border color in home public screen --- frontend/static/views/home.html | 2 +- frontend/static/views/tag.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/static/views/home.html b/frontend/static/views/home.html index 134052b3..ad65dc54 100644 --- a/frontend/static/views/home.html +++ b/frontend/static/views/home.html @@ -15,7 +15,7 @@
-
+
diff --git a/frontend/static/views/tag.html b/frontend/static/views/tag.html index 6f606d28..7dc8910a 100644 --- a/frontend/static/views/tag.html +++ b/frontend/static/views/tag.html @@ -1,5 +1,5 @@
-
+
From caa05110234fa24b8e9938799e75c44b30974860 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 25 Jan 2019 16:31:48 +0100 Subject: [PATCH 0006/1637] admin: add a new route to generate a file for movie links --- admin/api/exercice.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/admin/api/exercice.go b/admin/api/exercice.go index a1ab5153..6d698641 100644 --- a/admin/api/exercice.go +++ b/admin/api/exercice.go @@ -14,6 +14,7 @@ import ( func init() { router.GET("/api/exercices/", apiHandler(listExercices)) + router.GET("/api/resolutions.json", apiHandler(exportResolutionMovies)) router.GET("/api/exercices/:eid", apiHandler(exerciceHandler(showExercice))) router.PUT("/api/exercices/:eid", apiHandler(exerciceHandler(updateExercice))) @@ -85,6 +86,28 @@ func listExercices(_ httprouter.Params, body []byte) (interface{}, error) { return fic.GetExercices() } +// Generate the csv to export with: +// curl -s http://127.0.0.1:8081/api/resolutions.json | jq -r ".[] | [ .theme,.title, @uri \"https://fic.srs.epita.fr/resolution/\\(.videoURI)\" ] | join(\";\")" +func exportResolutionMovies(_ httprouter.Params, body []byte) (interface{}, error) { + if exercices, err := fic.GetExercices(); err != nil { + return nil, err + } else { + export := []map[string]string{} + for _, exercice := range exercices { + if theme, err := fic.GetTheme(exercice.IdTheme); err != nil { + return nil, err + } else { + export = append(export, map[string]string{ + "videoURI": exercice.VideoURI, + "theme": theme.Name, + "title": exercice.Title, + }) + } + } + return export, nil + } +} + func listExerciceFiles(exercice fic.Exercice, body []byte) (interface{}, error) { return exercice.GetFiles() } From 0b0a9789644dc9fc7db2c5831cf1e569cdc8fa4c Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 1 Feb 2019 20:50:58 +0100 Subject: [PATCH 0007/1637] admin: thanks to ng-base, don't need other modifications --- admin/index.go | 30 +++++++++++++++--------------- admin/static/index.html | 30 +++++++++++++++--------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/admin/index.go b/admin/index.go index 0461becf..595d0b2f 100644 --- a/admin/index.go +++ b/admin/index.go @@ -32,8 +32,8 @@ const indextpl = `
diff --git a/libfic/exercice_history.go b/libfic/exercice_history.go new file mode 100644 index 00000000..5e056311 --- /dev/null +++ b/libfic/exercice_history.go @@ -0,0 +1,110 @@ +package fic + +import ( + "fmt" + "time" +) + +// GetHistory aggregates all sources of events or actions for an Exercice +func (e Exercice) GetHistory() ([]map[string]interface{}, error) { + hist := make([]map[string]interface{}, 0) + + if rows, err := DBQuery(`SELECT id_team, U.name, U.color, "tries" AS kind, time, id_exercice, NULL, NULL FROM exercice_tries NATURAL JOIN teams U WHERE id_exercice = ? UNION + SELECT id_team, U.name, U.color, "solved" AS kind, time, id_exercice, coefficient, NULL FROM exercice_solved S NATURAL JOIN teams U WHERE id_exercice = ? UNION + SELECT id_team, U.name, U.color, "hint" AS kind, time, id_exercice, H.id_hint, H.title FROM team_hints T INNER JOIN exercice_hints H ON H.id_hint = T.id_hint NATURAL JOIN teams U WHERE id_exercice = ? UNION + SELECT id_team, U.name, U.color, "wchoices" AS kind, time, id_exercice, F.id_flag, F.type FROM team_wchoices W INNER JOIN exercice_flags F ON F.id_flag = W.id_flag NATURAL JOIN teams U WHERE id_exercice = ? UNION + SELECT id_team, U.name, U.color, "flag_found" AS kind, time, id_exercice, K.id_flag, K.type FROM flag_found F INNER JOIN exercice_flags K ON K.id_flag = F.id_flag NATURAL JOIN teams U WHERE id_exercice = ? UNION + SELECT id_team, U.name, U.color, "mcq_found" AS kind, time, id_exercice, Q.id_mcq, Q.title FROM mcq_found F INNER JOIN exercice_mcq Q ON Q.id_mcq = F.id_mcq NATURAL JOIN teams U WHERE id_exercice = ? + ORDER BY time DESC`, e.Id, e.Id, e.Id, e.Id, e.Id, e.Id); err != nil { + return nil, err + } else { + defer rows.Close() + + for rows.Next() { + var id_team int64 + var team_name string + var team_color uint32 + var kind string + var time time.Time + var exercice int64 + var secondary *int64 + var secondary_title *string + + if err := rows.Scan(&id_team, &team_name, &team_color, &kind, &time, &exercice, &secondary, &secondary_title); err != nil { + return nil, err + } + + h := map[string]interface{}{} + + h["team_id"] = id_team + h["team_name"] = team_name + h["team_color"] = fmt.Sprintf("#%x", team_color) + h["kind"] = kind + h["time"] = time + h["primary"] = e.Id + if secondary != nil { + h["secondary"] = secondary + h["secondary_title"] = secondary_title + } + + hist = append(hist, h) + } + } + + return hist, nil +} + +// DelHistoryItem removes from the database an entry from the history. +func (e Exercice) DelHistoryItem(tId int64, kind string, h time.Time, secondary *int64) (interface{}, error) { + if kind == "tries" { + if res, err := DBExec("DELETE FROM exercice_tries WHERE id_team = ? AND time = ? AND id_exercice = ?", tId, h, e.Id); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } + } else if kind == "hint" && secondary != nil { + if res, err := DBExec("DELETE FROM team_hints WHERE id_team = ? AND time = ? AND id_hint = ?", tId, h, *secondary); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } + } else if kind == "wchoices" && secondary != nil { + if res, err := DBExec("DELETE FROM team_wchoices WHERE id_team = ? AND time = ? AND id_flag = ?", tId, h, *secondary); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } + } else if kind == "flag_found" && secondary != nil { + if res, err := DBExec("DELETE FROM flag_found WHERE id_team = ? AND time = ? AND id_flag = ?", tId, h, *secondary); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } + } else if kind == "mcq_found" && secondary != nil { + if res, err := DBExec("DELETE FROM mcq_found WHERE id_team = ? AND time = ? AND id_mcq = ?", tId, h, *secondary); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } + } else if kind == "solved" { + if res, err := DBExec("DELETE FROM exercice_solved WHERE id_team = ? AND time = ? AND id_exercice = ?", tId, h, e.Id); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } + } else { + return nil, nil + } +} From 4125c7b1615c5241fee659607009742176ed9017 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 3 Feb 2019 22:40:44 +0100 Subject: [PATCH 0015/1637] admin: include coefficient in history.json --- admin/static/views/exercice.html | 3 +-- admin/static/views/team-edit.html | 2 +- libfic/exercice_history.go | 16 +++++++++------- libfic/team_history.go | 16 +++++++++------- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/admin/static/views/exercice.html b/admin/static/views/exercice.html index 9bfa0547..7c72891a 100644 --- a/admin/static/views/exercice.html +++ b/admin/static/views/exercice.html @@ -227,7 +227,7 @@ - {{ row.time | date:"mediumTime" }}
{{ row.kind }} + {{ row.time | date:"mediumTime" }}
{{ row.kind }} x{{ row.coefficient }} @@ -239,7 +239,6 @@ {{ row.secondary_title }} {{ row.secondary_title }} - (coeff x{{ row.secondary }}) : {{ row.secondary }} diff --git a/admin/static/views/team-edit.html b/admin/static/views/team-edit.html index 9053bdf3..0be2b267 100644 --- a/admin/static/views/team-edit.html +++ b/admin/static/views/team-edit.html @@ -109,7 +109,7 @@ - {{ row.time | date:"mediumTime" }}
{{ row.kind }} + {{ row.time | date:"mediumTime" }}
{{ row.kind }} x{{ row.coefficient }} diff --git a/libfic/exercice_history.go b/libfic/exercice_history.go index 5e056311..29873345 100644 --- a/libfic/exercice_history.go +++ b/libfic/exercice_history.go @@ -9,12 +9,12 @@ import ( func (e Exercice) GetHistory() ([]map[string]interface{}, error) { hist := make([]map[string]interface{}, 0) - if rows, err := DBQuery(`SELECT id_team, U.name, U.color, "tries" AS kind, time, id_exercice, NULL, NULL FROM exercice_tries NATURAL JOIN teams U WHERE id_exercice = ? UNION - SELECT id_team, U.name, U.color, "solved" AS kind, time, id_exercice, coefficient, NULL FROM exercice_solved S NATURAL JOIN teams U WHERE id_exercice = ? UNION - SELECT id_team, U.name, U.color, "hint" AS kind, time, id_exercice, H.id_hint, H.title FROM team_hints T INNER JOIN exercice_hints H ON H.id_hint = T.id_hint NATURAL JOIN teams U WHERE id_exercice = ? UNION - SELECT id_team, U.name, U.color, "wchoices" AS kind, time, id_exercice, F.id_flag, F.type FROM team_wchoices W INNER JOIN exercice_flags F ON F.id_flag = W.id_flag NATURAL JOIN teams U WHERE id_exercice = ? UNION - SELECT id_team, U.name, U.color, "flag_found" AS kind, time, id_exercice, K.id_flag, K.type FROM flag_found F INNER JOIN exercice_flags K ON K.id_flag = F.id_flag NATURAL JOIN teams U WHERE id_exercice = ? UNION - SELECT id_team, U.name, U.color, "mcq_found" AS kind, time, id_exercice, Q.id_mcq, Q.title FROM mcq_found F INNER JOIN exercice_mcq Q ON Q.id_mcq = F.id_mcq NATURAL JOIN teams U WHERE id_exercice = ? + if rows, err := DBQuery(`SELECT id_team, U.name, U.color, "tries" AS kind, time, 0, id_exercice, NULL, NULL FROM exercice_tries NATURAL JOIN teams U WHERE id_exercice = ? UNION + SELECT id_team, U.name, U.color, "solved" AS kind, time, coefficient, id_exercice, NULL, NULL FROM exercice_solved S NATURAL JOIN teams U WHERE id_exercice = ? UNION + SELECT id_team, U.name, U.color, "hint" AS kind, time, coefficient, id_exercice, H.id_hint, H.title FROM team_hints T INNER JOIN exercice_hints H ON H.id_hint = T.id_hint NATURAL JOIN teams U WHERE id_exercice = ? UNION + SELECT id_team, U.name, U.color, "wchoices" AS kind, time, coefficient, id_exercice, F.id_flag, F.type FROM team_wchoices W INNER JOIN exercice_flags F ON F.id_flag = W.id_flag NATURAL JOIN teams U WHERE id_exercice = ? UNION + SELECT id_team, U.name, U.color, "flag_found" AS kind, time, 0, id_exercice, K.id_flag, K.type FROM flag_found F INNER JOIN exercice_flags K ON K.id_flag = F.id_flag NATURAL JOIN teams U WHERE id_exercice = ? UNION + SELECT id_team, U.name, U.color, "mcq_found" AS kind, time, 0, id_exercice, Q.id_mcq, Q.title FROM mcq_found F INNER JOIN exercice_mcq Q ON Q.id_mcq = F.id_mcq NATURAL JOIN teams U WHERE id_exercice = ? ORDER BY time DESC`, e.Id, e.Id, e.Id, e.Id, e.Id, e.Id); err != nil { return nil, err } else { @@ -26,11 +26,12 @@ func (e Exercice) GetHistory() ([]map[string]interface{}, error) { var team_color uint32 var kind string var time time.Time + var coeff float32 var exercice int64 var secondary *int64 var secondary_title *string - if err := rows.Scan(&id_team, &team_name, &team_color, &kind, &time, &exercice, &secondary, &secondary_title); err != nil { + if err := rows.Scan(&id_team, &team_name, &team_color, &kind, &time, &coeff, &exercice, &secondary, &secondary_title); err != nil { return nil, err } @@ -41,6 +42,7 @@ func (e Exercice) GetHistory() ([]map[string]interface{}, error) { h["team_color"] = fmt.Sprintf("#%x", team_color) h["kind"] = kind h["time"] = time + h["coefficient"] = coeff h["primary"] = e.Id if secondary != nil { h["secondary"] = secondary diff --git a/libfic/team_history.go b/libfic/team_history.go index 8a2cb581..b73656d3 100644 --- a/libfic/team_history.go +++ b/libfic/team_history.go @@ -8,12 +8,12 @@ import ( func (t Team) GetHistory() ([]map[string]interface{}, error) { hist := make([]map[string]interface{}, 0) - if rows, err := DBQuery(`SELECT id_team, "tries" AS kind, time, E.id_exercice, E.title, NULL, NULL FROM exercice_tries T INNER JOIN exercices E ON E.id_exercice = T.id_exercice WHERE id_team = ? UNION - SELECT id_team, "solved" AS kind, time, E.id_exercice, E.title, coefficient, NULL FROM exercice_solved S INNER JOIN exercices E ON E.id_exercice = S.id_exercice WHERE id_team = ? UNION - SELECT id_team, "hint" AS kind, time, E.id_exercice, E.title, H.id_hint, H.title FROM team_hints T INNER JOIN exercice_hints H ON H.id_hint = T.id_hint INNER JOIN exercices E ON E.id_exercice = H.id_exercice WHERE id_team = ? UNION - SELECT id_team, "wchoices" AS kind, time, E.id_exercice, E.title, F.id_flag, F.type FROM team_wchoices W INNER JOIN exercice_flags F ON F.id_flag = W.id_flag INNER JOIN exercices E ON E.id_exercice = F.id_exercice WHERE id_team = ? UNION - SELECT id_team, "flag_found" AS kind, time, E.id_exercice, E.title, K.id_flag, K.type FROM flag_found F INNER JOIN exercice_flags K ON K.id_flag = F.id_flag INNER JOIN exercices E ON K.id_exercice = E.id_exercice WHERE id_team = ? UNION - SELECT id_team, "mcq_found" AS kind, time, E.id_exercice, E.title, Q.id_mcq, Q.title FROM mcq_found F INNER JOIN exercice_mcq Q ON Q.id_mcq = F.id_mcq INNER JOIN exercices E ON Q.id_exercice = E.id_exercice WHERE id_team = ? + if rows, err := DBQuery(`SELECT id_team, "tries" AS kind, time, 0, E.id_exercice, E.title, NULL, NULL FROM exercice_tries T INNER JOIN exercices E ON E.id_exercice = T.id_exercice WHERE id_team = ? UNION + SELECT id_team, "solved" AS kind, time, coefficient, E.id_exercice, E.title, NULL, NULL FROM exercice_solved S INNER JOIN exercices E ON E.id_exercice = S.id_exercice WHERE id_team = ? UNION + SELECT id_team, "hint" AS kind, time, coefficient, E.id_exercice, E.title, H.id_hint, H.title FROM team_hints T INNER JOIN exercice_hints H ON H.id_hint = T.id_hint INNER JOIN exercices E ON E.id_exercice = H.id_exercice WHERE id_team = ? UNION + SELECT id_team, "wchoices" AS kind, time, coefficient, E.id_exercice, E.title, F.id_flag, F.type FROM team_wchoices W INNER JOIN exercice_flags F ON F.id_flag = W.id_flag INNER JOIN exercices E ON E.id_exercice = F.id_exercice WHERE id_team = ? UNION + SELECT id_team, "flag_found" AS kind, time, 0, E.id_exercice, E.title, K.id_flag, K.type FROM flag_found F INNER JOIN exercice_flags K ON K.id_flag = F.id_flag INNER JOIN exercices E ON K.id_exercice = E.id_exercice WHERE id_team = ? UNION + SELECT id_team, "mcq_found" AS kind, time, 0, E.id_exercice, E.title, Q.id_mcq, Q.title FROM mcq_found F INNER JOIN exercice_mcq Q ON Q.id_mcq = F.id_mcq INNER JOIN exercices E ON Q.id_exercice = E.id_exercice WHERE id_team = ? ORDER BY time DESC`, t.Id, t.Id, t.Id, t.Id, t.Id, t.Id); err != nil { return nil, err } else { @@ -23,12 +23,13 @@ func (t Team) GetHistory() ([]map[string]interface{}, error) { var id_team int64 var kind string var time time.Time + var coefficient float32 var primary *int64 var primary_title *string var secondary *int64 var secondary_title *string - if err := rows.Scan(&id_team, &kind, &time, &primary, &primary_title, &secondary, &secondary_title); err != nil { + if err := rows.Scan(&id_team, &kind, &time, &coefficient, &primary, &primary_title, &secondary, &secondary_title); err != nil { return nil, err } @@ -36,6 +37,7 @@ func (t Team) GetHistory() ([]map[string]interface{}, error) { h["kind"] = kind h["time"] = time + h["coefficient"] = coefficient if primary != nil { h["primary"] = primary h["primary_title"] = primary_title From c2887a1812c2c9b76e2d83aaa9828c22b5b63552 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 3 Feb 2019 22:49:03 +0100 Subject: [PATCH 0016/1637] admin: add a new route to update team history coefficient --- admin/api/team.go | 25 +++++++++++++++++++++---- libfic/team_history.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/admin/api/team.go b/admin/api/team.go index dcffb055..8c32692d 100644 --- a/admin/api/team.go +++ b/admin/api/team.go @@ -238,10 +238,27 @@ func setTeamMember(team fic.Team, body []byte) (interface{}, error) { } type uploadedHistory struct { - Kind string - Time time.Time - Primary *int64 - Secondary *int64 + Kind string + Time time.Time + Primary *int64 + Secondary *int64 + Coefficient float32 +} + +func updateHistory(team *fic.Team, body []byte) (interface{}, error) { + var uh uploadedHistory + if err := json.Unmarshal(body, &uh); err != nil { + return nil, err + } + + var givenId int64 + if uh.Secondary != nil { + givenId = *uh.Secondary + } else if uh.Primary != nil { + givenId = *uh.Primary + } + + return team.UpdateHistoryCoeff(uh.Kind, uh.Time, givenId, uh.Coefficient) } func delHistory(team *fic.Team, body []byte) (interface{}, error) { diff --git a/libfic/team_history.go b/libfic/team_history.go index b73656d3..0b21276a 100644 --- a/libfic/team_history.go +++ b/libfic/team_history.go @@ -54,6 +54,37 @@ func (t Team) GetHistory() ([]map[string]interface{}, error) { return hist, nil } +// UpdateHistoryCoeff updates the coefficient for a given entry. +func (t Team) UpdateHistoryCoeff(kind string, h time.Time, givenId int64, newCoeff float32) (interface{}, error) { + if kind == "hint" { + if res, err := DBExec("UPDATE team_hints SET coefficient = ? WHERE id_team = ? AND time = ? AND id_hint = ?", newCoeff, t.Id, h, givenId); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } + } else if kind == "wchoices" { + if res, err := DBExec("UPDATE team_wchoices SET coefficient = ? WHERE id_team = ? AND time = ? AND id_flag = ?", newCoeff, t.Id, h, givenId); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } + } else if kind == "solved" { + if res, err := DBExec("UPDATE exercice_solved SET coefficient = ? WHERE id_team = ? AND time = ? AND id_exercice = ?", newCoeff, t.Id, h, givenId); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } + } else { + return nil, nil + } +} + // DelHistoryItem removes from the database an entry from the history. func (t Team) DelHistoryItem(kind string, h time.Time, primary *int64, secondary *int64) (interface{}, error) { if kind == "tries" && primary != nil { From ee2f65aae74267decc5d33de3a4f485e92d1cfd8 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 3 Feb 2019 22:48:14 +0100 Subject: [PATCH 0017/1637] admin: new route to get exercice stats --- admin/api/exercice.go | 10 ++++++++++ admin/static/js/app.js | 6 ++++++ libfic/exercice.go | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/admin/api/exercice.go b/admin/api/exercice.go index 30b7aff6..d7c7ebd6 100644 --- a/admin/api/exercice.go +++ b/admin/api/exercice.go @@ -22,6 +22,7 @@ func init() { router.PATCH("/api/exercices/:eid", apiHandler(exerciceHandler(partUpdateExercice))) router.DELETE("/api/exercices/:eid", apiHandler(exerciceHandler(deleteExercice))) + router.GET("/api/exercices/:eid/stats", apiHandler(exerciceHandler(getExerciceStats))) router.GET("/api/exercices/:eid/history.json", apiHandler(exerciceHandler(getExerciceHistory))) router.DELETE("/api/exercices/:eid/history.json", apiHandler(exerciceHandler(delExerciceHistory))) @@ -136,6 +137,15 @@ func showExercice(exercice fic.Exercice, body []byte) (interface{}, error) { return exercice, nil } +func getExerciceStats(exercice fic.Exercice, body []byte) (interface{}, error) { + team_tries, tries := exercice.TriesByTeam() + return map[string]interface{}{ + "solved": exercice.SolvedTeams(), + "total_tried": tries, + "team_tries": team_tries, + }, nil +} + func getExerciceHistory(exercice fic.Exercice, body []byte) (interface{}, error) { return exercice.GetHistory() } diff --git a/admin/static/js/app.js b/admin/static/js/app.js index ebf32265..e5bea0ab 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -1197,6 +1197,12 @@ angular.module("FICApp") } else { $scope.exercice = Exercice.get({ exerciceId: $routeParams.exerciceId }); } + $http({ + url: "/api/exercices/" + $routeParams.exerciceId + "/stats", + method: "GET" + }).then(function(response) { + $scope.stats = response.data; + }); $http({ url: "/api/themes.json", method: "GET" diff --git a/libfic/exercice.go b/libfic/exercice.go index ea2a3795..efce52b1 100644 --- a/libfic/exercice.go +++ b/libfic/exercice.go @@ -330,6 +330,21 @@ func (e Exercice) SolvedCount() int64 { } } +// SolvedTeams returns the list of Team that already have solved the challenge. +func (e Exercice) SolvedTeams() (teams []int64) { + if rows, err := DBQuery("SELECT id_team FROM exercice_solved WHERE id_exercice = ?", e.Id); err == nil { + defer rows.Close() + + for rows.Next() { + var tid int64 + if err := rows.Scan(&tid); err == nil { + teams = append(teams, tid) + } + } + } + return +} + // TriedTeamCount returns the number of Team that attempted to solve the exercice. func (e Exercice) TriedTeamCount() int64 { tries_table := "exercice_tries" @@ -345,6 +360,32 @@ func (e Exercice) TriedTeamCount() int64 { } } +type TbT struct { + IdTeam int64 `json:"id_team"` + Tries int64 `json:"tries"` +} + +// TriesByTeam returns the number of tries by Team. +func (e Exercice) TriesByTeam() (ts []TbT, sum int64) { + tries_table := "exercice_tries" + if SubmissionUniqueness { + tries_table = "exercice_distinct_tries" + } + + if rows, err := DBQuery("SELECT id_team, COUNT(id_team) FROM " + tries_table + " WHERE id_exercice = ? GROUP BY id_team", e.Id); err == nil { + defer rows.Close() + + for rows.Next() { + var tbt TbT + if err := rows.Scan(&tbt.IdTeam, &tbt.Tries); err == nil { + sum += tbt.Tries + ts = append(ts, tbt) + } + } + } + return +} + // TriedCount returns the number of cumulative attempts, all Team combined, for the exercice. func (e Exercice) TriedCount() int64 { tries_table := "exercice_tries" From db22c4af1b09b449597537ce50ce4cd50f8160cb Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 25 Jan 2019 07:26:52 +0100 Subject: [PATCH 0018/1637] frontend: polish public version checks --- frontend/static/js/challenge.js | 38 ++++++++++++++++++++++++++------- frontend/static/views/defi.html | 5 ++++- libfic/team_my.go | 4 ++++ 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/frontend/static/js/challenge.js b/frontend/static/js/challenge.js index bbf92bff..4a24f266 100644 --- a/frontend/static/js/challenge.js +++ b/frontend/static/js/challenge.js @@ -19,12 +19,21 @@ function treatFlagKey(flag) { } if (flag.found == null && flag.soluce !== undefined) { - if (check === undefined) check = true; + if (flag.value && flag.soluce) { + if (flag.ignore_case) + flag.value = flag.value.toLowerCase(); + if (flag.validator_regexp) { + var re = new RegExp(flag.validator_regexp, flag.ignore_case?'ui':'u'); + var match = re.exec(flag.value); + match.shift(); + flag.value = match.join("+"); + } - if (flag.value && flag.soluce == b2sum(flag.value)) - flag.found = new Date(); - check &= flag.found; + if (flag.soluce == b2sum(flag.value)) + flag.found = new Date(); + } } + return flag.found !== undefined && flag.found !== false; } angular.module("FICApp", ["ngRoute", "ngSanitize"]) @@ -329,7 +338,11 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) angular.forEach(data.exercices, function(exercice, eid) { if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].submitted) data.exercices[eid].timeouted = true; + if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].solved !== undefined) + data.exercices[eid].solved = $scope.my.exercices[eid].solved; angular.forEach(exercice.flags, function(flag, fid) { + if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].flags && $scope.my.exercices[eid].flags[fid] && $scope.my.exercices[eid].flags[fid].found !== undefined) + data.exercices[eid].flags[fid].found = $scope.my.exercices[eid].flags[fid].found; if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].flags && $scope.my.exercices[eid].flags[fid] && $scope.my.exercices[eid].flags[fid].value !== undefined) data.exercices[eid].flags[fid].value = $scope.my.exercices[eid].flags[fid].value; if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].flags && $scope.my.exercices[eid].flags[fid] && $scope.my.exercices[eid].flags[fid].values !== undefined) @@ -337,6 +350,10 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) else data.exercices[eid].flags[fid].values = [""]; }); + angular.forEach(exercice.mcqs, function(mcq, mid) { + if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].mcqs && $scope.my.exercices[eid].mcqs[mid] && $scope.my.exercices[eid].mcqs[mid].solved !== undefined) + data.exercices[eid].mcqs[mid].solved = $scope.my.exercices[eid].mcqs[mid].solved; + }); }); angular.forEach(data.exercices, function(exercice, eid) { angular.forEach(exercice.mcqs, function(mcq, mid) { @@ -474,18 +491,23 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) { resp["flags"] = {}; angular.forEach($scope.my.exercices[$rootScope.current_exercice].flags, function(flag,kid) { - treatFlagKey(flag); - if (flag.found == null && flag.soluce === undefined) { - resp["flags"][kid] = flag.value; + if (check === undefined) check = true; + + check &= treatFlagKey(flag) || flag.found; + if (flag.soluce === undefined) { + check = undefined; + if (flag.found == null) { + resp["flags"][kid] = flag.value; + } } }); } if ($scope.my.exercices[$rootScope.current_exercice].mcqs && Object.keys($scope.my.exercices[$rootScope.current_exercice].mcqs).length) { - var soluce = ""; resp["mcqs"] = {}; angular.forEach($scope.my.exercices[$rootScope.current_exercice].mcqs, function(mcq) { + var soluce = ""; if (mcq.solved == null) { angular.forEach(mcq.choices, function(choice, cid) { if (mcq.soluce !== undefined) { diff --git a/frontend/static/views/defi.html b/frontend/static/views/defi.html index 48c88b7a..64e93bc7 100644 --- a/frontend/static/views/defi.html +++ b/frontend/static/views/defi.html @@ -119,9 +119,12 @@ Défi réussi !
-

+

Vous êtes la {{ my.exercices[current_exercice].solved_rank }} équipe à avoir résolu ce défi à {{ my.exercices[current_exercice].solved_time | date:"mediumTime" }}. Vous avez marqué !

+

+ Bravo, vous avez résolu ce défi à {{ my.exercices[current_exercice].solved_time | date:"mediumTime" }}. Vous marquez ! +



diff --git a/libfic/team_my.go b/libfic/team_my.go index a471652b..0f74415f 100644 --- a/libfic/team_my.go +++ b/libfic/team_my.go @@ -30,6 +30,8 @@ type myTeamFlag struct { Help string `json:"help,omitempty"` Separator string `json:"separator,omitempty"` IgnoreOrder bool `json:"ignore_order,omitempty"` + IgnoreCase bool `json:"ignore_case,omitempty"` + ValidatorRe *string `json:"validator_regexp,omitempty"` Solved *time.Time `json:"found,omitempty"` Soluce string `json:"soluce,omitempty"` Choices map[string]string `json:"choices,omitempty"` @@ -189,6 +191,8 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { // Retrieve solved state or solution for public iface if t == nil { + flag.IgnoreCase = k.IgnoreCase + flag.ValidatorRe = k.ValidatorRegexp flag.Soluce = hex.EncodeToString(k.Checksum) } else if PartialValidation { flag.Solved = t.HasPartiallySolved(k) From e97cf884cede15221411ef4f643fd022fcad8d9d Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 25 Jan 2019 07:54:27 +0100 Subject: [PATCH 0019/1637] libfic: add igncorecase flag to regexp related to ignorecase flag --- libfic/flag_key.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libfic/flag_key.go b/libfic/flag_key.go index c31c8ac7..065cb89f 100644 --- a/libfic/flag_key.go +++ b/libfic/flag_key.go @@ -73,7 +73,10 @@ func getHashedFlag(raw_value []byte) [blake2b.Size]byte { return hash } -func ExecValidatorRegexp(vre string, val []byte) ([]byte, error) { +func ExecValidatorRegexp(vre string, val []byte, ignorecase bool) ([]byte, error) { + if (ignorecase) { + vre = "(?i)" + vre + } if re, err := regexp.Compile(vre); err != nil { return val, err } else if res := re.FindSubmatch(val); res == nil { @@ -92,7 +95,7 @@ func (e Exercice) AddRawFlagKey(name string, help string, ignorecase bool, valid // Check that raw value passes through the regexp if validator_regexp != nil { var err error - if raw_value, err = ExecValidatorRegexp(*validator_regexp, raw_value); err != nil { + if raw_value, err = ExecValidatorRegexp(*validator_regexp, raw_value, ignorecase); err != nil { return FlagKey{}, err } } @@ -127,7 +130,7 @@ func (k FlagKey) ComputeChecksum(val []byte) (cksum []byte, err error) { // Check that raw value passes through the regexp if k.ValidatorRegexp != nil { - if val, err = ExecValidatorRegexp(*k.ValidatorRegexp, val); err != nil { + if val, err = ExecValidatorRegexp(*k.ValidatorRegexp, val, k.IgnoreCase); err != nil { return } } From af73b2b8728ce7f781d070ead6966e6efaa7eac8 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 25 Jan 2019 08:29:39 +0100 Subject: [PATCH 0020/1637] frontend: avoid fetching events.json on public interface --- frontend/static/js/challenge.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/static/js/challenge.js b/frontend/static/js/challenge.js index 4a24f266..96cbeef7 100644 --- a/frontend/static/js/challenge.js +++ b/frontend/static/js/challenge.js @@ -250,6 +250,8 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) var refreshRate = 1200; if ($rootScope.notify_field == 0 && eventsLastTreated) refreshRate = 30000; + if ($scope.my && !$scope.my.team_id) + return; refreshEventsInterval = $interval(refreshEvents, Math.floor(Math.random() * refreshRate * 2) + refreshRate); if (!eventsLastTreated) { From 1757b40a9cebd6b9b10b0fdc63687828f504188c Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 25 Jan 2019 12:39:27 +0100 Subject: [PATCH 0021/1637] Remove old unused files --- admin/fill_exercices.sh | 262 ---------------------------------------- admin/get_files.sh | 23 ---- 2 files changed, 285 deletions(-) delete mode 100755 admin/fill_exercices.sh delete mode 100755 admin/get_files.sh diff --git a/admin/fill_exercices.sh b/admin/fill_exercices.sh deleted file mode 100755 index e2ce2141..00000000 --- a/admin/fill_exercices.sh +++ /dev/null @@ -1,262 +0,0 @@ -#!/bin/bash - -BASEURL="http://localhost:8081" -BASEURI="https://owncloud.srs.epita.fr/remote.php/webdav/FIC 2018" -BASEFILE="/mnt/fic/" -CLOUDPASS="$CLOUD_USER:$CLOUD_PASS" - -new_theme() { - NAME=`echo $1 | sed 's/"/\\\\"/g'` - AUTHORS=`echo $2 | sed 's/"/\\\\"/g'` - curl -f -s -d "{\"name\": \"$NAME\", \"authors\": \"$AUTHORS\"}" "${BASEURL}/api/themes" | - grep -Eo '"id":[0-9]+,' | grep -Eo "[0-9]+" -} - -new_exercice() { - THEME="$1" - TITLE=`echo "$2" | sed 's/"/\\\\"/g'` - STATEMENT=`echo "$3" | sed 's/"/\\\\"/g' | sed ':a;N;$!ba;s/\n/
/g'` - DEPEND="$4" - GAIN="$5" - VIDEO="$6" - - curl -f -s -d "{\"title\": \"$TITLE\", \"statement\": \"$STATEMENT\", \"depend\": $DEPEND, \"gain\": $GAIN, \"videoURI\": \"$VIDEO\"}" "${BASEURL}/api/themes/$THEME/exercices" | - grep -Eo '"id":[0-9]+,' | grep -Eo "[0-9]+" -} - -new_file() ( - THEME="$1" - EXERCICE="$2" - URI="$3" - DIGEST="$4" - ARGS="$5" - - FIRST= - PARTS=$(echo "$ARGS" | while read arg - do - [ -n "$arg" ] && { - [ -z "${FIRST}" ] || echo -n "," - echo "\"$arg\"" - } - FIRST=1 - done) - - [ -n "${DIGEST}" ] && DIGEST=", \"digest\": \"${DIGEST}\"" - - cat <&2 -{"path": "${BASEFILE}${URI}"${DIGEST}, "parts": [${PARTS}]} -EOF - -# curl -f -s -d "{\"URI\": \"${BASEFILE}${URI}\"}" "${BASEURL}/api/themes/$THEME/$EXERCICE/files" | - curl -f -s -d @- "${BASEURL}/api/themes/$THEME/exercices/$EXERCICE/files" </g'` - COST="$5" - URI="$6" - - [ -n "${CONTENT}" ] && CONTENT=", \"content\": \"${CONTENT}\"" - [ -n "${URI}" ] && URI=", \"path\": \"${BASEFILE}${URI}\"" - - curl -f -s -d "{\"title\": \"$TITLE\"$CONTENT$URI, \"cost\": $COST}" "${BASEURL}/api/themes/$THEME/exercices/$EXERCICE/hints" | - grep -Eo '"id":[0-9]+,' | grep -Eo "[0-9]+" -} - -new_key() { - THEME="$1" - EXERCICE="$2" - TYPE="$3" - KEY=`echo $4 | sed 's#\\\\#\\\\\\\\#g' | sed 's/"/\\\\"/g'` - - curl -f -s -d "{\"type\": \"$TYPE\", \"key\": \"$KEY\"}" "${BASEURL}/api/themes/$THEME/exercices/$EXERCICE/keys" | - grep -Eo '"id":[0-9]+,' | grep -Eo "[0-9]+" -} - -get_dir_from_cloud() { - curl -f -s -X PROPFIND -u "${CLOUDPASS}" "${BASEURI}$1" | xmllint --format - | grep 'd:href' | sed -E 's/^.*>(.*)<.*$/\1/' -} -get_dir() { - ls "${BASEFILE}$1" 2> /dev/null -} -#alias get_dir=get_dir_from_cloud - -get_file_from_cloud() { - curl -f -s -u "${CLOUDPASS}" "${BASEURI}$1" | tr -d '\r' -} -get_file() { - cat "${BASEFILE}$1" 2> /dev/null | tr -d '\r' - echo -} -#alias get_file=get_file_from_cloud - -unhtmlentities() { - cat | sed -E 's/%20/ /g' | sed -E "s/%27/'/g" | sed -E 's/%c3%a9/é/g' | sed -E 's/%c3%a8/è/g' -} - -# Theme -{ - if [ $# -ge 1 ]; then - echo $1 - else - get_dir "" - fi -} | while read f; do basename "$f"; done | while read THEME_URI -do - THM_BASEURI="/${THEME_URI}/" - THEME_NAME=$(echo "${THEME_URI#*-}" | unhtmlentities) - THEME_AUTHORS=$(get_file "${THM_BASEURI}/AUTHORS.txt" | sed '/^$/d;s/$/, /' | tr -d '\n' | sed 's/, $//') - THEME_ID=`new_theme "$THEME_NAME" "$THEME_AUTHORS"` - if [ -z "$THEME_ID" ]; then - echo -e "\e[31;01m!!! An error occured during theme add\e[00m" - continue - else - echo -e "\e[33m>>> New theme created:\e[00m $THEME_ID - $THEME_NAME" - fi - - LAST=null - EXO_NUM=0 - { - if [ $# -ge 2 ]; then - echo "$2" - else - get_dir "${THM_BASEURI}" - fi - } | while read f; do basename "$f"; done | while read EXO_URI - do - case ${EXO_URI} in - [0-9]-*) - ;; - *) - continue;; - esac - - #EXO_NUM=$((EXO_NUM + 1)) - EXO_NUM=${EXO_URI%-*} - EXO_NAME=$(echo "${EXO_URI#*-}" | unhtmlentities) - echo - echo -e "\e[36m--- Filling exercice ${EXO_NUM} in theme ${THEME_NAME}\e[00m" - - EXO_BASEURI="${EXO_URI}/" - - EXO_VIDEO=$(get_dir "${THM_BASEURI}${EXO_BASEURI}/resolution/" | grep -E "\.(mov|mkv|mp4|avi|flv|ogv|webm)$" | while read f; do basename "$f"; done | tail -1) - [ -n "$EXO_VIDEO" ] && EXO_VIDEO="/resolution${THM_BASEURI}${EXO_BASEURI}resolution/${EXO_VIDEO}" - - if [ "${LAST}" = "null" ]; then - echo ">>> Assuming this exercice has no dependency" - else - echo ">>> Assuming this exercice depends on the last entry (id=${LAST})" - fi - - EXO_GAIN=$((3 * (2 ** $EXO_NUM) - 1)) - HINT_COST=$(($EXO_GAIN / 4)) - echo ">>> Using default gain: ${EXO_GAIN} points" - - EXO_SCENARIO=$(get_file "${THM_BASEURI}${EXO_BASEURI}/scenario.txt") - - EXO_ID=`new_exercice "${THEME_ID}" "${EXO_NAME}" "${EXO_SCENARIO}" "${LAST}" "${EXO_GAIN}" "${EXO_VIDEO}"` - if [ -z "$EXO_ID" ]; then - echo -e "\e[31;01m!!! An error occured during exercice add.\e[00m" - continue - else - echo -e "\e[32m>>> New exercice created:\e[00m $EXO_ID - $EXO_NAME" - fi - - - # Keys - get_file "${THM_BASEURI}${EXO_BASEURI}/flags.txt" | while read KEYLINE - do - [ -z "${KEYLINE}" ] && continue - - KEY_NAME=$(echo "$KEYLINE" | cut -d$'\t' -f 1) - KEY_RAW=$(echo "$KEYLINE" | cut -d$'\t' -f 2-) - - if [ -z "${KEY_RAW}" ] || [ "${KEY_NAME}" = "${KEY_RAW}" ]; then - KEY_NAME=$(echo "$KEYLINE" | cut -d : -f 1) - KEY_RAW=$(echo "$KEYLINE" | cut -d : -f 2-) - fi - - if [ -z "${KEY_NAME}" ]; then - KEY_NAME="Flag" - fi - - KEY_ID=`new_key "${THEME_ID}" "${EXO_ID}" "${KEY_NAME}" "${KEY_RAW}"` - if [ -z "$KEY_ID" ]; then - echo -e "\e[31;01m!!! An error occured during key import!\e[00m (name=${KEYNAME};raw=${KEY_RAW})" - else - echo -e "\e[32m>>> New key added:\e[00m $KEY_ID - $KEY_NAME" - fi - done - - - # Hints - HINTS=$(get_dir "${THM_BASEURI}${EXO_BASEURI}/hints/" | sed -E 's#(.*)#hints/\1#') - [ -z "${HINTS}" ] && HINTS=$(get_dir "${THM_BASEURI}${EXO_BASEURI}/" | grep ^hint.) - [ -z "${HINTS}" ] && HINTS="hint.txt" - HINT_COUNT=1 - echo "${HINTS}" | while read HINT - do - EXO_HINT=$(get_file "${THM_BASEURI}${EXO_BASEURI}/${HINT}") - if [ -n "$EXO_HINT" ]; then - EXO_HINT_TYPE=$(echo "${EXO_HINT}" | file --mime-type -b -) - if echo "${EXO_HINT_TYPE}" | grep text/ && [ $(echo "${EXO_HINT}" | wc -l) -lt 25 ]; then - HINT_ID=`new_hint "${THEME_ID}" "${EXO_ID}" "Astuce #${HINT_COUNT}" "${EXO_HINT}" "${HINT_COST}"` - else - HINT_ID=`new_hint "${THEME_ID}" "${EXO_ID}" "Astuce #${HINT_COUNT}" "" "${HINT_COST}" "${THM_BASEURI}${EXO_BASEURI}/${HINT}"` - fi - - if [ -z "$HINT_ID" ]; then - echo -e "\e[31;01m!!! An error occured during hint import!\e[00m (title=Astuce #${HINT_COUNT};content::${EXO_HINT_TYPE};cost=${HINT_COST})" - else - echo -e "\e[32m>>> New hint added:\e[00m $HINT_ID - Astuce #${HINT_COUNT}" - fi - fi - HINT_COUNT=$(($HINT_COUNT + 1)) - done - - - # Files: splited - get_dir "${THM_BASEURI}${EXO_BASEURI}files/" | grep -v DIGESTS.txt | grep '[0-9][0-9]$' | sed -E 's/\.?([0-9][0-9])$//' | sort | uniq | while read f; do basename "$f"; done | while read FILE_URI - do - DIGEST=$(get_file "${THM_BASEURI}${EXO_BASEURI}files/DIGESTS.txt" | grep "${FILE_URI}\$" | awk '{ print $1; }') - - PARTS= - for part in $(get_dir "${THM_BASEURI}${EXO_BASEURI}files/" | grep "${FILE_URI}" | sort) - do - PARTS="${PARTS}${BASEFILE}${THM_BASEURI}${EXO_BASEURI}files/${part} -" - done - echo -e "\e[35mImport splited file ${THM_BASEURI}${EXO_BASEURI}files/${FILE_URI} from\e[00m `echo ${PARTS} | tr '\n' ' '`" - - FILE_ID=`new_file "${THEME_ID}" "${EXO_ID}" "${THM_BASEURI}${EXO_BASEURI}files/${FILE_URI}" "${DIGEST}" "${PARTS}"` - if [ -z "$FILE_ID" ]; then - echo -e "\e[31;01m!!! An error occured during file import! Please check path.\e[00m" - else - echo -e "\e[32m>>> New file added:\e[00m $FILE_ID - $FILE_URI" - fi - done - - # Files: entire - get_dir "${THM_BASEURI}${EXO_BASEURI}files/" | grep -v DIGESTS.txt | grep -v '[0-9][0-9]$' | while read f; do basename "$f"; done | while read FILE_URI - do - DIGEST=$(get_file "${THM_BASEURI}${EXO_BASEURI}files/DIGESTS.txt" | grep "${FILE_URI}\$" | awk '{ print $1; }') - - echo "Import file ${THM_BASEURI}${EXO_BASEURI}files/${FILE_URI}" - FILE_ID=`new_file "${THEME_ID}" "${EXO_ID}" "${THM_BASEURI}${EXO_BASEURI}files/${FILE_URI}" "${DIGEST}"` - if [ -z "$FILE_ID" ]; then - echo -e "\e[31;01m!!! An error occured during file import! Please check path.\e[00m" - else - echo -e "\e[32m>>> New file added:\e[00m $FILE_ID - $FILE_URI" - fi - done - - - LAST=$EXO_ID - done - echo -done diff --git a/admin/get_files.sh b/admin/get_files.sh deleted file mode 100755 index 68c032cb..00000000 --- a/admin/get_files.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh - -BASEURL="http://localhost:8081" -BASEURI="https://srs.epita.fr/owncloud/remote.php/webdav/FIC 2016" -CLOUDUSER='fic' -CLOUDPASS='f>t\nV33R|(+?$i*' - -if [ $# -gt 0 ] -then - WHERE=$1 -else - WHERE="files" -fi - -curl -q -f ${BASEURL}/api/themes/files-bindings | while read l -do - FROM=$(echo "$l" | cut -d ";" -f 1) - DEST=$(echo "$l" | cut -d ";" -f 2) - - mkdir -p $(dirname "${WHERE}${DEST}") - - wget -O "${WHERE}${DEST}" --user "${CLOUDUSER}" --password "${CLOUDPASS}" "${BASEURI}${FROM}" -done From 41ef7f2555045a390b2a042ffa31fdf63553df44 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 25 Jan 2019 13:29:51 +0100 Subject: [PATCH 0022/1637] frontend: prefer default border color in home public screen --- frontend/static/views/home.html | 2 +- frontend/static/views/tag.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/static/views/home.html b/frontend/static/views/home.html index 134052b3..ad65dc54 100644 --- a/frontend/static/views/home.html +++ b/frontend/static/views/home.html @@ -15,7 +15,7 @@
-
+
diff --git a/frontend/static/views/tag.html b/frontend/static/views/tag.html index 6f606d28..7dc8910a 100644 --- a/frontend/static/views/tag.html +++ b/frontend/static/views/tag.html @@ -1,5 +1,5 @@
-
+
From 650f1f4d590c932623835a74e4b667ac1db27432 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 25 Jan 2019 16:31:48 +0100 Subject: [PATCH 0023/1637] admin: add a new route to generate a file for movie links --- admin/api/exercice.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/admin/api/exercice.go b/admin/api/exercice.go index a1ab5153..6d698641 100644 --- a/admin/api/exercice.go +++ b/admin/api/exercice.go @@ -14,6 +14,7 @@ import ( func init() { router.GET("/api/exercices/", apiHandler(listExercices)) + router.GET("/api/resolutions.json", apiHandler(exportResolutionMovies)) router.GET("/api/exercices/:eid", apiHandler(exerciceHandler(showExercice))) router.PUT("/api/exercices/:eid", apiHandler(exerciceHandler(updateExercice))) @@ -85,6 +86,28 @@ func listExercices(_ httprouter.Params, body []byte) (interface{}, error) { return fic.GetExercices() } +// Generate the csv to export with: +// curl -s http://127.0.0.1:8081/api/resolutions.json | jq -r ".[] | [ .theme,.title, @uri \"https://fic.srs.epita.fr/resolution/\\(.videoURI)\" ] | join(\";\")" +func exportResolutionMovies(_ httprouter.Params, body []byte) (interface{}, error) { + if exercices, err := fic.GetExercices(); err != nil { + return nil, err + } else { + export := []map[string]string{} + for _, exercice := range exercices { + if theme, err := fic.GetTheme(exercice.IdTheme); err != nil { + return nil, err + } else { + export = append(export, map[string]string{ + "videoURI": exercice.VideoURI, + "theme": theme.Name, + "title": exercice.Title, + }) + } + } + return export, nil + } +} + func listExerciceFiles(exercice fic.Exercice, body []byte) (interface{}, error) { return exercice.GetFiles() } From 73db9da6826666a6784605159c87da156c1e2048 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 1 Feb 2019 20:50:58 +0100 Subject: [PATCH 0024/1637] admin: thanks to ng-base, don't need other modifications --- admin/index.go | 30 +++++++++++++++--------------- admin/static/index.html | 30 +++++++++++++++--------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/admin/index.go b/admin/index.go index 0461becf..595d0b2f 100644 --- a/admin/index.go +++ b/admin/index.go @@ -32,8 +32,8 @@ const indextpl = `
diff --git a/libfic/exercice_history.go b/libfic/exercice_history.go new file mode 100644 index 00000000..5e056311 --- /dev/null +++ b/libfic/exercice_history.go @@ -0,0 +1,110 @@ +package fic + +import ( + "fmt" + "time" +) + +// GetHistory aggregates all sources of events or actions for an Exercice +func (e Exercice) GetHistory() ([]map[string]interface{}, error) { + hist := make([]map[string]interface{}, 0) + + if rows, err := DBQuery(`SELECT id_team, U.name, U.color, "tries" AS kind, time, id_exercice, NULL, NULL FROM exercice_tries NATURAL JOIN teams U WHERE id_exercice = ? UNION + SELECT id_team, U.name, U.color, "solved" AS kind, time, id_exercice, coefficient, NULL FROM exercice_solved S NATURAL JOIN teams U WHERE id_exercice = ? UNION + SELECT id_team, U.name, U.color, "hint" AS kind, time, id_exercice, H.id_hint, H.title FROM team_hints T INNER JOIN exercice_hints H ON H.id_hint = T.id_hint NATURAL JOIN teams U WHERE id_exercice = ? UNION + SELECT id_team, U.name, U.color, "wchoices" AS kind, time, id_exercice, F.id_flag, F.type FROM team_wchoices W INNER JOIN exercice_flags F ON F.id_flag = W.id_flag NATURAL JOIN teams U WHERE id_exercice = ? UNION + SELECT id_team, U.name, U.color, "flag_found" AS kind, time, id_exercice, K.id_flag, K.type FROM flag_found F INNER JOIN exercice_flags K ON K.id_flag = F.id_flag NATURAL JOIN teams U WHERE id_exercice = ? UNION + SELECT id_team, U.name, U.color, "mcq_found" AS kind, time, id_exercice, Q.id_mcq, Q.title FROM mcq_found F INNER JOIN exercice_mcq Q ON Q.id_mcq = F.id_mcq NATURAL JOIN teams U WHERE id_exercice = ? + ORDER BY time DESC`, e.Id, e.Id, e.Id, e.Id, e.Id, e.Id); err != nil { + return nil, err + } else { + defer rows.Close() + + for rows.Next() { + var id_team int64 + var team_name string + var team_color uint32 + var kind string + var time time.Time + var exercice int64 + var secondary *int64 + var secondary_title *string + + if err := rows.Scan(&id_team, &team_name, &team_color, &kind, &time, &exercice, &secondary, &secondary_title); err != nil { + return nil, err + } + + h := map[string]interface{}{} + + h["team_id"] = id_team + h["team_name"] = team_name + h["team_color"] = fmt.Sprintf("#%x", team_color) + h["kind"] = kind + h["time"] = time + h["primary"] = e.Id + if secondary != nil { + h["secondary"] = secondary + h["secondary_title"] = secondary_title + } + + hist = append(hist, h) + } + } + + return hist, nil +} + +// DelHistoryItem removes from the database an entry from the history. +func (e Exercice) DelHistoryItem(tId int64, kind string, h time.Time, secondary *int64) (interface{}, error) { + if kind == "tries" { + if res, err := DBExec("DELETE FROM exercice_tries WHERE id_team = ? AND time = ? AND id_exercice = ?", tId, h, e.Id); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } + } else if kind == "hint" && secondary != nil { + if res, err := DBExec("DELETE FROM team_hints WHERE id_team = ? AND time = ? AND id_hint = ?", tId, h, *secondary); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } + } else if kind == "wchoices" && secondary != nil { + if res, err := DBExec("DELETE FROM team_wchoices WHERE id_team = ? AND time = ? AND id_flag = ?", tId, h, *secondary); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } + } else if kind == "flag_found" && secondary != nil { + if res, err := DBExec("DELETE FROM flag_found WHERE id_team = ? AND time = ? AND id_flag = ?", tId, h, *secondary); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } + } else if kind == "mcq_found" && secondary != nil { + if res, err := DBExec("DELETE FROM mcq_found WHERE id_team = ? AND time = ? AND id_mcq = ?", tId, h, *secondary); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } + } else if kind == "solved" { + if res, err := DBExec("DELETE FROM exercice_solved WHERE id_team = ? AND time = ? AND id_exercice = ?", tId, h, e.Id); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } + } else { + return nil, nil + } +} From 6a1f73c8950b7c1c3b8015993c94355055afeb83 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 3 Feb 2019 22:40:44 +0100 Subject: [PATCH 0032/1637] admin: include coefficient in history.json --- admin/static/views/exercice.html | 3 +-- admin/static/views/team-edit.html | 2 +- libfic/exercice_history.go | 16 +++++++++------- libfic/team_history.go | 16 +++++++++------- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/admin/static/views/exercice.html b/admin/static/views/exercice.html index 9bfa0547..7c72891a 100644 --- a/admin/static/views/exercice.html +++ b/admin/static/views/exercice.html @@ -227,7 +227,7 @@ - {{ row.time | date:"mediumTime" }}
{{ row.kind }} + {{ row.time | date:"mediumTime" }}
{{ row.kind }} x{{ row.coefficient }} @@ -239,7 +239,6 @@ {{ row.secondary_title }} {{ row.secondary_title }} - (coeff x{{ row.secondary }}) : {{ row.secondary }} diff --git a/admin/static/views/team-edit.html b/admin/static/views/team-edit.html index 9053bdf3..0be2b267 100644 --- a/admin/static/views/team-edit.html +++ b/admin/static/views/team-edit.html @@ -109,7 +109,7 @@ - {{ row.time | date:"mediumTime" }}
{{ row.kind }} + {{ row.time | date:"mediumTime" }}
{{ row.kind }} x{{ row.coefficient }} diff --git a/libfic/exercice_history.go b/libfic/exercice_history.go index 5e056311..29873345 100644 --- a/libfic/exercice_history.go +++ b/libfic/exercice_history.go @@ -9,12 +9,12 @@ import ( func (e Exercice) GetHistory() ([]map[string]interface{}, error) { hist := make([]map[string]interface{}, 0) - if rows, err := DBQuery(`SELECT id_team, U.name, U.color, "tries" AS kind, time, id_exercice, NULL, NULL FROM exercice_tries NATURAL JOIN teams U WHERE id_exercice = ? UNION - SELECT id_team, U.name, U.color, "solved" AS kind, time, id_exercice, coefficient, NULL FROM exercice_solved S NATURAL JOIN teams U WHERE id_exercice = ? UNION - SELECT id_team, U.name, U.color, "hint" AS kind, time, id_exercice, H.id_hint, H.title FROM team_hints T INNER JOIN exercice_hints H ON H.id_hint = T.id_hint NATURAL JOIN teams U WHERE id_exercice = ? UNION - SELECT id_team, U.name, U.color, "wchoices" AS kind, time, id_exercice, F.id_flag, F.type FROM team_wchoices W INNER JOIN exercice_flags F ON F.id_flag = W.id_flag NATURAL JOIN teams U WHERE id_exercice = ? UNION - SELECT id_team, U.name, U.color, "flag_found" AS kind, time, id_exercice, K.id_flag, K.type FROM flag_found F INNER JOIN exercice_flags K ON K.id_flag = F.id_flag NATURAL JOIN teams U WHERE id_exercice = ? UNION - SELECT id_team, U.name, U.color, "mcq_found" AS kind, time, id_exercice, Q.id_mcq, Q.title FROM mcq_found F INNER JOIN exercice_mcq Q ON Q.id_mcq = F.id_mcq NATURAL JOIN teams U WHERE id_exercice = ? + if rows, err := DBQuery(`SELECT id_team, U.name, U.color, "tries" AS kind, time, 0, id_exercice, NULL, NULL FROM exercice_tries NATURAL JOIN teams U WHERE id_exercice = ? UNION + SELECT id_team, U.name, U.color, "solved" AS kind, time, coefficient, id_exercice, NULL, NULL FROM exercice_solved S NATURAL JOIN teams U WHERE id_exercice = ? UNION + SELECT id_team, U.name, U.color, "hint" AS kind, time, coefficient, id_exercice, H.id_hint, H.title FROM team_hints T INNER JOIN exercice_hints H ON H.id_hint = T.id_hint NATURAL JOIN teams U WHERE id_exercice = ? UNION + SELECT id_team, U.name, U.color, "wchoices" AS kind, time, coefficient, id_exercice, F.id_flag, F.type FROM team_wchoices W INNER JOIN exercice_flags F ON F.id_flag = W.id_flag NATURAL JOIN teams U WHERE id_exercice = ? UNION + SELECT id_team, U.name, U.color, "flag_found" AS kind, time, 0, id_exercice, K.id_flag, K.type FROM flag_found F INNER JOIN exercice_flags K ON K.id_flag = F.id_flag NATURAL JOIN teams U WHERE id_exercice = ? UNION + SELECT id_team, U.name, U.color, "mcq_found" AS kind, time, 0, id_exercice, Q.id_mcq, Q.title FROM mcq_found F INNER JOIN exercice_mcq Q ON Q.id_mcq = F.id_mcq NATURAL JOIN teams U WHERE id_exercice = ? ORDER BY time DESC`, e.Id, e.Id, e.Id, e.Id, e.Id, e.Id); err != nil { return nil, err } else { @@ -26,11 +26,12 @@ func (e Exercice) GetHistory() ([]map[string]interface{}, error) { var team_color uint32 var kind string var time time.Time + var coeff float32 var exercice int64 var secondary *int64 var secondary_title *string - if err := rows.Scan(&id_team, &team_name, &team_color, &kind, &time, &exercice, &secondary, &secondary_title); err != nil { + if err := rows.Scan(&id_team, &team_name, &team_color, &kind, &time, &coeff, &exercice, &secondary, &secondary_title); err != nil { return nil, err } @@ -41,6 +42,7 @@ func (e Exercice) GetHistory() ([]map[string]interface{}, error) { h["team_color"] = fmt.Sprintf("#%x", team_color) h["kind"] = kind h["time"] = time + h["coefficient"] = coeff h["primary"] = e.Id if secondary != nil { h["secondary"] = secondary diff --git a/libfic/team_history.go b/libfic/team_history.go index 8a2cb581..b73656d3 100644 --- a/libfic/team_history.go +++ b/libfic/team_history.go @@ -8,12 +8,12 @@ import ( func (t Team) GetHistory() ([]map[string]interface{}, error) { hist := make([]map[string]interface{}, 0) - if rows, err := DBQuery(`SELECT id_team, "tries" AS kind, time, E.id_exercice, E.title, NULL, NULL FROM exercice_tries T INNER JOIN exercices E ON E.id_exercice = T.id_exercice WHERE id_team = ? UNION - SELECT id_team, "solved" AS kind, time, E.id_exercice, E.title, coefficient, NULL FROM exercice_solved S INNER JOIN exercices E ON E.id_exercice = S.id_exercice WHERE id_team = ? UNION - SELECT id_team, "hint" AS kind, time, E.id_exercice, E.title, H.id_hint, H.title FROM team_hints T INNER JOIN exercice_hints H ON H.id_hint = T.id_hint INNER JOIN exercices E ON E.id_exercice = H.id_exercice WHERE id_team = ? UNION - SELECT id_team, "wchoices" AS kind, time, E.id_exercice, E.title, F.id_flag, F.type FROM team_wchoices W INNER JOIN exercice_flags F ON F.id_flag = W.id_flag INNER JOIN exercices E ON E.id_exercice = F.id_exercice WHERE id_team = ? UNION - SELECT id_team, "flag_found" AS kind, time, E.id_exercice, E.title, K.id_flag, K.type FROM flag_found F INNER JOIN exercice_flags K ON K.id_flag = F.id_flag INNER JOIN exercices E ON K.id_exercice = E.id_exercice WHERE id_team = ? UNION - SELECT id_team, "mcq_found" AS kind, time, E.id_exercice, E.title, Q.id_mcq, Q.title FROM mcq_found F INNER JOIN exercice_mcq Q ON Q.id_mcq = F.id_mcq INNER JOIN exercices E ON Q.id_exercice = E.id_exercice WHERE id_team = ? + if rows, err := DBQuery(`SELECT id_team, "tries" AS kind, time, 0, E.id_exercice, E.title, NULL, NULL FROM exercice_tries T INNER JOIN exercices E ON E.id_exercice = T.id_exercice WHERE id_team = ? UNION + SELECT id_team, "solved" AS kind, time, coefficient, E.id_exercice, E.title, NULL, NULL FROM exercice_solved S INNER JOIN exercices E ON E.id_exercice = S.id_exercice WHERE id_team = ? UNION + SELECT id_team, "hint" AS kind, time, coefficient, E.id_exercice, E.title, H.id_hint, H.title FROM team_hints T INNER JOIN exercice_hints H ON H.id_hint = T.id_hint INNER JOIN exercices E ON E.id_exercice = H.id_exercice WHERE id_team = ? UNION + SELECT id_team, "wchoices" AS kind, time, coefficient, E.id_exercice, E.title, F.id_flag, F.type FROM team_wchoices W INNER JOIN exercice_flags F ON F.id_flag = W.id_flag INNER JOIN exercices E ON E.id_exercice = F.id_exercice WHERE id_team = ? UNION + SELECT id_team, "flag_found" AS kind, time, 0, E.id_exercice, E.title, K.id_flag, K.type FROM flag_found F INNER JOIN exercice_flags K ON K.id_flag = F.id_flag INNER JOIN exercices E ON K.id_exercice = E.id_exercice WHERE id_team = ? UNION + SELECT id_team, "mcq_found" AS kind, time, 0, E.id_exercice, E.title, Q.id_mcq, Q.title FROM mcq_found F INNER JOIN exercice_mcq Q ON Q.id_mcq = F.id_mcq INNER JOIN exercices E ON Q.id_exercice = E.id_exercice WHERE id_team = ? ORDER BY time DESC`, t.Id, t.Id, t.Id, t.Id, t.Id, t.Id); err != nil { return nil, err } else { @@ -23,12 +23,13 @@ func (t Team) GetHistory() ([]map[string]interface{}, error) { var id_team int64 var kind string var time time.Time + var coefficient float32 var primary *int64 var primary_title *string var secondary *int64 var secondary_title *string - if err := rows.Scan(&id_team, &kind, &time, &primary, &primary_title, &secondary, &secondary_title); err != nil { + if err := rows.Scan(&id_team, &kind, &time, &coefficient, &primary, &primary_title, &secondary, &secondary_title); err != nil { return nil, err } @@ -36,6 +37,7 @@ func (t Team) GetHistory() ([]map[string]interface{}, error) { h["kind"] = kind h["time"] = time + h["coefficient"] = coefficient if primary != nil { h["primary"] = primary h["primary_title"] = primary_title From a35aa7be707a10a43b10c3ce9abd25689e505398 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 3 Feb 2019 22:49:03 +0100 Subject: [PATCH 0033/1637] admin: add a new route to update team history coefficient --- admin/api/team.go | 25 +++++++++++++++++++++---- libfic/team_history.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/admin/api/team.go b/admin/api/team.go index dcffb055..8c32692d 100644 --- a/admin/api/team.go +++ b/admin/api/team.go @@ -238,10 +238,27 @@ func setTeamMember(team fic.Team, body []byte) (interface{}, error) { } type uploadedHistory struct { - Kind string - Time time.Time - Primary *int64 - Secondary *int64 + Kind string + Time time.Time + Primary *int64 + Secondary *int64 + Coefficient float32 +} + +func updateHistory(team *fic.Team, body []byte) (interface{}, error) { + var uh uploadedHistory + if err := json.Unmarshal(body, &uh); err != nil { + return nil, err + } + + var givenId int64 + if uh.Secondary != nil { + givenId = *uh.Secondary + } else if uh.Primary != nil { + givenId = *uh.Primary + } + + return team.UpdateHistoryCoeff(uh.Kind, uh.Time, givenId, uh.Coefficient) } func delHistory(team *fic.Team, body []byte) (interface{}, error) { diff --git a/libfic/team_history.go b/libfic/team_history.go index b73656d3..0b21276a 100644 --- a/libfic/team_history.go +++ b/libfic/team_history.go @@ -54,6 +54,37 @@ func (t Team) GetHistory() ([]map[string]interface{}, error) { return hist, nil } +// UpdateHistoryCoeff updates the coefficient for a given entry. +func (t Team) UpdateHistoryCoeff(kind string, h time.Time, givenId int64, newCoeff float32) (interface{}, error) { + if kind == "hint" { + if res, err := DBExec("UPDATE team_hints SET coefficient = ? WHERE id_team = ? AND time = ? AND id_hint = ?", newCoeff, t.Id, h, givenId); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } + } else if kind == "wchoices" { + if res, err := DBExec("UPDATE team_wchoices SET coefficient = ? WHERE id_team = ? AND time = ? AND id_flag = ?", newCoeff, t.Id, h, givenId); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } + } else if kind == "solved" { + if res, err := DBExec("UPDATE exercice_solved SET coefficient = ? WHERE id_team = ? AND time = ? AND id_exercice = ?", newCoeff, t.Id, h, givenId); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } + } else { + return nil, nil + } +} + // DelHistoryItem removes from the database an entry from the history. func (t Team) DelHistoryItem(kind string, h time.Time, primary *int64, secondary *int64) (interface{}, error) { if kind == "tries" && primary != nil { From 921644deb42d997a717f1d85305e84c9c118b030 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 4 Feb 2019 17:38:12 +0100 Subject: [PATCH 0034/1637] frontend: rely on angular base path --- frontend/static/js/challenge.js | 42 ++++++++++++++++---------------- frontend/static/views/defi.html | 6 ++--- frontend/static/views/home.html | 6 ++--- frontend/static/views/tag.html | 6 ++--- frontend/static/views/theme.html | 4 +-- 5 files changed, 32 insertions(+), 32 deletions(-) diff --git a/frontend/static/js/challenge.js b/frontend/static/js/challenge.js index 2d360b2f..8304bfb1 100644 --- a/frontend/static/js/challenge.js +++ b/frontend/static/js/challenge.js @@ -170,7 +170,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) $interval.cancel(refreshSettingsInterval); refreshSettingsInterval = $interval(refreshSettings, Math.floor(Math.random() * 24000) + 32000); - $http.get("/settings.json").then(function(response) { + $http.get("settings.json").then(function(response) { var time = $rootScope.recvTime(response); response.data.start = new Date(response.data.start); response.data.end = new Date(response.data.end); @@ -197,7 +197,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) $interval.cancel(refreshThemesInterval); refreshThemesInterval = $interval(refreshThemes, Math.floor(Math.random() * 24000) + 32000); - $http.get("/themes.json").then(function(response) { + $http.get("themes.json").then(function(response) { $scope.themes = response.data; $scope.max_gain = 0; $scope.max_solved = 0; @@ -228,7 +228,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) $interval.cancel(refreshTeamsInterval); refreshTeamsInterval = $interval($rootScope.refreshTeams, Math.floor(Math.random() * 24000) + 32000); - $http.get("/teams.json").then(function(response) { + $http.get("teams.json").then(function(response) { var teams = response.data; $scope.teams_count = Object.keys(teams).length $scope.teams = teams; @@ -260,7 +260,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) return; } - $http.get("/events.json").then(function(response) { + $http.get("events.json").then(function(response) { if (eventsLastRefresh != undefined && eventsLastRefresh == response.headers()["last-modified"]) return; eventsLastRefresh = response.headers()["last-modified"]; @@ -301,7 +301,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) if ((kind & $rootScope.notify_field) == kind) { var notification = new Notification("Challenge forensic", {body: event.txt.replace(/&#(\d+);/g, function(match, dec) {return String.fromCharCode(dec);}).replace(/(<([^>]+)>)/ig,""), badge: "/img/icon-" + event.kind + ".ico", icon: "/img/icon-" + event.kind + ".ico"}); notification.onclick = function(ev) { - $location.url("/edit"); + $location.url("edit"); }; setTimeout(notification.close.bind(notification), 4000); } @@ -318,11 +318,11 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) $interval.cancel(refreshMyInterval); refreshMyInterval = $interval(refreshMy, Math.floor(Math.random() * 24000) + 24000); - $http.get("/my.json").then(function(response) { + $http.get("my.json").then(function(response) { $rootScope.recvMy(response.data); }, function(response) { if (!$scope.my && response.status == 404) { - $location.url("/register"); + $location.url("register"); } }); } @@ -458,9 +458,9 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) $scope.hsubmit = function(hint) { hint.submitted = true; $scope.hinterror = null; - $http({ url: "/openhint/" + $rootScope.current_exercice, method: "POST", data: { id: hint.id } }).then(function(response) { + $http({ url: "openhint/" + $rootScope.current_exercice, method: "POST", data: { id: hint.id } }).then(function(response) { var checkDiffHint = function() { - $http.get("/my.json").then(function(response) { + $http.get("my.json").then(function(response) { var my = response.data; angular.forEach(my.exercices[$rootScope.current_exercice].hints, function(h,hid){ if (hint.id == h.id) { @@ -554,7 +554,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) return; } - $http({ url: "/submit/" + $rootScope.current_exercice, method: "POST", data: resp }).then(function(response) { + $http({ url: "submit/" + $rootScope.current_exercice, method: "POST", data: resp }).then(function(response) { $scope.messageClass = {"text-success": true}; $scope.message = response.data.errmsg; $scope.sberr = null; @@ -564,7 +564,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) }); var checkDiff = function() { - $http.get("/my.json").then(function(response) { + $http.get("my.json").then(function(response) { var my = response.data; if ($scope.my.exercices[$rootScope.current_exercice].tries != my.exercices[$rootScope.current_exercice].tries || $scope.my.exercices[$rootScope.current_exercice].solved_time != my.exercices[$rootScope.current_exercice].solved_time) { $scope.my.exercices[$rootScope.current_exercice].submitted = false; @@ -592,9 +592,9 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) $scope.wantchoices = function(kid) { $scope.my.exercices[$rootScope.current_exercice].flags[kid].wcsubmitted = true; - $http({ url: "/wantchoices/" + $rootScope.current_exercice, method: "POST", data: { id: Math.floor(kid) } }).then(function(response) { + $http({ url: "wantchoices/" + $rootScope.current_exercice, method: "POST", data: { id: Math.floor(kid) } }).then(function(response) { var checkDiffWC = function() { - $http.get("/my.json").then(function(response) { + $http.get("my.json").then(function(response) { var my = response.data; if (my.exercices[$rootScope.current_exercice].flags[kid].choices) $rootScope.recvMy(my); @@ -650,7 +650,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) localStorage.notification = $rootScope.notify_field; else delete localStorage.notification; - $location.url("/"); + $location.url("."); } var cbt; @@ -674,7 +674,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) } $http({ - url: "/submit/name", + url: "submit/name", method: "POST", data: {newName: $scope.my.newName} }).then(function(response) { @@ -682,7 +682,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) $rootScope.message = response.data.errmsg; var checkDiff = function() { - $http.get("/my.json").then(function(response) { + $http.get("my.json").then(function(response) { if ($scope.my.name != response.data.name) { $scope.my.newName = undefined; $rootScope.message = ""; @@ -749,7 +749,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) } $http({ - url: "/registration", + url: "registration", method: "POST", data: $scope.form }).then(function(response) { @@ -757,7 +757,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) $scope.message = response.data.errmsg; $interval(function(){ - $http.get("/my.json").then(function(response) { + $http.get("my.json").then(function(response) { $rootScope.refresh(); }); }, 1500); @@ -773,7 +773,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) $scope.$watch("my", function(my){ if (my) - $location.url("/"); + $location.url("."); }); }) .controller("TagController", function($scope, $rootScope, $routeParams, $location) { @@ -797,7 +797,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) }) $scope.goDefi = function() { - $location.url("/" + this.ex.theme.urlid + "/" + this.ex.exercice.urlid); + $location.url(this.ex.theme.urlid + "/" + this.ex.exercice.urlid); } }) @@ -835,7 +835,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) $rootScope.authors = ""; $scope.goTheme = function() { - $location.url("/" + this.theme.urlid); + $location.url(this.theme.urlid); } }); diff --git a/frontend/static/views/defi.html b/frontend/static/views/defi.html index c7039348..6afb0a9a 100644 --- a/frontend/static/views/defi.html +++ b/frontend/static/views/defi.html @@ -2,7 +2,7 @@
diff --git a/frontend/static/views/home.html b/frontend/static/views/home.html index ad65dc54..4c3b9fdc 100644 --- a/frontend/static/views/home.html +++ b/frontend/static/views/home.html @@ -2,13 +2,13 @@ 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 !
- Félicitations et , {{ member.firstname | capitalize }} {{ member.lastname | capitalize }} ! vous êtes maintenant connecté à l'espace de votre équipe {{ teams[my.team_id].name }}. Vous pouvez changer ce nom dès maintenant en vous rendant sur la page de votre équipe. + Félicitations et , {{ member.firstname | capitalize }} {{ member.lastname | capitalize }} ! vous êtes maintenant connecté à l'espace de votre équipe {{ teams[my.team_id].name }}. Vous pouvez changer ce nom dès maintenant en vous rendant sur la page de votre équipe.
Les membres de votre équipes ne sont pas encore enregistrés. Passez voir l'équipe serveur pour corriger cela.
- Votre équipe n'est pas encore enregistrée. Rendez-vous sur cette page pour procéder à votre inscription. + Votre équipe n'est pas encore enregistrée. Rendez-vous sur cette page pour procéder à votre inscription.
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. @@ -19,7 +19,7 @@
diff --git a/frontend/static/views/tag.html b/frontend/static/views/tag.html index 7dc8910a..e76d4d6b 100644 --- a/frontend/static/views/tag.html +++ b/frontend/static/views/tag.html @@ -3,12 +3,12 @@
diff --git a/frontend/static/views/theme.html b/frontend/static/views/theme.html index c4597c38..8ee2b184 100644 --- a/frontend/static/views/theme.html +++ b/frontend/static/views/theme.html @@ -7,14 +7,14 @@
- + {{ exercice.title }} {{ exercice.title }} - #{{tag}} + #{{tag}}
From 2b959951046fda02adf9147aa0887476a27671cc Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 4 Feb 2019 18:14:46 +0100 Subject: [PATCH 0035/1637] settings: add canJoinTeam parameter --- admin/main.go | 1 + admin/static/views/settings.html | 7 +++ backend/main.go | 2 + backend/registration.go | 77 ++++++++++++++++++++--------- frontend/static/js/challenge.js | 41 ++++++++++++--- frontend/static/views/register.html | 65 +++++++++++++++++++++--- settings/settings.go | 2 + 7 files changed, 159 insertions(+), 36 deletions(-) diff --git a/admin/main.go b/admin/main.go index 62e9f9b2..9055ebad 100644 --- a/admin/main.go +++ b/admin/main.go @@ -170,6 +170,7 @@ func main() { FirstBlood: fic.FirstBlood, SubmissionCostBase: fic.SubmissionCostBase, AllowRegistration: false, + CanJoinTeam: false, DenyNameChange: false, EnableResolutionRoute: false, PartialValidation: true, diff --git a/admin/static/views/settings.html b/admin/static/views/settings.html index add31700..f6e67f6b 100644 --- a/admin/static/views/settings.html +++ b/admin/static/views/settings.html @@ -114,6 +114,13 @@
+
+ +
+
-
+

Historique

- +
@@ -242,13 +242,55 @@ : {{ row.secondary }} +
+ + +
+ + diff --git a/admin/static/views/team-edit.html b/admin/static/views/team-edit.html index 26a6b1c7..e730ad31 100644 --- a/admin/static/views/team-edit.html +++ b/admin/static/views/team-edit.html @@ -129,8 +129,8 @@
-
- +
+
@@ -151,10 +151,52 @@ : {{ row.secondary }} +
+ + +
+ + diff --git a/libfic/exercice_history.go b/libfic/exercice_history.go index 29873345..fccc7618 100644 --- a/libfic/exercice_history.go +++ b/libfic/exercice_history.go @@ -56,6 +56,37 @@ func (e Exercice) GetHistory() ([]map[string]interface{}, error) { return hist, nil } +// UpdateHistoryItem sets values an entry from the history. +func (e Exercice) UpdateHistoryItem(coeff float32, tId int64, kind string, h time.Time, secondary *int64) (interface{}, error) { + if kind == "hint" && secondary != nil { + if res, err := DBExec("UPDATE team_hints SET coefficient = ?, time = ? WHERE id_team = ? AND time = ? AND id_hint = ?", coeff, h, tId, h, *secondary); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } + } else if kind == "wchoices" && secondary != nil { + if res, err := DBExec("UPDATE team_wchoices SET coefficient = ?, time = ? WHERE id_team = ? AND time = ? AND id_flag = ?", coeff, h, tId, h, *secondary); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } + } else if kind == "solved" { + if res, err := DBExec("UPDATE exercice_solved SET coefficient = ?, time = ? WHERE id_team = ? AND time = ? AND id_exercice = ?", coeff, h, tId, h, e.Id); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } + } else { + return nil, nil + } +} + // DelHistoryItem removes from the database an entry from the history. func (e Exercice) DelHistoryItem(tId int64, kind string, h time.Time, secondary *int64) (interface{}, error) { if kind == "tries" { From 3e5b4ebad28c271f47bbc1aaaa091302cfb02803 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 12 Jul 2019 19:22:05 +0200 Subject: [PATCH 0057/1637] admin: add missing default settings --- admin/main.go | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/admin/main.go b/admin/main.go index 9055ebad..39e886ac 100644 --- a/admin/main.go +++ b/admin/main.go @@ -25,7 +25,7 @@ import ( var StaticDir string type ResponseWriterPrefix struct { - real http.ResponseWriter + real http.ResponseWriter prefix string } @@ -35,7 +35,7 @@ func (r ResponseWriterPrefix) Header() http.Header { func (r ResponseWriterPrefix) WriteHeader(s int) { if v, exists := r.real.Header()["Location"]; exists { - r.real.Header().Set("Location", r.prefix + v[0]) + r.real.Header().Set("Location", r.prefix+v[0]) } r.real.WriteHeader(s) } @@ -50,7 +50,7 @@ func StripPrefix(prefix string, h http.Handler) http.Handler { } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if prefix != "/" && r.URL.Path == "/" { - http.Redirect(w, r, prefix + "/", http.StatusFound) + http.Redirect(w, r, prefix+"/", http.StatusFound) } else if p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) { r2 := new(http.Request) *r2 = *r @@ -165,16 +165,22 @@ func main() { // Initialize settings and load them if !settings.ExistsSettings(path.Join(settings.SettingsDir, settings.SettingsFile)) { if err = settings.SaveSettings(path.Join(settings.SettingsDir, settings.SettingsFile), settings.FICSettings{ - Title: "Challenge FIC", - Authors: "Laboratoire SRS, ÉPITA", - FirstBlood: fic.FirstBlood, - SubmissionCostBase: fic.SubmissionCostBase, - AllowRegistration: false, - CanJoinTeam: false, - DenyNameChange: false, - EnableResolutionRoute: false, - PartialValidation: true, - UnlockedChallengeDepth:0, + Title: "Challenge FIC", + Authors: "Laboratoire SRS, ÉPITA", + FirstBlood: fic.FirstBlood, + SubmissionCostBase: fic.SubmissionCostBase, + ExerciceCurCoefficient: 1, + HintCurCoefficient: 1, + WChoiceCurCoefficient: 1, + AllowRegistration: false, + CanJoinTeam: false, + DenyNameChange: false, + EnableResolutionRoute: false, + PartialValidation: true, + UnlockedChallengeDepth: 0, + SubmissionUniqueness: false, + DisplayAllFlags: false, + EventKindness: false, }); err != nil { log.Fatal("Unable to initialize settings.json:", err) } @@ -200,12 +206,12 @@ func main() { } // Update base URL on main page - log.Println("Changing base URL to", *baseURL + "/", "...") + log.Println("Changing base URL to", *baseURL+"/", "...") if file, err := os.OpenFile(path.Join(StaticDir, "index.html"), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0644)); err != nil { log.Println("Unable to open index.html: ", err) } else if indexTmpl, err := template.New("index").Parse(indextpl); err != nil { log.Println("Cannot create template: ", err) - } else if err = indexTmpl.Execute(file, map[string]string{"urlbase": path.Clean(path.Join(*baseURL + "/", "nuke"))[:len(path.Clean(path.Join(*baseURL + "/", "nuke"))) - 4]}); err != nil { + } else if err = indexTmpl.Execute(file, map[string]string{"urlbase": path.Clean(path.Join(*baseURL+"/", "nuke"))[:len(path.Clean(path.Join(*baseURL+"/", "nuke")))-4]}); err != nil { log.Println("An error occurs during template execution: ", err) } @@ -214,7 +220,7 @@ func main() { signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM) srv := &http.Server{ - Addr: *bind, + Addr: *bind, Handler: StripPrefix(*baseURL, api.Router()), } From 973363b3dac6abb4cdbcdb91d2b6ff7af0ef3e43 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 21 Jul 2019 21:54:48 +0200 Subject: [PATCH 0058/1637] admin/api: refactor file API --- admin/api/exercice.go | 43 -------------------------------- admin/api/file.go | 41 ++++++++++++++++++++++++++++++ admin/static/views/exercice.html | 1 - 3 files changed, 41 insertions(+), 44 deletions(-) diff --git a/admin/api/exercice.go b/admin/api/exercice.go index c9c3d9c7..4522a3fc 100644 --- a/admin/api/exercice.go +++ b/admin/api/exercice.go @@ -1,7 +1,6 @@ package api import ( - "encoding/hex" "encoding/json" "errors" "strings" @@ -26,11 +25,6 @@ func init() { router.PATCH("/api/exercices/:eid/history.json", apiHandler(exerciceHandler(updateExerciceHistory))) router.DELETE("/api/exercices/:eid/history.json", apiHandler(exerciceHandler(delExerciceHistory))) - router.GET("/api/exercices/:eid/files", apiHandler(exerciceHandler(listExerciceFiles))) - router.POST("/api/exercices/:eid/files", apiHandler(exerciceHandler(createExerciceFile))) - router.GET("/api/exercices/:eid/files/:fid", apiHandler(exerciceFileHandler(showExerciceFile))) - router.DELETE("/api/exercices/:eid/files/:fid", apiHandler(exerciceFileHandler(deleteExerciceFile))) - router.GET("/api/exercices/:eid/hints", apiHandler(exerciceHandler(listExerciceHints))) router.POST("/api/exercices/:eid/hints", apiHandler(exerciceHandler(createExerciceHint))) router.GET("/api/exercices/:eid/hints/:hid", apiHandler(hintHandler(showExerciceHint))) @@ -64,10 +58,6 @@ func init() { _, _, errs := sync.SyncExercice(sync.GlobalImporter, theme, exercice.Path, nil) return errs, nil }))) - router.POST("/api/sync/exercices/:eid/files", apiHandler(exerciceHandler( - func(exercice fic.Exercice, _ []byte) (interface{}, error) { - return sync.SyncExerciceFiles(sync.GlobalImporter, exercice), nil - }))) router.POST("/api/sync/exercices/:eid/hints", apiHandler(exerciceHandler( func(exercice fic.Exercice, _ []byte) (interface{}, error) { return sync.SyncExerciceHints(sync.GlobalImporter, exercice), nil @@ -113,10 +103,6 @@ func exportResolutionMovies(_ httprouter.Params, body []byte) (interface{}, erro } } -func listExerciceFiles(exercice fic.Exercice, body []byte) (interface{}, error) { - return exercice.GetFiles() -} - func listExerciceHints(exercice fic.Exercice, body []byte) (interface{}, error) { return exercice.GetHints() } @@ -530,35 +516,6 @@ func deleteExerciceQuiz(quiz fic.MCQ, _ fic.Exercice, _ []byte) (interface{}, er return quiz.Delete() } -type uploadedFile struct { - URI string - Digest string -} - -func createExerciceFile(exercice fic.Exercice, body []byte) (interface{}, error) { - var uf uploadedFile - if err := json.Unmarshal(body, &uf); err != nil { - return nil, err - } - - return 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 { - return exercice.ImportFile(filePath, origin, digest) - } - }) -} - -func showExerciceFile(file fic.EFile, body []byte) (interface{}, error) { - return file, nil -} - -func deleteExerciceFile(file fic.EFile, _ []byte) (interface{}, error) { - return file.Delete() -} - func listExerciceTags(exercice fic.Exercice, _ []byte) (interface{}, error) { return exercice.GetTags() } diff --git a/admin/api/file.go b/admin/api/file.go index a70dd9ba..e8cf1eaa 100644 --- a/admin/api/file.go +++ b/admin/api/file.go @@ -1,8 +1,10 @@ package api import ( + "encoding/hex" "encoding/json" + "srs.epita.fr/fic-server/admin/sync" "srs.epita.fr/fic-server/libfic" "github.com/julienschmidt/httprouter" @@ -16,7 +18,21 @@ func init() { router.PUT("/api/files/:fileid", apiHandler(fileHandler(updateFile))) router.DELETE("/api/files/:fileid", apiHandler(fileHandler(deleteFile))) + router.GET("/api/exercices/:eid/files", apiHandler(exerciceHandler(listExerciceFiles))) + router.POST("/api/exercices/:eid/files", apiHandler(exerciceHandler(createExerciceFile))) + + router.GET("/api/exercices/:eid/files/:fid", apiHandler(exerciceFileHandler(showFile))) + router.PUT("/api/exercices/:eid/files/:fid", apiHandler(exerciceFileHandler(updateFile))) + router.DELETE("/api/exercices/:eid/files/:fid", apiHandler(exerciceFileHandler(deleteFile))) + + // Check router.POST("/api/files/:fileid/check", apiHandler(fileHandler(checkFile))) + + // Synchronize + router.POST("/api/sync/exercices/:eid/files", apiHandler(exerciceHandler( + func(exercice fic.Exercice, _ []byte) (interface{}, error) { + return sync.SyncExerciceFiles(sync.GlobalImporter, exercice), nil + }))) } func listFiles(_ httprouter.Params, body []byte) (interface{}, error) { @@ -24,6 +40,10 @@ func listFiles(_ httprouter.Params, body []byte) (interface{}, error) { return fic.GetFiles() } +func listExerciceFiles(exercice fic.Exercice, body []byte) (interface{}, error) { + return exercice.GetFiles() +} + func clearFiles(_ httprouter.Params, _ []byte) (interface{}, error) { return fic.ClearFiles() } @@ -32,6 +52,27 @@ func showFile(file fic.EFile, _ []byte) (interface{}, error) { return file, nil } +type uploadedFile struct { + URI string + Digest string +} + +func createExerciceFile(exercice fic.Exercice, body []byte) (interface{}, error) { + var uf uploadedFile + if err := json.Unmarshal(body, &uf); err != nil { + return nil, err + } + + return 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 { + return exercice.ImportFile(filePath, origin, digest) + } + }) +} + func updateFile(file fic.EFile, body []byte) (interface{}, error) { var uf fic.EFile if err := json.Unmarshal(body, &uf); err != nil { diff --git a/admin/static/views/exercice.html b/admin/static/views/exercice.html index f9a4e6b5..251d6113 100644 --- a/admin/static/views/exercice.html +++ b/admin/static/views/exercice.html @@ -44,7 +44,6 @@
From 936ef09e33da9a8f99f55a19ecdb20cb6df4d062 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 21 Jul 2019 21:55:36 +0200 Subject: [PATCH 0059/1637] admin: fix strange behaviour when deleting some items --- admin/static/js/app.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 79c4cb44..793ef969 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -1330,7 +1330,7 @@ angular.module("FICApp") $scope.deleteFile = function() { this.file.$delete(function() { - $scope.files.splice($scope.files.indexOf(this.file), 1); + $scope.files = ExerciceFile.query({ exerciceId: $routeParams.exerciceId }); }); $rootScope.staticFilesNeedUpdate++; return false; @@ -1368,7 +1368,7 @@ angular.module("FICApp") } $scope.deleteHint = function() { this.hint.$delete(function() { - $scope.hints.splice($scope.hints.indexOf(this.hint), 1); + $scope.hints = ExerciceHint.query({ exerciceId: $routeParams.exerciceId }); $rootScope.staticFilesNeedUpdate++; }, function(response) { $rootScope.newBox('danger', 'An error occurs when trying to delete hint:', response.data.errmsg); @@ -1415,7 +1415,7 @@ angular.module("FICApp") } $scope.deleteFlag = function() { this.flag.$delete(function() { - $scope.flags.splice($scope.flags.indexOf(this.flag), 1); + $scope.flags = ExerciceFlag.query({ exerciceId: $routeParams.exerciceId }); $rootScope.staticFilesNeedUpdate++; }, function(response) { $rootScope.newBox('danger', 'An error occurs when trying to delete flag:', response.data.errmsg); @@ -1473,7 +1473,7 @@ angular.module("FICApp") } $scope.deleteQuiz = function() { this.q.$delete(function() { - $scope.quiz.splice($scope.quiz.indexOf(this.q), 1); + $scope.quiz = ExerciceMCQFlag.query({ exerciceId: $routeParams.exerciceId }); $rootScope.staticFilesNeedUpdate++; }, function(response) { $rootScope.newBox('danger', 'An error occurs when trying to delete flag:', response.data.errmsg); From c8ece39cb2adbe2968ec3dfad7492bd8a50248d2 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 21 Jul 2019 22:31:43 +0200 Subject: [PATCH 0060/1637] sync: alert about unknown keys in challenge.txt --- admin/sync/exercice_defines.go | 9 ++++++--- admin/sync/exercice_hints.go | 2 +- admin/sync/exercices.go | 14 ++++++++++++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/admin/sync/exercice_defines.go b/admin/sync/exercice_defines.go index aa389929..e02c712e 100644 --- a/admin/sync/exercice_defines.go +++ b/admin/sync/exercice_defines.go @@ -64,11 +64,14 @@ type ExerciceParams struct { } // parseExerciceParams reads challenge definitions from defines.txt and extract usefull data to set up the challenge. -func parseExerciceParams(i Importer, exPath string) (p ExerciceParams, err error) { +func parseExerciceParams(i Importer, exPath string) (p ExerciceParams, md toml.MetaData, err error) { var defs string defs, err = getFileContent(i, path.Join(exPath, "challenge.txt")) + if err != nil { + return + } - _, err = toml.Decode(defs, &p) + md, err = toml.Decode(defs, &p) return } @@ -76,7 +79,7 @@ func parseExerciceParams(i Importer, exPath string) (p ExerciceParams, err error // getExerciceParams returns normalized func getExerciceParams(i Importer, exercice fic.Exercice) (params ExerciceParams, errs []string) { var err error - if params, err = parseExerciceParams(i, exercice.Path); err != nil { + if params, _, err = parseExerciceParams(i, exercice.Path); err != nil { errs = append(errs, fmt.Sprintf("%q: challenge.txt: %s", path.Base(exercice.Path), err)) } else if len(params.Flags) == 0 && len(params.FlagsUCQ) == 0 && len(params.FlagsMCQ) == 0 { errs = append(errs, fmt.Sprintf("%q: has no flag", path.Base(exercice.Path))) diff --git a/admin/sync/exercice_hints.go b/admin/sync/exercice_hints.go index 9b2f2ddd..b6ff8d98 100644 --- a/admin/sync/exercice_hints.go +++ b/admin/sync/exercice_hints.go @@ -16,7 +16,7 @@ import ( ) func buildExerciceHints(i Importer, exercice fic.Exercice) (hints []fic.EHint, errs []string) { - params, err := parseExerciceParams(i, exercice.Path) + params, _, err := parseExerciceParams(i, exercice.Path) if err != nil { errs = append(errs, fmt.Sprintf("%q: challenge.txt: %s", path.Base(exercice.Path), err)) return diff --git a/admin/sync/exercices.go b/admin/sync/exercices.go index 4c913ed6..b97a7a88 100644 --- a/admin/sync/exercices.go +++ b/admin/sync/exercices.go @@ -7,7 +7,9 @@ import ( "strconv" "strings" + "github.com/BurntSushi/toml" "gopkg.in/russross/blackfriday.v2" + "srs.epita.fr/fic-server/libfic" ) @@ -133,12 +135,20 @@ func BuildExercice(i Importer, theme fic.Theme, epath string, dmap *map[int64]fi } // Parse challenge.txt - p, err = parseExerciceParams(i, epath) + var md toml.MetaData + p, md, err = parseExerciceParams(i, epath) if err != nil { - errs = append(errs, fmt.Sprintf("%q: challenge.txt: %s", edir, err)) + errs = append(errs, fmt.Sprintf("%q: %s", edir, err)) return } + // Alert about unknown keys in challenge.txt + if len(md.Undecoded()) > 0 { + for _, k := range md.Undecoded() { + errs = append(errs, fmt.Sprintf("%q: challenge.txt: unknown key %q found, check https://srs.nemunai.re/fic/files/challenge.html", edir, k)) + } + } + if p.Gain == 0 { errs = append(errs, fmt.Sprintf("%q: challenge.txt: Undefined gain for challenge", edir)) } else { From 8131fda0e7c131f21ce8061b4e4e9f070beda0d5 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 21 Jul 2019 23:46:23 +0200 Subject: [PATCH 0061/1637] admin: display file dependancies and be able to remove them --- admin/api/file.go | 52 ++++++++++++++++++++++++++++++-- admin/api/handlers.go | 12 ++++++++ admin/static/js/app.js | 12 ++++++++ admin/static/views/exercice.html | 10 ++++++ libfic/file.go | 10 ++++++ 5 files changed, 93 insertions(+), 3 deletions(-) diff --git a/admin/api/file.go b/admin/api/file.go index e8cf1eaa..6b28b7e7 100644 --- a/admin/api/file.go +++ b/admin/api/file.go @@ -3,6 +3,8 @@ package api import ( "encoding/hex" "encoding/json" + "errors" + "fmt" "srs.epita.fr/fic-server/admin/sync" "srs.epita.fr/fic-server/libfic" @@ -18,6 +20,8 @@ func init() { router.PUT("/api/files/:fileid", apiHandler(fileHandler(updateFile))) router.DELETE("/api/files/:fileid", apiHandler(fileHandler(deleteFile))) + router.DELETE("/api/files/:fileid/dependancies/:depid", apiHandler(fileDependancyHandler(deleteFileDep))) + router.GET("/api/exercices/:eid/files", apiHandler(exerciceHandler(listExerciceFiles))) router.POST("/api/exercices/:eid/files", apiHandler(exerciceHandler(createExerciceFile))) @@ -35,13 +39,51 @@ func init() { }))) } +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 { + err = errors.New(fmt.Sprintf("Unknown type %T to handle file dependancy", k)) + return + } + } + + out = append(out, g) + } + + return +} + func listFiles(_ httprouter.Params, body []byte) (interface{}, error) { - // List all files - return fic.GetFiles() + return genFileList(fic.GetFiles()) } func listExerciceFiles(exercice fic.Exercice, body []byte) (interface{}, error) { - return exercice.GetFiles() + return genFileList(exercice.GetFiles()) } func clearFiles(_ httprouter.Params, _ []byte) (interface{}, error) { @@ -92,6 +134,10 @@ func deleteFile(file fic.EFile, _ []byte) (interface{}, error) { return file.Delete() } +func deleteFileDep(file fic.EFile, depid int64, _ []byte) (interface{}, error) { + return true, file.DeleteDepend(fic.FlagKey{Id: depid}) +} + func checkFile(file fic.EFile, _ []byte) (interface{}, error) { return true, file.CheckFileOnDisk() } diff --git a/admin/api/handlers.go b/admin/api/handlers.go index 2be16b34..5bae2ca8 100644 --- a/admin/api/handlers.go +++ b/admin/api/handlers.go @@ -316,6 +316,18 @@ func fileHandler(f func(fic.EFile, []byte) (interface{}, error)) func(httprouter } } +func fileDependancyHandler(f func(fic.EFile, int64, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { + return func(ps httprouter.Params, body []byte) (interface{}, error) { + if depid, err := strconv.ParseInt(string(ps.ByName("depid")), 10, 64); err != nil { + return nil, err + } else { + return fileHandler(func(file fic.EFile, b []byte) (interface{}, error) { + return f(file, depid, b) + })(ps, body) + } + } +} + func certificateHandler(f func(fic.Certificate, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { return func(ps httprouter.Params, body []byte) (interface{}, error) { if certid, err := strconv.ParseUint(strings.TrimSuffix(string(ps.ByName("certid")), ".p12"), 10, 64); err != nil { diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 793ef969..6e318533 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -1335,6 +1335,18 @@ angular.module("FICApp") $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) { + $rootScope.newBox('danger', 'An error occurs when removing file dep:', response.data.errmsg); + }); + $rootScope.staticFilesNeedUpdate++; + return false; + } $scope.saveFile = function() { this.file.$update(); $rootScope.staticFilesNeedUpdate++; diff --git a/admin/static/views/exercice.html b/admin/static/views/exercice.html index 251d6113..52cef198 100644 --- a/admin/static/views/exercice.html +++ b/admin/static/views/exercice.html @@ -57,6 +57,16 @@ Taille : {{ file.size | size }} ‐ BLAKE2b : {{ file.checksum | cksum }}
+
+ Dépend de la validation de : + aucun flag +
    +
  • + {{ dep.label }} + +
  • +
+
diff --git a/libfic/file.go b/libfic/file.go index 42195a36..3bf70570 100644 --- a/libfic/file.go +++ b/libfic/file.go @@ -300,6 +300,16 @@ func (f EFile) AddDepend(j Flag) (err error) { return } +// DeleteDepend insert a new dependency to a given flag. +func (f EFile) DeleteDepend(j Flag) (err error) { + if k, ok := j.(FlagKey); ok { + _, err = DBExec("DELETE FROM exercice_files_deps WHERE id_file = ? AND id_flag = ?", f.Id, k.Id) + } else { + err = errors.New("Dependancy type not implemented for this file.") + } + return +} + // GetDepends retrieve the flag's dependency list. func (f EFile) GetDepends() ([]Flag, error) { if rows, err := DBQuery("SELECT id_flag FROM exercice_files_deps WHERE id_file = ?", f.Id); err != nil { From 41a3279bf8b104f19ade5fe64f02430be6c43c07 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 30 Jul 2019 17:13:55 +0200 Subject: [PATCH 0062/1637] libfic: avoid stange MYSQL_HOST variable, expect IP or DN --- docker-compose.yml | 4 ++-- fickit-backend.yml | 4 ++-- libfic/db.go | 7 ++++++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 1febdb21..e40197bc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,7 +31,7 @@ services: depends_on: - mysql environment: - - MYSQL_HOST=tcp(mysql:3306) + - MYSQL_HOST=mysql - FICCA_PASS fic-backend: @@ -49,7 +49,7 @@ services: depends_on: - mysql environment: - - MYSQL_HOST=tcp(mysql:3306) + - MYSQL_HOST=mysql fic-frontend: build: diff --git a/fickit-backend.yml b/fickit-backend.yml index 75642f0b..cf7ac02a 100644 --- a/fickit-backend.yml +++ b/fickit-backend.yml @@ -154,7 +154,7 @@ services: image: nemunaire/fic-admin:latest command: ["/srv/admin", "-bind=:8081", "-baseurl=/admin/", "-localimport=/mnt/fic"] env: - - MYSQL_HOST=tcp(db:3306) + - MYSQL_HOST=db - FICCA_PASS=jee8AhloAith1aesCeQu5ahgIegaeM4K binds: - /etc/hosts:/etc/hosts:ro @@ -179,7 +179,7 @@ services: - name: fic-backend image: nemunaire/fic-backend:latest env: - - MYSQL_HOST=tcp(db:3306) + - MYSQL_HOST=db binds: - /etc/hosts:/etc/hosts:ro - /var/lib/fic/teams:/srv/TEAMS diff --git a/libfic/db.go b/libfic/db.go index 93e7d959..0ff9c7cd 100644 --- a/libfic/db.go +++ b/libfic/db.go @@ -19,7 +19,12 @@ func DSNGenerator() string { db_db := "fic" if v, exists := os.LookupEnv("MYSQL_HOST"); exists { - db_host = v + db_host = "tcp(" + v + ":" + if p, exists := os.LookupEnv("MYSQL_PORT"); exists { + db_host += p + ")" + } else { + db_host += "3306)" + } } if v, exists := os.LookupEnv("MYSQL_PASSWORD"); exists { db_password = v From 6d72e6b97063d85cd4f9ee663bb8d6e70292b254 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 30 Jul 2019 19:31:38 +0200 Subject: [PATCH 0063/1637] Complete commit 75463dcebbcdabfba6416436e22f6ce639bd5635: if not configured, challenge should not be considered as started --- backend/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/main.go b/backend/main.go index b078ce54..c2911dd2 100644 --- a/backend/main.go +++ b/backend/main.go @@ -53,7 +53,7 @@ func reloadSettings(config settings.FICSettings) { fic.HintCoefficient = config.HintCurCoefficient fic.WChoiceCoefficient = config.WChoiceCurCoefficient fic.ExerciceCurrentCoefficient = config.ExerciceCurCoefficient - ChStarted = time.Since(config.Start) >= 0 + ChStarted = config.Start.Unix() > 0 && time.Since(config.Start) >= 0 if lastRegeneration != config.Generation || fic.PartialValidation != config.PartialValidation || fic.UnlockedChallengeDepth != config.UnlockedChallengeDepth || fic.DisplayAllFlags != config.DisplayAllFlags || fic.FirstBlood != config.FirstBlood || fic.SubmissionCostBase != config.SubmissionCostBase || fic.SubmissionUniqueness != config.SubmissionUniqueness { fic.PartialValidation = config.PartialValidation fic.UnlockedChallengeDepth = config.UnlockedChallengeDepth From 0cbd6390ba44bc61071f5f1d9335889f992b80f1 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 30 Jul 2019 19:32:07 +0200 Subject: [PATCH 0064/1637] docker-compose: fix started detection --- configs/nginx-frontend-htpasswd.conf | 2 +- docker-compose.yml | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/configs/nginx-frontend-htpasswd.conf b/configs/nginx-frontend-htpasswd.conf index c672bc32..08e04a0b 100644 --- a/configs/nginx-frontend-htpasswd.conf +++ b/configs/nginx-frontend-htpasswd.conf @@ -96,7 +96,7 @@ server { expires epoch; add_header Cache-Control no-cache; - if (!-f $document_root/../started) { + if (!-f $document_root/../../startingblock/started) { rewrite ^/ /wait.json; } } diff --git a/docker-compose.yml b/docker-compose.yml index e40197bc..5344bdbb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -56,6 +56,7 @@ services: context: . dockerfile: Dockerfile-frontend image: nemunaire/fic-frontend:latest + command: "-startedFile /srv/startingblock/started" ports: - "8080:8080" volumes: @@ -64,6 +65,7 @@ services: - teams:/srv/TEAMS:ro - settings:/srv/SETTINGS:ro - submissions:/srv/submissions + - startingblock:/srv/startingblock depends_on: - fic-backend @@ -93,6 +95,7 @@ services: - files:/srv/FILES:ro - settings:/srv/SETTINGS:ro - teams:/srv/TEAMS:ro + - startingblock:/srv/startingblock:ro depends_on: - fic-frontend - fic-admin @@ -104,5 +107,9 @@ volumes: htdocs: pki: settings: + startingblock: + driver_opts: + type: tmpfs + device: tmpfs submissions: teams: From 8e618565ad4a61780ead17bb09e9a30566897dc8 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 5 Sep 2019 02:16:30 +0200 Subject: [PATCH 0065/1637] sync: Fix long running bug known as "why my fresh uploaded file is now empty again" Thanks to Nicolas Ribeyrolle --- admin/sync/importer_localfs.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/admin/sync/importer_localfs.go b/admin/sync/importer_localfs.go index 6fe32cfa..263c3d1a 100644 --- a/admin/sync/importer_localfs.go +++ b/admin/sync/importer_localfs.go @@ -47,9 +47,11 @@ func (i LocalImporter) importFile(URI string, next func(string, string) (interfa } else { os.Symlink(i.toURL(URI) + "_MERGED", dest) } - } - return ImportFile(i, URI, next) + return next(dest, URI) + } else { + return ImportFile(i, URI, next) + } } func (i LocalImporter) getFile(filename string, writer *bufio.Writer) error { From 846f2ce8a46630e041c45a9a2598182a6fe7ebce Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 5 Sep 2019 10:28:45 +0200 Subject: [PATCH 0066/1637] admin: Double check before doing dangerous actions in settings panel Suggested-by: Nicolas Ribeyrolle --- admin/static/js/app.js | 28 +++++++++++++++++++--------- admin/static/views/settings.html | 16 +++++++++++++++- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 6e318533..7d70c888 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -440,6 +440,7 @@ angular.module("FICApp") }) .controller("SettingsController", function($scope, $rootScope, Settings, ROSettings, $location, $http, $interval) { + $scope.displayDangerousActions = false; $scope.config = Settings.get(); $scope.config.$promise.then(function(response) { response.enableExerciceDepend = response.unlockedChallengeDepth >= 0; @@ -521,16 +522,25 @@ angular.module("FICApp") var d = new Date(ts + 120000); this.config.activateTime = d.toISOString(); } - $scope.reset = function(type) { - if (confirm("Êtes-vous sûr ?")) { - $http.post("/api/reset", {"type": type}).then(function(time) { - $rootScope.newBox('success', type + 'reseted'); - $location.url("/"); - }, function(response) { - $rootScope.newBox('danger', 'An error occurs when reseting ' + type + ':', response.data.errmsg); - }); + $scope.reset = function(type) { + var txts = { + "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.", } - }; + $rootScope.newYesNoBox('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) { + $rootScope.newBox('success', type + 'reseted'); + $location.url("/"); + }, function(response) { + $rootScope.newBox('danger', 'An error occurs when reseting ' + type + ':', response.data.errmsg); + }); + + } + }); + }; $scope.deepSyncInProgress = false; $scope.deepSync = function(type) { $rootScope.newYesNoBox('warning', 'Faire une synchronisation intégrale ?', '', diff --git a/admin/static/views/settings.html b/admin/static/views/settings.html index f6e67f6b..c7ea853c 100644 --- a/admin/static/views/settings.html +++ b/admin/static/views/settings.html @@ -175,7 +175,21 @@
-
+
+
+ Synchronisation et suppressions de masse +
+
+
+ +
+
+
+ +
From 168be0f7ccaa2086b2f19e2ccfae11e7a4ca640e Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 5 Sep 2019 10:50:00 +0200 Subject: [PATCH 0067/1637] config: Allow unconditional access to admin interface with compose --- configs/nginx-frontend-htpasswd.conf | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/nginx-frontend-htpasswd.conf b/configs/nginx-frontend-htpasswd.conf index 08e04a0b..aa4312f3 100644 --- a/configs/nginx-frontend-htpasswd.conf +++ b/configs/nginx-frontend-htpasswd.conf @@ -121,9 +121,9 @@ server { #auth_basic "Secure Zone"; #auth_basic_user_file ficpasswd; - if ($remote_user !~ "^nemunaire|bombal_s$") { - return 403; - } + #if ($remote_user !~ "^nemunaire|bombal_s$") { + # return 403; + #} proxy_pass http://fic-admin:8081/admin/api/; proxy_set_header X-Forwarded-For $remote_addr; @@ -134,9 +134,9 @@ server { #auth_basic "Secure Zone"; #auth_basic_user_file ficpasswd; - if ($remote_user !~ "^nemunaire|bombal_s$") { - return 403; - } + #if ($remote_user !~ "^nemunaire|bombal_s$") { + # return 403; + #} proxy_pass http://fic-admin:8081; proxy_set_header X-Forwarded-For $remote_addr; From 6f7bd3b78523f42b6db15bc8f03d9a5980353759 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 5 Sep 2019 11:01:11 +0200 Subject: [PATCH 0068/1637] admin interface need rw access to TEAMS directory to do associations --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 5344bdbb..3028efde 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,7 +26,7 @@ services: - files:/srv/FILES - pki:/srv/PKI - settings:/srv/SETTINGS - - teams:/srv/TEAMS:ro + - teams:/srv/TEAMS command: --baseurl /admin/ -localimport /mnt/fic -localimportsymlink depends_on: - mysql From 4e01377a2969d8fe7ff52ff8a8f75b494f6e5449 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 6 Sep 2019 20:12:01 +0200 Subject: [PATCH 0069/1637] sync: search theme's label in a title.txt file, fallback on dirname --- admin/sync/themes.go | 6 ++++-- libfic/theme.go | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/admin/sync/themes.go b/admin/sync/themes.go index 2da28f89..85cb12cc 100644 --- a/admin/sync/themes.go +++ b/admin/sync/themes.go @@ -57,7 +57,9 @@ func BuildTheme(i Importer, tdir string) (th *fic.Theme, errs []string) { th.Path = tdir // Extract theme's label - if f := strings.Index(tdir, "-"); f >= 0 { + 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) @@ -130,7 +132,7 @@ func SyncThemes(i Importer) []string { } var theme fic.Theme - if theme, err = fic.GetThemeByName(btheme.Name); err != nil { + if theme, err = fic.GetThemeByPath(btheme.Path); err != nil { if _, err := fic.CreateTheme(*btheme); err != nil { errs = append(errs, fmt.Sprintf("%q: an error occurs during add: %s", tdir, err)) continue diff --git a/libfic/theme.go b/libfic/theme.go index 3f89cc75..e4f07ede 100644 --- a/libfic/theme.go +++ b/libfic/theme.go @@ -62,6 +62,16 @@ func GetThemeByName(name string) (Theme, error) { return t, nil } +// GetThemeByPath retrieves a Theme from its dirname +func GetThemeByPath(dirname string) (Theme, error) { + var t Theme + if err := DBQueryRow("SELECT id_theme, name, url_id, path, authors, intro, headline, image FROM themes WHERE path=?", dirname).Scan(&t.Id, &t.Name, &t.URLId, &t.Path, &t.Authors, &t.Intro, &t.Headline, &t.Image); err != nil { + return t, err + } + + return t, nil +} + // CreateTheme creates and fills a new struct Theme and registers it into the database. func CreateTheme(theme Theme) (Theme, error) { if res, err := DBExec("INSERT INTO themes (name, url_id, authors, path, intro, headline, image) VALUES (?, ?, ?, ?, ?, ?, ?)", theme.Name, theme.URLId, theme.Authors, theme.Path, theme.Intro, theme.Headline, theme.Image); err != nil { From d7f0425d8a19690c168c5786c449805eed186842 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 6 Sep 2019 20:41:15 +0200 Subject: [PATCH 0070/1637] repochecker: Fix given URL to documentation --- admin/sync/exercices.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/sync/exercices.go b/admin/sync/exercices.go index b97a7a88..04f295b1 100644 --- a/admin/sync/exercices.go +++ b/admin/sync/exercices.go @@ -145,7 +145,7 @@ func BuildExercice(i Importer, theme fic.Theme, epath string, dmap *map[int64]fi // Alert about unknown keys in challenge.txt if len(md.Undecoded()) > 0 { for _, k := range md.Undecoded() { - errs = append(errs, fmt.Sprintf("%q: challenge.txt: unknown key %q found, check https://srs.nemunai.re/fic/files/challenge.html", edir, k)) + errs = append(errs, fmt.Sprintf("%q: challenge.txt: unknown key %q found, check https://srs.nemunai.re/fic/files/challenge/", edir, k)) } } From 33f7d104e4b2bfdbf8d64caad420255c0efdf5ff Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 6 Sep 2019 22:58:30 +0200 Subject: [PATCH 0071/1637] sync: MCQ justifications are given in the choice tag directly --- admin/sync/exercice_defines.go | 13 ++++++------- admin/sync/exercice_keys.go | 6 +++--- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/admin/sync/exercice_defines.go b/admin/sync/exercice_defines.go index e02c712e..e6f04494 100644 --- a/admin/sync/exercice_defines.go +++ b/admin/sync/exercice_defines.go @@ -27,13 +27,6 @@ type ExerciceUnlockFile struct { Filename string `toml:",omitempty"` } -// ExerciceFlagChoice holds informations about a choice (for MCQ and UCQ). -type ExerciceFlagChoice struct { - Label string `toml:",omitempty"` - Value interface{} `toml:",omitempty"` - Justification *ExerciceFlag `toml:",omitempty""` -} - // ExerciceFlag holds informations about one flag. type ExerciceFlag struct { Id int64 @@ -52,6 +45,12 @@ type ExerciceFlag struct { NoShuffle bool } +// 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 { Gain int64 diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index 5e7c86b7..d0d93b8e 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -233,7 +233,7 @@ func buildExerciceFlags(i Importer, exercice fic.Exercice) (flags map[int64]impo for cid, choice := range flag.Choice { var val bool - if choice.Justification != nil { + if choice.Raw != nil { if hasOne && !isJustified { errs = append(errs, fmt.Sprintf("%q: error MCQ #%d: all true items has to be justified in this MCQ.", path.Base(exercice.Path), nline+1)) continue @@ -259,8 +259,8 @@ func buildExerciceFlags(i Importer, exercice fic.Exercice) (flags map[int64]impo Response: val, }) - if isJustified && choice.Justification != nil { - addedFlag, choices, berrs := buildKeyFlag(exercice, *choice.Justification, nline+1, "Flag correspondant") + if isJustified && choice.Raw != nil { + addedFlag, choices, berrs := buildKeyFlag(exercice, choice.ExerciceFlag, nline+1, "Flag correspondant") if len(berrs) > 0 { errs = append(errs, berrs...) } From 99fcc99e82c4f6c5890647399ec0147818e8354a Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 6 Sep 2019 23:03:08 +0200 Subject: [PATCH 0072/1637] sync: turn IgnoreCase on by default with reverse field CaseSensitive --- admin/sync/exercice_defines.go | 28 ++++++++++++++-------------- admin/sync/exercice_keys.go | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/admin/sync/exercice_defines.go b/admin/sync/exercice_defines.go index e6f04494..e2b79a41 100644 --- a/admin/sync/exercice_defines.go +++ b/admin/sync/exercice_defines.go @@ -29,20 +29,20 @@ type ExerciceUnlockFile struct { // 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"` - Ordered bool `toml:",omitempty"` - IgnoreCase bool `toml:",omitempty"` - ValidatorRe string `toml:"validator_regexp,omitempty"` - Help string `toml:",omitempty"` - ChoicesCost int64 `toml:"choices_cost,omitempty"` - Choice []ExerciceFlagChoice - LockedFile []ExerciceUnlockFile `toml:"unlock_file,omitempty"` - NeedFlag []ExerciceDependency `toml:"need_flag,omitempty"` - NoShuffle bool + Id int64 + Label string `toml:",omitempty"` + Type string `toml:",omitempty"` + Raw interface{} + Separator string `toml:",omitempty"` + Ordered bool `toml:",omitempty"` + CaseSensitive bool `toml:",omitempty"` + ValidatorRe string `toml:"validator_regexp,omitempty"` + Help string `toml:",omitempty"` + ChoicesCost int64 `toml:"choices_cost,omitempty"` + Choice []ExerciceFlagChoice + LockedFile []ExerciceUnlockFile `toml:"unlock_file,omitempty"` + NeedFlag []ExerciceDependency `toml:"need_flag,omitempty"` + NoShuffle bool } // ExerciceFlagChoice holds informations about a choice (for MCQ and UCQ). diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index d0d93b8e..66c0c435 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -112,7 +112,7 @@ func buildKeyFlag(exercice fic.Exercice, flag ExerciceFlag, flagline int, defaul IdExercice: exercice.Id, Label: flag.Label, Help: flag.Help, - IgnoreCase: flag.IgnoreCase, + IgnoreCase: !flag.CaseSensitive, ValidatorRegexp: validatorRegexp(flag.ValidatorRe), Checksum: hashedFlag[:], ChoicesCost: flag.ChoicesCost, From 6265f85149439781b037a6ae1c277bc169e6e922 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 6 Sep 2019 23:08:59 +0200 Subject: [PATCH 0073/1637] sync: Implement vector flags --- admin/sync/exercice_keys.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index 66c0c435..a3459ba6 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -193,16 +193,18 @@ func buildExerciceFlags(i Importer, exercice fic.Exercice) (flags map[int64]impo flag.Type = "key" case "key": flag.Type = "key" + case "vector": + flag.Type = "vector" case "ucq": flag.Type = "ucq" case "mcq": flag.Type = "mcq" default: - errs = append(errs, fmt.Sprintf("%q: flag #%d: invalid type of flag: should be 'key', 'mcq' or 'ucq'.", path.Base(exercice.Path), nline+1)) + errs = append(errs, fmt.Sprintf("%q: flag #%d: invalid type of flag: should be 'key', 'mcq', 'ucq' or 'vector'.", path.Base(exercice.Path), nline+1)) continue } - if flag.Type == "key" || flag.Type == "ucq" { + if flag.Type == "key" || flag.Type == "ucq" || flag.Type == "vector" { addedFlag, choices, berrs := buildKeyFlag(exercice, flag, nline+1, "Flag") if len(berrs) > 0 { errs = append(errs, berrs...) From ca891cd9b2394951500804caef134a7cbab4e013 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 6 Sep 2019 23:23:50 +0200 Subject: [PATCH 0074/1637] sync: Fix non-import of MCQ during sync --- admin/sync/exercice_keys.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index a3459ba6..13635fc1 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -275,6 +275,11 @@ func buildExerciceFlags(i Importer, exercice fic.Exercice) (flags map[int64]impo } } } + + flags[flag.Id] = importFlag{ + Line: nline + 1, + Flag: addedFlag, + } } // Read dependency to flag From ded583008a29654a76e7309fc3a1ca8af9bab90b Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 7 Sep 2019 00:25:56 +0200 Subject: [PATCH 0075/1637] dashboard: handle correctly baseurl option On the first public.html GET, load the index.html file, as Go Template. --- dashboard/main.go | 12 +++++------- dashboard/static.go | 30 ++++++++++++++++++++++++++++-- dashboard/static/index.html | 26 +++++++++++++------------- 3 files changed, 46 insertions(+), 22 deletions(-) diff --git a/dashboard/main.go b/dashboard/main.go index fadc3d09..d5472054 100644 --- a/dashboard/main.go +++ b/dashboard/main.go @@ -66,7 +66,7 @@ func StripPrefix(prefix string, h http.Handler) http.Handler { func main() { // Read parameters from command line var bind = flag.String("bind", "127.0.0.1:8082", "Bind port/socket") - var baseURL = flag.String("baseurl", "/", "URL prepended to each URL") + flag.StringVar(&BaseURL, "baseurl", BaseURL, "URL prepended to each URL") flag.StringVar(&StaticDir, "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") @@ -90,12 +90,10 @@ func main() { if settings.SettingsDir, err = filepath.Abs(settings.SettingsDir); err != nil { log.Fatal(err) } - if *baseURL != "/" { - tmp := path.Clean(*baseURL) - baseURL = &tmp + if BaseURL != "/" { + BaseURL = path.Clean(BaseURL) } else { - tmp := "" - baseURL = &tmp + BaseURL = "" } if fwdr != nil && len(*fwdr) > 0 { forwarder = fwdr @@ -107,7 +105,7 @@ func main() { srv := &http.Server{ Addr: *bind, - Handler: StripPrefix(*baseURL, api.Router()), + Handler: StripPrefix(BaseURL, api.Router()), } // Serve content diff --git a/dashboard/static.go b/dashboard/static.go index f10d373f..3f272339 100644 --- a/dashboard/static.go +++ b/dashboard/static.go @@ -1,8 +1,13 @@ package main import ( + "bytes" "fmt" + "io" + "io/ioutil" + "log" "net/http" + "os" "path" "strings" "time" @@ -14,16 +19,37 @@ import ( "github.com/julienschmidt/httprouter" ) +var BaseURL = "/" var forwarder *string = nil var fwdPublicJson = false +var indexTmpl []byte + +func getIndexHtml(w io.Writer) { + if len(indexTmpl) == 0 { + if file, err := os.Open(path.Join(StaticDir, "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 init() { api.Router().GET("/", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, "index.html")) + http.Redirect(w, r, "public0.html", http.StatusFound) }) for i := 0; i <= 9; i++ { api.Router().GET(fmt.Sprintf("/public%d.html", i), func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, "index.html")) + getIndexHtml(w) }) } diff --git a/dashboard/static/index.html b/dashboard/static/index.html index e01a2a8f..cf6fbcbc 100644 --- a/dashboard/static/index.html +++ b/dashboard/static/index.html @@ -8,11 +8,11 @@ - - - - - + + + + +
@@ -554,13 +554,13 @@
- - - - - - - - + + + + + + + + From f2fc1428693ad66af86661ec20590e9e4b7f6d83 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 7 Sep 2019 01:25:42 +0200 Subject: [PATCH 0076/1637] api: remote route takes advantage from builds functions --- admin/api/exercice.go | 5 +++++ admin/api/theme.go | 2 +- admin/sync/exercice_hints.go | 26 ++++++++++++++++++++++++-- admin/sync/exercice_keys.go | 23 +++++++++++++++++++++++ admin/sync/exercices.go | 25 +++++++++++++++++++++++-- admin/sync/themes.go | 8 +++++++- 6 files changed, 83 insertions(+), 6 deletions(-) diff --git a/admin/api/exercice.go b/admin/api/exercice.go index 4522a3fc..81d1e63c 100644 --- a/admin/api/exercice.go +++ b/admin/api/exercice.go @@ -52,6 +52,11 @@ func init() { router.POST("/api/exercices/:eid/tags", apiHandler(exerciceHandler(addExerciceTag))) router.PUT("/api/exercices/:eid/tags", apiHandler(exerciceHandler(updateExerciceTags))) + // Remote + router.GET("/api/remote/themes/:thid/exercices/:exid", apiHandler(sync.ApiGetRemoteExercice)) + router.GET("/api/remote/themes/:thid/exercices/:exid/hints", apiHandler(sync.ApiGetRemoteExerciceHints)) + router.GET("/api/remote/themes/:thid/exercices/:exid/flags", apiHandler(sync.ApiGetRemoteExerciceFlags)) + // Synchronize router.POST("/api/sync/themes/:thid/exercices/:eid", apiHandler(themedExerciceHandler( func(theme fic.Theme, exercice fic.Exercice, _ []byte) (interface{}, error) { diff --git a/admin/api/theme.go b/admin/api/theme.go index 1fbcdd65..dc4083ca 100644 --- a/admin/api/theme.go +++ b/admin/api/theme.go @@ -41,7 +41,7 @@ func init() { // Remote router.GET("/api/remote/themes", apiHandler(sync.ApiListRemoteThemes)) router.GET("/api/remote/themes/:thid", apiHandler(sync.ApiGetRemoteTheme)) - router.GET("/api/remote/themes/:thid/exercices", apiHandler(themeHandler(sync.ApiListRemoteExercices))) + router.GET("/api/remote/themes/:thid/exercices", apiHandler(sync.ApiListRemoteExercices)) // Synchronize router.GET("/api/sync/deep", apiHandler( diff --git a/admin/sync/exercice_hints.go b/admin/sync/exercice_hints.go index b6ff8d98..ac1042ee 100644 --- a/admin/sync/exercice_hints.go +++ b/admin/sync/exercice_hints.go @@ -4,15 +4,17 @@ import ( "bufio" "crypto" "encoding/hex" + "errors" "fmt" "io" "os" "path" "strings" - "srs.epita.fr/fic-server/libfic" - + "github.com/julienschmidt/httprouter" _ "golang.org/x/crypto/blake2b" + + "srs.epita.fr/fic-server/libfic" ) func buildExerciceHints(i Importer, exercice fic.Exercice) (hints []fic.EHint, errs []string) { @@ -106,3 +108,23 @@ func SyncExerciceHints(i Importer, exercice fic.Exercice) (errs []string) { } return } + +// ApiListRemoteExerciceHints is an accessor letting foreign packages to access remote exercice hints. +func ApiGetRemoteExerciceHints(ps httprouter.Params, _ []byte) (interface{}, error) { + theme, errs := BuildTheme(GlobalImporter, ps.ByName("thid")) + if theme != nil { + exercice, _, _, _, errs := BuildExercice(GlobalImporter, *theme, path.Join(theme.Path, ps.ByName("exid")), nil) + if exercice != nil { + hints, errs := CheckExerciceHints(GlobalImporter, *exercice) + if hints != nil { + return hints, nil + } else { + return hints, errors.New(fmt.Sprintf("%q", errs)) + } + } else { + return exercice, errors.New(fmt.Sprintf("%q", errs)) + } + } else { + return nil, errors.New(fmt.Sprintf("%q", errs)) + } +} diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index 13635fc1..f270437f 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -1,6 +1,7 @@ package sync import ( + "errors" "fmt" "math/rand" "path" @@ -8,6 +9,8 @@ import ( "strings" "unicode" + "github.com/julienschmidt/httprouter" + "srs.epita.fr/fic-server/libfic" ) @@ -385,3 +388,23 @@ func SyncExerciceFlags(i Importer, exercice fic.Exercice) (errs []string) { return } + +// ApiListRemoteExerciceFlags is an accessor letting foreign packages to access remote exercice flags. +func ApiGetRemoteExerciceFlags(ps httprouter.Params, _ []byte) (interface{}, error) { + theme, errs := BuildTheme(GlobalImporter, ps.ByName("thid")) + if theme != nil { + exercice, _, _, _, errs := BuildExercice(GlobalImporter, *theme, path.Join(theme.Path, ps.ByName("exid")), nil) + if exercice != nil { + flags, errs := CheckExerciceFlags(GlobalImporter, *exercice, []fic.EFile{}) + if flags != nil { + return flags, nil + } else { + return flags, errors.New(fmt.Sprintf("%q", errs)) + } + } else { + return exercice, errors.New(fmt.Sprintf("%q", errs)) + } + } else { + return nil, errors.New(fmt.Sprintf("%q", errs)) + } +} diff --git a/admin/sync/exercices.go b/admin/sync/exercices.go index 04f295b1..4b7116eb 100644 --- a/admin/sync/exercices.go +++ b/admin/sync/exercices.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/BurntSushi/toml" + "github.com/julienschmidt/httprouter" "gopkg.in/russross/blackfriday.v2" "srs.epita.fr/fic-server/libfic" @@ -264,6 +265,26 @@ func SyncExercices(i Importer, theme fic.Theme) (errs []string) { } // ApiListRemoteExercices is an accessor letting foreign packages to access remote exercices list. -func ApiListRemoteExercices(theme fic.Theme, _ []byte) (interface{}, error) { - return GetExercices(GlobalImporter, theme) +func ApiListRemoteExercices(ps httprouter.Params, _ []byte) (interface{}, error) { + theme, errs := BuildTheme(GlobalImporter, ps.ByName("thid")) + if theme != nil { + return GetExercices(GlobalImporter, *theme) + } else { + return nil, errors.New(fmt.Sprintf("%q", errs)) + } +} + +// ApiListRemoteExercice is an accessor letting foreign packages to access remote exercice attributes. +func ApiGetRemoteExercice(ps httprouter.Params, _ []byte) (interface{}, error) { + theme, errs := BuildTheme(GlobalImporter, ps.ByName("thid")) + if theme != nil { + exercice, _, _, _, errs := BuildExercice(GlobalImporter, *theme, path.Join(theme.Path, ps.ByName("exid")), nil) + if exercice != nil { + return exercice, nil + } else { + return exercice, errors.New(fmt.Sprintf("%q", errs)) + } + } else { + return nil, errors.New(fmt.Sprintf("%q", errs)) + } } diff --git a/admin/sync/themes.go b/admin/sync/themes.go index 85cb12cc..d3aa485c 100644 --- a/admin/sync/themes.go +++ b/admin/sync/themes.go @@ -1,6 +1,7 @@ package sync import ( + "errors" "fmt" "math/rand" "path" @@ -159,5 +160,10 @@ func ApiListRemoteThemes(_ httprouter.Params, _ []byte) (interface{}, error) { // ApiListRemoteTheme is an accessor letting foreign packages to access remote main theme attributes. func ApiGetRemoteTheme(ps httprouter.Params, _ []byte) (interface{}, error) { - return getAuthors(GlobalImporter, ps.ByName("thid")) + r, errs := BuildTheme(GlobalImporter, ps.ByName("thid")) + if r == nil { + return r, errors.New(fmt.Sprintf("%q", errs)) + } else { + return r, nil + } } From 0c8099a639d446363436b59c7c9cd373c0ae8875 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 7 Sep 2019 01:30:39 +0200 Subject: [PATCH 0077/1637] config: add a route to fic-dashboard --- configs/nginx-frontend-htpasswd.conf | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/configs/nginx-frontend-htpasswd.conf b/configs/nginx-frontend-htpasswd.conf index aa4312f3..74b5af77 100644 --- a/configs/nginx-frontend-htpasswd.conf +++ b/configs/nginx-frontend-htpasswd.conf @@ -117,6 +117,19 @@ server { add_header Cache-Control no-cache; } + location /dashboard/ { + #auth_basic "Secure Zone"; + #auth_basic_user_file ficpasswd; + + #if ($remote_user !~ "^nemunaire|bombal_s$") { + # return 403; + #} + + proxy_pass http://fic-dashboard:8082; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_redirect off; + } + location /api/ { #auth_basic "Secure Zone"; #auth_basic_user_file ficpasswd; From 48a941e2c5b57ad4d72f928d8672db433267e9d4 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 7 Sep 2019 01:31:31 +0200 Subject: [PATCH 0078/1637] compose: use /dashboard baseurl to be proxified by nginx --- docker-compose.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 3028efde..a7cea8b2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -27,7 +27,7 @@ services: - pki:/srv/PKI - settings:/srv/SETTINGS - teams:/srv/TEAMS - command: --baseurl /admin/ -localimport /mnt/fic -localimportsymlink + command: -baseurl /admin/ -localimport /mnt/fic -localimportsymlink depends_on: - mysql environment: @@ -74,6 +74,7 @@ services: context: . dockerfile: Dockerfile-dashboard image: nemunaire/fic-dashboard:latest + command: "-baseurl /dashboard/" ports: - "8082:8082" volumes: From cb7f3326c4b9761004f5cf61209bd89acb7697ac Mon Sep 17 00:00:00 2001 From: Tristan Ruter-Naon Date: Wed, 2 Oct 2019 16:20:06 +0200 Subject: [PATCH 0079/1637] admin: fix typo --- admin/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/main.go b/admin/main.go index 39e886ac..22d9c69f 100644 --- a/admin/main.go +++ b/admin/main.go @@ -76,7 +76,7 @@ func main() { pki.SetCAPassword(v) } else { log.Println("WARNING: no password defined for the CA, will use empty password to secure CA private key") - log.Println("WARNING: PLEASE DEFINED ENVIRONMENT VARIABLE: FICCA_PASS") + log.Println("WARNING: PLEASE DEFINE ENVIRONMENT VARIABLE: FICCA_PASS") } if v, exists := os.LookupEnv("FICCLOUD_URL"); exists { cloudDAVBase = v From 0766fbe48032cb848d2555ad81e3edb2039e1685 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 12 Oct 2019 12:54:01 +0200 Subject: [PATCH 0080/1637] sync: don't rely on map order to import flags Sometimes, maps order doesn't match file order. Return flag ID as list to keep the order. --- admin/sync/exercice_keys.go | 100 +++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index f270437f..e452aa34 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -171,10 +171,10 @@ type importFlag struct { } // buildExerciceFlags read challenge.txt and extract all flags. -func buildExerciceFlags(i Importer, exercice fic.Exercice) (flags map[int64]importFlag, errs []string) { +func buildExerciceFlags(i Importer, exercice fic.Exercice) (flags map[int64]importFlag, flagids []int64, errs []string) { params, gerrs := getExerciceParams(i, exercice) if len(gerrs) > 0 { - return flags, gerrs + return flags, flagids, gerrs } flags = map[int64]importFlag{} @@ -285,6 +285,8 @@ func buildExerciceFlags(i Importer, exercice fic.Exercice) (flags map[int64]impo } } + flagids = append(flagids, flag.Id) + // Read dependency to flag for _, nf := range flag.NeedFlag { if v, ok := flags[flag.Id]; ok { @@ -307,32 +309,34 @@ func buildExerciceFlags(i Importer, exercice fic.Exercice) (flags map[int64]impo // CheckExerciceFlags checks if all flags for the given challenge are correct. func CheckExerciceFlags(i Importer, exercice fic.Exercice, files []fic.EFile) (rf []fic.Flag, errs []string) { - flags, berrs := buildExerciceFlags(i, exercice) + flags, flagsids, berrs := buildExerciceFlags(i, exercice) errs = append(errs, berrs...) - for _, flag := range flags { - // Check dependency to flag - for _, nf := range flag.FlagsDeps { - if _, ok := flags[nf]; !ok { - errs = append(errs, fmt.Sprintf("%q: error flag #%d dependency to flag id=%d: id not defined", path.Base(exercice.Path), flag.Line, nf)) - } - } - - // Check dependency to file - for _, lf := range flag.FilesDeps { - found := false - for _, f := range files { - if f.Name == lf { - found = true - break + 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 = append(errs, fmt.Sprintf("%q: error flag #%d dependency to flag id=%d: id not defined", path.Base(exercice.Path), flag.Line, nf)) } } - if !found { - errs = append(errs, fmt.Sprintf("%q: error flag #%d dependency to %s: No such file", path.Base(exercice.Path), flag.Line, lf)) - } - } - rf = append(rf, flag.Flag) + // Check dependency to file + for _, lf := range flag.FilesDeps { + found := false + for _, f := range files { + if f.Name == lf { + found = true + break + } + } + if !found { + errs = append(errs, fmt.Sprintf("%q: error flag #%d dependency to %s: No such file", path.Base(exercice.Path), flag.Line, lf)) + } + } + + rf = append(rf, flag.Flag) + } } return @@ -345,41 +349,43 @@ func SyncExerciceFlags(i Importer, exercice fic.Exercice) (errs []string) { } else if _, err := exercice.WipeMCQs(); err != nil { errs = append(errs, err.Error()) } else { - flags, berrs := buildExerciceFlags(i, exercice) + flags, flagids, berrs := buildExerciceFlags(i, exercice) errs = append(errs, berrs...) kmap := map[int64]fic.Flag{} // Import flags - for flagid, flag := range flags { - if addedFlag, err := exercice.AddFlag(flag.Flag); err != nil { - errs = append(errs, fmt.Sprintf("%q: error flag #%d: %s", path.Base(exercice.Path), flag.Line, err)) - } else { - if f, ok := addedFlag.(fic.FlagKey); ok { - for _, choice := range flag.Choices { - if _, err := f.AddChoice(choice); err != nil { - errs = append(errs, fmt.Sprintf("%q: error in flag #%d choice #FIXME: %s", path.Base(exercice.Path), flag.Line, err)) + for _, flagid := range flagids { + if flag, ok := flags[flagid]; ok { + if addedFlag, err := exercice.AddFlag(flag.Flag); err != nil { + errs = append(errs, fmt.Sprintf("%q: error flag #%d: %s", path.Base(exercice.Path), flag.Line, err)) + } else { + if f, ok := addedFlag.(fic.FlagKey); ok { + for _, choice := range flag.Choices { + if _, err := f.AddChoice(choice); err != nil { + errs = append(errs, fmt.Sprintf("%q: error in flag #%d choice #FIXME: %s", path.Base(exercice.Path), flag.Line, err)) + } } } - } - kmap[flagid] = addedFlag + kmap[flagid] = addedFlag - // Import dependency to flag - for _, nf := range flag.FlagsDeps { - if rf, ok := kmap[nf]; !ok { - errs = append(errs, fmt.Sprintf("%q: error flag #%d dependency to flag id=%d: id not defined, perhaps not available at time of processing", path.Base(exercice.Path), flag.Line, nf)) - } else if err := addedFlag.AddDepend(rf); err != nil { - errs = append(errs, fmt.Sprintf("%q: error flag #%d dependency to id=%d: %s", path.Base(exercice.Path), flag.Line, nf, err)) + // Import dependency to flag + for _, nf := range flag.FlagsDeps { + if rf, ok := kmap[nf]; !ok { + errs = append(errs, fmt.Sprintf("%q: error flag #%d dependency to flag id=%d: id not defined, perhaps not available at time of processing", path.Base(exercice.Path), flag.Line, nf)) + } else if err := addedFlag.AddDepend(rf); err != nil { + errs = append(errs, fmt.Sprintf("%q: error flag #%d dependency to id=%d: %s", path.Base(exercice.Path), flag.Line, nf, err)) + } } - } - // Import dependency to file - for _, lf := range flag.FilesDeps { - if rf, err := exercice.GetFileByFilename(lf); err != nil { - errs = append(errs, fmt.Sprintf("%q: error flag #%d dependency to %s: %s", path.Base(exercice.Path), flag.Line, lf, err)) - } else if err := rf.AddDepend(addedFlag); err != nil { - errs = append(errs, fmt.Sprintf("%q: error flag #%d dependency to %s: %s", path.Base(exercice.Path), flag.Line, lf, err)) + // Import dependency to file + for _, lf := range flag.FilesDeps { + if rf, err := exercice.GetFileByFilename(lf); err != nil { + errs = append(errs, fmt.Sprintf("%q: error flag #%d dependency to %s: %s", path.Base(exercice.Path), flag.Line, lf, err)) + } else if err := rf.AddDepend(addedFlag); err != nil { + errs = append(errs, fmt.Sprintf("%q: error flag #%d dependency to %s: %s", path.Base(exercice.Path), flag.Line, lf, err)) + } } } } From 97a3aa713fa31012aedd7f8394ada9071cf5dfc0 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 12 Oct 2019 13:37:24 +0200 Subject: [PATCH 0081/1637] sync: fix hash computation by factorizing --- admin/sync/exercice_keys.go | 6 +++- libfic/flag_key.go | 60 ++++++++++++++++--------------------- libfic/team_my.go | 2 +- 3 files changed, 32 insertions(+), 36 deletions(-) diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index e452aa34..348976b2 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -110,7 +110,11 @@ func buildKeyFlag(exercice fic.Exercice, flag ExerciceFlag, flagline int, defaul errs = append(errs, fmt.Sprintf("%q: WARNING flag #%d: non-printable characters in flag, is this really expected?", path.Base(exercice.Path), flagline)) } - hashedFlag := fic.ComputeHashedFlag([]byte(raw)) + hashedFlag, err := fic.ComputeHashedFlag([]byte(raw), !flag.CaseSensitive, validatorRegexp(flag.ValidatorRe)) + if err != nil { + errs = append(errs, err.Error()) + return + } fl := fic.Flag(fic.FlagKey{ IdExercice: exercice.Id, Label: flag.Label, diff --git a/libfic/flag_key.go b/libfic/flag_key.go index 9dd3308e..9a29ecaf 100644 --- a/libfic/flag_key.go +++ b/libfic/flag_key.go @@ -68,9 +68,25 @@ func (e Exercice) GetFlagKeyByLabel(label string) (k FlagKey, err error) { } // ComputeHashedFlag calculates the expected checksum for the given raw_value. -func ComputeHashedFlag(raw_value []byte) [blake2b.Size]byte { - hash := blake2b.Sum512(raw_value) - return hash +func ComputeHashedFlag(raw_value []byte, ignorecase bool, validator_regexp *string) (hash [blake2b.Size]byte, err error) { + if ignorecase { + raw_value = bytes.ToLower(raw_value) + } + + // Check that raw value passes through the regexp + if validator_regexp != nil { + if raw_value, err = ExecValidatorRegexp(*validator_regexp, raw_value, ignorecase); err != nil { + return + } + } + + // Check that the value is not empty + if len(raw_value) == 0 { + err = errors.New("Empty flag after applying filters") + } + + hash = blake2b.Sum512(raw_value) + return } func ExecValidatorRegexp(vre string, val []byte, ignorecase bool) ([]byte, error) { @@ -88,19 +104,11 @@ func ExecValidatorRegexp(vre string, val []byte, ignorecase bool) ([]byte, error // AddRawFlagKey creates and fills a new struct FlagKey, from a non-hashed flag, and registers it into the database. func (e Exercice) AddRawFlagKey(name string, help string, ignorecase bool, validator_regexp *string, raw_value []byte, choicescost int64) (f FlagKey, err error) { - if ignorecase { - raw_value = bytes.ToLower(raw_value) + hash, errr := ComputeHashedFlag(raw_value, ignorecase, validator_regexp) + if errr != nil { + return f, err } - // Check that raw value passes through the regexp - if validator_regexp != nil { - if raw_value, err = ExecValidatorRegexp(*validator_regexp, raw_value, ignorecase); err != nil { - return - } - } - - hash := ComputeHashedFlag(raw_value) - f = FlagKey{ Label: name, Help: help, @@ -139,26 +147,10 @@ func (k FlagKey) Create(e Exercice) (Flag, error) { } } -// SetChecksumFromValue . -func (k FlagKey) ComputeChecksum(val []byte) (cksum []byte, err error) { - if k.IgnoreCase { - val = bytes.ToLower(val) - } - - // Check that raw value passes through the regexp - if k.ValidatorRegexp != nil { - if val, err = ExecValidatorRegexp(*k.ValidatorRegexp, val, k.IgnoreCase); err != nil { - return - } - } - - // Check that the value is not empty - if len(val) == 0 { - err = errors.New("Empty flag after applying filters") - } - - hash := ComputeHashedFlag(val) - return hash[:], err +// ComputeChecksum calculates the checksum for a given value. +func (k FlagKey) ComputeChecksum(val []byte) ([]byte, error) { + cksum, err := ComputeHashedFlag(val, k.IgnoreCase, k.ValidatorRegexp) + return cksum[:], err } // Update applies modifications back to the database. diff --git a/libfic/team_my.go b/libfic/team_my.go index c9d44f8d..75b798a0 100644 --- a/libfic/team_my.go +++ b/libfic/team_my.go @@ -296,7 +296,7 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { } if t == nil { - h := ComputeHashedFlag([]byte(soluce)) + h, _ := ComputeHashedFlag([]byte(soluce), false, nil) m.Soluce = hex.EncodeToString(h[:]) } From c2c5cf4ce385c0cbf8a5faa3882169ddd282305a Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 12 Oct 2019 15:50:31 +0200 Subject: [PATCH 0082/1637] backend: add parameter to launch a number of generation workers --- backend/generation.go | 6 +++++- backend/main.go | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/backend/generation.go b/backend/generation.go index 0c98edda..2b61f4ad 100644 --- a/backend/generation.go +++ b/backend/generation.go @@ -28,6 +28,7 @@ type genStruct struct { Type GenerateType } +var parallelJobs = runtime.NumCPU() var genTeamQueue chan *fic.Team var genQueue chan genStruct var inQueueMutex sync.RWMutex @@ -37,8 +38,11 @@ func init() { genTeamQueue = make(chan *fic.Team) genQueue = make(chan genStruct) inGenQueue = map[GenerateType]bool{} +} - for i := runtime.NumCPU(); i > 0; i-- { +func launchWorkers() { + log.Println("Running with", parallelJobs, "worker(s)") + for i := parallelJobs; i > 0; i-- { go consumer() } } diff --git a/backend/main.go b/backend/main.go index c2911dd2..5f44b47d 100644 --- a/backend/main.go +++ b/backend/main.go @@ -87,6 +87,7 @@ func main() { flag.StringVar(&fic.FilesDir, "files", "/files", "Request path prefix to reach files") var debugINotify = flag.Bool("debuginotify", false, "Show skipped inotofy events") flag.BoolVar(&skipInitialGeneration, "skipfullgeneration", skipInitialGeneration, "Skip the initial regeneration") + flag.IntVar(¶llelJobs, "jobs", parallelJobs, "Number of generation workers") flag.Parse() log.SetPrefix("[backend] ") @@ -97,6 +98,8 @@ func main() { rand.Seed(time.Now().UnixNano()) + launchWorkers() + log.Println("Creating submission directory...") if _, err := os.Stat(SubmissionDir); os.IsNotExist(err) { if err := os.MkdirAll(SubmissionDir, 0777); err != nil { From cefed3bf231a1a471a983a7190efdb8e023ca1e8 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 26 Oct 2019 09:06:06 +0200 Subject: [PATCH 0083/1637] admin: fix synchronisation when idtheme is not in url --- admin/static/js/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 7d70c888..1fdb4053 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -1241,7 +1241,7 @@ angular.module("FICApp") $scope.syncExo = function() { $scope.inSync = true; $http({ - url: "/api/sync/themes/" + $routeParams.themeId + "/exercices/" + $routeParams.exerciceId, + url: "/api/sync/themes/" + $scope.exercice.id_theme + "/exercices/" + $routeParams.exerciceId, method: "POST" }).then(function(response) { $scope.inSync = false; From 20f2597248d1ec8fb1ce668441040a5c204ecb49 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 26 Oct 2019 09:55:12 +0200 Subject: [PATCH 0084/1637] backend: fix a pointer reuse --- backend/generation.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/generation.go b/backend/generation.go index 2b61f4ad..5ea259bb 100644 --- a/backend/generation.go +++ b/backend/generation.go @@ -200,7 +200,8 @@ func genAll() { log.Println("Team retrieval error: ", err) } else { for _, team := range teams { - genTeamQueue <- &team + myteam := team // team is reused, we need to create a new variable here to store the value + genTeamQueue <- &myteam } } } From 6740256a3212501b59c92b6854357f7631d7cf55 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 26 Oct 2019 11:33:30 +0200 Subject: [PATCH 0085/1637] sync: implement hint dependency on flags --- admin/sync/exercice_defines.go | 1 + libfic/db.go | 10 +++++++++ libfic/flag.go | 2 ++ libfic/hint.go | 40 +++++++++++++++++++++++++++++++++- libfic/team.go | 19 ++++++++++++++++ libfic/team_my.go | 2 +- 6 files changed, 72 insertions(+), 2 deletions(-) diff --git a/admin/sync/exercice_defines.go b/admin/sync/exercice_defines.go index e2b79a41..b8f46d0b 100644 --- a/admin/sync/exercice_defines.go +++ b/admin/sync/exercice_defines.go @@ -14,6 +14,7 @@ type ExerciceHintParams struct { Content string Cost int64 Title string + NeedFlag []ExerciceDependency `toml:"need_flag,omitempty"` } // ExerciceDependency holds dependency definitions information. diff --git a/libfic/db.go b/libfic/db.go index 0ff9c7cd..27b65f90 100644 --- a/libfic/db.go +++ b/libfic/db.go @@ -179,6 +179,16 @@ CREATE TABLE IF NOT EXISTS exercice_flags( choices_cost INTEGER NOT NULL, FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; +`); err != nil { + return err + } + if _, err := db.Exec(` +CREATE TABLE IF NOT EXISTS exercice_hints_deps( + id_hint INTEGER NOT NULL, + id_flag_dep INTEGER NOT NULL, + FOREIGN KEY(id_hint) REFERENCES exercice_hints(id_hint), + FOREIGN KEY(id_flag_dep) REFERENCES exercice_flags(id_flag) +) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin; `); err != nil { return err } diff --git a/libfic/flag.go b/libfic/flag.go index 0e70093a..28e512b9 100644 --- a/libfic/flag.go +++ b/libfic/flag.go @@ -49,6 +49,8 @@ func (e Exercice) WipeFlags() (int64, error) { return 0, err } else if _, err := DBExec("DELETE FROM exercice_flags_omcq_deps WHERE id_flag IN (SELECT id_flag FROM exercice_flags WHERE id_exercice = ?)", e.Id); err != nil { return 0, err + } else if _, err := DBExec("DELETE FROM exercice_hints_deps WHERE id_flag_dep IN (SELECT id_flag FROM exercice_flags WHERE id_exercice = ?)", e.Id); err != nil { + return 0, err } else if _, err := DBExec("DELETE FROM exercice_mcq_okey_deps WHERE id_flag_dep IN (SELECT id_flag FROM exercice_flags WHERE id_exercice = ?)", e.Id); err != nil { return 0, err } else if _, err := DBExec("DELETE FROM team_wchoices WHERE id_flag IN (SELECT id_flag FROM exercice_flags WHERE id_exercice = ?)", e.Id); err != nil { diff --git a/libfic/hint.go b/libfic/hint.go index 774c81d9..a04793be 100644 --- a/libfic/hint.go +++ b/libfic/hint.go @@ -1,6 +1,8 @@ package fic import ( + "errors" + "fmt" "path" "strings" ) @@ -105,7 +107,9 @@ func (h EHint) Delete() (int64, error) { // WipeHints deletes (only in the database, not on disk) hints coming with the challenge. func (e Exercice) WipeHints() (int64, error) { - if res, err := DBExec("DELETE FROM exercice_hints WHERE id_exercice = ?", e.Id); err != nil { + if _, err := DBExec("DELETE FROM exercice_hints_deps WHERE id_hint IN (SELECT id_hint FROM exercice_hints WHERE id_exercice = ?)", e.Id); err != nil { + return 0, err + } else if res, err := DBExec("DELETE FROM exercice_hints WHERE id_exercice = ?", e.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err @@ -114,6 +118,40 @@ func (e Exercice) WipeHints() (int64, error) { } } +// AddDepend insert a new dependency to a given flag. +func (h EHint) AddDepend(f Flag) (err error) { + if d, ok := f.(FlagKey); ok { + _, err = DBExec("INSERT INTO exercice_hints_deps (id_hint, id_flag_dep) VALUES (?, ?)", h.Id, d.Id) + } else { + err = errors.New(fmt.Sprintf("Dependancy type for key (%T) not implemented for this flag.", f)) + } + return +} + +// GetDepends retrieve the flag's dependency list. +func (h EHint) GetDepends() ([]Flag, error) { + var deps = make([]Flag, 0) + + if rows, err := DBQuery("SELECT id_flag_dep FROM exercice_hints_deps WHERE id_hint = ?", h.Id); err != nil { + return nil, err + } else { + defer rows.Close() + + for rows.Next() { + var d int64 + if err := rows.Scan(&d); err != nil { + return nil, err + } + deps = append(deps, FlagKey{Id: d, IdExercice: h.IdExercice}) + } + if err := rows.Err(); err != nil { + return nil, err + } + } + + return deps, nil +} + // GetExercice returns the parent Exercice where this hint can be found. func (h EHint) GetExercice() (Exercice, error) { var eid int64 diff --git a/libfic/team.go b/libfic/team.go index 29d2e994..709d6460 100644 --- a/libfic/team.go +++ b/libfic/team.go @@ -155,6 +155,25 @@ func (t Team) CanDownload(f EFile) bool { } } +// CanSeeHint checks if the Team has access to the given hint. +func (t Team) CanSeeHint(h EHint) bool { + if deps, err := h.GetDepends(); err != nil { + log.Printf("Unable to retrieve flag dependencies: %s\n", err) + return false + } else { + res := true + + for _, dep := range deps { + if t.HasPartiallySolved(dep) == nil { + res = false + break + } + } + + return res + } +} + // CanSeeFlag checks if the Team has access to the given flag. func (t Team) CanSeeFlag(k Flag) bool { if deps, err := k.GetDepends(); err != nil { diff --git a/libfic/team_my.go b/libfic/team_my.go index 75b798a0..ad494faf 100644 --- a/libfic/team_my.go +++ b/libfic/team_my.go @@ -168,7 +168,7 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { for _, h := range hints { if t == nil || HintCoefficient < 0 || t.HasHint(h) { exercice.Hints = append(exercice.Hints, myTeamHint{h.Id, h.Title, h.Content, h.File, h.Cost}) - } else { + } else if t.CanSeeHint(h) { exercice.Hints = append(exercice.Hints, myTeamHint{h.Id, h.Title, "", "", h.Cost}) } } From fa33fac0032c08e21b045668de8320d2c913c578 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 31 Oct 2019 15:35:10 +0100 Subject: [PATCH 0086/1637] frontend: add a timestamp file for time checking on backend --- frontend/main.go | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/frontend/main.go b/frontend/main.go index c82798ba..332d5dac 100644 --- a/frontend/main.go +++ b/frontend/main.go @@ -11,6 +11,7 @@ import ( "path" "strings" "syscall" + "time" "srs.epita.fr/fic-server/settings" ) @@ -35,7 +36,7 @@ func main() { log.Println("Creating submission directory...") if _, err := os.Stat(TmpSubmissionDir); os.IsNotExist(err) { if err = os.MkdirAll(TmpSubmissionDir, 0700); err != nil { - log.Fatal("Unable to create submission directory: ", err) + log.Fatal("Unable to create submission directory:", err) } } @@ -95,8 +96,21 @@ func main() { }() log.Println(fmt.Sprintf("Ready, listening on %s", *bind)) - // Wait shutdown signal - <-interrupt + // Wait shutdown signal and touch timestamp + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + +loop: + for { + select { + case <-interrupt: + break loop + case <-ticker.C: + now := time.Now() + os.Chtimes(SubmissionDir, now, now) + } + } + log.Print("The service is shutting down...") srv.Shutdown(context.Background()) From 5dcb13629aa39141b26ff3acfacc0d7d7d09fd5b Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 31 Oct 2019 16:05:58 +0100 Subject: [PATCH 0087/1637] admin: display on interface time synchronization diff --- admin/api/timestamp.go | 26 ++++++++++++++++++++++++++ admin/main.go | 4 ++++ admin/static/js/app.js | 12 ++++++++++++ admin/static/views/home.html | 4 ++++ fickit-backend.yml | 3 ++- 5 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 admin/api/timestamp.go diff --git a/admin/api/timestamp.go b/admin/api/timestamp.go new file mode 100644 index 00000000..9da1f77c --- /dev/null +++ b/admin/api/timestamp.go @@ -0,0 +1,26 @@ +package api + +import ( + "os" + "time" + + "github.com/julienschmidt/httprouter" +) + +var TimestampCheck = "submissions" + +func init() { + router.GET("/api/timestamps.json", apiHandler( + func(httprouter.Params, []byte) (interface{}, error) { + if stat, err := os.Stat(TimestampCheck); err != nil { + return nil, err + } else { + now := time.Now().UTC() + return map[string]interface{}{ + "frontend": stat.ModTime().UTC(), + "backend": now, + "diffFB": now.Sub(stat.ModTime()), + }, nil + } + })) +} diff --git a/admin/main.go b/admin/main.go index 22d9c69f..12e2f1df 100644 --- a/admin/main.go +++ b/admin/main.go @@ -92,6 +92,7 @@ func main() { var bind = flag.String("bind", "127.0.0.1:8081", "Bind port/socket") var dsn = flag.String("dsn", fic.DSNGenerator(), "DSN to connect to the MySQL server") var baseURL = flag.String("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") flag.StringVar(&StaticDir, "static", "./htdocs-admin/", "Directory containing static files") flag.StringVar(&api.TeamsDir, "teams", "./TEAMS", "Base directory where save teams JSON files") @@ -144,6 +145,9 @@ func main() { 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) } diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 1fdb4053..9c97203d 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -129,6 +129,9 @@ angular.module("FICApp") .factory("Version", function($resource) { return $resource("/api/version") }) + .factory("Timestamp", function($resource) { + return $resource("/api/timestamps.json") + }) .factory("Monitor", function($resource) { return $resource("/api/monitor/:machineId", { machineId: '@id' }) }) @@ -365,6 +368,15 @@ angular.module("FICApp") $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("MonitorController", function($scope, Monitor) { $scope.monitor = Monitor.get(); }) diff --git a/admin/static/views/home.html b/admin/static/views/home.html index dfbf4321..c5d49dd7 100644 --- a/admin/static/views/home.html +++ b/admin/static/views/home.html @@ -7,4 +7,8 @@

Version de l'API : {{ v.version }}

+

+ Latence frontend-backend :
+ Dernière synchronisation du frontend : {{ t.frontend | date:"mediumTime" }} +

diff --git a/fickit-backend.yml b/fickit-backend.yml index cf7ac02a..e424d30f 100644 --- a/fickit-backend.yml +++ b/fickit-backend.yml @@ -152,7 +152,7 @@ services: - /var/lib/fic/backups - name: fic-admin image: nemunaire/fic-admin:latest - command: ["/srv/admin", "-bind=:8081", "-baseurl=/admin/", "-localimport=/mnt/fic"] + command: ["/srv/admin", "-bind=:8081", "-baseurl=/admin/", "-localimport=/mnt/fic", "-timestampCheck=/srv/submissions"] env: - MYSQL_HOST=db - FICCA_PASS=jee8AhloAith1aesCeQu5ahgIegaeM4K @@ -164,6 +164,7 @@ services: - /var/lib/fic/pki:/srv/PKI - /var/lib/fic/teams:/srv/TEAMS - /var/lib/fic/settings:/srv/SETTINGS + - /var/lib/fic/submissions:/srv/submissions:ro net: /run/netns/fic-admin pid: new ipc: new From 26eab7ed679a09f240a95509d3a3cb65db0b44a3 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 23 Nov 2019 11:59:13 +0100 Subject: [PATCH 0088/1637] sync: import heading.jpg only in Sync phase --- admin/sync/themes.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/admin/sync/themes.go b/admin/sync/themes.go index d3aa485c..8623eccc 100644 --- a/admin/sync/themes.go +++ b/admin/sync/themes.go @@ -99,17 +99,6 @@ func BuildTheme(i Importer, tdir string) (th *fic.Theme, errs []string) { th.Image = path.Join(tdir, "heading.png") } - if len(th.Image) > 0 { - if _, err := i.importFile(th.Image, - func(filePath string, origin string) (interface{}, error) { - th.Image = strings.TrimPrefix(filePath, fic.FilesDir) - return nil, nil - }); err != nil { - errs = append(errs, fmt.Sprintf("%q: unable to import heading image: %s", tdir, err)) - return nil, errs - } - } - return } @@ -132,6 +121,16 @@ func SyncThemes(i Importer) []string { continue } + if len(btheme.Image) > 0 { + if _, err := i.importFile(btheme.Image, + func(filePath string, origin string) (interface{}, error) { + btheme.Image = strings.TrimPrefix(filePath, fic.FilesDir) + return nil, nil + }); err != nil { + errs = append(errs, fmt.Sprintf("%q: unable to import heading image: %s", tdir, err)) + } + } + var theme fic.Theme if theme, err = fic.GetThemeByPath(btheme.Path); err != nil { if _, err := fic.CreateTheme(*btheme); err != nil { From fbae34ee4fd22561e04c452562761d12ac7c0483 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 23 Nov 2019 12:00:21 +0100 Subject: [PATCH 0089/1637] sync: add error message when missing heading.jpg --- admin/sync/themes.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/admin/sync/themes.go b/admin/sync/themes.go index 8623eccc..f3b9fb7f 100644 --- a/admin/sync/themes.go +++ b/admin/sync/themes.go @@ -97,6 +97,8 @@ func BuildTheme(i Importer, tdir string) (th *fic.Theme, errs []string) { 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 = append(errs, fmt.Sprintf("%q: heading.jpg: No such file", tdir)) } return From 698c2f1a47c5c7b9c5338f84d2f6173d3a6ed126 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 25 Nov 2019 14:19:29 +0100 Subject: [PATCH 0090/1637] libfic: add new functions to retrieve the Id of some contents from Title, Label, ... --- libfic/flag.go | 1 + libfic/flag_key.go | 9 +++++++++ libfic/hint.go | 11 +++++++++++ libfic/mcq.go | 9 +++++++++ 4 files changed, 30 insertions(+) diff --git a/libfic/flag.go b/libfic/flag.go index 28e512b9..777a09a2 100644 --- a/libfic/flag.go +++ b/libfic/flag.go @@ -4,6 +4,7 @@ import () type Flag interface { GetId() int64 + RecoverId() (Flag, error) Create(e Exercice) (Flag, error) Update() (int64, error) Delete() (int64, error) diff --git a/libfic/flag_key.go b/libfic/flag_key.go index 9a29ecaf..b6b4f9c4 100644 --- a/libfic/flag_key.go +++ b/libfic/flag_key.go @@ -127,6 +127,15 @@ func (k FlagKey) GetId() int64 { return k.Id } +// RecoverId returns the Flag identifier as register in DB. +func (k FlagKey) RecoverId() (Flag, error) { + if err := DBQueryRow("SELECT id_flag FROM exercice_flags WHERE type LIKE ? AND id_exercice = ?", k.Label, k.IdExercice).Scan(&k.Id); err != nil { + return FlagKey{}, err + } else { + return k, err + } +} + // AddFlagKey creates and fills a new struct Flag, from a hashed flag, and registers it into the database. func (k FlagKey) Create(e Exercice) (Flag, error) { // Check the regexp compile diff --git a/libfic/hint.go b/libfic/hint.go index a04793be..721c747a 100644 --- a/libfic/hint.go +++ b/libfic/hint.go @@ -47,6 +47,17 @@ func GetHint(id int64) (EHint, error) { return h, nil } +// GetHintByTitle retrieves the hint with the given id. +func (e Exercice) GetHintByTitle(id int64) (EHint, error) { + var h EHint + if err := DBQueryRow("SELECT id_hint, id_exercice, title, content, cost FROM exercice_hints WHERE title = ? AND id_exercice = ?", id, e.Id).Scan(&h.Id, &h.IdExercice, &h.Title, &h.Content, &h.Cost); err != nil { + return h, err + } + treatHintContent(&h) + + return h, nil +} + // GetHints returns a list of hints comming with the challenge. func (e Exercice) GetHints() ([]EHint, error) { if rows, err := DBQuery("SELECT id_hint, title, content, cost FROM exercice_hints WHERE id_exercice = ?", e.Id); err != nil { diff --git a/libfic/mcq.go b/libfic/mcq.go index a589bad7..d25904d9 100644 --- a/libfic/mcq.go +++ b/libfic/mcq.go @@ -102,6 +102,15 @@ func (m MCQ) GetId() int64 { return m.Id } +// RecoverId returns the MCQ identifier as register in DB. +func (m MCQ) RecoverId() (Flag, error) { + if err := DBQueryRow("SELECT id_mcq FROM exercice_mcq WHERE title LIKE ? AND id_exercice = ?", m.Title, m.IdExercice).Scan(&m.Id); err != nil { + return MCQ{}, err + } else { + return m, err + } +} + // Create registers a MCQ into the database and recursively add its entries. func (m MCQ) Create(e Exercice) (Flag, error) { if res, err := DBExec("INSERT INTO exercice_mcq (id_exercice, title) VALUES (?, ?)", e.Id, m.Title); err != nil { From 1db035e0503d68037700cc8c5a4cbdecba4d9978 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 25 Nov 2019 14:27:24 +0100 Subject: [PATCH 0091/1637] dashboard: fix urlbase --- dashboard/static/index.html | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/dashboard/static/index.html b/dashboard/static/index.html index cf6fbcbc..73283d5e 100644 --- a/dashboard/static/index.html +++ b/dashboard/static/index.html @@ -9,10 +9,10 @@ - - - - + + + +
@@ -554,13 +554,13 @@
- - - - - - - - + + + + + + + + From 14f5cf29b76e4620f791d6ee662f04d155939daa Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 25 Nov 2019 14:28:21 +0100 Subject: [PATCH 0092/1637] dashboard: parametrize URL in welcome team --- admin/static/js/app.js | 2 +- admin/static/views/public.html | 8 ++++++++ dashboard/static/index.html | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 9c97203d..2040eb8b 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -704,7 +704,7 @@ angular.module("FICApp") $scope.display.scenes = [ { type: "welcome", - params: { kind: "teams" }, + params: { kind: "teams", url: "https://fic.srs.epita.fr/" }, }, { type: "welcome", diff --git a/admin/static/views/public.html b/admin/static/views/public.html index 0ffdb661..140de800 100644 --- a/admin/static/views/public.html +++ b/admin/static/views/public.html @@ -181,6 +181,14 @@
+ +
+ +
+ +
+
+
From 4a490b1a339b174ac1797d807743e9bb89835709 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 25 Nov 2019 14:49:19 +0100 Subject: [PATCH 0093/1637] admin: PKI validity no more hardcoded --- admin/api/certificate.go | 49 ++++++++++++++++++++++++------------- admin/static/js/app.js | 8 +++++- admin/static/views/pki.html | 13 ++++++++++ 3 files changed, 52 insertions(+), 18 deletions(-) diff --git a/admin/api/certificate.go b/admin/api/certificate.go index 4eeb91ac..b962be6f 100644 --- a/admin/api/certificate.go +++ b/admin/api/certificate.go @@ -2,6 +2,8 @@ package api import ( "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" "encoding/base32" "encoding/json" "errors" @@ -11,9 +13,9 @@ import ( "math/big" "os" "path" - "time" "strconv" "strings" + "time" "srs.epita.fr/fic-server/admin/pki" "srs.epita.fr/fic-server/libfic" @@ -31,8 +33,12 @@ func init() { router.GET("/api/ca/", apiHandler(infoCA)) router.GET("/api/ca.pem", apiHandler(getCAPEM)) router.POST("/api/ca/new", apiHandler( - func(_ httprouter.Params, _ []byte) (interface{}, error) { - return true, pki.GenerateCA(time.Date(2019, 01, 19, 0, 0, 0, 0, time.UTC), time.Date(2019, 01, 23, 23, 59, 59, 0, time.UTC)) + func(_ httprouter.Params, body []byte) (interface{}, error) { + var upki PKISettings + if err := json.Unmarshal(body, &upki); err != nil { + return nil, err + } + return true, pki.GenerateCA(upki.NotBefore, upki.NotAfter) })) router.GET("/api/teams/:tid/certificates", apiHandler(teamHandler( @@ -83,7 +89,7 @@ func init() { } func genHtpasswd() (ret string, err error) { - var teams []fic.Team + var teams []fic.Team teams, err = fic.GetTeams() if err != nil { return @@ -125,24 +131,33 @@ func genHtpasswd() (ret string, err error) { 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(_ httprouter.Params, _ []byte) (interface{}, error) { _, cacert, err := pki.LoadCA() if err != nil { return nil, err } - ret := map[string]interface{}{} - - ret["version"] = cacert.Version - ret["serialnumber"] = cacert.SerialNumber - ret["issuer"] = cacert.Issuer - ret["subject"] = cacert.Subject - ret["notbefore"] = cacert.NotBefore - ret["notafter"] = cacert.NotAfter - ret["signatureAlgorithm"] = cacert.SignatureAlgorithm - ret["publicKeyAlgorithm"] = cacert.PublicKeyAlgorithm - - return ret, nil + return 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, + }, nil } func getCAPEM(_ httprouter.Params, _ []byte) (interface{}, error) { @@ -214,7 +229,7 @@ func generateClientCert(_ httprouter.Params, _ []byte) (interface{}, error) { type CertExported struct { Id string `json:"id"` Creation time.Time `json:"creation"` - IdTeam *uint64 `json:"id_team"` + IdTeam *uint64 `json:"id_team"` Revoked *time.Time `json:"revoked"` } diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 2040eb8b..4e19079e 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -570,6 +570,12 @@ angular.module("FICApp") }) .controller("PKIController", function($scope, $rootScope, Certificate, CACertificate, Team, $location, $http) { + var ts = Date.now() - Date.now() % 86400000; + var d = new Date(ts); + $scope.notBefore = d.toISOString(); + var f = new Date(ts + 3 * 86400000); + $scope.notAfter = f.toISOString(); + $scope.teams = Team.query(); $scope.certificates = Certificate.query(); $scope.certificates.$promise.then(function(certificates) { @@ -612,7 +618,7 @@ angular.module("FICApp") }; $scope.generateCA = function() { - $http.post("/api/ca/new").then(function() { + $http.post("/api/ca/new", {"notbefore": $scope.notBefore, "notafter": $scope.notAfter}).then(function() { $scope.ca = CACertificate.get(); }, function(response) { $rootScope.newBox('danger', 'An error occurs when generating CA:', response.data.errmsg); diff --git a/admin/static/views/pki.html b/admin/static/views/pki.html index 41031b26..3a4dc8be 100644 --- a/admin/static/views/pki.html +++ b/admin/static/views/pki.html @@ -115,6 +115,19 @@
Aucune CA n'a été générée pour le moment. + +
+ +
+ +
+
+
+ +
+ +
+
From d97ecde3fbc2027f9846707676bf644bf5f3ebf9 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 25 Nov 2019 14:57:21 +0100 Subject: [PATCH 0094/1637] sync: return binding between challenge.txt IDs and DB item --- admin/api/exercice.go | 6 ++++-- admin/api/theme.go | 6 ++++-- admin/sync/exercice_hints.go | 6 ++++-- admin/sync/exercice_keys.go | 4 ++-- admin/sync/full.go | 8 ++++++-- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/admin/api/exercice.go b/admin/api/exercice.go index 81d1e63c..a5869d88 100644 --- a/admin/api/exercice.go +++ b/admin/api/exercice.go @@ -65,11 +65,13 @@ func init() { }))) router.POST("/api/sync/exercices/:eid/hints", apiHandler(exerciceHandler( func(exercice fic.Exercice, _ []byte) (interface{}, error) { - return sync.SyncExerciceHints(sync.GlobalImporter, exercice), nil + _, errs := sync.SyncExerciceHints(sync.GlobalImporter, exercice) + return errs, nil }))) router.POST("/api/sync/exercices/:eid/flags", apiHandler(exerciceHandler( func(exercice fic.Exercice, _ []byte) (interface{}, error) { - return sync.SyncExerciceFlags(sync.GlobalImporter, exercice), nil + _, errs := sync.SyncExerciceFlags(sync.GlobalImporter, exercice) + return errs, nil }))) router.POST("/api/sync/exercices/:eid/fixurlid", apiHandler(exerciceHandler( diff --git a/admin/api/theme.go b/admin/api/theme.go index dc4083ca..15c9fba7 100644 --- a/admin/api/theme.go +++ b/admin/api/theme.go @@ -70,11 +70,13 @@ func init() { }))) router.POST("/api/sync/themes/:thid/exercices/:eid/hints", apiHandler(exerciceHandler( func(exercice fic.Exercice, _ []byte) (interface{}, error) { - return sync.SyncExerciceHints(sync.GlobalImporter, exercice), nil + _, errs := sync.SyncExerciceHints(sync.GlobalImporter, exercice) + return errs, nil }))) router.POST("/api/sync/themes/:thid/exercices/:eid/keys", apiHandler(exerciceHandler( func(exercice fic.Exercice, _ []byte) (interface{}, error) { - return sync.SyncExerciceFlags(sync.GlobalImporter, exercice), nil + _, errs := sync.SyncExerciceFlags(sync.GlobalImporter, exercice) + return errs, nil }))) router.POST("/api/sync/themes/:thid/fixurlid", apiHandler(themeHandler( diff --git a/admin/sync/exercice_hints.go b/admin/sync/exercice_hints.go index ac1042ee..994adbcc 100644 --- a/admin/sync/exercice_hints.go +++ b/admin/sync/exercice_hints.go @@ -92,7 +92,7 @@ func CheckExerciceHints(i Importer, exercice fic.Exercice) ([]fic.EHint, []strin } // SyncExerciceHints reads the content of hints/ directories and import it as EHint for the given challenge. -func SyncExerciceHints(i Importer, exercice fic.Exercice) (errs []string) { +func SyncExerciceHints(i Importer, exercice fic.Exercice) (hintsBindings map[int]fic.EHint, errs []string) { if _, err := exercice.WipeHints(); err != nil { errs = append(errs, err.Error()) } else { @@ -101,8 +101,10 @@ func SyncExerciceHints(i Importer, exercice fic.Exercice) (errs []string) { for n, hint := range hints { // Import hint - if _, err := exercice.AddHint(hint.Title, hint.Content, hint.Cost); err != nil { + if h, err := exercice.AddHint(hint.Title, hint.Content, hint.Cost); err != nil { errs = append(errs, fmt.Sprintf("%q: hint #%d %s: %s", path.Base(exercice.Path), n+1, hint.Title, err)) + } else { + hintsBindings[n+1] = h } } } diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index 348976b2..7b568674 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -347,7 +347,7 @@ func CheckExerciceFlags(i Importer, exercice fic.Exercice, files []fic.EFile) (r } // SyncExerciceFlags imports all kind of flags for the given challenge. -func SyncExerciceFlags(i Importer, exercice fic.Exercice) (errs []string) { +func SyncExerciceFlags(i Importer, exercice fic.Exercice) (kmap map[int64]fic.Flag, errs []string) { if _, err := exercice.WipeFlags(); err != nil { errs = append(errs, err.Error()) } else if _, err := exercice.WipeMCQs(); err != nil { @@ -356,7 +356,7 @@ func SyncExerciceFlags(i Importer, exercice fic.Exercice) (errs []string) { flags, flagids, berrs := buildExerciceFlags(i, exercice) errs = append(errs, berrs...) - kmap := map[int64]fic.Flag{} + kmap = map[int64]fic.Flag{} // Import flags for _, flagid := range flagids { diff --git a/admin/sync/full.go b/admin/sync/full.go index c92ec6b3..a835bded 100644 --- a/admin/sync/full.go +++ b/admin/sync/full.go @@ -45,10 +45,14 @@ func SyncDeep(i Importer) (errs map[string][]string) { for eid, exercice := range exercices { DeepSyncProgress = 3 + uint8(tid) * themeStep + uint8(eid) * exerciceStep errs[theme.Name] = append(errs[theme.Name], SyncExerciceFiles(i, exercice)...) + DeepSyncProgress += exerciceStep / 3 - errs[theme.Name] = append(errs[theme.Name], SyncExerciceFlags(i, exercice)...) + _, ferrs := SyncExerciceFlags(i, exercice) + errs[theme.Name] = append(errs[theme.Name], ferrs...) + DeepSyncProgress += exerciceStep / 3 - errs[theme.Name] = append(errs[theme.Name], SyncExerciceHints(i, exercice)...) + _, herrs := SyncExerciceHints(i, exercice) + errs[theme.Name] = append(errs[theme.Name], herrs...) } } } From 9693940d8c4073322698dc840e8b58774f7c8db4 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 25 Nov 2019 15:08:50 +0100 Subject: [PATCH 0095/1637] sync: add logs on stderr when doing deepsync --- admin/sync/full.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/admin/sync/full.go b/admin/sync/full.go index a835bded..acb7df62 100644 --- a/admin/sync/full.go +++ b/admin/sync/full.go @@ -24,12 +24,18 @@ var DeepSyncProgress uint8 // SyncDeep performs a recursive synchronisation: from themes to challenge items. func SyncDeep(i Importer) (errs map[string][]string) { oneDeepSync.Lock() - defer oneDeepSync.Unlock() + defer func(){ + oneDeepSync.Unlock() + if DeepSyncProgress != 255 { + log.Printf("Full synchronization terminated at step %d/255", DeepSyncProgress) + } + }() DeepSyncProgress = 1 errs = map[string][]string{} - errs["_date"] = []string{fmt.Sprintf("%v", time.Now())} + startTime := time.Now() + errs["_date"] = []string{fmt.Sprintf("%v", startTime)} errs["_themes"] = SyncThemes(i) if themes, err := fic.GetThemes(); err == nil { @@ -43,6 +49,8 @@ func SyncDeep(i Importer) (errs map[string][]string) { if exercices, err := theme.GetExercices(); err == nil { var exerciceStep uint8 = themeStep / uint8(len(exercices)) for eid, exercice := range exercices { + log.Printf("Full 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[theme.Name] = append(errs[theme.Name], SyncExerciceFiles(i, exercice)...) @@ -82,5 +90,6 @@ func SyncDeep(i Importer) (errs map[string][]string) { } DeepSyncProgress = 255 + log.Println("Full synchronization done in", time.Since(startTime)) return } From 6ad11e49d58ae91707464a607324f40046641e72 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 25 Nov 2019 16:16:27 +0100 Subject: [PATCH 0096/1637] backend: add an identifier to each treated file --- backend/choices.go | 27 ++++++++++++++++++--------- backend/hint.go | 31 ++++++++++++++++++++----------- backend/main.go | 6 +++--- backend/registration.go | 38 +++++++++++++++++++++++--------------- backend/rename.go | 19 ++++++++++++++----- 5 files changed, 78 insertions(+), 43 deletions(-) diff --git a/backend/choices.go b/backend/choices.go index eec60ac6..c2dc52aa 100644 --- a/backend/choices.go +++ b/backend/choices.go @@ -1,9 +1,12 @@ package main import ( + "encoding/base64" + "encoding/binary" "encoding/json" "io/ioutil" "log" + "math/rand" "os" "srs.epita.fr/fic-server/libfic" @@ -14,29 +17,35 @@ type wantChoices struct { } 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 if cnt_raw, err := ioutil.ReadFile(pathname); err != nil { - log.Println("[ERR]", err) + log.Printf("%s [ERR] %s\n", id, err) } else if err = json.Unmarshal(cnt_raw, &ask); err != nil { - log.Println("[ERR]", err) + log.Printf("%s [ERR] %s\n", id, err) } else if ask.FlagId == 0 { - log.Println("[WRN] Invalid content in wantChoices file: ", pathname) + log.Printf("%s [WRN] Invalid content in wantChoices file: %s\n", id, pathname) os.Remove(pathname) } else if flag, err := fic.GetFlagKey(ask.FlagId); err != nil { - log.Println("[ERR]", err) + log.Printf("%s [ERR] %s\n", id, err) } else if !team.CanSeeFlag(flag) { - log.Println("[!!!] The team asks to display choices whereas it doesn't have access to the flag") + log.Printf("%s [!!!] The team asks to display choices whereas it doesn't have access to the flag\n", id) } else if exercice, err := flag.GetExercice(); err != nil { - log.Println("[ERR] Unable to retrieve the flag's underlying exercice:", err) + log.Printf("%s [ERR] Unable to retrieve the flag's underlying exercice: %s\n", id, err) } else if !team.HasAccess(exercice) { - log.Println("[!!!] The team asks to display choices whereas it doesn't have access to the exercice") + log.Printf("%s [!!!] The team asks to display choices whereas it doesn't have access to the exercice\n", id) } else if err = team.DisplayChoices(flag); err != nil { - log.Println("[ERR]", err) + log.Printf("%s [ERR] %s\n", id, err) } else { genTeamQueue <- &team if err = os.Remove(pathname); err != nil { - log.Println("[ERR]", err) + log.Printf("%s [ERR] %s\n", id, err) } } } diff --git a/backend/hint.go b/backend/hint.go index f991d4be..a73c40e7 100644 --- a/backend/hint.go +++ b/backend/hint.go @@ -1,10 +1,13 @@ package main import ( + "encoding/base64" + "encoding/binary" "encoding/json" "fmt" "io/ioutil" "log" + "math/rand" "os" "srs.epita.fr/fic-server/libfic" @@ -15,37 +18,43 @@ type askOpenHint struct { } 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 if cnt_raw, err := ioutil.ReadFile(pathname); err != nil { - log.Println("[ERR]", err) + log.Printf("%s [ERR] %s\n", id, err) } else if err = json.Unmarshal(cnt_raw, &ask); err != nil { - log.Println("[ERR]", err) + log.Printf("%s [ERR] %s\n", id, err) } else if ask.HintId == 0 { - log.Println("[WRN] Invalid content in hint file: ", pathname) + log.Printf("%s [WRN] Invalid content in hint file: %s\n", id, pathname) os.Remove(pathname) } else if hint, err := fic.GetHint(ask.HintId); err != nil { - log.Println("[ERR] Unable to retrieve the given hint:", err) + log.Printf("%s [ERR] Unable to retrieve the given hint: %s\n", id, err) } else if exercice, err := hint.GetExercice(); err != nil { - log.Println("[ERR] Unable to retrieve the hint's underlying exercice:", err) + log.Printf("%s [ERR] Unable to retrieve the hint's underlying exercice: %s\n", id, err) } else if !team.HasAccess(exercice) { - log.Println("[!!!] The team asks to open an hint whereas it doesn't have access to the exercice") + log.Printf("%s [!!!] The team asks to open an hint whereas it doesn't have access to the exercice\n", id) } else if err = team.OpenHint(hint); err != nil { - log.Println("[ERR] Unable to open hint", err) + log.Printf("%s [ERR] Unable to open hint: %s\n", id, err) } else { // Write event if lvl, err := exercice.GetLevel(); err != nil { - log.Println("[WRN]", err) + log.Printf("%s [WRN] %s\n", id, err) } else if theme, err := fic.GetTheme(exercice.IdTheme); err != nil { - log.Println("[WRN]", err) + 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 !", team.Name, lvl, theme.Name), "info"); err != nil { - log.Println("[WRN] Unable to create event:", err) + log.Printf("%s [WRN] Unable to create event: %s\n", id, err) } genTeamQueue <- &team appendGenQueue(genStruct{Type: GenEvents}) if err = os.Remove(pathname); err != nil { - log.Println("[ERR]", err) + log.Printf("%s [ERR] %s\n", id, err) } } } diff --git a/backend/main.go b/backend/main.go index 5f44b47d..0058b83e 100644 --- a/backend/main.go +++ b/backend/main.go @@ -169,17 +169,17 @@ func treat(raw_path string) { if teamid, err = strconv.ParseInt(spath[1], 10, 64); err != nil { if lnk, err := os.Readlink(path.Join(TeamsDir, spath[1])); err != nil { - log.Println("[ERR]", err) + 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.Println("[ERR]", err) + 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.Println("[ERR]", err) + log.Printf("[ERR] Unable to retrieve team %d: %s\n", teamid, err) return } diff --git a/backend/registration.go b/backend/registration.go index 3e6f27d4..6975414b 100644 --- a/backend/registration.go +++ b/backend/registration.go @@ -1,6 +1,8 @@ package main import ( + "encoding/base64" + "encoding/binary" "encoding/json" "fmt" "io/ioutil" @@ -23,7 +25,7 @@ type uTeamRegistration struct { Members []fic.Member } -func registrationProcess(team fic.Team, members []fic.Member, team_id string) { +func registrationProcess(id string, team fic.Team, members []fic.Member, team_id string) { for _, m := range members { // Force Id to 0, as it shouldn't have been defined yet m.Id = 0 @@ -36,10 +38,10 @@ func registrationProcess(team fic.Team, members []fic.Member, team_id string) { // Create team directories into TEAMS if err := os.MkdirAll(path.Join(TeamsDir, teamDirPath), 0777); err != nil { - log.Println("[ERR]", err) + log.Println(id, "[ERR]", err) } if err := os.Symlink(teamDirPath, path.Join(TeamsDir, team_id)); err != nil { - log.Println("[ERR]", err) + log.Println(id, "[ERR]", err) } genTeamQueue <- &team @@ -47,39 +49,45 @@ func registrationProcess(team fic.Team, members []fic.Member, team_id string) { } 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.Println("[ERR] Registration received, whereas disabled. Skipped.") + log.Printf("%s [ERR] Registration received, whereas disabled. Skipped.\n", id) } else if cnt_raw, err := ioutil.ReadFile(pathname); err != nil { - log.Println("[ERR]", err) + log.Printf("%s [ERR] %s\n", id, err) } else if err := json.Unmarshal(cnt_raw, &nTeam); err != nil { - log.Println("[ERR]", err) + log.Printf("%s [ERR] %s\n", id, err) } else if nTeam.JTeam > 0 { if !canJoinTeam { - log.Println("[ERR] Join team received, whereas disabled. Skipped.") + log.Printf("%s [ERR] Join team received, whereas disabled. Skipped.\n", id) } else if len(nTeam.Members) != 1 { - log.Printf("[ERR] Join team received, with incorrect member length: %d. Skipped.\n", len(nTeam.Members)) + 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("[ERR] Unable to join registered team %s: %s\n", nTeam.JTeam, err) + log.Printf("%s [ERR] Unable to join registered team %d: %s\n", id, nTeam.JTeam, err) } else { - registrationProcess(team, nTeam.Members, team_id) + registrationProcess(id, team, nTeam.Members, team_id) if err := os.Remove(pathname); err != nil { - log.Println("[WRN]", err) + log.Printf("%s [WRN] %s\n", id, err) } } } else if validTeamName(nTeam.TeamName) { if team, err := fic.CreateTeam(nTeam.TeamName, uint32(rand.Int31n(16581376))); err != nil { - log.Printf("[ERR] Unable to register new team %s: %s\n", nTeam.TeamName, err) + log.Printf("%s [ERR] Unable to register new team %s: %s\n", id, nTeam.TeamName, err) } else { - registrationProcess(team, nTeam.Members, team_id) + registrationProcess(id, team, nTeam.Members, team_id) if err := os.Remove(pathname); err != nil { - log.Println("[WRN]", err) + 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 !", team.Name), "info"); err != nil { - log.Println("[WRN] Unable to create event:", err) + log.Printf("%s [WRN] Unable to create event: %s\n", id, err) } appendGenQueue(genStruct{Type: GenEvents}) diff --git a/backend/rename.go b/backend/rename.go index ed294dad..a5f93ccc 100644 --- a/backend/rename.go +++ b/backend/rename.go @@ -1,10 +1,13 @@ package main import ( + "encoding/base64" + "encoding/binary" "encoding/json" "fmt" "io/ioutil" "log" + "math/rand" "os" "regexp" @@ -17,24 +20,30 @@ func validTeamName(name string) bool { } 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.Println("[ERR]", err) + log.Printf("%s [ERR] %s\n", id, err) } else if err := json.Unmarshal(cnt_raw, &keys); err != nil { - log.Println("[ERR]", err) + log.Printf("%s [ERR] %s\n", id, err) } else if validTeamName(keys["newName"]) { team.Name = keys["newName"] if _, err := team.Update(); err != nil { - log.Println("[WRN] Unable to change team name:", err) + log.Printf("%s [WRN] Unable to change team name: %s\n", id, err) } genTeamQueue <- &team if _, err := fic.NewEvent(fmt.Sprintf("Souhaitons bonne chance à l'équipe %s qui vient de nous rejoindre !", team.Name), "info"); err != nil { - log.Println("[WRN] Unable to create event:", err) + log.Printf("%s [WRN] Unable to create event: %s\n", id, err) } appendGenQueue(genStruct{Type: GenEvents}) if err := os.Remove(pathname); err != nil { - log.Println("[ERR]", err) + log.Printf("%s [ERR] %s\n", id, err) } } } From f3a34c00db4c5c0c0130931bbbaf8d8a2300d697 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 25 Nov 2019 16:18:59 +0100 Subject: [PATCH 0097/1637] sync: implement hint dependency on flags --- admin/api/exercice.go | 5 +- admin/api/theme.go | 5 +- admin/sync/exercice_hints.go | 43 +++++-- admin/sync/exercice_keys.go | 236 ++++++++++++++++++++--------------- admin/sync/full.go | 4 +- 5 files changed, 176 insertions(+), 117 deletions(-) diff --git a/admin/api/exercice.go b/admin/api/exercice.go index a5869d88..a1a24b03 100644 --- a/admin/api/exercice.go +++ b/admin/api/exercice.go @@ -65,13 +65,14 @@ func init() { }))) router.POST("/api/sync/exercices/:eid/hints", apiHandler(exerciceHandler( func(exercice fic.Exercice, _ []byte) (interface{}, error) { - _, errs := sync.SyncExerciceHints(sync.GlobalImporter, exercice) + _, errs := sync.SyncExerciceHints(sync.GlobalImporter, exercice, sync.ExerciceFlagsMap(sync.GlobalImporter, exercice)) return errs, nil }))) router.POST("/api/sync/exercices/:eid/flags", apiHandler(exerciceHandler( func(exercice fic.Exercice, _ []byte) (interface{}, error) { _, errs := sync.SyncExerciceFlags(sync.GlobalImporter, exercice) - return errs, nil + _, herrs := sync.SyncExerciceHints(sync.GlobalImporter, exercice, sync.ExerciceFlagsMap(sync.GlobalImporter, exercice)) + return append(errs, herrs...), nil }))) router.POST("/api/sync/exercices/:eid/fixurlid", apiHandler(exerciceHandler( diff --git a/admin/api/theme.go b/admin/api/theme.go index 15c9fba7..3caf2e98 100644 --- a/admin/api/theme.go +++ b/admin/api/theme.go @@ -70,13 +70,14 @@ func init() { }))) router.POST("/api/sync/themes/:thid/exercices/:eid/hints", apiHandler(exerciceHandler( func(exercice fic.Exercice, _ []byte) (interface{}, error) { - _, errs := sync.SyncExerciceHints(sync.GlobalImporter, exercice) + _, errs := sync.SyncExerciceHints(sync.GlobalImporter, exercice, sync.ExerciceFlagsMap(sync.GlobalImporter, exercice)) return errs, nil }))) router.POST("/api/sync/themes/:thid/exercices/:eid/keys", apiHandler(exerciceHandler( func(exercice fic.Exercice, _ []byte) (interface{}, error) { _, errs := sync.SyncExerciceFlags(sync.GlobalImporter, exercice) - return errs, nil + _, herrs := sync.SyncExerciceHints(sync.GlobalImporter, exercice, sync.ExerciceFlagsMap(sync.GlobalImporter, exercice)) + return append(errs, herrs...), nil }))) router.POST("/api/sync/themes/:thid/fixurlid", apiHandler(themeHandler( diff --git a/admin/sync/exercice_hints.go b/admin/sync/exercice_hints.go index 994adbcc..9eb8c871 100644 --- a/admin/sync/exercice_hints.go +++ b/admin/sync/exercice_hints.go @@ -17,7 +17,13 @@ import ( "srs.epita.fr/fic-server/libfic" ) -func buildExerciceHints(i Importer, exercice fic.Exercice) (hints []fic.EHint, errs []string) { +type importHint struct { + Line int + Hint fic.EHint + FlagsDeps []int64 +} + +func buildExerciceHints(i Importer, exercice fic.Exercice) (hints []importHint, errs []string) { params, _, err := parseExerciceParams(i, exercice.Path) if err != nil { errs = append(errs, fmt.Sprintf("%q: challenge.txt: %s", path.Base(exercice.Path), err)) @@ -80,31 +86,52 @@ func buildExerciceHints(i Importer, exercice fic.Exercice) (hints []fic.EHint, e errs = append(errs, fmt.Sprintf("%q: challenge.txt: hint %s (%d): error during markdown formating: %s", path.Base(exercice.Path), hint.Title, n+1, err)) } - hints = append(hints, h) + 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) ([]fic.EHint, []string) { +func CheckExerciceHints(i Importer, exercice fic.Exercice) ([]importHint, []string) { return buildExerciceHints(i, exercice) } // SyncExerciceHints reads the content of hints/ directories and import it as EHint for the given challenge. -func SyncExerciceHints(i Importer, exercice fic.Exercice) (hintsBindings map[int]fic.EHint, errs []string) { +func SyncExerciceHints(i Importer, exercice fic.Exercice, flagsBindings map[int64]fic.Flag) (hintsBindings map[int]fic.EHint, errs []string) { if _, err := exercice.WipeHints(); err != nil { errs = append(errs, err.Error()) } else { hints, berrs := buildExerciceHints(i, exercice) errs = append(errs, berrs...) - for n, hint := range hints { + hintsBindings = map[int]fic.EHint{} + + for _, hint := range hints { // Import hint - if h, err := exercice.AddHint(hint.Title, hint.Content, hint.Cost); err != nil { - errs = append(errs, fmt.Sprintf("%q: hint #%d %s: %s", path.Base(exercice.Path), n+1, hint.Title, err)) + if h, err := exercice.AddHint(hint.Hint.Title, hint.Hint.Content, hint.Hint.Cost); err != nil { + errs = append(errs, fmt.Sprintf("%q: hint #%d %s: %s", path.Base(exercice.Path), hint.Line, hint.Hint.Title, err)) } else { - hintsBindings[n+1] = h + hintsBindings[hint.Line] = h + + // Handle hints dependencies on flags + for _, nf := range hint.FlagsDeps { + if f, ok := flagsBindings[nf]; ok { + h.AddDepend(f) + } else { + errs = append(errs, fmt.Sprintf("%q: error hint #%d dependency to flag #%d: Unexistant flag", path.Base(exercice.Path), hint.Line, nf)) + } + } } } } diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index 7b568674..5f7f98e0 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -174,6 +174,104 @@ type importFlag struct { FlagsDeps []int64 } +// buildExerciceFlags read challenge.txt and extract all flags. +func buildExerciceFlag(i Importer, exercice fic.Exercice, flag ExerciceFlag, nline int) (ret []importFlag, errs []string) { + switch strings.ToLower(flag.Type) { + case "": + flag.Type = "key" + case "key": + flag.Type = "key" + case "vector": + flag.Type = "vector" + case "ucq": + flag.Type = "ucq" + case "mcq": + flag.Type = "mcq" + default: + errs = append(errs, fmt.Sprintf("%q: flag #%d: invalid type of flag: should be 'key', 'mcq', 'ucq' or 'vector'.", path.Base(exercice.Path), nline+1)) + return + } + + if flag.Type == "key" || flag.Type == "ucq" || flag.Type == "vector" { + addedFlag, choices, berrs := buildKeyFlag(exercice, flag, nline+1, "Flag") + if len(berrs) > 0 { + errs = append(errs, berrs...) + } + if addedFlag != nil { + ret = append(ret, importFlag{ + Line: nline + 1, + Flag: *addedFlag, + Choices: choices, + }) + } + } else if flag.Type == "mcq" { + addedFlag := fic.MCQ{ + IdExercice: exercice.Id, + Title: flag.Label, + Entries: []fic.MCQ_entry{}, + } + + hasOne := false + isJustified := 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 cid, choice := range flag.Choice { + var val bool + + if choice.Raw != nil { + if hasOne && !isJustified { + errs = append(errs, fmt.Sprintf("%q: error MCQ #%d: all true items has to be justified in this MCQ.", path.Base(exercice.Path), nline+1)) + continue + } + + val = true + isJustified = true + } else if p, ok := choice.Value.(bool); ok { + val = p + if isJustified { + errs = append(errs, fmt.Sprintf("%q: error MCQ #%d: all true items has to be justified in this MCQ.", path.Base(exercice.Path), nline+1)) + continue + } + } else if choice.Value == nil { + val = false + } else { + errs = append(errs, fmt.Sprintf("%q: error in MCQ %d choice %d: incorrect type for value: %T is not boolean.", path.Base(exercice.Path), nline+1, cid, choice.Value)) + continue + } + + addedFlag.Entries = append(addedFlag.Entries, fic.MCQ_entry{ + Label: choice.Label, + Response: val, + }) + + if isJustified && choice.Raw != nil { + addedFlag, choices, berrs := buildKeyFlag(exercice, choice.ExerciceFlag, nline+1, "Flag correspondant") + if len(berrs) > 0 { + errs = append(errs, berrs...) + } + if addedFlag != nil { + ret = append(ret, importFlag{ + Line: nline + 1, + Flag: *addedFlag, + Choices: choices, + }) + } + } + } + + ret = append(ret, importFlag{ + Line: nline + 1, + Flag: addedFlag, + }) + } + return +} + // buildExerciceFlags read challenge.txt and extract all flags. func buildExerciceFlags(i Importer, exercice fic.Exercice) (flags map[int64]importFlag, flagids []int64, errs []string) { params, gerrs := getExerciceParams(i, exercice) @@ -195,115 +293,29 @@ func buildExerciceFlags(i Importer, exercice fic.Exercice) (flags map[int64]impo flag.Id = rand.Int63() } - switch strings.ToLower(flag.Type) { - case "": - flag.Type = "key" - case "key": - flag.Type = "key" - case "vector": - flag.Type = "vector" - case "ucq": - flag.Type = "ucq" - case "mcq": - flag.Type = "mcq" - default: - errs = append(errs, fmt.Sprintf("%q: flag #%d: invalid type of flag: should be 'key', 'mcq', 'ucq' or 'vector'.", path.Base(exercice.Path), nline+1)) - continue + newFlags, ferrs := buildExerciceFlag(i, exercice, flag, nline) + if len(ferrs) > 0 { + errs = append(errs, ferrs...) } - - if flag.Type == "key" || flag.Type == "ucq" || flag.Type == "vector" { - addedFlag, choices, berrs := buildKeyFlag(exercice, flag, nline+1, "Flag") - if len(berrs) > 0 { - errs = append(errs, berrs...) - } - if addedFlag != nil { - flags[flag.Id] = importFlag{ - Line: nline + 1, - Flag: *addedFlag, - Choices: choices, - } - } - } else if flag.Type == "mcq" { - addedFlag := fic.MCQ{ - IdExercice: exercice.Id, - Title: flag.Label, - Entries: []fic.MCQ_entry{}, - } - - hasOne := false - isJustified := 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 cid, choice := range flag.Choice { - var val bool - - if choice.Raw != nil { - if hasOne && !isJustified { - errs = append(errs, fmt.Sprintf("%q: error MCQ #%d: all true items has to be justified in this MCQ.", path.Base(exercice.Path), nline+1)) - continue - } - - val = true - isJustified = true - } else if p, ok := choice.Value.(bool); ok { - val = p - if isJustified { - errs = append(errs, fmt.Sprintf("%q: error MCQ #%d: all true items has to be justified in this MCQ.", path.Base(exercice.Path), nline+1)) - continue - } - } else if choice.Value == nil { - val = false - } else { - errs = append(errs, fmt.Sprintf("%q: error in MCQ %d choice %d: incorrect type for value: %T is not boolean.", path.Base(exercice.Path), nline+1, cid, choice.Value)) - continue + if len(newFlags) > 0 { + for _, newFlag := range newFlags { + fId := flag.Id + for _, ok := flags[fId]; ok; _, ok = flags[fId] { + fId = rand.Int63() } - addedFlag.Entries = append(addedFlag.Entries, fic.MCQ_entry{ - Label: choice.Label, - Response: val, - }) - - if isJustified && choice.Raw != nil { - addedFlag, choices, berrs := buildKeyFlag(exercice, choice.ExerciceFlag, nline+1, "Flag correspondant") - if len(berrs) > 0 { - errs = append(errs, berrs...) - } - if addedFlag != nil { - flags[flag.Id] = importFlag{ - Line: nline, - Flag: *addedFlag, - Choices: choices, - } - } + // Read dependency to flag + for _, nf := range flag.NeedFlag { + newFlag.FlagsDeps = append(newFlag.FlagsDeps, nf.Id) } - } - flags[flag.Id] = importFlag{ - Line: nline + 1, - Flag: addedFlag, - } - } + // Read dependency to file + for _, lf := range flag.LockedFile { + newFlag.FilesDeps = append(newFlag.FilesDeps, lf.Filename) + } - flagids = append(flagids, flag.Id) - - // Read dependency to flag - for _, nf := range flag.NeedFlag { - if v, ok := flags[flag.Id]; ok { - v.FlagsDeps = append(v.FlagsDeps, nf.Id) - flags[flag.Id] = v - } - } - - // Read dependency to file - for _, lf := range flag.LockedFile { - if v, ok := flags[flag.Id]; ok { - v.FilesDeps = append(v.FilesDeps, lf.Filename) - flags[flag.Id] = v + flags[fId] = newFlag + flagids = append(flagids, fId) } } } @@ -346,6 +358,24 @@ func CheckExerciceFlags(i Importer, exercice fic.Exercice, files []fic.EFile) (r 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) + + 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) (kmap map[int64]fic.Flag, errs []string) { if _, err := exercice.WipeFlags(); err != nil { diff --git a/admin/sync/full.go b/admin/sync/full.go index acb7df62..a361abdd 100644 --- a/admin/sync/full.go +++ b/admin/sync/full.go @@ -55,11 +55,11 @@ func SyncDeep(i Importer) (errs map[string][]string) { errs[theme.Name] = append(errs[theme.Name], SyncExerciceFiles(i, exercice)...) DeepSyncProgress += exerciceStep / 3 - _, ferrs := SyncExerciceFlags(i, exercice) + flagsBindings, ferrs := SyncExerciceFlags(i, exercice) errs[theme.Name] = append(errs[theme.Name], ferrs...) DeepSyncProgress += exerciceStep / 3 - _, herrs := SyncExerciceHints(i, exercice) + _, herrs := SyncExerciceHints(i, exercice, flagsBindings) errs[theme.Name] = append(errs[theme.Name], herrs...) } } From 56053f3350e71e8a670f0b7c6e0b3c98990900c7 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 25 Nov 2019 16:21:58 +0100 Subject: [PATCH 0098/1637] backend: implement hint dependencies --- backend/hint.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/hint.go b/backend/hint.go index a73c40e7..7fb5a729 100644 --- a/backend/hint.go +++ b/backend/hint.go @@ -39,6 +39,8 @@ func treatOpeningHint(pathname string, team fic.Team) { log.Printf("%s [ERR] Unable to retrieve the hint's underlying exercice: %s\n", id, err) } else 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) + } else 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) } else if err = team.OpenHint(hint); err != nil { log.Printf("%s [ERR] Unable to open hint: %s\n", id, err) } else { From 11a66346e76eb086d94ee472e7e357aa38077859 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 25 Nov 2019 16:22:40 +0100 Subject: [PATCH 0099/1637] repochecker: use a temporary directory to import files --- repochecker/main.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/repochecker/main.go b/repochecker/main.go index 32eecd8a..283cc75a 100644 --- a/repochecker/main.go +++ b/repochecker/main.go @@ -3,6 +3,7 @@ package main import ( "flag" "fmt" + "io/ioutil" "log" "os" "path" @@ -80,6 +81,16 @@ func main() { regenImporter = true } + var err error + + // Create temporary directory for storing FILES/ content + fic.FilesDir, err = ioutil.TempDir("", "fic-repochecker.") + if err != nil { + + } + defer os.RemoveAll(fic.FilesDir) + + if sync.GlobalImporter != nil { log.Println("Using", sync.GlobalImporter.Kind()) From a545112cb2cfb29a252766362f5a6fbe5809709a Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 25 Nov 2019 16:49:37 +0100 Subject: [PATCH 0100/1637] frontend: highlight current questions --- frontend/static/js/challenge.js | 2 +- frontend/static/views/defi.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/static/js/challenge.js b/frontend/static/js/challenge.js index 44a179b3..809a985b 100644 --- a/frontend/static/js/challenge.js +++ b/frontend/static/js/challenge.js @@ -112,7 +112,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) }, template: `
- +
diff --git a/frontend/static/views/defi.html b/frontend/static/views/defi.html index 6afb0a9a..753d8676 100644 --- a/frontend/static/views/defi.html +++ b/frontend/static/views/defi.html @@ -95,7 +95,7 @@
-

{{ mcq.title }}

+

{{ mcq.title }}

From b4fa57f9c998cf2bbecbac8950aba33b94ce87b9 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 25 Nov 2019 17:45:41 +0100 Subject: [PATCH 0101/1637] sync: introducing showlines property for vectors It allows players to know in advance how many items the vector is composed. --- admin/sync/exercice_defines.go | 1 + admin/sync/exercice_keys.go | 18 ++++++++++++++---- frontend/static/js/challenge.js | 18 +++++++++++++++--- libfic/team_my.go | 7 +++++-- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/admin/sync/exercice_defines.go b/admin/sync/exercice_defines.go index b8f46d0b..fab93625 100644 --- a/admin/sync/exercice_defines.go +++ b/admin/sync/exercice_defines.go @@ -35,6 +35,7 @@ type ExerciceFlag struct { Type string `toml:",omitempty"` Raw interface{} Separator string `toml:",omitempty"` + ShowLines bool `toml:",omitempty"` Ordered bool `toml:",omitempty"` CaseSensitive bool `toml:",omitempty"` ValidatorRe string `toml:"validator_regexp,omitempty"` diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index 5f7f98e0..c2f2c4b3 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -34,7 +34,7 @@ func validatorRegexp(vre string) (validator_regexp *string) { return } -func getRawKey(input interface{}, validatorRe string, ordered bool) (raw string, prep string, errs []string) { +func getRawKey(input interface{}, validatorRe string, ordered bool, showLines bool) (raw string, prep string, errs []string) { separator := "," // Concatenate array @@ -71,9 +71,19 @@ func getRawKey(input interface{}, validatorRe string, ordered bool) (raw string, sort.Strings(fitems) ignord = "t" } + + nbLines := 0 + if showLines { + if len(fitems) > 9 { + errs = append(errs, "too much items in vector to use ShowLines features, max 9.") + } else { + nbLines = len(fitems) + } + } + raw = strings.Join(fitems, separator) + separator - prep = "`" + separator + ignord + 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.(string); !ok { @@ -95,7 +105,7 @@ func buildKeyFlag(exercice fic.Exercice, flag ExerciceFlag, flagline int, defaul flag.Label = flag.Label[1:] } - raw, prep, terrs := getRawKey(flag.Raw, flag.ValidatorRe, flag.Ordered) + raw, prep, terrs := getRawKey(flag.Raw, flag.ValidatorRe, flag.Ordered, flag.ShowLines) if len(terrs) > 0 { for _, err := range terrs { @@ -137,7 +147,7 @@ func buildKeyFlag(exercice fic.Exercice, flag ExerciceFlag, flagline int, defaul } for _, choice := range flag.Choice { - val, prep, terrs := getRawKey(choice.Value, "", false) + val, prep, terrs := getRawKey(choice.Value, "", false, false) if len(terrs) > 0 { for _, err := range terrs { errs = append(errs, fmt.Sprintf("%q: flag #%d: %s", path.Base(exercice.Path), flagline, err)) diff --git a/frontend/static/js/challenge.js b/frontend/static/js/challenge.js index 809a985b..b0ccbed5 100644 --- a/frontend/static/js/challenge.js +++ b/frontend/static/js/challenge.js @@ -1,9 +1,19 @@ +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.values[i].length) - flag.values.splice(i, 1); + 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) @@ -123,7 +133,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) Liste de propositions ()
-
+
@@ -357,6 +367,8 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) data.exercices[eid].flags[fid].value = $scope.my.exercices[eid].flags[fid].value; if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].flags && $scope.my.exercices[eid].flags[fid] && $scope.my.exercices[eid].flags[fid].values !== undefined) data.exercices[eid].flags[fid].values = $scope.my.exercices[eid].flags[fid].values; + else if (data.exercices[eid].flags[fid].nb_lines) + data.exercices[eid].flags[fid].values = Array(data.exercices[eid].flags[fid].nb_lines); else data.exercices[eid].flags[fid].values = [""]; }); diff --git a/libfic/team_my.go b/libfic/team_my.go index ad494faf..ad9166fc 100644 --- a/libfic/team_my.go +++ b/libfic/team_my.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "path" + "strconv" "strings" "time" ) @@ -29,6 +30,7 @@ type myTeamFlag struct { Label string `json:"label"` Help string `json:"help,omitempty"` Separator string `json:"separator,omitempty"` + NbLines uint64 `json:"nb_lines,omitempty"` IgnoreOrder bool `json:"ignore_order,omitempty"` IgnoreCase bool `json:"ignore_case,omitempty"` ValidatorRe *string `json:"validator_regexp,omitempty"` @@ -205,10 +207,11 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { } // Treat array flags - if k.Label[0] == '`' && len(k.Label) > 3 { - flag.Label = k.Label[3:] + if k.Label[0] == '`' && len(k.Label) > 4 { + flag.Label = k.Label[4:] flag.Separator = string(k.Label[1]) flag.IgnoreOrder = k.Label[2] == 't' + flag.NbLines, _ = strconv.ParseUint(string(k.Label[3]), 10, 8) } else { flag.Label = k.Label } From a475617657245dae8abde55cb9ed0d4c28705cdf Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 6 Dec 2019 18:37:35 +0100 Subject: [PATCH 0102/1637] admin: heath api now checks untreated files --- admin/api/health.go | 56 ++++++++++++++++++++++++++++++++++++++++++ admin/api/timestamp.go | 26 -------------------- 2 files changed, 56 insertions(+), 26 deletions(-) create mode 100644 admin/api/health.go delete mode 100644 admin/api/timestamp.go diff --git a/admin/api/health.go b/admin/api/health.go new file mode 100644 index 00000000..43f3da77 --- /dev/null +++ b/admin/api/health.go @@ -0,0 +1,56 @@ +package api + +import ( + "fmt" + "io/ioutil" + "os" + "path" + "strings" + "time" + + "github.com/julienschmidt/httprouter" +) + +var TimestampCheck = "submissions" + +func init() { + router.GET("/api/timestamps.json", apiHandler( + func(httprouter.Params, []byte) (interface{}, error) { + if stat, err := os.Stat(TimestampCheck); err != nil { + return nil, err + } else { + now := time.Now().UTC() + return map[string]interface{}{ + "frontend": stat.ModTime().UTC(), + "backend": now, + "diffFB": now.Sub(stat.ModTime()), + }, nil + } + })) + router.GET("/api/health.json", apiHandler(GetHealth)) +} + +func getHealth(pathname string) (ret []string) { + if ds, err := ioutil.ReadDir(pathname); err != nil { + ret = append(ret, fmt.Sprintf("%s: unable to ReadDir: %s", strings.TrimPrefix(pathname, TimestampCheck), 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 { + ret = append(ret, fmt.Sprintf("%s/%s: existing untreated file.", strings.TrimPrefix(pathname, TimestampCheck), d.Name())) + } + } + return + } +} + +func GetHealth(httprouter.Params, []byte) (interface{}, error) { + if _, err := os.Stat(TimestampCheck); err != nil { + return nil, err + } else { + return getHealth(TimestampCheck), nil + } +} diff --git a/admin/api/timestamp.go b/admin/api/timestamp.go deleted file mode 100644 index 9da1f77c..00000000 --- a/admin/api/timestamp.go +++ /dev/null @@ -1,26 +0,0 @@ -package api - -import ( - "os" - "time" - - "github.com/julienschmidt/httprouter" -) - -var TimestampCheck = "submissions" - -func init() { - router.GET("/api/timestamps.json", apiHandler( - func(httprouter.Params, []byte) (interface{}, error) { - if stat, err := os.Stat(TimestampCheck); err != nil { - return nil, err - } else { - now := time.Now().UTC() - return map[string]interface{}{ - "frontend": stat.ModTime().UTC(), - "backend": now, - "diffFB": now.Sub(stat.ModTime()), - }, nil - } - })) -} From f3f14dcd25f034020f333c3b53a81dfe0e80bc75 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 12 Dec 2019 18:42:50 +0100 Subject: [PATCH 0103/1637] password_paper: fix ^ char --- password_paper/pass.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/password_paper/pass.sh b/password_paper/pass.sh index 82e95706..dc8a47fc 100755 --- a/password_paper/pass.sh +++ b/password_paper/pass.sh @@ -48,8 +48,8 @@ ID=0 while read LINE do ID=$(($ID + 1)) - NAME=$(echo $LINE | cut -d : -f 1 | sed 's/&/\\&/g;s/%/\\%/g;s/\$/\\$/g;s/#/\\#/g;s/_/ /g;s/{/\\{/g;s/}/\\}/g;s/~/\\textasciitilde{}/g;s/\^/\\\\textasciicircum{}/g') - PASS=$(echo $LINE | cut -d : -f 2- | sed 's/&/\\&/g;s/%/\\%/g;s/\$/\\$/g;s/#/\\#/g;s/_/\\_/g;s/{/\\{/g;s/}/\\}/g;s/~/\\textasciitilde{}/g;s/\^/\\\\textasciicircum{}/g') + NAME=$(echo $LINE | cut -d : -f 1 | sed 's/&/\\&/g;s/%/\\%/g;s/\$/\\$/g;s/#/\\#/g;s/_/ /g;s/{/\\{/g;s/}/\\}/g;s/~/\\textasciitilde{}/g;s/\^/\\\^{}/g') + PASS=$(echo $LINE | cut -d : -f 2- | sed 's/&/\\&/g;s/%/\\%/g;s/\$/\\$/g;s/#/\\#/g;s/_/\\_/g;s/{/\\{/g;s/}/\\}/g;s/~/\\textasciitilde{}/g;s/\^/\\\^{}/g') echo "\\zz{$ID}{$NAME}{$PASS}" From 91e40c1e1a1e5b10888cbb279808b91ac1e52d8f Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 14 Dec 2019 14:26:53 +0100 Subject: [PATCH 0104/1637] pkg/unbound: define default command to run --- fickit-pkg/unbound/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fickit-pkg/unbound/Dockerfile b/fickit-pkg/unbound/Dockerfile index 3a36e754..d4951d44 100644 --- a/fickit-pkg/unbound/Dockerfile +++ b/fickit-pkg/unbound/Dockerfile @@ -9,3 +9,5 @@ RUN wget -O /out/etc/unbound/root.hints ftp://ftp.internic.net/domain/named.cach FROM scratch COPY --from=mirror /out/ / COPY etc/unbound/ /etc/unbound + +CMD ["/usr/sbin/unbound", "-d"] From 13548d913f336f88fe5e6ec34974c09b1fcd53c3 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 14 Dec 2019 14:36:33 +0100 Subject: [PATCH 0105/1637] fickit-frontend: update unbound container --- fickit-frontend.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fickit-frontend.yml b/fickit-frontend.yml index fa05226d..5421486e 100644 --- a/fickit-frontend.yml +++ b/fickit-frontend.yml @@ -201,7 +201,7 @@ services: ipc: new uts: new - name: dns-server - image: nemunaire/unbound:93f5e52c6854356df113e547e0f60c8a23f98021 + image: nemunaire/unbound:8f177e138bad81fc26a9a0202f95a725f28b6eea binds: - /etc/unbound/unbound.d:/etc/unbound/unbound.d:ro net: /run/netns/nginx From 5ffbeabf5b30b3bdc298f2662af8f64bf87e9e4b Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 16 Dec 2019 12:19:46 +0100 Subject: [PATCH 0106/1637] fill_team: avoid \ char in password + fix substitution of UTF-8 chars --- admin/fill_teams.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/admin/fill_teams.sh b/admin/fill_teams.sh index 83f2c346..45855c62 100755 --- a/admin/fill_teams.sh +++ b/admin/fill_teams.sh @@ -63,11 +63,11 @@ new_team() { COLOR=$((($R*256 + $G) * 256 + $B)) [ "${GEN_PASSWD}" -eq 1 ] && { - PASSWD=$(pwgen -B -y 12) + PASSWD=$(pwgen -r '\\' -B -y 12) cat >> teams.pass <> htpasswd < Date: Mon, 16 Dec 2019 12:25:34 +0100 Subject: [PATCH 0107/1637] fill_team: fix generation of htpasswd --- admin/fill_teams.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/fill_teams.sh b/admin/fill_teams.sh index 45855c62..76ad1581 100755 --- a/admin/fill_teams.sh +++ b/admin/fill_teams.sh @@ -69,7 +69,7 @@ ${N}:${PASSWD} EOF NP=$(echo $N | cut -d : -f 1 | sed 's/[[:upper:]]/\l&/g;s/[âáàä]/a/g;s/[êéèë]/e/g') SALT="$(openssl rand -base64 3)" - HASHED="{SSHA}$(echo -n $PASS$SALT | openssl dgst -binary -sha1 | sed 's#$#'"$SALT"'#' | base64)" + HASHED="{SSHA}$(echo -n $PASSWD$SALT | openssl dgst -binary -sha1 | sed 's#$#'"$SALT"'#' | base64)" cat >> htpasswd < Date: Mon, 16 Dec 2019 12:26:40 +0100 Subject: [PATCH 0108/1637] fill_teams: also generate apr1 htpasswd --- admin/fill_teams.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/admin/fill_teams.sh b/admin/fill_teams.sh index 76ad1581..a51cea40 100755 --- a/admin/fill_teams.sh +++ b/admin/fill_teams.sh @@ -64,13 +64,17 @@ new_team() { [ "${GEN_PASSWD}" -eq 1 ] && { PASSWD=$(pwgen -r '\\' -B -y 12) + NP=$(echo $N | cut -d : -f 1 | sed 's/[[:upper:]]/\l&/g;s/[âáàä]/a/g;s/[êéèë]/e/g') cat >> teams.pass <> htpasswd <> htpasswd.ssha <> htpasswd.apr1 < Date: Mon, 16 Dec 2019 13:04:52 +0100 Subject: [PATCH 0109/1637] admin: Use SSHA password instead of APR1 --- admin/api/certificate.go | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/admin/api/certificate.go b/admin/api/certificate.go index b962be6f..6d22d299 100644 --- a/admin/api/certificate.go +++ b/admin/api/certificate.go @@ -2,9 +2,11 @@ package api import ( "crypto/rand" + "crypto/sha1" "crypto/x509" "crypto/x509/pkix" "encoding/base32" + "encoding/base64" "encoding/json" "errors" "fmt" @@ -28,7 +30,11 @@ var TeamsDir string func init() { router.GET("/api/htpasswd", apiHandler( func(httprouter.Params, []byte) (interface{}, error) { - return genHtpasswd() + return genHtpasswd(true) + })) + router.GET("/api/htpasswd.apr1", apiHandler( + func(httprouter.Params, []byte) (interface{}, error) { + return genHtpasswd(false) })) router.GET("/api/ca/", apiHandler(infoCA)) router.GET("/api/ca.pem", apiHandler(getCAPEM)) @@ -88,7 +94,7 @@ func init() { func(cert fic.Certificate, _ []byte) (interface{}, error) { return cert.Revoke() }))) } -func genHtpasswd() (ret string, err error) { +func genHtpasswd(ssha bool) (ret string, err error) { var teams []fic.Team teams, err = fic.GetTeams() if err != nil { @@ -111,20 +117,38 @@ func genHtpasswd() (ret string, err error) { var cert fic.Certificate cert, err = fic.GetCertificate(serial) if err != nil { - return + // Ignore invalid/incorrect/non-existant certificates + continue } if cert.Revoked != nil { continue } - b := make([]byte, 5) - if _, err = rand.Read(b); err != nil { + salt := make([]byte, 5) + if _, err = rand.Read(salt); err != nil { return } - salt := base32.StdEncoding.EncodeToString(b) - ret += fmt.Sprintf("%s:$apr1$%s$%s\n", strings.ToLower(team.Name), salt, fic.Apr1Md5(cert.Password, salt)) + if ssha { + hash := sha1.New() + hash.Write([]byte(cert.Password)) + hash.Write([]byte(salt)) + + ret += fmt.Sprintf( + "%s:{SSHA}%s\n", + strings.ToLower(team.Name), + base64.StdEncoding.EncodeToString(append(hash.Sum(nil), salt...)), + ) + } else { + salt32 := base32.StdEncoding.EncodeToString(salt) + ret += fmt.Sprintf( + "%s:$apr1$%s$%s\n", + strings.ToLower(team.Name), + salt32, + fic.Apr1Md5(cert.Password, salt32), + ) + } } } From e00a67832e1efad55f3d74b51b092686b5787a55 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 16 Dec 2019 13:05:42 +0100 Subject: [PATCH 0110/1637] Fix missing lower/ part --- configs/nsenter_iptables.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/nsenter_iptables.sh b/configs/nsenter_iptables.sh index 1f20fe0d..78cf11c2 100755 --- a/configs/nsenter_iptables.sh +++ b/configs/nsenter_iptables.sh @@ -1,9 +1,9 @@ #!/bin/sh if [ -d /containers/onboot/004-admin-ip-setup ]; then - LOWER=/containers/onboot/004-admin-ip-setup + LOWER=/containers/onboot/004-admin-ip-setup/lower elif [ -d /containers/onboot/004-nginx-ip-setup ]; then - LOWER=/containers/onboot/004-nginx-ip-setup + LOWER=/containers/onboot/004-nginx-ip-setup/lower else nsenter -t 1 -a "$0" $@ exit $? From 3465406b6fdcfb15b835e538a40c04da4271fc5c Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 16 Dec 2019 13:08:23 +0100 Subject: [PATCH 0111/1637] libfic: add missing tables during reset --- libfic/reset.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libfic/reset.go b/libfic/reset.go index aae70ecf..4b3d2c39 100644 --- a/libfic/reset.go +++ b/libfic/reset.go @@ -47,7 +47,10 @@ func ResetExercices() (error) { "exercice_files_deps", "exercice_files", "flag_found", + "exercice_flags_omcq_deps", + "exercice_mcq_okey_deps", "exercice_flags_deps", + "exercice_hints_deps", "flag_choices", "exercice_flags", "exercice_solved", From 778c30035b514a1eaf8627a52faf6faf8f7daa4d Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 16 Dec 2019 14:36:55 +0100 Subject: [PATCH 0112/1637] fickit-pkg/mdadm: add busybox to include a shell to pass command --- fickit-pkg/mdadm/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/fickit-pkg/mdadm/Dockerfile b/fickit-pkg/mdadm/Dockerfile index 59e23d30..78b65798 100644 --- a/fickit-pkg/mdadm/Dockerfile +++ b/fickit-pkg/mdadm/Dockerfile @@ -2,6 +2,7 @@ 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 5e1f314822326b2ded18056178e38dfc646bd6ee Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 16 Dec 2019 14:39:39 +0100 Subject: [PATCH 0113/1637] fickit: update mdadm image --- fickit-backend.yml | 2 +- fickit-frontend.yml | 2 +- fickit-prepare.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fickit-backend.yml b/fickit-backend.yml index e424d30f..ace8c399 100644 --- a/fickit-backend.yml +++ b/fickit-backend.yml @@ -8,7 +8,7 @@ init: - linuxkit/containerd:326b096cd5fbab0f864e52721d036cade67599d6 - linuxkit/ca-certificates:v0.6 - linuxkit/getty:2eb742cd7a68e14cf50577c02f30147bc406e478 - - nemunaire/mdadm:18541ef20acd7e67e07bb2bde4f378239e67c42d + - nemunaire/mdadm:18de5ca414227f38a5c0619662077ba5fa26176d onboot: - name: mod diff --git a/fickit-frontend.yml b/fickit-frontend.yml index 5421486e..16c45973 100644 --- a/fickit-frontend.yml +++ b/fickit-frontend.yml @@ -8,7 +8,7 @@ init: - linuxkit/containerd:326b096cd5fbab0f864e52721d036cade67599d6 - linuxkit/ca-certificates:v0.6 - linuxkit/getty:2eb742cd7a68e14cf50577c02f30147bc406e478 - - nemunaire/mdadm:18541ef20acd7e67e07bb2bde4f378239e67c42d + - nemunaire/mdadm:18de5ca414227f38a5c0619662077ba5fa26176d onboot: - name: mod diff --git a/fickit-prepare.yml b/fickit-prepare.yml index f2b59108..d88efa4c 100644 --- a/fickit-prepare.yml +++ b/fickit-prepare.yml @@ -24,7 +24,7 @@ onboot: - /etc/sfdisk_schema:/etc/sfdisk_schema:ro - name: raid-setup - image: nemunaire/mdadm:90509bdc0a6ee2bb36635ddc2573ae50130f0f04 + image: nemunaire/mdadm:18de5ca414227f38a5c0619662077ba5fa26176d command: ["/bin/sh", "-c", "/sbin/mdadm --create /dev/md2 --run --level=1 --metadata=1.0 --raid-devices=2 /dev/sda1 /dev/sdb1; /sbin/mdadm --create /dev/md1 --run --level=1 --metadata=1.1 --raid-devices=2 /dev/sda2 /dev/sdb2; /sbin/mdadm --create /dev/md0 --run --level=1 --metadata=0 --raid-devices=2 /dev/sda3 /dev/sdb3;"] - name: format From f078919459770c5cf8aabc315f22ee1fb926b7d4 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 16 Dec 2019 14:44:06 +0100 Subject: [PATCH 0114/1637] fickit: update unbound image --- fickit-frontend.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fickit-frontend.yml b/fickit-frontend.yml index 16c45973..6fbc1807 100644 --- a/fickit-frontend.yml +++ b/fickit-frontend.yml @@ -201,7 +201,7 @@ services: ipc: new uts: new - name: dns-server - image: nemunaire/unbound:8f177e138bad81fc26a9a0202f95a725f28b6eea + image: nemunaire/unbound:2c5d3b808517f1ad1cb829a474dec77db0a6513e binds: - /etc/unbound/unbound.d:/etc/unbound/unbound.d:ro net: /run/netns/nginx From bea31faa8ebaa77171be1b8e12ac58ca829e476e Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 16 Dec 2019 14:46:25 +0100 Subject: [PATCH 0115/1637] fickit: frontend has 4 eth cards --- fickit-frontend.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fickit-frontend.yml b/fickit-frontend.yml index 6fbc1807..eacf0ef3 100644 --- a/fickit-frontend.yml +++ b/fickit-frontend.yml @@ -51,15 +51,17 @@ onboot: # - name: eth1 # - name: eth2 # - name: eth3 +# - name: eth4 - name: frontal-ip-setup # with bonding image: linuxkit/ip:v0.6 - command: ["/bin/sh", "-c", "ip link set bond-frontal up; ifenslave bond-frontal eth1 eth2 eth3; ip a add 172.23.42.1/24 dev bond-frontal; ip a add 163.5.55.58/32 dev bond-frontal; ip link add link bond-frontal name internet type vlan id 1; ip link set internet up;" ] + command: ["/bin/sh", "-c", "ip link set bond-frontal up; ifenslave bond-frontal eth1 eth2 eth3 eth4; ip a add 172.23.42.1/24 dev bond-frontal; ip a add 163.5.55.58/32 dev bond-frontal; ip link add link bond-frontal name internet type vlan id 1; ip link set internet up;" ] net: /run/netns/nginx runtime: interfaces: - name: eth1 - name: eth2 - name: eth3 + - name: eth4 - name: bond-frontal add: bond - name: frontend-ip-setup From 104cb067ea18edb0f7963cbc6dd55992cd325d5e Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 16 Dec 2019 14:47:00 +0100 Subject: [PATCH 0116/1637] fickit: save ssh-keys between reboots --- fickit-backend.yml | 2 ++ fickit-frontend.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/fickit-backend.yml b/fickit-backend.yml index ace8c399..a4c3f8ed 100644 --- a/fickit-backend.yml +++ b/fickit-backend.yml @@ -217,6 +217,7 @@ services: command: ["/bin/ash", "/root/synchro.sh"] binds: - /etc/hosts:/etc/hosts:ro + - /var/lib/fic/ssh:/etc/ssh:ro - /root/.ssh/id_ed25519:/root/.ssh/id_ed25519:ro - /root/synchro.sh:/root/synchro.sh:ro - /var/lib/fic/files:/srv/FILES:ro @@ -231,6 +232,7 @@ services: - /var/lib/fic/files - /var/lib/fic/pki/shared - /var/lib/fic/settings + - /var/lib/fic/ssh - /var/lib/fic/submissions - /var/lib/fic/teams - name: sshd diff --git a/fickit-frontend.yml b/fickit-frontend.yml index eacf0ef3..f0005102 100644 --- a/fickit-frontend.yml +++ b/fickit-frontend.yml @@ -175,6 +175,7 @@ services: - all binds: - /etc/hosts:/etc/hosts:ro + - /var/lib/fic/ssh:/etc/ssh:ro - /root/.ssh/id_synchro.pub:/root/.ssh/authorized_keys:ro - /var/lib/fic/files:/srv/FILES - /var/lib/fic/pki:/srv/PKI @@ -187,6 +188,7 @@ services: - /var/lib/fic/files - /var/lib/fic/pki - /var/lib/fic/settings + - /var/lib/fic/ssh - /var/lib/fic/submissions - /var/lib/fic/teams From 141c5dd33d256c84b59403aa035511603a12a42e Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 15 Jan 2020 09:54:54 +0100 Subject: [PATCH 0117/1637] frontend: update notification icons --- configs/fic-auth-demo.conf | 19 ------------------- configs/fic-auth.conf | 15 ++++++++++++++- frontend/static/img/icon-danger.ico | Bin 19086 -> 10366 bytes frontend/static/img/icon-dark.ico | Bin 19086 -> 10366 bytes frontend/static/img/icon-info.ico | Bin 19086 -> 10366 bytes frontend/static/img/icon-light.ico | Bin 19086 -> 10366 bytes frontend/static/img/icon-primary.ico | Bin 19086 -> 10366 bytes frontend/static/img/icon-secondary.ico | Bin 19086 -> 10366 bytes frontend/static/img/icon-success.ico | Bin 19086 -> 10366 bytes frontend/static/img/icon-warning.ico | Bin 19086 -> 10366 bytes 10 files changed, 14 insertions(+), 20 deletions(-) delete mode 100644 configs/fic-auth-demo.conf diff --git a/configs/fic-auth-demo.conf b/configs/fic-auth-demo.conf deleted file mode 100644 index e0026b09..00000000 --- a/configs/fic-auth-demo.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/fic-auth.conf b/configs/fic-auth.conf index 773c5619..e0026b09 100644 --- a/configs/fic-auth.conf +++ b/configs/fic-auth.conf @@ -1,6 +1,19 @@ +set $auth_basic "Challenge FIC"; if ($ssl_client_verify != "SUCCESS") { - return 401; + 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/frontend/static/img/icon-danger.ico b/frontend/static/img/icon-danger.ico index e2f7b999ea4b49d05f1c8604779464036345d1bf..0debd0a84f810c8571c9bd873b0a6d8befc742d0 100644 GIT binary patch literal 10366 zcmeHM&5ImG6z_n4!1iW#Gdnxmu%7f_V9$ZT-qgcF?jf)@5fWHDcuAJ*B@j?#z(YdJ z;t(_!GcclAFlJ%#10tkxG=9cuT=QXcx9e~4_j}b{UDe$?von1WohRvf_1^ovSM{pu zRgLR;NATapi&OYM=e_oV=goSahsL{ze-MhCY$-v%e)?1I((AXqx8B(D-np>lUAc7I zd%v~i-Msdh_t6KRdt0A;;eCDYp7-s8p7(42q4)cvUGK@Wr-{2G{@64{7h|IVtO%!r zD&c{zFndOxsuq5N{ttpy(f(40;AR-s$s?L_`=UK zi?zE2ge&MgLO;MN6yp2D52a?b<2PcAy%3)XI0S`QCq@b$WX5mz$Xgz3ibdv*=NLUtBq}l_(RQ1htn+Y%!Nkhe}IMtEmztqzTPmR!e z!Ua8c!+4R+tlDLBFg4QyG@YJbNr@>7&0%FB`cpfl4LVs2ij$!({`;9j8|_7)pb>qX*g?#SuE5n3teL- zj8Yq}06kp+eU-U?2 zm|WzY++rbEE`3Fh#5bd#*jz}f5<8$;5nq&u<#n}x^=cI5-V??2A3Xq%*EtS){(W4W zEIk&|FC&<{bLWOI*5?eYtuEN2M_2FmqC)Y2N2^%9eE!Osi*S1gZ+3>(gerTloQE1z zJ{_7Lqk4ZyBlQ=8bU{OEN%@j(>{&cOJ4PTLd%7EnPCmIY8gnL#l&vZ~wnQINk<#}D z;xL=3K2GOIk79B%Rq56mqUFJ|NlraHO8R*Ug<{?0wteD*I@cgliqCLA6j*$7`uS_Z znWS3L<#oDq0T7UWg0h@3{R|(gM zfKHJ1h?^H=En0|fNd`94;?YpiG99J6pKj~{&qoln#lrqTnAYxXE$`?!G}i-4%tHuv zZK&`?AtXp7Yk(bI=d&;YCb{c6)gC$gT(mIn5jgJEB?nKB823pu%Z@@41C2un!`~HVewvuOJ|_T7&nWlqO;Ta z&7u!^I}3D1=Lq;eGGWHmyBRMW<0&N*dk&!-3Mb3llz6=2O z+vUsmi7s5!wrv^D!(>yIj56yI*@}%;`v1O^m>n#_q0Xp#u-$>0yP1P=;J$x777zWB zm)C(o9mVWj&fptIHtGB)GH|5YET*dd{kSr|k^}zzy+Q`$@AvdEp-BcN8JJ{Xl7UGE KCK>oYW#AuVF%ZH4 literal 19086 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 A*8l(j literal 19086 zcmeHO2Xqxh7rsd-F(ft+k%)qz@<$XwKok)XjFm1C1wx6G0EQ@16p#`@1Vm7Z3J8QA zLjqFLfCLiYrC)pRO7ES}{yQ_fJG=XK-&@Fw9M54m-`hKP?%Z#7cgmgF0N_XeMvn&i z4TKTT08|Blx&%@|34!VD($1u|ursM0>`Z28Pv`(US9gG2t2+`p!LBvW6FL(jVAtmi zFA%!GuC-lY_u3Z;FTw703|$G`VE6j&gh<%Ep$F{Q&=dBg^dj_zJ*iQImkE7fPg-Bt zo7NBZruQdA!`_Smgn_Vk;~>H-uy0cgVKD6597}i=_GP|C7(#d*_GJx){aM3c|CZr| z5wL&j8-zDufA&biC^(Qk8V=+zyhRuT2evVcC5(du+Zo0a;@}{|j<*RD2ovF;i{Tx@ zBsiGM@GfC89Li&uLYNAN@)_PEOoKxO?-QmIX29V>h7SlI!r>x@nS@zzxR_x!VGbO| zkKhP?3`f5BgfJJ5d^wLWAC7$WDPaK|{c0f`{dy509*%yqn6Lzne!G;g432%boUj6p zeZP{B0LOm#jF1S&eq05|e@r4I!||V16V|}-pFbz8h2y`hgA>23hZDbUAfymd;lytY zX@qn*@jF8XVI!RUgJBb4Go1W0laNK&0w@1s*hHwTSbr8;os^Cb+&;X=s?xNw%?B;gcXILC0Ba0V`% zFCmbS;g-^E40iQyg06rO$OM@i&YsG_aWa||9aPxH*^h&6J+*^ND)_rRgyNB z;uVqZy&|lV^uNIGf46L}uO%n<-w>0d%W95x!atT4iK6(3GMT5H@V`g=l(xq;@%Q!P z2Pgb>i!t}3Le_-T!L@zd0w?@18?p5b5%H`Am=wjf7d}gl`0F7i_yN?3O=B%6nAOB* z^p2iCz+A+~7^i>zk=v*RK3p>$-9OxmxRM?KOM&4t^QsBa7r)NB%fSIOGTF{O-N3{5?9{O594Mv7g&E77 z=bZAxGQ@^@xAm6@Zsm7wJ$<2b*)e}h#80K$N^y+WHSOgm8i#kCypiOBK9@B)JLdQM z46(l2fGP>ZnEt}GNyFwqsqR?^Ye!hWwgqvZN#`KznV&`IT6zxgL!Nn<9(K5IKF!3pODyF zMjZj`u#nHZixG!f5Ok9-$fDFn<&ociGj0!(75gK0r(6TX&H8t*8U#?U(0V9S8lIkN zWx9IguT7m=XHTn89A!r$_Sb2p*c@?$%)#|JGvu0~(;GyO{Phus>#VDaDPS$4^#WKf z12>nK_FWK1$()Vkkv~k_uv+z2qys#l-(*!|x(-A9pHxvdF}x00qLV!G2N&Zy-I#uc z_q&FOZ|NN9#pe;}_*Y4{72|rPvR}I@s@8n<$Pe=oTgV#ocog(F;$W#WNcSW5(-Z>` zqt#4I{)dfJ*?9HL-x_h97HzbpDYpUuT_lOo8O4c%RE-~XliStIO)ku|8ZzJ|{_~>; zuu|FjbjU?)PYx**(RWIff?Xd%?5*ah@9Gxp(TLsD62sN={X_K>#2v#L)u`2uMc-4{ z`wT^#XK*1iFCuE^38N6Z$dcQOFhw^sWPulcsF_T>N}9^gYW%Ztn{HB)t^lUV{vWsn z^KR6YQPFd{tR{NnhoDzzVPPqylhT{VA(|H-;Vy71W~xEJQ;1VF5)x=AC+dchR^*i* zAh^|#N$VdRjOBIt+JLfzE?zs{PA0c#MSG%G0|NPXpg zMPDT!H!BS;*Pv6s$d*;9Z!WZ`bQ%2c7`+UO35ELjB9$RhT_UTG%H(gaXaNnMdTOz} zUJgIdM6}GU3sUDV(&pP0h+#zH*Vo0_m7nR7SC#Det2|;kTtu~MsWiB6QC;w>-=Jc| z7*%x#;u@06Pa@)s*IS1*=}E7j+E+_8(5`m;k(d?tXgSTyB-GhTF+e+Aao9p~KSk~Q zFc`59RT+Yq-(QIW!d8;S7?U6S`H#YVI&O%y?C0d9F3qmEzLz+S2zhQM-8_!e+T)*s z#dNIQv>07YHO7il5#4vLHuqnHIkcbX0Ir?F4bqo>G;eJ_6H}hnn1Dv;e9F-drK40r zMaswO@?5mx@5{YoC$qUOA2zK>7_r|?z4hnVv^k-b;y%L)KsDL=JTJzt@>2ZcTr<*3oZ6HEN#EE3L z1PiV+n$s5MRinmc{f!a)PnnFJW!dOt8msJ-9bpb<^B7u_ofrxy)!ACYo%oqm{#?vgD^go23aRTd!FfHM~z|@VRLNW}0eLN1Sa^S@KgH%hDqz zTbT``?q!jBi+amy@G^>>v;a&Px1dSFRLzP%AcOnK(pFqk9q>f!Rjg=Hyj9G>OTzNa@YSFAx!-{{b z;BR2%lGhxkjMGW29?8y&raN!wJvE+Oe^(V&{WeZ>&8!~7A)nAf5@<=%esm| zl+@6QAF-TI{Gu-A&uwcvcj~K=?8eUmNs%(+8e2UyVZh=$HB}ix;ixI4_X$ypJzmZH zH|y^MQ-^91z3N3VSghuud{nlHTV&IpwQ9XqO%crez0AInqiY)K^b`hNRb_43YS^TC zH>pPUS2Mqrx8*(LBnrZyP<7o}%of%yNWI6NSSc~_-)8A3S-MdS)RX9*B2MSE3YSle z9TXe4LW~cta@mIv*8I_ezh}3_I>&r3quBQs1{7YiMB7Xy70qg;2AjdHKF~Um%i9`h zy8mSE&P4{}#DiFKugB0aKM6k$@JTbW*AlE`hb*!|(? z>1<;_eFWHaXO1V+8*=z0M>X_~~4S z(z_-TnPne-D2-qmB}%kA3g?vqsq<1Y^e5?!O9uXB+lThD6GDV1+;;=wM^ zXgmqQZJhGMn}UP?E{nX`;Pn$xR*NgyX(HlegTduwjQ0-}-V-F^w6Zf*U9R6L96Zws4>N|h}5$zQeZ@$xnB z`39o+4Qt=D28isPr6Q-w*8Efu8kH<4P|O9N@mzu0ADfB7m@?xh$F-x`-dW5ywFI9Q zOI##ue=ZFa{B1P19}7hbO>udGXMXZoz7lsVB4JmE#?oH! zuVS%CDUw%y;X6#QX`lB1DvHnkz3z2#{NVKc zyAzP6nh3|~@8I1215ZPmIo8KGm>o-T|I~TuP*bF|m*pmnz7Bq%%-*Z|-e}o!sVo&g zJf<}O3l~K_zz)}MY}i*!)x2SG-sZ>B@k7%b^$GB6ah3>D6lL)suYI7D{BXNCkTA4D z&E&R~&)H5YEk6XkX1HaQo34xN*pce=rRJy4s?%kxAn7x@IEqbtzz=ZeaJI|Pr_~Yq zsZw{pX3;17^tT@m#wHXh4R9^(=V?=JY=MvXnbYz$8ij|4H?Hor&+A*@Lw>zm$MTit zZ|1nAWnIW$JTqyKe@L7Bi@*w(9mgKDu8p#K6A@~+# zaeOMY4*2h(D8bQM#hB5=G_9_4KZCz>?*Ey|Zg%IT;NH@?XU;vpbI<+VbI+Y5QM81A zTU*O`z7V~2C5l#}C_)!);XjnDC$X_%{A_e9`g8y7=<)uY=!gA}qHp&nyhJ;2d6XZ9d(6Qmm&%gFALrYBSTo)nfQGq$D5su z51m>I5nxw^`pQ=BK{vm9gG9L`#}GR7;0)3Sj9)`zAqKVEVm%x0k*Q85%x~`&Xh%H8 zuK0n;%+2GJEUl=85gto0&6i0Byfqy6$RN$we7b?4IuyFz^xuBl95 zpehU2M-3nUV#l1|C5BC20y~-qK26ZDymVO;y z*8sY1k9{TDtVaI^<~NhMzR(x>$6@#l^w&L-m#+GrxmJGZe3FF8f(12c7%`CJyLVMt za2{eJD|PRX8X{8TLzvdC@;X_K^s|S~{rkJhm3`LfkA06^82i?`K`3e=0dMLd;Hx*^ zeZ2u@RJ7QqLHX@Fk9~2ImOSa};}Q*zh3^3hwIQ)8q-1eUeWTjIEj96o_%al^dPO9r zQ5|V&SS&X;P9C?(G|u!Gcex*;^E9wFyIM&eSv0P-MXJYxE-oxy{;EcHH2qh2#EqAY z0gzG2mSB+^Uho-ixGVRBctefnYCL#y@z5M9(s3S2l4wNAN6LE0FAV|E8iiQlVTISX zK|dp1ZYJBIb=Wtfb+vm3!zrsbAd!I1-*v*9tYka&7?|R*R_KzEFrE*=k!MJUs$SG$ zDD|?%NLde+N6c5`9$t(*Q>TYAu+oP=rtrgXDFD4g4o*KDr0$g@Xb=%?i3v=PXHbQv zA||syr^`{jDk!Scz}V66)QUd3wW`6=NzI)hNyKt!ri)Kw353sQA)c=8!%&rRIifS1 zh65aINNJM_Kuup2&YV`1#9xrrIw263y{;^zTU54G9g^G{K%iaTYv6@#4y5`4-2=cp z*g#B7?{A{0dR9h!sYVLu@~bP+503ip)UrppxRe@Kx~KW1bl)3=lyuF)Z6*6EIQzHUZ?#44uZlk5oZUx22V_AS9b71 z!ltsKgV228U&ssp*fGxzSnsqIGp2yWl7Qc3`!#G;{uT^Rf60Ky2SA@K=`*R^o(}RX z_`=s;+pkhEwJ&eaxJl#ya6i5G{uhL9xR9DqPNzPg6qKITyU$skBT)|7R?e{*Ba4QR zo-@lNwdY_SxDa7~sF(laEDP UFtA`?!N7un1p^BP{!a}22L>?wp8x;= literal 19086 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 literal 19086 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 pRUjVT-D?^RZz<;{}e*-W9hd=-T literal 19086 zcmeHO3w)2&8-MnjcF~1mDTS?wkfK;qF6EzpsbLM}zg8$D#=pp4h1_x*X3eH-Z8mJP zvCX#MlS)MwBvFzsl5UEKluJnd&pGFP-}7F7zjqgW`h52IJbRyW&U2pcIp;l>=REHZ zKmh%lFahXq2#o6ia5(_9Af_xhNzC${XsS03rZL8c;RWJpI1pZV^%0oPc=aHd!Fe#u zTs;_Oy*319zBUwQy*`BaahU!3Fqr+ua7ZMcvt|S&t{Dk)-y8+=-g*j>IFEt(Z;yug zYoCT>&g0<4wZ!Y5g_L#A!Gd?jL+ZN|A?>}%u#otr_ou+Z_ou?k>!-n@^)q16h8d8) zVHRW%zp`;AWNe&G&)Jaq0pq!lMZ9>^JjmLVM9(D1{%}6za88Dt%_*>i@kgn!lz7>e zG{|MV^<~H-&fl62`5&jla>k!zKmlE*--T95-8rD10~y+5-)>Q zpXEXMj(jNHu^h^X%Resw@8^YpU#x^zzbuB;#IJo-098sayO{&53r-m?)t+VcTyVf@o3*vk0l&G0etC!Dvywx5ajZh=qtegdEF+X~zFeGH#* z-Ud505AA|)4}AmQ5r6;N zx9|hw!{5V?#Cv}K0e&L>`HwxsKf&H3dto2(FGqfX{YUq~fusB3z_I=C>#+lH@Yq2( zbo?OvcKkOu{O2L!!|?lw!|=z6Ki~-Q(Gy4D=*gpS?Bp>xe(EUkarl$*Unk%M@k!!S zr%w_81*iW$1%Lg0nx3cOZ^mcNz!~Bi@;?Gdlpho9U$J^MLShRDm~7JD6t-WTb83*H z;3tQkb8HS+djao%UfB782WU`0$k_XQ!2`q+bly3ida}RlR2(Uj=SAv%UQi`yJ1L$Q z>Du#xDnb7V_?~dt-s+az^uGnB^*dX8v>W^<=#VI%)%~pO({AveCVZk539UWVJ^bJX zKirGOlgd~VvL0$w%`I?)4-*k1FX=vuwE$CN>Q!eGT;VrGOb!5O9KV>gpmaj(>T7{3 ze3*iG3&Zp@8&XIusKztX9sae5^JxGW1xJl9sV~Ip`0EZIrX#l1;Ob~n)diRMtq|u_ z9n8jpOMIZoZ=H3uuIU*2Ky;VJ%B!(4?>EaWep|#uqp^TC!}Hj3Aj(dNFqXMbxy6UM zh%Ba{QM1_sTlqVssa8@U!8Lw+#22LH&;*8CH1_^mTSfI4y@ce_*h+2AuJHq2L~N!u zpl&iDX8-#lCGEM5rJoN{i!1PSjSo?XlNH0E1m}&{pN9d_C5TT}VjjB3_t1q&8Gt7d zi*Gk|G9n-GK~tWKq;Gsf+^Q4ebKK&MG}uuV+s%ux^lD44zYO2_BU5_HQTC)`8Q*ym zk%DAax>vp+YoSw)PkhfZT%j9+zKHjco#u$kWUHiM56uc~hO%s3RjFl?eBw8vNv+eT zQz(IwLlFDwa;4Y~@iCc#iwmBSXNE4{Ao|2_hB!#4T~BNQixBM(z`z__R!i#NjW|@M z93-Fk5u(Ft)yoklQFIkCP0cYqA4Sw{#57F|ufwD)l0NZ6y_l|B)3@O7HUE#$HrE)-*x>~kL@QF{+V7ysYn99AN4u}uQJcD!{VjnHz0mOc4B5u`lEmhh0 z^o<{hn67ynooUOx0DvA6#AuBC#Qthd08NuC)x@Pmm}|FWz>ohOKm#yex%%8ujM$Y7 zk}IO0lqv*!wn6NvrfKf#4(!Jf?^R0-Pv8BAuuR02kG5=ZRTt)ck74gK3~{Q#giO$k z*Ww975$~2ISC(U@ZfVE@Klsovjc^qj!(%m`0xWdcS;orwADoXR+M6RdD(WOrrQ{DE zLjIF4;4`}^vw1k8`S20;f|p~i>I8J8(UK?tw3JhHOGzv8iw_VQIpE2s;%}g-C#W^z zEIU&`2Y`k-!LV|uB6-j^supxl7x+4DE_zr;u5Un>?2mA)i zpz}rta1mJ}%APZ6>Y`Ol%Iv5i0j^g$bI}1mmYGECW>(jwBgU2s zCH8k|PkkJlE+<_9j3)rqY+Q;<%(Kb?e*rgLa)}PlDu#6*lSQj4H~F!af%?*rLR?9w zU4P3qF%Hoj@3qHoB6{|W-l~HfVp)iAC*mm5o{gm!8Y|Hi=3!%w!~PQ|@O7`$RLxhC ztxmQPWMPI$t)Y5@komU#4@l?udf!{t@Fm?55ggo&Zsk*$QX!_8bT6cn!<1!3=6o<3M|(9M68k>K8tq~HMcX$^M(n97LE#{fVN0ONMI!5U~?54{2<|Q zE!IvGbDaysk)G|Spi~H>!*`%m>VsIQ_|np%4SomFyGNvwNop??2AHy~$xE~pJYQzTOAS*^loe+pWiUge~k*r(%7J)WVB+RM&l6It;?S90@Z=P z2XTW~O1#2S^W0~8R(!f&F?v8}#VAr7hLO8qPe?TL6o|4OtZg(SQda!ug}!qrXQEJW z{UxMf*O-2HUgI#F7W_Kgo4{nd#`3&NDAGn`R;Uv-XkyBey!6!P<093#prZ|0V8JIh zmQlfoHi1%0FDVK!`n-_t^Vm#NY1Nd@j(exR8p#%X=12<4O=x8|XhMPccPiB#K<=oC zvSV#uw^~Jen(;5w_XD#AvV+_}ih{gb*NQ1HVT)w;?RKp<*rJ3Pzqi>|TEB*dI=zHK z4>hw9T{RqN>ZPiZ^V5uPXKh}z+(bbrl&Pj8#cpBWf~;$tft3;y{uQ={(xe$hVX~6b zOZe%$R@uB!@%`fylEnJpSw(GSO?%0N-%sH8x;Ig0nBQfT>V1VmSCNbsDf(MJAyRcV zLnEKnCQ*^UGtz{g#tjzH0Dhl2O(P{8oElsG{!`e9QELZ_bbV*U zV<;4AXLOH)s+ujFxp`{R`ZV;vxiGre!T0;0C=O>ETFA49a=AcvFq^d1WKjFjMd;j2BYr#eK*7{l%gpM^A(d}$VqmAM zC_bN|o!sIB-H|{|QVwm%JVzfb_G6`e<;ew|%coNEI-6!MBb;WL$Bv8cZ z!Z+f|z-o0&_z*Z)dj-5DCF5^b0u4Px%aq^#h?$}s)S9)53ub)!zVxzw2uNNj9MD9b zt@_I-+Aeg|jy4?Hbs=g5e2u{&0w|KvOuDxH46J z1=eSAXH#cwl2yfrkVgz%*0mzh&-W4DnkwVdXVr1iSCH^IcN=#Lo4&KGYQP6*GKk$W z^t7leHSOjEahC73O!&>swHb_4;+F210*@(H~;_u diff --git a/frontend/static/img/icon-warning.ico b/frontend/static/img/icon-warning.ico index 7d01ccbdf764eae6ba67b2f8797f7ebf10dfae94..a2e4a3560dcdb0ed269382f37ca9ccb9b37a52b3 100644 GIT binary patch literal 10366 zcmeHN&5j#I5FRABGjY7uuDx^Sx`*75p8z3QC>IVZapMBgynwZ@un&MG9|@9?IY1PF zg{4G@MGTnmCqxETQ37Ol`x*GEs=KGBd+eE=y}@*p>FTQbs%yUPu4zY!qL=XJP{rSh1CmsLNJ314@)q zOEYNGt5apDs&#-vPX3$L__+PlGA9VQQA^Hm1dk65et@me zVl(wL0VBZCj;@`J;QKuHUW*KlH#Fr;!t@l6NYk`20RuG`|K!^1*{9u`yfD+LwUO$3 zaitk^i<0&Hs~#N3`xoprTgJ{V98kDXBOdkEK{s$tAmM2*AZkr3@LFR3MT$?;^{=ki)H+tQWHo!+P-9+rDgm%AQW)Fwk*h6<5`-Y@Dh5pV{$T0c`B?zfOi&T zM~o3j9w-C}@gSfWNBsfnm$%#$k9Rk`U16{BGx3X`F(-Aizk{uQV{HmDch4tidzW z6HINUgh=ce$d^83T~ZuM;Yz4{Y}k^`L5qkO+cM2IQg}yvsxBt|1s^)el?W)Z?iZp& zpfA6P#r+HlMK9f#%>cY#7F_=^*3}(@tksK@GzS-~2cA+eZI&8IdQ9e6U0YAKo`-j1 zk@t4Ie(Q00eh=RRe&f#S$g=1vE#>dw1Nt0%LU)Q0*mW_k4D&AhfW8LRWx>vZ)iNj4 z>Z|}GFRnYMR}gm4hcGLpPSFiyB{^=Ea%UbP{wHH{dS{0@A{_d_&mBjI{&pg3acXQm z5>QAV)u~jB#WQ(oWS0u)LVT!!8-GkWgqJM{`p1Npz7ij2`fhyeEmlb1LJnU!Xgrin zdg$LOLVT#suutS-UgDj)=Ba>#OXf3Mo3^xl|S&P?&Oty&=t;qHsPG`?AnbIorrE z6C*vh1)!IuR{Zdyh!e&y$Y7cdDS?aU*x0X9t+e!+(Ar~JfE+0|c%>eR8WFEZqI2yw zZ`sIT=RX}k6u62uTWC7H8w!H^T^!pv|uzSh`0B?_zg;WFJmo}yr;Owp_X`w2! ztQREYMVvjdD&Aib*KeUG)*Wr3vIZYDj}tmIr<|AfO(Wncz*D-Vn0jJ#RRwDl>|lg6 z)rwQo_;CgOY@hS9c#ywRkF6q;+MRF1TSp@>zE}hdY2A+-<0S!e)_RzD1N^En9zTwq z6sVF>X4k+MWfr(ZKeuMD@$TSXlc;}*WW~1E85rOc! zv+rRXYWpb{NBeITn7_0wYw>cuvoKwS>S1LMi?=UQw%3jD7Mix)N_805KC{aOwxjC{^a3|YfR9yC!}`UQuwgD(3%eTl^@40B z4Uys|_ZPX=QCSIZ-ZkMCgi-43&YdqrI;{bCW;#r6D{h#c`-MC}m|IR?w|0(En9RnQ$9RnQ$9RnQ$|7`~T E1vvrgF8}}l literal 19086 zcmeHO30#%M7C(S?r9~@~EPHDC$Sk$g%rXsAsmPZ}YPgVwY36Q{hWSj=TyjGpB-2Dd zKm?TSa!k#A&3(xeMjG!nQy<}z4t?|@q51?<8SVqbLPzX&&)U9nKSbtfEWE6 zIuz))3`D#O;0FNDQ6>@Gpv>gXQu~DY&@u66=#aR8$^{UbxDY~tJBkdKi@bD}0%~0U}t= zSr1?3Y=ptN8(>J@M#`IDXx?TRYTpdQ>|0=1K9%`f;2Ycmk+_x0tuTE4HW)$q+xgpJ znEZ->zfw!^3eJ7Dy}?J#EHZ}1(6?x~`sTxs zx%nt$ZlQ9^5y;wl1m}K4skbH_WHJ;P(@-kmX$` zVG+x_Pr%~cr(nq+Ct=y1Q?PW;X;}W}X;`uM6s+8P23E1W?=-C5cNW%AUc3Jcto!RM z<#X`sfitlFz&Y4(@GNXRcpf$#ItQB%U4YGp&%+kV+m2j-9Y-(1ZT1&R&DVl#iaf4oA;lgJb8f!`~OK!SM^% z;pD~ZaN^<(IDP3RoW6Vm&Rn?(XDOe*dJ`^CK7aKVT)K7(E?z5uOV@A0xADa{VCxmNOgt*w1J z0h3+&jeXqn_T5{o7%Sn&__|lV7aG2?eEez5TNg{Jm`&Lp9$3PMQHZZHOz#?fGN=b0+%v7=KZiJ38nZ1RGN=4MJ$@LASYH7xt58KJ ztc@S)AVw7(%*294e4xeexavwh+ob(R!GV=c>{=CB#jlSz!CY1( zTE&Mb#9CUbH6{vd>339hZKZYEGJZqE?^Ls8!WnMXkPqLe6VNhZ7SVa_@5(t_#)t0_ zYseia7ek1N|G8h{ecojJh|+R(Axi7`0f-|d!@dNU-?fYuCO8LikXB%kwvO*j2d3wR z)j^2aO$`A4vk^NPWENQ0_`1VEQ^aq$#g}Qa0~~DE%)-2XIY`|JT;mUnZKFmVKGV_8 zJ?{j>?k0BMS09i`trL+;eD7qOtC@lhh^@#>O~m9fCgyx-RVXWz)oYrXs0)!x{3^7l zH7#iu3MaM?Vh4>>B^x02QaN}q^J{g_(8zV9OZ*y$JvG{o2p2F7(R>5+O2Xt)s(uT^ zzAER0a*6LR1}ryy9PwKK`i+w%hO!6Zzf}<}6W!xbYc!NgeBXSWuF-vyue(}^{WL-? z`7Yv{3jaht&XAeFax!HsUE;$S#Mjgoqj?ncI${@9rlVd!?5GGnLJXCeu-tEI%e209 zjo%n?x)N+L|y;3*_fj|IfA1qHpZt($p8+98rsOw9#^hBzQI9{f8C2auw z2{B=$?#&?qMbIznv;|`U-`L7$s@e8O?w;1l3zBlVTuhca>{avaj=>Edp|v^IXo>i{ zz=v1qVVF1+T5;oKgkX7#tk5@wKRx?cdVQ+P)oOV+_&|;*%3%r8;oJ_~ z6wm8+0BTn`4`&%yl@ol5wJGI1q@bA*enErd>8(nM=~Y{qdZuT~z zuQ`5IF{-c4Pz^GhcHpA!EyRIDO~kwh^vZOE`9v>q;(x*feiMVTHI}`d?4S~_#y?WS zO7aOpHDo^i=^S4hd#f#6QSbQMY;V)4d=yj4aM*DkaNIde@hQ+|iA`dm61_EiO(t>Q z^b@8ST30|EZV;K`Qyj~ZajmFL{b_lbq&A{mR7GA#v6B*j3FX(wNf?@$;rk?UJ89Ae zW@tgIcS2TVbH{ZD-qqWM4-9)zYC!L>5`YR(Z^R~2rCgZdllx%F*j2d+F#m!$XMme~ zAHRG|)4jlD=VG=tCSnH7@Y#JgrJ5?Bf$Ap|Y8k{vmK?6`O5h8`-bNi0{L&)eO7Jo2 z%KUv|OHXvFASwFM=KGnd)Dba5ilt>srsIEIjP6ra$w1Ln5KG+*Vn?<)?IL4&96(_+ z+c26r=$MRO>@3=ooS~JZAQ?7P>e$xob)3+#$tx|tCP$sm_GCkH#P^-Y28YuOiYjZH z>qbFUg_p#u#2chG(NxH!BR-w4So%oTa44Gz1#6c*wqO=1P>Szl>N6biM+klAS2^@G zf+fpeLMjD@gueBx!!U%v0lzGdCiwRT^Cl1FaQGNQZP|dH!((TqO`kF%tkM03Iv1=1 zK83L?We#{*x2LNnB!v)FLOXuYB<}b{Tg)BR+^O%>HY3^rp9iYkPD*$kgED7@0*mkD zRIb|`30N%>+nYXaxrtI5@gLQW1LM2bP&6e%QkbB~ONFGXuo^0{siNY7Fygn>Sm0z_ zXeC|q)WDj)-@*>ec=j@So7N zmzg+KUs4z;Nm`3Ioh#ZW4eZ*vYk0JHKe#B%c?cmHFyMy@$ky*q&=}@t8A+e+Kv@bD zY_Q)ayyQ)MJU=f`hM!f}GzmKgz#WJBHjklREql zgn9MMtWzbXmr&prNfb`86m2&7qFwbop`ybunAFvYz-E?{jfeh_cosL9$_}w!Vsv@W z7z}Dz=@|F9R%W1e_;jyBNf)!~)aee3rCv-ZMTw3#8k!b1TD+8W>q%41NF6@E^f3Ll zBU;h9o+Z8~eaT6?{=GLC2WdCquZyux@H%|DG^C_%ZH6Xvzsb@}Z<2e_DU;F|gNlKD zQ265R2C;_K;nTGdOO7A9MFTp%v&8RE=&GHi_cW%=ND<6UGOH3Y9ezz=G{H&i_g_)X z-{4p(m=)9YH z{08!df>YW#tgJu|DSdkr6Ki&(>(~e1W>)cmPRL4%3Vu{)Yow?VcbJno6QNVM8%95V zxbG_ko(TK+R_Z--IpNKs1&Vl5+!5amtkTAS4_>{MN5FfM6WLT+JS|1f+G_Eojaso* zc*2NJ-k@WMP6}V^2*uF z6%{F&;FG=bt;Va5LEPDfh~Bi2(CF(TOI3>mPL0j+slqQLjw2unU+_KW^5pecUsQ(O zGkh{!C6t|=g}-@9;0>4Y9uUXW1TR@|m!;G3b9>%3x+jTm{hb7UGX>^bA$Uy@+#TT> zpKO*Y!wri_I7>ZLO5QNmc%fd%Zn(u4_WZ?YHU6%&h%NAgLIN7pE#lLIV6oThn$}YM zm##U8Yig~M7V*iPPo401?LO+8B<7@O|9`?dKI?*RR6znC$E%A%S8s_a6u!aYD5(Q+ z`WphSWov8AvNbIOu*8-Z;W+&roWpTW`d{PTbv55!p z0jl;CpG;KrAA+>5()sikas4ZXZ7+3=k%P9ZsE*Gl748U+j9-A1s#rT9AfQeKcaXYz z58~_WT4jnGzrNLu-Xp!Bdt}$D*`lA|tRk7~r8vJnGjHHQDRUA0FM2&_voNOGF#Oc= zg=ykRUBB53AEPaLN>4&FTV-O9<6}(WJeG(0QJy~J2z*Qpw)(ZsL)i4eur<5AmhkCM rh{A&!_*eUfl7EAsFU0Mo$A(j4sAL98W}svSN@k#B21;h2IA`F0+)D%4 From 546cae869b40051d41df421440d2feab65ce7978 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 15 Jan 2020 10:52:39 +0100 Subject: [PATCH 0118/1637] admin: passwd authentication can be made with team name or certificate ID --- admin/api/certificate.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/admin/api/certificate.go b/admin/api/certificate.go index 6d22d299..7b0340f6 100644 --- a/admin/api/certificate.go +++ b/admin/api/certificate.go @@ -135,11 +135,11 @@ func genHtpasswd(ssha bool) (ret string, err error) { hash.Write([]byte(cert.Password)) hash.Write([]byte(salt)) - ret += fmt.Sprintf( - "%s:{SSHA}%s\n", - strings.ToLower(team.Name), - base64.StdEncoding.EncodeToString(append(hash.Sum(nil), 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 } else { salt32 := base32.StdEncoding.EncodeToString(salt) ret += fmt.Sprintf( From 998354265304313da3dfb29a304383a803189c8b Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 15 Jan 2020 10:54:03 +0100 Subject: [PATCH 0119/1637] admin: always use normalized hexadecimal certificate ID --- admin/pki/client.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/admin/pki/client.go b/admin/pki/client.go index 011d945c..a64ebc4b 100644 --- a/admin/pki/client.go +++ b/admin/pki/client.go @@ -6,6 +6,7 @@ import ( "crypto/x509" "crypto/x509/pkix" "fmt" + "math" "math/big" "os" "os/exec" @@ -14,15 +15,15 @@ import ( ) func ClientCertificatePath(serial uint64) string { - return path.Join(PKIDir, fmt.Sprintf("%d", serial), "cert.pem") + 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("%d", serial), "privkey.pem") + 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("%d", serial), "team.p12") + 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 { @@ -35,7 +36,7 @@ func GenerateClient(serial uint64, notBefore time.Time, notAfter time.Time, pare OrganizationalUnit: []string{"SRS laboratory"}, Country: []string{"FR"}, Locality: []string{"Paris"}, - CommonName: fmt.Sprintf("TEAM-%o", serial), + CommonName: fmt.Sprintf("TEAM-%0[2]*[1]X", serial, int(math.Ceil(math.Log2(float64(serial))/8)*2)), }, NotBefore: notBefore, NotAfter: notAfter, @@ -56,7 +57,7 @@ func GenerateClient(serial uint64, notBefore time.Time, notAfter time.Time, pare } // Create intermediate directory - os.MkdirAll(path.Join(PKIDir, fmt.Sprintf("%d", serial)), 0777) + 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 { @@ -75,7 +76,7 @@ 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-%o", 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)) From 0cfa695873a36d9fda0dd75ba40981b8a394c779 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 15 Jan 2020 11:02:27 +0100 Subject: [PATCH 0120/1637] fickit: update kernels --- fickit-backend.yml | 2 +- fickit-boot.yml | 2 +- fickit-frontend.yml | 2 +- fickit-prepare.yml | 3 +-- fickit-update.yml | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/fickit-backend.yml b/fickit-backend.yml index a4c3f8ed..0521ca60 100644 --- a/fickit-backend.yml +++ b/fickit-backend.yml @@ -1,5 +1,5 @@ kernel: - image: nemunaire/kernel:4.9.140-4080ab71159a0b09a0b2ce7d87a7cb7fd719e35d-dirty-amd64 + image: nemunaire/kernel:4.9.210-edfde5a675ea193ae1cadf790b69d67ca1f520c2-dirty-amd64 cmdline: "console=ttyS0 console=tty0" init: diff --git a/fickit-boot.yml b/fickit-boot.yml index dc2233c9..fb60d130 100644 --- a/fickit-boot.yml +++ b/fickit-boot.yml @@ -1,5 +1,5 @@ kernel: - image: nemunaire/kernel:4.9.140-4080ab71159a0b09a0b2ce7d87a7cb7fd719e35d-dirty-amd64 + image: nemunaire/kernel:4.9.210-edfde5a675ea193ae1cadf790b69d67ca1f520c2-dirty-amd64 cmdline: "console=ttyS0 console=tty0" init: diff --git a/fickit-frontend.yml b/fickit-frontend.yml index f0005102..bfc824db 100644 --- a/fickit-frontend.yml +++ b/fickit-frontend.yml @@ -1,5 +1,5 @@ kernel: - image: nemunaire/kernel:4.9.140-4080ab71159a0b09a0b2ce7d87a7cb7fd719e35d-dirty-amd64 + image: nemunaire/kernel:4.9.210-edfde5a675ea193ae1cadf790b69d67ca1f520c2-dirty-amd64 cmdline: "console=ttyS0 console=tty0" init: diff --git a/fickit-prepare.yml b/fickit-prepare.yml index d88efa4c..e99e696b 100644 --- a/fickit-prepare.yml +++ b/fickit-prepare.yml @@ -1,6 +1,5 @@ kernel: -# image: nemunaire/kernel:4.9.140-4080ab71159a0b09a0b2ce7d87a7cb7fd719e35d-amd64 - image: nemunaire/kernel:4.9.77 + image: nemunaire/kernel:4.9.210-edfde5a675ea193ae1cadf790b69d67ca1f520c2-dirty-amd64 cmdline: "console=ttyS0 console=tty0" diff --git a/fickit-update.yml b/fickit-update.yml index 577f090f..265ef2ad 100644 --- a/fickit-update.yml +++ b/fickit-update.yml @@ -1,5 +1,5 @@ kernel: - image: nemunaire/kernel:4.9.140-4080ab71159a0b09a0b2ce7d87a7cb7fd719e35d-dirty-amd64 + image: nemunaire/kernel:4.9.210-edfde5a675ea193ae1cadf790b69d67ca1f520c2-dirty-amd64 cmdline: "console=ttyS0 console=tty0" From a8b6e9fba55bb852e32861c4d7d1c076aaca1e2e Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 15 Jan 2020 11:09:36 +0100 Subject: [PATCH 0121/1637] fickit: update images --- fickit-backend.yml | 42 +++++++++++++++++++++--------------------- fickit-frontend.yml | 44 ++++++++++++++++++++++---------------------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/fickit-backend.yml b/fickit-backend.yml index 0521ca60..18bb8b1e 100644 --- a/fickit-backend.yml +++ b/fickit-backend.yml @@ -3,39 +3,39 @@ kernel: cmdline: "console=ttyS0 console=tty0" init: - - linuxkit/init:c563953a2277eb73a89d89f70e4b6dcdcfebc2d1 - - linuxkit/runc:83d0edb4552b1a5df1f0976f05f442829eac38fe - - linuxkit/containerd:326b096cd5fbab0f864e52721d036cade67599d6 - - linuxkit/ca-certificates:v0.6 - - linuxkit/getty:2eb742cd7a68e14cf50577c02f30147bc406e478 + - linuxkit/init:1d8e0532ca588c5ad0d9ca6038349a70bb7ac626 + - linuxkit/runc:c1f0db27e71d948f3134b31ce76276f843849b0a + - linuxkit/containerd:751de142273e1b5d2d247d2832d654ab92e907bc + - linuxkit/ca-certificates:v0.7 + - linuxkit/getty:v0.7 - nemunaire/mdadm:18de5ca414227f38a5c0619662077ba5fa26176d onboot: - - name: mod - image: linuxkit/modprobe:v0.6 - command: ["/bin/sh", "-c", "modprobe e1000e"] +# - name: mod +# image: linuxkit/modprobe:v0.7 +# command: ["/bin/sh", "-c", "modprobe e1000e"] - name: sysctl - image: linuxkit/sysctl:v0.6 + image: linuxkit/sysctl:v0.7 binds: - /etc/sysctl.d/01-fic.conf:/etc/sysctl.d/01-fic.conf:ro # Filesystem - name: swap - image: linuxkit/swap:v0.6 + image: linuxkit/swap:v0.7 command: ["/sbin/swapon", "/dev/sda2", "/dev/sdb2"] - name: mount - image: linuxkit/mount:v0.6 + image: linuxkit/mount:v0.7 command: ["/usr/bin/mountie", "-device", "/dev/md0", "/var/lib/fic" ] # Network # - name: dhcpcd -# image: linuxkit/dhcpcd:0d59a6cc03412289ef4313f2491ec666c1715cc9 +# image: linuxkit/dhcpcd:v0.7 # command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"] # - name: ntp # image: linuxkit/openntpd:536e5947607c9e6a6771957c2ff817230cba0d3c - name: synchro-ip-setup - image: linuxkit/ip:v0.6 + image: linuxkit/ip:v0.7 command: ["/bin/sh", "-c", "ip a add 10.10.10.1/29 dev eth0; ip link set eth0 up;" ] net: new runtime: @@ -44,7 +44,7 @@ onboot: bindNS: net: /run/netns/synchro - name: admin-ip-setup - image: linuxkit/ip:v0.6 + image: linuxkit/ip:v0.7 command: ["/bin/sh", "-c", "ip a add 192.168.23.1/24 dev eth1; ip link set eth1 up; ip a add 172.17.0.2/24 dev vethin-admin; ip link set vethin-admin up;" ] net: new runtime: @@ -56,7 +56,7 @@ onboot: bindNS: net: /run/netns/fic-admin - name: backend-ip-setup - image: linuxkit/ip:v0.6 + image: linuxkit/ip:v0.7 command: ["/bin/sh", "-c", "ip a add 172.17.0.3/24 dev vethin-backend; ip link set vethin-backend up;" ] net: new runtime: @@ -67,7 +67,7 @@ onboot: bindNS: net: /run/netns/fic-backend - name: mysql-ip-setup - image: linuxkit/ip:v0.6 + image: linuxkit/ip:v0.7 command: ["/bin/sh", "-c", "ip a add 172.17.0.4/24 dev vethin-db; ip link set vethin-db up;" ] net: new runtime: @@ -78,7 +78,7 @@ onboot: bindNS: net: /run/netns/db - name: bridge-setup - image: linuxkit/ip:v0.6 + image: linuxkit/ip:v0.7 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-backend master br0; ip link set veth-db master br0; ip link set br0 up; ip link set veth-admin up; ip link set veth-backend up; ip link set veth-db up;" ] runtime: interfaces: @@ -86,7 +86,7 @@ onboot: add: bridge - name: firewall-synchro - image: linuxkit/ip:v0.6 + image: linuxkit/ip:v0.7 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 @@ -96,7 +96,7 @@ onboot: mkdir: - /var/lib/fic/teams - name: firewall-admin - image: linuxkit/ip:v0.6 + image: linuxkit/ip:v0.7 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 @@ -105,11 +105,11 @@ onboot: services: # - name: getty -# image: linuxkit/getty:2eb742cd7a68e14cf50577c02f30147bc406e478 +# image: linuxkit/getty:v0.7 # env: # - INSECURE=true - name: rngd - image: linuxkit/rngd:v0.6 + image: linuxkit/rngd:v0.7 - name: db image: mariadb:latest command: ["/bin/bash", "/usr/local/bin/docker-entrypoint.sh", "mysqld"] diff --git a/fickit-frontend.yml b/fickit-frontend.yml index bfc824db..f982a066 100644 --- a/fickit-frontend.yml +++ b/fickit-frontend.yml @@ -3,36 +3,36 @@ kernel: cmdline: "console=ttyS0 console=tty0" init: - - linuxkit/init:c563953a2277eb73a89d89f70e4b6dcdcfebc2d1 - - linuxkit/runc:83d0edb4552b1a5df1f0976f05f442829eac38fe - - linuxkit/containerd:326b096cd5fbab0f864e52721d036cade67599d6 - - linuxkit/ca-certificates:v0.6 - - linuxkit/getty:2eb742cd7a68e14cf50577c02f30147bc406e478 + - linuxkit/init:1d8e0532ca588c5ad0d9ca6038349a70bb7ac626 + - linuxkit/runc:c1f0db27e71d948f3134b31ce76276f843849b0a + - linuxkit/containerd:751de142273e1b5d2d247d2832d654ab92e907bc + - linuxkit/ca-certificates:v0.7 + - linuxkit/getty:v0.7 - nemunaire/mdadm:18de5ca414227f38a5c0619662077ba5fa26176d onboot: - - name: mod - image: linuxkit/modprobe:v0.6 - command: ["/bin/sh", "-c", "modprobe r8169;"] +# - name: mod +# image: linuxkit/modprobe:v0.7 +# command: ["/bin/sh", "-c", "modprobe r8169;"] - name: sysctl - image: linuxkit/sysctl:v0.6 + image: linuxkit/sysctl:v0.7 binds: - /etc/sysctl.d/01-fic.conf:/etc/sysctl.d/01-fic.conf:ro # Filesystem - name: swap - image: linuxkit/swap:v0.6 + image: linuxkit/swap:v0.7 command: ["/sbin/swapon", "/dev/sda2", "/dev/sdb2"] - name: mount - image: linuxkit/mount:v0.6 + image: linuxkit/mount:v0.7 command: ["/usr/bin/mountie", "-device", "/dev/md0", "/var/lib/fic" ] # Network # - name: ntp # image: linuxkit/openntpd:536e5947607c9e6a6771957c2ff817230cba0d3c - name: nginx-ip-setup - image: linuxkit/ip:v0.6 + image: linuxkit/ip:v0.7 command: ["/bin/sh", "-c", "ip a add 172.17.1.2/24 dev vethin-nginx; ip link set vethin-nginx up;" ] net: new runtime: @@ -43,7 +43,7 @@ onboot: bindNS: net: /run/netns/nginx # - name: frontal-ip-setup # without bonding -# image: linuxkit/ip:v0.6 +# image: linuxkit/ip:v0.7 # command: ["/bin/sh", "-c", "ip link set eth1 up; ip a add 172.23.42.1/24 dev eth1; ip a add 172.23.42.254/24 dev eth1; ip a add 163.5.55.58/32 dev eth1; ip link add link eth1 name internet type vlan id 1; ip link set internet up;" ] # net: /run/netns/nginx # runtime: @@ -53,7 +53,7 @@ onboot: # - name: eth3 # - name: eth4 - name: frontal-ip-setup # with bonding - image: linuxkit/ip:v0.6 + image: linuxkit/ip:v0.7 command: ["/bin/sh", "-c", "ip link set bond-frontal up; ifenslave bond-frontal eth1 eth2 eth3 eth4; ip a add 172.23.42.1/24 dev bond-frontal; ip a add 163.5.55.58/32 dev bond-frontal; ip link add link bond-frontal name internet type vlan id 1; ip link set internet up;" ] net: /run/netns/nginx runtime: @@ -65,7 +65,7 @@ onboot: - name: bond-frontal add: bond - name: frontend-ip-setup - image: linuxkit/ip:v0.6 + image: linuxkit/ip:v0.7 command: ["/bin/sh", "-c", "ip a add 172.17.1.3/24 dev vethin-frontend; ip link set vethin-frontend up;" ] net: new runtime: @@ -76,7 +76,7 @@ onboot: bindNS: net: /run/netns/fic-frontend - name: sshd-ip-setup - image: linuxkit/ip:v0.6 + image: linuxkit/ip:v0.7 command: ["/bin/sh", "-c", "ip a add 10.10.10.2/29 dev eth0; ip link set eth0 up;" ] net: new runtime: @@ -85,7 +85,7 @@ onboot: bindNS: net: /run/netns/sshd - name: bridge-setup - image: linuxkit/ip:v0.6 + image: linuxkit/ip:v0.7 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-frontend master br0; ip link set br0 up; ip link set veth-nginx up; ip link set veth-frontend up;" ] runtime: interfaces: @@ -93,14 +93,14 @@ onboot: add: bridge - name: firewall-frontal - image: linuxkit/ip:v0.6 + image: linuxkit/ip:v0.7 command: ["/bin/bash", "-c", "/sbin/iptables-restore < /etc/iptables/rules-frontal.v4; /sbin/ip6tables-restore < /etc/iptables/rules.v6" ] binds: - /etc/iptables/rules-frontal.v4:/etc/iptables/rules-frontal.v4:ro - /etc/iptables/rules.v6:/etc/iptables/rules.v6:ro net: /run/netns/nginx - name: firewall-sshd - image: linuxkit/ip:v0.6 + image: linuxkit/ip:v0.7 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 @@ -109,13 +109,13 @@ onboot: services: # - name: getty -# image: linuxkit/getty:2eb742cd7a68e14cf50577c02f30147bc406e478 +# image: linuxkit/getty:v0.7 # env: # - INSECURE=true - name: rngd - image: linuxkit/rngd:v0.6 + image: linuxkit/rngd:v0.7 - name: dhcpcd - image: linuxkit/dhcpcd:v0.6 + image: linuxkit/dhcpcd:v0.7 net: /run/netns/nginx binds: - /etc/dhcpcd.conf:/dhcpcd.conf:ro From 8f998485bbe9cc9de6fc9783ed9fc324cdd12b63 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 16 Jan 2020 14:57:13 +0100 Subject: [PATCH 0122/1637] sync: resize heading pictures --- admin/sync/themes.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/admin/sync/themes.go b/admin/sync/themes.go index f3b9fb7f..a9b5366e 100644 --- a/admin/sync/themes.go +++ b/admin/sync/themes.go @@ -3,13 +3,17 @@ package sync import ( "errors" "fmt" + "image" + "image/jpeg" "math/rand" + "os" "path" "regexp" "strings" "unicode" "github.com/julienschmidt/httprouter" + "golang.org/x/image/draw" "gopkg.in/russross/blackfriday.v2" "srs.epita.fr/fic-server/libfic" ) @@ -31,6 +35,42 @@ func GetThemes(i Importer) ([]string, error) { return themes, nil } +// resizePicture makes the given image just fill the given rectangle. +func resizePicture(importedPath string, rect image.Rectangle) error { + if fl, err := os.Open(importedPath); err != nil { + return err + } else { + if src, _, err := image.Decode(fl); err != nil { + fl.Close() + return err + } else if src.Bounds().Max.X > rect.Max.X && src.Bounds().Max.Y > rect.Max.Y { + fl.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 := os.Create(importedPath) + if err != nil { + return err + } + defer dstFile.Close() + + if err = jpeg.Encode(dstFile, dst, &jpeg.Options{100}); err != nil { + return err + } + } + } + return 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 { @@ -126,6 +166,10 @@ func SyncThemes(i Importer) []string { if len(btheme.Image) > 0 { if _, err := i.importFile(btheme.Image, func(filePath string, origin string) (interface{}, error) { + if err := resizePicture(filePath, image.Rect(0, 0, 500, 300)); err != nil { + return nil, err + } + btheme.Image = strings.TrimPrefix(filePath, fic.FilesDir) return nil, nil }); err != nil { From 47ba134b55d019599c8078eb4006479d1008f690 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 16 Jan 2020 15:33:28 +0100 Subject: [PATCH 0123/1637] Implement flag type 'text': this is like keys, but on multiple lines --- admin/api/exercice.go | 3 ++- admin/static/views/exercice.html | 4 ++++ admin/sync/exercice_keys.go | 9 ++++++--- frontend/static/js/challenge.js | 3 ++- frontend/submissions.go | 2 +- libfic/db.go | 1 + libfic/file.go | 2 +- libfic/flag_key.go | 17 ++++++++++------- libfic/team_my.go | 3 +++ 9 files changed, 30 insertions(+), 14 deletions(-) diff --git a/admin/api/exercice.go b/admin/api/exercice.go index a1a24b03..0e85a77e 100644 --- a/admin/api/exercice.go +++ b/admin/api/exercice.go @@ -325,6 +325,7 @@ type uploadedFlag struct { Label string Help string IgnoreCase bool + Multiline bool ValidatorRe *string `json:"validator_regexp"` Flag string Value []byte @@ -346,7 +347,7 @@ func createExerciceFlag(exercice fic.Exercice, body []byte) (interface{}, error) vre = uk.ValidatorRe } - return exercice.AddRawFlagKey(uk.Label, uk.Help, uk.IgnoreCase, vre, []byte(uk.Flag), uk.ChoicesCost) + return exercice.AddRawFlagKey(uk.Label, uk.Help, uk.IgnoreCase, uk.Multiline, vre, []byte(uk.Flag), uk.ChoicesCost) } func showExerciceFlag(flag fic.FlagKey, _ fic.Exercice, body []byte) (interface{}, error) { diff --git a/admin/static/views/exercice.html b/admin/static/views/exercice.html index 52cef198..d369d615 100644 --- a/admin/static/views/exercice.html +++ b/admin/static/views/exercice.html @@ -146,6 +146,10 @@
+
+ + +
diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index c2f2c4b3..2090b2d9 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -116,7 +116,7 @@ func buildKeyFlag(exercice fic.Exercice, flag ExerciceFlag, flagline int, defaul } flag.Label = prep + flag.Label - if !isFullGraphic(raw) { + if (flag.Type == "text" && !isFullGraphic(strings.Replace(raw, "\n", "", -1))) || (flag.Type != "text" && !isFullGraphic(raw)) { errs = append(errs, fmt.Sprintf("%q: WARNING flag #%d: non-printable characters in flag, is this really expected?", path.Base(exercice.Path), flagline)) } @@ -130,6 +130,7 @@ func buildKeyFlag(exercice fic.Exercice, flag ExerciceFlag, flagline int, defaul Label: flag.Label, Help: flag.Help, IgnoreCase: !flag.CaseSensitive, + Multiline: flag.Type == "text", ValidatorRegexp: validatorRegexp(flag.ValidatorRe), Checksum: hashedFlag[:], ChoicesCost: flag.ChoicesCost, @@ -191,6 +192,8 @@ func buildExerciceFlag(i Importer, exercice fic.Exercice, flag ExerciceFlag, nli flag.Type = "key" case "key": flag.Type = "key" + case "text": + flag.Type = "text" case "vector": flag.Type = "vector" case "ucq": @@ -198,11 +201,11 @@ func buildExerciceFlag(i Importer, exercice fic.Exercice, flag ExerciceFlag, nli case "mcq": flag.Type = "mcq" default: - errs = append(errs, fmt.Sprintf("%q: flag #%d: invalid type of flag: should be 'key', 'mcq', 'ucq' or 'vector'.", path.Base(exercice.Path), nline+1)) + errs = append(errs, fmt.Sprintf("%q: flag #%d: invalid type of flag: should be 'key', 'text', 'mcq', 'ucq' or 'vector'.", path.Base(exercice.Path), nline+1)) return } - if flag.Type == "key" || flag.Type == "ucq" || flag.Type == "vector" { + if flag.Type == "key" || flag.Type == "text" || flag.Type == "ucq" || flag.Type == "vector" { addedFlag, choices, berrs := buildKeyFlag(exercice, flag, nline+1, "Flag") if len(berrs) > 0 { errs = append(errs, berrs...) diff --git a/frontend/static/js/challenge.js b/frontend/static/js/challenge.js index b0ccbed5..614cde60 100644 --- a/frontend/static/js/challenge.js +++ b/frontend/static/js/challenge.js @@ -125,7 +125,8 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"])
- + +
From 1833a7550d190c6559d394734a594e054e60e623 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 16 Jan 2020 17:15:34 +0100 Subject: [PATCH 0127/1637] frontend: hardcode special social engineering challenge --- fickit-frontend.yml | 3 + frontend/static/js/challenge.js | 7 +- frontend/static/views/defi-SE.html | 117 +++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 frontend/static/views/defi-SE.html diff --git a/fickit-frontend.yml b/fickit-frontend.yml index f982a066..79ebcbcd 100644 --- a/fickit-frontend.yml +++ b/fickit-frontend.yml @@ -381,6 +381,9 @@ files: - path: www/htdocs-frontend/views/defi.html source: frontend/static/views/defi.html mode: "0644" + - path: www/htdocs-frontend/views/defi-SE.html + source: frontend/static/views/defi-SE.html + mode: "0644" - path: www/htdocs-frontend/views/home.html source: frontend/static/views/home.html mode: "0644" diff --git a/frontend/static/js/challenge.js b/frontend/static/js/challenge.js index 614cde60..4939546b 100644 --- a/frontend/static/js/challenge.js +++ b/frontend/static/js/challenge.js @@ -79,7 +79,12 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) }) .when("/:theme/:exercice", { controller: "ExerciceController", - templateUrl: "views/defi.html" + templateUrl: function(e) { + if (e.theme == "BlueMoney" && e.exercice == "JackSpearrow") + return "views/defi-SE.html" + else + return "views/defi.html"; + } }) .when("/", { controller: "HomeController", diff --git a/frontend/static/views/defi-SE.html b/frontend/static/views/defi-SE.html new file mode 100644 index 00000000..b090e167 --- /dev/null +++ b/frontend/static/views/defi-SE.html @@ -0,0 +1,117 @@ + + +
+ Vous n'avez pas encore accès à cet exercice. +
+
+

+

+
+
+

{{ themes[current_theme].exercices[current_exercice].title }}

+ #{{ tag }} +

+
+
+
+
+
    +
  • Gain : {{ 1 + settings.firstBlood | coeff }} prem's {{ themes[current_theme].exercices[current_exercice].curcoeff * settings.exerciceCurrentCoefficient | coeff }} bonus
  • +
  • Tenté par : (cumulant )
  • +
  • Résolu par :
  • +
+
+ +
+
+
+
+ NSEC – Réinitialisation de mot de passe +
+
    +
  • . Dernière tentative envoyée à {{ my.exercices[current_exercice].solved_time | date:"mediumTime" }}. .
  • +
  • Votre demande de réinitialisation de mot de passe a bien été envoyée !{{ sberr }} {{ message }}
  • +
  • Oops La requête a dépassé le délai d'attente. Vous devriez réessayer dans quelques instant…
  • +
+
+
+ + +
+

{{ mcq.title }}

+
+
+ + + + + +
+
+
+
+ +
+ +
+
+
+
+ +
+
+ NSEC – Réinitialisation de mot de passe +
+
+

+
+

+ Vous êtes la {{ my.exercices[current_exercice].solved_rank }} équipe à avoir résolu ce défi à {{ my.exercices[current_exercice].solved_time | date:"mediumTime" }}. Vous avez marqué ! +

+

+ Bravo, vous avez résolu ce défi à {{ my.exercices[current_exercice].solved_time | date:"mediumTime" }}. Vous marquez ! +

+
+ Se connecter +
+
+ +
+
+ Solution du défi +
+
+
+ +
+
+
+ +
+
+
+
From 16c337c2bcda231d0e6a9b5eb3aff1a332190026 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 16 Jan 2020 17:35:39 +0100 Subject: [PATCH 0128/1637] Update angularJS, jQuery and bootstrap --- admin/static/js/angular-resource.min.js | 4 +- admin/static/js/popper.min.js | 4 +- dashboard/static/js/angular-animate.min.js | 108 ++-- dashboard/static/js/angular-resource.min.js | 22 +- frontend/static/css/bootstrap.min.css | 12 +- frontend/static/js/angular-route.min.js | 4 +- frontend/static/js/angular-sanitize.min.js | 4 +- frontend/static/js/angular.min.js | 679 ++++++++++---------- frontend/static/js/jquery.min.js | 6 +- 9 files changed, 423 insertions(+), 420 deletions(-) diff --git a/admin/static/js/angular-resource.min.js b/admin/static/js/angular-resource.min.js index 9b9de6e0..8b924c37 100644 --- a/admin/static/js/angular-resource.min.js +++ b/admin/static/js/angular-resource.min.js @@ -1,9 +1,9 @@ /* - AngularJS v1.7.5 + 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.5"}).provider("$resource",function(){var m=/^https?:\/\/\[[^\]]*][^/]*/,f=this;this.defaults={stripTrailingSlashes:!0,cancellable:!1,actions:{get:{method:"GET"},save:{method:"POST"},query:{method:"GET", +(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, diff --git a/admin/static/js/popper.min.js b/admin/static/js/popper.min.js index 79ccbf58..8a17212f 100644 --- a/admin/static/js/popper.min.js +++ b/admin/static/js/popper.min.js @@ -1,5 +1,5 @@ /* - Copyright (C) Federico Zivolo 2018 + Copyright (C) Federico Zivolo 2019 Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT). - */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=getComputedStyle(e,null);return t?o[t]:o}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll|overlay)/.test(r+s+p)?e:n(o(e))}function r(e){return 11===e?re:10===e?pe:re||pe}function p(e){if(!e)return document.documentElement;for(var o=r(10)?document.body:null,n=e.offsetParent;n===o&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TD','TABLE'].indexOf(n.nodeName)&&'static'===t(n,'position')?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function s(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||p(e.firstElementChild)===e)}function d(e){return null===e.parentNode?e:d(e.parentNode)}function a(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,n=o?e:t,i=o?t:e,r=document.createRange();r.setStart(n,0),r.setEnd(i,0);var l=r.commonAncestorContainer;if(e!==l&&t!==l||n.contains(i))return s(l)?l:p(l);var f=d(e);return f.host?a(f.host,t):a(e,d(t).host)}function l(e){var t=1=o.clientWidth&&n>=o.clientHeight}),l=0a[e]&&!t.escapeWithReference&&(n=J(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(!q(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,y=t(e.instance.popper),w=parseFloat(y['margin'+f],10),E=parseFloat(y['border'+f+'Width'],10),v=b-e.offsets.popper[m]-w-E;return v=$(J(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},ae(n,m,Q(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 he.FLIP:p=[n,i];break;case he.CLOCKWISE:p=z(n);break;case he.COUNTERCLOCKWISE:p=z(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,y=-1!==['top','bottom'].indexOf(n),w=!!t.flipVariations&&(y&&'start'===r&&h||y&&'end'===r&&c||!y&&'start'===r&&g||!y&&'end'===r&&u);(m||b||w)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),w&&(r=G(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'},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(!q(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.right=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],10),E=parseFloat(w['border'+f+'Width'],10),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/dashboard/static/js/angular-animate.min.js b/dashboard/static/js/angular-animate.min.js index 8b0935ca..96dcfb63 100644 --- a/dashboard/static/js/angular-animate.min.js +++ b/dashboard/static/js/angular-animate.min.js @@ -1,57 +1,59 @@ /* - AngularJS v1.6.8 - (c) 2010-2017 Google, Inc. http://angularjs.org + AngularJS v1.7.9 + (c) 2010-2018 Google, Inc. http://angularjs.org License: MIT */ -(function(S,q){'use strict';function Ea(a,b,c){if(!a)throw Pa("areq",b||"?",c||"required");return a}function Fa(a,b){if(!a&&!b)return"";if(!a)return b;if(!b)return a;V(a)&&(a=a.join(" "));V(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 W(a,b,c){var d="";a=V(a)?a:a&&C(a)&&a.length?a.split(/\s+/):[];t(a,function(a,f){a&&0=a&&(a=g,g=0,b.push(e),e=[]);e.push(f.fn);f.children.forEach(function(a){g++;c.push(a)});a--}e.length&&b.push(e);return b}(c)}var s=[],y=X(a);return function(n,q,v){function E(a){a=a.hasAttribute("ng-animate-ref")? -[a]:a.querySelectorAll("[ng-animate-ref]");var b=[];t(a,function(a){var c=a.getAttribute("ng-animate-ref");c&&c.length&&b.push(a)});return b}function g(a){var b=[],c={};t(a,function(a,d){var k=J(a.element),g=0<=["enter","move"].indexOf(a.event),k=a.structural?E(k):[];if(k.length){var e=g?"to":"from";t(k,function(a){var b=a.getAttribute("ng-animate-ref");c[b]=c[b]||{};c[b][e]={animationID:d,element:A(a)}})}else b.push(a)});var d={},g={};t(c,function(c,e){var f=c.from,p=c.to;if(f&&p){var H=a[f.animationID], -z=a[p.animationID],m=f.animationID.toString();if(!g[m]){var l=g[m]={structural:!0,beforeStart:function(){H.beforeStart();z.beforeStart()},close:function(){H.close();z.close()},classes:M(H.classes,z.classes),from:H,to:z,anchors:[]};l.classes.length?b.push(l):(b.push(H),b.push(z))}g[m].anchors.push({out:f.element,"in":p.element})}else f=f?f.animationID:p.animationID,p=f.toString(),d[p]||(d[p]=!0,b.push(a[f]))});return b}function M(a,b){a=a.split(" ");b=b.split(" ");for(var c=[],d=0;d=P&&b>=N&&(ba=!0,m()))}function ga(){function b(){if(!M){L(!1);t(x,function(a){k.style[a[0]]=a[1]});g(a,h); -e.addClass(a,ca);if(r.recalculateTimingStyles){ma=k.getAttribute("class")+" "+fa;ja=q(k,ma);B=E(k,ma,ja);$=B.maxDelay;w=Math.max($,0);N=B.maxDuration;if(0===N){m();return}r.hasTransitions=0s.expectedEndTime)?n.cancel(s.timer):f.push(m)}F&&(l=n(c,l,!1),f[0]={timer:l,expectedEndTime:d},f.push(m),a.data("$$animateCss",f));if(ea.length)a.on(ea.join(" "),z);h.to&&(h.cleanupStyles&&Ma(p,k,Object.keys(h.to)),Ia(a,h))}}function c(){var b= -a.data("$$animateCss");if(b){for(var d=1;d=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;ccode{color:inherit}kbd{padding:0.2rem 0.4rem;font-size:87.5%;color:#fff;background-color:#272B30;border-radius:0.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:inherit}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width: 576px){.container{max-width:540px}}@media (min-width: 768px){.container{max-width:720px}}@media (min-width: 992px){.container{max-width:960px}}@media (min-width: 1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-webkit-box;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-1,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-10,.col-11,.col-12,.col,.col-auto,.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,.col-sm,.col-sm-auto,.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,.col-md,.col-md-auto,.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,.col-lg,.col-lg-auto,.col-xl-1,.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-10,.col-xl-11,.col-xl-12,.col-xl,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-1{margin-left:8.3333333333%}.offset-2{margin-left:16.6666666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.3333333333%}.offset-5{margin-left:41.6666666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.3333333333%}.offset-8{margin-left:66.6666666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.3333333333%}.offset-11{margin-left:91.6666666667%}@media (min-width: 576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-sm-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-sm-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-sm-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-sm-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-sm-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-sm-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-sm-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-sm-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-sm-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-sm-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-sm-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-sm-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-sm-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-sm-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-sm-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-sm-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-sm-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-sm-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-sm-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-sm-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-sm-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-sm-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.3333333333%}.offset-sm-2{margin-left:16.6666666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.3333333333%}.offset-sm-5{margin-left:41.6666666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.3333333333%}.offset-sm-8{margin-left:66.6666666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.3333333333%}.offset-sm-11{margin-left:91.6666666667%}}@media (min-width: 768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-md-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-md-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-md-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-md-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-md-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-md-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-md-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-md-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-md-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-md-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-md-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-md-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-md-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-md-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-md-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-md-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-md-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-md-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-md-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-md-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-md-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-md-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.3333333333%}.offset-md-2{margin-left:16.6666666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.3333333333%}.offset-md-5{margin-left:41.6666666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.3333333333%}.offset-md-8{margin-left:66.6666666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.3333333333%}.offset-md-11{margin-left:91.6666666667%}}@media (min-width: 992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-lg-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-lg-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-lg-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-lg-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-lg-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-lg-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-lg-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-lg-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-lg-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-lg-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-lg-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-lg-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-lg-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-lg-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-lg-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-lg-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-lg-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-lg-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-lg-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-lg-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-lg-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-lg-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.3333333333%}.offset-lg-2{margin-left:16.6666666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.3333333333%}.offset-lg-5{margin-left:41.6666666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.3333333333%}.offset-lg-8{margin-left:66.6666666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.3333333333%}.offset-lg-11{margin-left:91.6666666667%}}@media (min-width: 1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-xl-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-xl-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-xl-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-xl-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-xl-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-xl-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-xl-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-xl-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-xl-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-xl-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-xl-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-xl-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-xl-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-xl-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-xl-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-xl-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-xl-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-xl-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-xl-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-xl-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-xl-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-xl-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.3333333333%}.offset-xl-2{margin-left:16.6666666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.3333333333%}.offset-xl-5{margin-left:41.6666666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.3333333333%}.offset-xl-8{margin-left:66.6666666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.3333333333%}.offset-xl-11{margin-left:91.6666666667%}}.table{width:100%;margin-bottom:1rem;background-color:transparent}.table th,.table td{padding:0.75rem;vertical-align:top;border-top:1px solid rgba(0,0,0,0.6)}.table thead th{vertical-align:bottom;border-bottom:2px solid rgba(0,0,0,0.6)}.table tbody+tbody{border-top:2px solid rgba(0,0,0,0.6)}.table .table{background-color:#272B30}.table-sm th,.table-sm td{padding:0.3rem}.table-bordered{border:1px solid rgba(0,0,0,0.6)}.table-bordered th,.table-bordered td{border:1px solid rgba(0,0,0,0.6)}.table-bordered thead th,.table-bordered thead td{border-bottom-width:2px}.table-borderless th,.table-borderless td,.table-borderless thead th,.table-borderless tbody+tbody{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,0.05)}.table-hover tbody tr:hover{background-color:rgba(255,255,255,0.075)}.table-primary,.table-primary>th,.table-primary>td{background-color:#c8c9cb}.table-primary th,.table-primary td,.table-primary thead th,.table-primary tbody+tbody{border-color:#999b9e}.table-hover .table-primary:hover{background-color:#bbbcbf}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#bbbcbf}.table-secondary,.table-secondary>th,.table-secondary>td{background-color:#dadcde}.table-secondary th,.table-secondary td,.table-secondary thead th,.table-secondary tbody+tbody{border-color:#babec1}.table-hover .table-secondary:hover{background-color:#cdcfd2}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#cdcfd2}.table-success,.table-success>th,.table-success>td{background-color:#d3eed3}.table-success th,.table-success td,.table-success thead th,.table-success tbody+tbody{border-color:#ade0ad}.table-hover .table-success:hover{background-color:#c1e7c1}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#c1e7c1}.table-info,.table-info>th,.table-info>td{background-color:#d1edf6}.table-info th,.table-info td,.table-info thead th,.table-info tbody+tbody{border-color:#aadeee}.table-hover .table-info:hover{background-color:#bce5f2}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#bce5f2}.table-warning,.table-warning>th,.table-warning>td{background-color:#fde1b9}.table-warning th,.table-warning td,.table-warning thead th,.table-warning tbody+tbody{border-color:#fbc77e}.table-hover .table-warning:hover{background-color:#fcd6a0}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#fcd6a0}.table-danger,.table-danger>th,.table-danger>td{background-color:#fad2d1}.table-danger th,.table-danger td,.table-danger thead th,.table-danger tbody+tbody{border-color:#f6acaa}.table-hover .table-danger:hover{background-color:#f8bcba}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f8bcba}.table-light,.table-light>th,.table-light>td{background-color:#f9fafb}.table-light th,.table-light td,.table-light thead th,.table-light tbody+tbody{border-color:#f4f5f7}.table-hover .table-light:hover{background-color:#eaedf1}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#eaedf1}.table-dark,.table-dark>th,.table-dark>td{background-color:#c3c4c5}.table-dark th,.table-dark td,.table-dark thead th,.table-dark tbody+tbody{border-color:#8f9193}.table-hover .table-dark:hover{background-color:#b6b7b8}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b6b7b8}.table-active,.table-active>th,.table-active>td{background-color:rgba(255,255,255,0.075)}.table-hover .table-active:hover{background-color:rgba(242,242,242,0.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(242,242,242,0.075)}.table .thead-dark th{color:#fff;background-color:#272B30;border-color:rgba(0,0,0,0.6)}.table .thead-light th{color:#52575C;background-color:#e9ecef;border-color:rgba(0,0,0,0.6)}.table-dark{color:#fff;background-color:#272B30}.table-dark th,.table-dark td,.table-dark thead th{border-color:rgba(0,0,0,0.6)}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,0.05)}.table-dark.table-hover tbody tr:hover{background-color:rgba(255,255,255,0.075)}@media (max-width: 575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.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;-ms-overflow-style:-ms-autohiding-scrollbar}.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;-ms-overflow-style:-ms-autohiding-scrollbar}.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;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(2.90625rem + 2px);padding:0.75rem 1rem;font-size:0.9375rem;font-weight:400;line-height:1.5;color:#52575C;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:0.25rem;-webkit-transition:border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out}@media screen and (prefers-reduced-motion: reduce){.form-control{-webkit-transition:none;transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#52575C;background-color:#fff;border-color:#757f89;outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25)}.form-control::-webkit-input-placeholder{color:#7A8288;opacity:1}.form-control::-ms-input-placeholder{color:#7A8288;opacity:1}.form-control::placeholder{color:#7A8288;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#ccc;opacity:1}select.form-control:focus::-ms-value{color:#52575C;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(0.75rem + 1px);padding-bottom:calc(0.75rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(0.5rem + 1px);padding-bottom:calc(0.5rem + 1px);font-size:1.171875rem;line-height:1.5}.col-form-label-sm{padding-top:calc(0.25rem + 1px);padding-bottom:calc(0.25rem + 1px);font-size:0.8203125rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:0.75rem;padding-bottom:0.75rem;margin-bottom:0;line-height:1.5;color:#aaa;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-sm,.form-control-plaintext.form-control-lg{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.73046875rem + 2px);padding:0.25rem 0.5rem;font-size:0.8203125rem;line-height:1.5;border-radius:0.2rem}.form-control-lg{height:calc(2.7578125rem + 2px);padding:0.5rem 1rem;font-size:1.171875rem;line-height:1.5;border-radius:0.3rem}select.form-control[size],select.form-control[multiple]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:0.25rem}.form-row{display:-webkit-box;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:0.3rem;margin-left:-1.25rem}.form-check-input:disabled ~ .form-check-label{color:#7A8288}.form-check-label{margin-bottom:0}.form-check-inline{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:0.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:0.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:0.25rem;font-size:80%;color:#62c462}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:0.25rem 0.5rem;margin-top:.1rem;font-size:0.8203125rem;line-height:1.5;color:#272B30;background-color:rgba(98,196,98,0.9);border-radius:0.25rem}.was-validated .form-control:valid,.form-control.is-valid{border-color:#62c462;padding-right:2.90625rem;background-repeat:no-repeat;background-position:center right calc(2.90625rem / 4);background-size:calc(2.90625rem / 2) calc(2.90625rem / 2);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2362c462' 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")}.was-validated .form-control:valid:focus,.form-control.is-valid:focus{border-color:#62c462;-webkit-box-shadow:0 0 0 0.2rem rgba(98,196,98,0.25);box-shadow:0 0 0 0.2rem rgba(98,196,98,0.25)}.was-validated .form-control:valid ~ .valid-feedback,.was-validated .form-control:valid ~ .valid-tooltip,.form-control.is-valid ~ .valid-feedback,.form-control.is-valid ~ .valid-tooltip{display:block}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:2.90625rem;background-position:top calc(2.90625rem / 4) right calc(2.90625rem / 4)}.was-validated .custom-select:valid,.custom-select.is-valid{border-color:#62c462;padding-right:4.1796875rem;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%233A3F44' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 1rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2362c462' 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") no-repeat center right 2rem/1.453125rem 1.453125rem}.was-validated .custom-select:valid:focus,.custom-select.is-valid:focus{border-color:#62c462;-webkit-box-shadow:0 0 0 0.2rem rgba(98,196,98,0.25);box-shadow:0 0 0 0.2rem rgba(98,196,98,0.25)}.was-validated .custom-select:valid ~ .valid-feedback,.was-validated .custom-select:valid ~ .valid-tooltip,.custom-select.is-valid ~ .valid-feedback,.custom-select.is-valid ~ .valid-tooltip{display:block}.was-validated .form-control-file:valid ~ .valid-feedback,.was-validated .form-control-file:valid ~ .valid-tooltip,.form-control-file.is-valid ~ .valid-feedback,.form-control-file.is-valid ~ .valid-tooltip{display:block}.was-validated .form-check-input:valid ~ .form-check-label,.form-check-input.is-valid ~ .form-check-label{color:#62c462}.was-validated .form-check-input:valid ~ .valid-feedback,.was-validated .form-check-input:valid ~ .valid-tooltip,.form-check-input.is-valid ~ .valid-feedback,.form-check-input.is-valid ~ .valid-tooltip{display:block}.was-validated .custom-control-input:valid ~ .custom-control-label,.custom-control-input.is-valid ~ .custom-control-label{color:#62c462}.was-validated .custom-control-input:valid ~ .custom-control-label::before,.custom-control-input.is-valid ~ .custom-control-label::before{border-color:#62c462}.was-validated .custom-control-input:valid ~ .valid-feedback,.was-validated .custom-control-input:valid ~ .valid-tooltip,.custom-control-input.is-valid ~ .valid-feedback,.custom-control-input.is-valid ~ .valid-tooltip{display:block}.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before,.custom-control-input.is-valid:checked ~ .custom-control-label::before{border-color:#87d287;background-color:#87d287}.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before,.custom-control-input.is-valid:focus ~ .custom-control-label::before{-webkit-box-shadow:0 0 0 0.2rem rgba(98,196,98,0.25);box-shadow:0 0 0 0.2rem rgba(98,196,98,0.25)}.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before,.custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before{border-color:#62c462}.was-validated .custom-file-input:valid ~ .custom-file-label,.custom-file-input.is-valid ~ .custom-file-label{border-color:#62c462}.was-validated .custom-file-input:valid ~ .valid-feedback,.was-validated .custom-file-input:valid ~ .valid-tooltip,.custom-file-input.is-valid ~ .valid-feedback,.custom-file-input.is-valid ~ .valid-tooltip{display:block}.was-validated .custom-file-input:valid:focus ~ .custom-file-label,.custom-file-input.is-valid:focus ~ .custom-file-label{border-color:#62c462;-webkit-box-shadow:0 0 0 0.2rem rgba(98,196,98,0.25);box-shadow:0 0 0 0.2rem rgba(98,196,98,0.25)}.invalid-feedback{display:none;width:100%;margin-top:0.25rem;font-size:80%;color:#ee5f5b}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:0.25rem 0.5rem;margin-top:.1rem;font-size:0.8203125rem;line-height:1.5;color:#fff;background-color:rgba(238,95,91,0.9);border-radius:0.25rem}.was-validated .form-control:invalid,.form-control.is-invalid{border-color:#ee5f5b;padding-right:2.90625rem;background-repeat:no-repeat;background-position:center right calc(2.90625rem / 4);background-size:calc(2.90625rem / 2) calc(2.90625rem / 2);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23ee5f5b' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23d9534f' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E")}.was-validated .form-control:invalid:focus,.form-control.is-invalid:focus{border-color:#ee5f5b;-webkit-box-shadow:0 0 0 0.2rem rgba(238,95,91,0.25);box-shadow:0 0 0 0.2rem rgba(238,95,91,0.25)}.was-validated .form-control:invalid ~ .invalid-feedback,.was-validated .form-control:invalid ~ .invalid-tooltip,.form-control.is-invalid ~ .invalid-feedback,.form-control.is-invalid ~ .invalid-tooltip{display:block}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:2.90625rem;background-position:top calc(2.90625rem / 4) right calc(2.90625rem / 4)}.was-validated .custom-select:invalid,.custom-select.is-invalid{border-color:#ee5f5b;padding-right:4.1796875rem;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%233A3F44' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 1rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23ee5f5b' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23d9534f' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E") no-repeat center right 2rem/1.453125rem 1.453125rem}.was-validated .custom-select:invalid:focus,.custom-select.is-invalid:focus{border-color:#ee5f5b;-webkit-box-shadow:0 0 0 0.2rem rgba(238,95,91,0.25);box-shadow:0 0 0 0.2rem rgba(238,95,91,0.25)}.was-validated .custom-select:invalid ~ .invalid-feedback,.was-validated .custom-select:invalid ~ .invalid-tooltip,.custom-select.is-invalid ~ .invalid-feedback,.custom-select.is-invalid ~ .invalid-tooltip{display:block}.was-validated .form-control-file:invalid ~ .invalid-feedback,.was-validated .form-control-file:invalid ~ .invalid-tooltip,.form-control-file.is-invalid ~ .invalid-feedback,.form-control-file.is-invalid ~ .invalid-tooltip{display:block}.was-validated .form-check-input:invalid ~ .form-check-label,.form-check-input.is-invalid ~ .form-check-label{color:#ee5f5b}.was-validated .form-check-input:invalid ~ .invalid-feedback,.was-validated .form-check-input:invalid ~ .invalid-tooltip,.form-check-input.is-invalid ~ .invalid-feedback,.form-check-input.is-invalid ~ .invalid-tooltip{display:block}.was-validated .custom-control-input:invalid ~ .custom-control-label,.custom-control-input.is-invalid ~ .custom-control-label{color:#ee5f5b}.was-validated .custom-control-input:invalid ~ .custom-control-label::before,.custom-control-input.is-invalid ~ .custom-control-label::before{border-color:#ee5f5b}.was-validated .custom-control-input:invalid ~ .invalid-feedback,.was-validated .custom-control-input:invalid ~ .invalid-tooltip,.custom-control-input.is-invalid ~ .invalid-feedback,.custom-control-input.is-invalid ~ .invalid-tooltip{display:block}.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before,.custom-control-input.is-invalid:checked ~ .custom-control-label::before{border-color:#f38c89;background-color:#f38c89}.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before,.custom-control-input.is-invalid:focus ~ .custom-control-label::before{-webkit-box-shadow:0 0 0 0.2rem rgba(238,95,91,0.25);box-shadow:0 0 0 0.2rem rgba(238,95,91,0.25)}.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before,.custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before{border-color:#ee5f5b}.was-validated .custom-file-input:invalid ~ .custom-file-label,.custom-file-input.is-invalid ~ .custom-file-label{border-color:#ee5f5b}.was-validated .custom-file-input:invalid ~ .invalid-feedback,.was-validated .custom-file-input:invalid ~ .invalid-tooltip,.custom-file-input.is-invalid ~ .invalid-feedback,.custom-file-input.is-invalid ~ .invalid-tooltip{display:block}.was-validated .custom-file-input:invalid:focus ~ .custom-file-label,.custom-file-input.is-invalid:focus ~ .custom-file-label{border-color:#ee5f5b;-webkit-box-shadow:0 0 0 0.2rem rgba(238,95,91,0.25);box-shadow:0 0 0 0.2rem rgba(238,95,91,0.25)}.form-inline{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width: 576px){.form-inline label{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-box-align:center;-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 .input-group,.form-inline .custom-select{width:auto}.form-inline .form-check{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;margin-top:0;margin-right:0.25rem;margin-left:0}.form-inline .custom-control{-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#aaa;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:0.75rem 1rem;font-size:0.9375rem;line-height:1.5;border-radius:0.25rem;-webkit-transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out}@media screen and (prefers-reduced-motion: reduce){.btn{-webkit-transition:none;transition:none}}.btn:hover{color:#aaa;text-decoration:none}.btn:focus,.btn.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25)}.btn.disabled,.btn:disabled{opacity:0.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#3A3F44;border-color:#3A3F44}.btn-primary:hover{color:#fff;background-color:#282c2f;border-color:#232628}.btn-primary:focus,.btn-primary.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(88,92,96,0.5);box-shadow:0 0 0 0.2rem rgba(88,92,96,0.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#3A3F44;border-color:#3A3F44}.btn-primary:not(:disabled):not(.disabled):active,.btn-primary:not(:disabled):not(.disabled).active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#232628;border-color:#1d1f22}.btn-primary:not(:disabled):not(.disabled):active:focus,.btn-primary:not(:disabled):not(.disabled).active:focus,.show>.btn-primary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(88,92,96,0.5);box-shadow:0 0 0 0.2rem rgba(88,92,96,0.5)}.btn-secondary{color:#fff;background-color:#7A8288;border-color:#7A8288}.btn-secondary:hover{color:#fff;background-color:#686f74;border-color:#62686d}.btn-secondary:focus,.btn-secondary.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(142,149,154,0.5);box-shadow:0 0 0 0.2rem rgba(142,149,154,0.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#7A8288;border-color:#7A8288}.btn-secondary:not(:disabled):not(.disabled):active,.btn-secondary:not(:disabled):not(.disabled).active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#62686d;border-color:#5c6267}.btn-secondary:not(:disabled):not(.disabled):active:focus,.btn-secondary:not(:disabled):not(.disabled).active:focus,.show>.btn-secondary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(142,149,154,0.5);box-shadow:0 0 0 0.2rem rgba(142,149,154,0.5)}.btn-success{color:#272B30;background-color:#62c462;border-color:#62c462}.btn-success:hover{color:#fff;background-color:#46ba46;border-color:#42b142}.btn-success:focus,.btn-success.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(89,173,91,0.5);box-shadow:0 0 0 0.2rem rgba(89,173,91,0.5)}.btn-success.disabled,.btn-success:disabled{color:#272B30;background-color:#62c462;border-color:#62c462}.btn-success:not(:disabled):not(.disabled):active,.btn-success:not(:disabled):not(.disabled).active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#42b142;border-color:#3fa73f}.btn-success:not(:disabled):not(.disabled):active:focus,.btn-success:not(:disabled):not(.disabled).active:focus,.show>.btn-success.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(89,173,91,0.5);box-shadow:0 0 0 0.2rem rgba(89,173,91,0.5)}.btn-info{color:#272B30;background-color:#5bc0de;border-color:#5bc0de}.btn-info:hover{color:#fff;background-color:#3bb4d8;border-color:#31b0d5}.btn-info:focus,.btn-info.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(83,170,196,0.5);box-shadow:0 0 0 0.2rem rgba(83,170,196,0.5)}.btn-info.disabled,.btn-info:disabled{color:#272B30;background-color:#5bc0de;border-color:#5bc0de}.btn-info:not(:disabled):not(.disabled):active,.btn-info:not(:disabled):not(.disabled).active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#31b0d5;border-color:#2aaacf}.btn-info:not(:disabled):not(.disabled):active:focus,.btn-info:not(:disabled):not(.disabled).active:focus,.show>.btn-info.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(83,170,196,0.5);box-shadow:0 0 0 0.2rem rgba(83,170,196,0.5)}.btn-warning{color:#272B30;background-color:#f89406;border-color:#f89406}.btn-warning:hover{color:#fff;background-color:#d37e05;border-color:#c67605}.btn-warning:focus,.btn-warning.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(217,132,12,0.5);box-shadow:0 0 0 0.2rem rgba(217,132,12,0.5)}.btn-warning.disabled,.btn-warning:disabled{color:#272B30;background-color:#f89406;border-color:#f89406}.btn-warning:not(:disabled):not(.disabled):active,.btn-warning:not(:disabled):not(.disabled).active,.show>.btn-warning.dropdown-toggle{color:#fff;background-color:#c67605;border-color:#ba6f04}.btn-warning:not(:disabled):not(.disabled):active:focus,.btn-warning:not(:disabled):not(.disabled).active:focus,.show>.btn-warning.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(217,132,12,0.5);box-shadow:0 0 0 0.2rem rgba(217,132,12,0.5)}.btn-danger{color:#fff;background-color:#ee5f5b;border-color:#ee5f5b}.btn-danger:hover{color:#fff;background-color:#ea3d38;border-color:#e9322d}.btn-danger:focus,.btn-danger.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(241,119,116,0.5);box-shadow:0 0 0 0.2rem rgba(241,119,116,0.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#ee5f5b;border-color:#ee5f5b}.btn-danger:not(:disabled):not(.disabled):active,.btn-danger:not(:disabled):not(.disabled).active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#e9322d;border-color:#e82721}.btn-danger:not(:disabled):not(.disabled):active:focus,.btn-danger:not(:disabled):not(.disabled).active:focus,.show>.btn-danger.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(241,119,116,0.5);box-shadow:0 0 0 0.2rem rgba(241,119,116,0.5)}.btn-light{color:#272B30;background-color:#e9ecef;border-color:#e9ecef}.btn-light:hover{color:#272B30;background-color:#d3d9df;border-color:#cbd3da}.btn-light:focus,.btn-light.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(204,207,210,0.5);box-shadow:0 0 0 0.2rem rgba(204,207,210,0.5)}.btn-light.disabled,.btn-light:disabled{color:#272B30;background-color:#e9ecef;border-color:#e9ecef}.btn-light:not(:disabled):not(.disabled):active,.btn-light:not(:disabled):not(.disabled).active,.show>.btn-light.dropdown-toggle{color:#272B30;background-color:#cbd3da;border-color:#c4ccd4}.btn-light:not(:disabled):not(.disabled):active:focus,.btn-light:not(:disabled):not(.disabled).active:focus,.show>.btn-light.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(204,207,210,0.5);box-shadow:0 0 0 0.2rem rgba(204,207,210,0.5)}.btn-dark{color:#fff;background-color:#272B30;border-color:#272B30}.btn-dark:hover{color:#fff;background-color:#16181b;border-color:#101214}.btn-dark:focus,.btn-dark.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(71,75,79,0.5);box-shadow:0 0 0 0.2rem rgba(71,75,79,0.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#272B30;border-color:#272B30}.btn-dark:not(:disabled):not(.disabled):active,.btn-dark:not(:disabled):not(.disabled).active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#101214;border-color:#0a0b0d}.btn-dark:not(:disabled):not(.disabled):active:focus,.btn-dark:not(:disabled):not(.disabled).active:focus,.show>.btn-dark.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(71,75,79,0.5);box-shadow:0 0 0 0.2rem rgba(71,75,79,0.5)}.btn-outline-primary{color:#3A3F44;border-color:#3A3F44}.btn-outline-primary:hover{color:#fff;background-color:#3A3F44;border-color:#3A3F44}.btn-outline-primary:focus,.btn-outline-primary.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.5);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#3A3F44;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:#3A3F44;border-color:#3A3F44}.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{-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.5);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.5)}.btn-outline-secondary{color:#7A8288;border-color:#7A8288}.btn-outline-secondary:hover{color:#fff;background-color:#7A8288;border-color:#7A8288}.btn-outline-secondary:focus,.btn-outline-secondary.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(122,130,136,0.5);box-shadow:0 0 0 0.2rem rgba(122,130,136,0.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#7A8288;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:#7A8288;border-color:#7A8288}.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{-webkit-box-shadow:0 0 0 0.2rem rgba(122,130,136,0.5);box-shadow:0 0 0 0.2rem rgba(122,130,136,0.5)}.btn-outline-success{color:#62c462;border-color:#62c462}.btn-outline-success:hover{color:#272B30;background-color:#62c462;border-color:#62c462}.btn-outline-success:focus,.btn-outline-success.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(98,196,98,0.5);box-shadow:0 0 0 0.2rem rgba(98,196,98,0.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#62c462;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:#272B30;background-color:#62c462;border-color:#62c462}.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{-webkit-box-shadow:0 0 0 0.2rem rgba(98,196,98,0.5);box-shadow:0 0 0 0.2rem rgba(98,196,98,0.5)}.btn-outline-info{color:#5bc0de;border-color:#5bc0de}.btn-outline-info:hover{color:#272B30;background-color:#5bc0de;border-color:#5bc0de}.btn-outline-info:focus,.btn-outline-info.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(91,192,222,0.5);box-shadow:0 0 0 0.2rem rgba(91,192,222,0.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#5bc0de;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:#272B30;background-color:#5bc0de;border-color:#5bc0de}.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{-webkit-box-shadow:0 0 0 0.2rem rgba(91,192,222,0.5);box-shadow:0 0 0 0.2rem rgba(91,192,222,0.5)}.btn-outline-warning{color:#f89406;border-color:#f89406}.btn-outline-warning:hover{color:#272B30;background-color:#f89406;border-color:#f89406}.btn-outline-warning:focus,.btn-outline-warning.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(248,148,6,0.5);box-shadow:0 0 0 0.2rem rgba(248,148,6,0.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#f89406;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:#272B30;background-color:#f89406;border-color:#f89406}.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{-webkit-box-shadow:0 0 0 0.2rem rgba(248,148,6,0.5);box-shadow:0 0 0 0.2rem rgba(248,148,6,0.5)}.btn-outline-danger{color:#ee5f5b;border-color:#ee5f5b}.btn-outline-danger:hover{color:#fff;background-color:#ee5f5b;border-color:#ee5f5b}.btn-outline-danger:focus,.btn-outline-danger.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(238,95,91,0.5);box-shadow:0 0 0 0.2rem rgba(238,95,91,0.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#ee5f5b;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:#ee5f5b;border-color:#ee5f5b}.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{-webkit-box-shadow:0 0 0 0.2rem rgba(238,95,91,0.5);box-shadow:0 0 0 0.2rem rgba(238,95,91,0.5)}.btn-outline-light{color:#e9ecef;border-color:#e9ecef}.btn-outline-light:hover{color:#272B30;background-color:#e9ecef;border-color:#e9ecef}.btn-outline-light:focus,.btn-outline-light.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(233,236,239,0.5);box-shadow:0 0 0 0.2rem rgba(233,236,239,0.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#e9ecef;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:#272B30;background-color:#e9ecef;border-color:#e9ecef}.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{-webkit-box-shadow:0 0 0 0.2rem rgba(233,236,239,0.5);box-shadow:0 0 0 0.2rem rgba(233,236,239,0.5)}.btn-outline-dark{color:#272B30;border-color:#272B30}.btn-outline-dark:hover{color:#fff;background-color:#272B30;border-color:#272B30}.btn-outline-dark:focus,.btn-outline-dark.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(39,43,48,0.5);box-shadow:0 0 0 0.2rem rgba(39,43,48,0.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#272B30;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:#272B30;border-color:#272B30}.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{-webkit-box-shadow:0 0 0 0.2rem rgba(39,43,48,0.5);box-shadow:0 0 0 0.2rem rgba(39,43,48,0.5)}.btn-link{font-weight:400;color:#fff}.btn-link:hover{color:#d9d9d9;text-decoration:underline}.btn-link:focus,.btn-link.focus{text-decoration:underline;-webkit-box-shadow:none;box-shadow:none}.btn-link:disabled,.btn-link.disabled{color:#7A8288;pointer-events:none}.btn-lg,.btn-group-lg>.btn{padding:0.5rem 1rem;font-size:1.171875rem;line-height:1.5;border-radius:0.3rem}.btn-sm,.btn-group-sm>.btn{padding:0.25rem 0.5rem;font-size:0.8203125rem;line-height:1.5;border-radius:0.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:0.5rem}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{-webkit-transition:opacity 0.15s linear;transition:opacity 0.15s linear}@media screen and (prefers-reduced-motion: reduce){.fade{-webkit-transition:none;transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height 0.35s ease;transition:height 0.35s ease}@media screen and (prefers-reduced-motion: reduce){.collapsing{-webkit-transition:none;transition:none}}.dropup,.dropright,.dropdown,.dropleft{position:relative}.dropdown-toggle::after{display:inline-block;margin-left:0.255em;vertical-align:0.255em;content:"";border-top:0.3em solid;border-right:0.3em solid transparent;border-bottom:0;border-left:0.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:0.5rem 0;margin:0.125rem 0 0;font-size:0.9375rem;color:#aaa;text-align:left;list-style:none;background-color:#3A3F44;background-clip:padding-box;border:1px solid rgba(0,0,0,0.15);border-radius:0.25rem}.dropdown-menu-right{right:0;left:auto}@media (min-width: 576px){.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width: 768px){.dropdown-menu-md-right{right:0;left:auto}}@media (min-width: 992px){.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width: 1200px){.dropdown-menu-xl-right{right:0;left:auto}}.dropdown-menu-left{right:auto;left:0}@media (min-width: 576px){.dropdown-menu-sm-left{right:auto;left:0}}@media (min-width: 768px){.dropdown-menu-md-left{right:auto;left:0}}@media (min-width: 992px){.dropdown-menu-lg-left{right:auto;left:0}}@media (min-width: 1200px){.dropdown-menu-xl-left{right:auto;left:0}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:0.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:0.255em;vertical-align:0.255em;content:"";border-top:0;border-right:0.3em solid transparent;border-bottom:0.3em solid;border-left:0.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:0.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:0.255em;vertical-align:0.255em;content:"";border-top:0.3em solid transparent;border-right:0;border-bottom:0.3em solid transparent;border-left:0.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:0.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:0.255em;vertical-align:0.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:0.255em;vertical-align:0.255em;content:"";border-top:0.3em solid transparent;border-right:0.3em solid;border-bottom:0.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^="top"],.dropdown-menu[x-placement^="right"],.dropdown-menu[x-placement^="bottom"],.dropdown-menu[x-placement^="left"]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:0.5rem 0;overflow:hidden;border-top:1px solid rgba(0,0,0,0.15)}.dropdown-item{display:block;width:100%;padding:0.25rem 1.5rem;clear:both;font-weight:400;color:#aaa;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:first-child{border-top-left-radius:calc(0.25rem - 1px);border-top-right-radius:calc(0.25rem - 1px)}.dropdown-item:last-child{border-bottom-right-radius:calc(0.25rem - 1px);border-bottom-left-radius:calc(0.25rem - 1px)}.dropdown-item:hover,.dropdown-item:focus{color:#fff;text-decoration:none;background-color:#272B30}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#3A3F44}.dropdown-item.disabled,.dropdown-item:disabled{color:#7A8288;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:0.5rem 1.5rem;margin-bottom:0;font-size:0.8203125rem;color:#7A8288;white-space:nowrap}.dropdown-item-text{display:block;padding:0.25rem 1.5rem;color:#aaa}.btn-group,.btn-group-vertical{position:relative;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover{z-index:1}.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:1}.btn-toolbar{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn:not(:first-child),.btn-group>.btn-group:not(:first-child){margin-left:-1px}.btn-group>.btn:not(:last-child):not(.dropdown-toggle),.btn-group>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:not(:first-child),.btn-group>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:0.75rem;padding-left:0.75rem}.dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-sm+.dropdown-toggle-split,.btn-group-sm>.btn+.dropdown-toggle-split{padding-right:0.375rem;padding-left:0.375rem}.btn-lg+.dropdown-toggle-split,.btn-group-lg>.btn+.dropdown-toggle-split{padding-right:0.75rem;padding-left:0.75rem}.btn-group-vertical{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn:not(:first-child),.btn-group-vertical>.btn-group:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle),.btn-group-vertical>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:not(:first-child),.btn-group-vertical>.btn-group:not(:first-child)>.btn{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="radio"],.btn-group-toggle>.btn input[type="checkbox"],.btn-group-toggle>.btn-group>.btn input[type="radio"],.btn-group-toggle>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-control-plaintext,.input-group>.custom-select,.input-group>.custom-file{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.form-control+.form-control,.input-group>.form-control+.custom-select,.input-group>.form-control+.custom-file,.input-group>.form-control-plaintext+.form-control,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.custom-file,.input-group>.custom-select+.form-control,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.custom-file,.input-group>.custom-file+.form-control,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.custom-file{margin-left:-1px}.input-group>.form-control:focus,.input-group>.custom-select:focus,.input-group>.custom-file .custom-file-input:focus ~ .custom-file-label{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.form-control:not(:last-child),.input-group>.custom-select:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.form-control:not(:first-child),.input-group>.custom-select:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-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-prepend,.input-group-append{display:-webkit-box;display:-ms-flexbox;display:flex}.input-group-prepend .btn,.input-group-append .btn{position:relative;z-index:2}.input-group-prepend .btn:focus,.input-group-append .btn:focus{z-index:3}.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.input-group-text,.input-group-append .input-group-text+.btn{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:0.75rem 1rem;margin-bottom:0;font-size:0.9375rem;font-weight:400;line-height:1.5;color:#52575C;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:0.25rem}.input-group-text input[type="radio"],.input-group-text input[type="checkbox"]{margin-top:0}.input-group-lg>.form-control:not(textarea),.input-group-lg>.custom-select{height:calc(2.7578125rem + 2px)}.input-group-lg>.form-control,.input-group-lg>.custom-select,.input-group-lg>.input-group-prepend>.input-group-text,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-append>.btn{padding:0.5rem 1rem;font-size:1.171875rem;line-height:1.5;border-radius:0.3rem}.input-group-sm>.form-control:not(textarea),.input-group-sm>.custom-select{height:calc(1.73046875rem + 2px)}.input-group-sm>.form-control,.input-group-sm>.custom-select,.input-group-sm>.input-group-prepend>.input-group-text,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-append>.btn{padding:0.25rem 0.5rem;font-size:0.8203125rem;line-height:1.5;border-radius:0.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:2rem}.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text,.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.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){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:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.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){border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.40625rem;padding-left:1.5rem}.custom-control-inline{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked ~ .custom-control-label::before{color:#fff;border-color:#3A3F44;background-color:#3A3F44}.custom-control-input:focus ~ .custom-control-label::before{-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25)}.custom-control-input:focus:not(:checked) ~ .custom-control-label::before{border-color:#757f89}.custom-control-input:not(:disabled):active ~ .custom-control-label::before{color:#fff;background-color:#9098a0;border-color:#9098a0}.custom-control-input:disabled ~ .custom-control-label{color:#7A8288}.custom-control-input:disabled ~ .custom-control-label::before{background-color:#ccc}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:0.203125rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#999 solid 1px}.custom-control-label::after{position:absolute;top:0.203125rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background-repeat:no-repeat;background-position:center center;background-size:50% 50%}.custom-checkbox .custom-control-label::before{border-radius:0.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' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before{border-color:#3A3F44;background-color:#3A3F44}.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' 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(58,63,68,0.5)}.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before{background-color:rgba(58,63,68,0.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' 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(58,63,68,0.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:0.5rem}.custom-switch .custom-control-label::after{top:calc(0.203125rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#999;border-radius:0.5rem;-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out}@media screen and (prefers-reduced-motion: reduce){.custom-switch .custom-control-label::after{-webkit-transition:none;transition:none}}.custom-switch .custom-control-input:checked ~ .custom-control-label::after{background-color:#fff;-webkit-transform:translateX(0.75rem);transform:translateX(0.75rem)}.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before{background-color:rgba(58,63,68,0.5)}.custom-select{display:inline-block;width:100%;height:calc(2.90625rem + 2px);padding:0.75rem 2rem 0.75rem 1rem;font-weight:400;line-height:1.5;color:#52575C;vertical-align:middle;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%233A3F44' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 1rem center/8px 10px;background-color:#fff;border:1px solid #ced4da;border-radius:0.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#757f89;outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(117,127,137,0.5);box-shadow:0 0 0 0.2rem rgba(117,127,137,0.5)}.custom-select:focus::-ms-value{color:#52575C;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:1rem;background-image:none}.custom-select:disabled{color:#7A8288;background-color:#e9ecef}.custom-select::-ms-expand{opacity:0}.custom-select-sm{height:calc(1.73046875rem + 2px);padding-top:0.25rem;padding-bottom:0.25rem;padding-left:0.5rem;font-size:0.8203125rem}.custom-select-lg{height:calc(2.7578125rem + 2px);padding-top:0.5rem;padding-bottom:0.5rem;padding-left:1rem;font-size:1.171875rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(2.90625rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(2.90625rem + 2px);margin:0;opacity:0}.custom-file-input:focus ~ .custom-file-label{border-color:#757f89;-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25)}.custom-file-input:disabled ~ .custom-file-label{background-color:#ccc}.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(2.90625rem + 2px);padding:0.75rem 1rem;font-weight:400;line-height:1.5;color:#52575C;background-color:#fff;border:1px solid #ced4da;border-radius:0.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:2.90625rem;padding:0.75rem 1rem;line-height:1.5;color:#52575C;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 0.25rem 0.25rem 0}.custom-range{width:100%;height:calc(1rem + 0.4rem);padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:none}.custom-range:focus::-webkit-slider-thumb{-webkit-box-shadow:0 0 0 1px #272B30,0 0 0 0.2rem rgba(58,63,68,0.25);box-shadow:0 0 0 1px #272B30,0 0 0 0.2rem rgba(58,63,68,0.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #272B30,0 0 0 0.2rem rgba(58,63,68,0.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #272B30,0 0 0 0.2rem rgba(58,63,68,0.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-0.25rem;background-color:#3A3F44;border:0;border-radius:1rem;-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;-webkit-appearance:none;appearance:none}@media screen and (prefers-reduced-motion: reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#9098a0}.custom-range::-webkit-slider-runnable-track{width:100%;height:0.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:#3A3F44;border:0;border-radius:1rem;-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;-moz-appearance:none;appearance:none}@media screen and (prefers-reduced-motion: reduce){.custom-range::-moz-range-thumb{-webkit-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#9098a0}.custom-range::-moz-range-track{width:100%;height:0.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:0.2rem;margin-left:0.2rem;background-color:#3A3F44;border:0;border-radius:1rem;-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;appearance:none}@media screen and (prefers-reduced-motion: reduce){.custom-range::-ms-thumb{-webkit-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#9098a0}.custom-range::-ms-track{width:100%;height:0.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:0.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:#999}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#999}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#999}.custom-control-label::before,.custom-file-label,.custom-select{-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out}@media screen and (prefers-reduced-motion: reduce){.custom-control-label::before,.custom-file-label,.custom-select{-webkit-transition:none;transition:none}}.nav{display:-webkit-box;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:0.5rem 1rem}.nav-link:hover,.nav-link:focus{text-decoration:none}.nav-link.disabled{color:#7A8288;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid rgba(0,0,0,0.6)}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:0.25rem;border-top-right-radius:0.25rem}.nav-tabs .nav-link:hover,.nav-tabs .nav-link:focus{border-color:rgba(0,0,0,0.6)}.nav-tabs .nav-link.disabled{color:#7A8288;background-color:transparent;border-color:transparent}.nav-tabs .nav-link.active,.nav-tabs .nav-item.show .nav-link{color:#fff;background-color:#272B30;border-color:rgba(0,0,0,0.6)}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:0.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#3A3F44}.nav-fill .nav-item{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-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:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:0 1rem}.navbar>.container,.navbar>.container-fluid{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:0.32421875rem;padding-bottom:0.32421875rem;margin-right:1rem;font-size:1.171875rem;line-height:inherit;white-space:nowrap}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-nav{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-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:0.5rem;padding-bottom:0.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:0.25rem 0.75rem;font-size:1.171875rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:0.25rem}.navbar-toggler:hover,.navbar-toggler:focus{text-decoration:none}.navbar-toggler:not(:disabled):not(.disabled){cursor:pointer}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width: 575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width: 576px){.navbar-expand-sm{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-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:0.5rem;padding-left:0.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-webkit-box !important;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{padding-right:0;padding-left:0}}@media (min-width: 768px){.navbar-expand-md{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-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:0.5rem;padding-left:0.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-webkit-box !important;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{padding-right:0;padding-left:0}}@media (min-width: 992px){.navbar-expand-lg{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-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:0.5rem;padding-left:0.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-webkit-box !important;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{padding-right:0;padding-left:0}}@media (min-width: 1200px){.navbar-expand-xl{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-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:0.5rem;padding-left:0.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:0.5rem;padding-left:0.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-webkit-box !important;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:#3A3F44}.navbar-light .navbar-brand:hover,.navbar-light .navbar-brand:focus{color:#3A3F44}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,0.5)}.navbar-light .navbar-nav .nav-link:hover,.navbar-light .navbar-nav .nav-link:focus{color:#3A3F44}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,0.3)}.navbar-light .navbar-nav .show>.nav-link,.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .nav-link.active{color:#3A3F44}.navbar-light .navbar-toggler{color:rgba(0,0,0,0.5);border-color:rgba(0,0,0,0.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,0.5)}.navbar-light .navbar-text a{color:#3A3F44}.navbar-light .navbar-text a:hover,.navbar-light .navbar-text a:focus{color:#3A3F44}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:hover,.navbar-dark .navbar-brand:focus{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,0.5)}.navbar-dark .navbar-nav .nav-link:hover,.navbar-dark .navbar-nav .nav-link:focus{color:#fff}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,0.25)}.navbar-dark .navbar-nav .show>.nav-link,.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .nav-link.active{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,0.5);border-color:rgba(255,255,255,0.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,0.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:hover,.navbar-dark .navbar-text a:focus{color:#fff}.card{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#32383e;background-clip:border-box;border:1px solid rgba(0,0,0,0.6);border-radius:0.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:0.25rem;border-top-right-radius:0.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:0.25rem;border-bottom-left-radius:0.25rem}.card-body{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:0.75rem}.card-subtitle{margin-top:-0.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:0.75rem 1.25rem;margin-bottom:0;color:inherit;background-color:#515960;border-bottom:1px solid rgba(0,0,0,0.6)}.card-header:first-child{border-radius:calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:0.75rem 1.25rem;background-color:#515960;border-top:1px solid rgba(0,0,0,0.6)}.card-footer:last-child{border-radius:0 0 calc(0.25rem - 1px) calc(0.25rem - 1px)}.card-header-tabs{margin-right:-0.625rem;margin-bottom:-0.75rem;margin-left:-0.625rem;border-bottom:0}.card-header-pills{margin-right:-0.625rem;margin-left:-0.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(0.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(0.25rem - 1px);border-top-right-radius:calc(0.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(0.25rem - 1px);border-bottom-left-radius:calc(0.25rem - 1px)}.card-deck{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width: 576px){.card-deck{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex:1 0 0%;flex:1 0 0%;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width: 576px){.card-group{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-webkit-box-flex:1;-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:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:first-child .card-img-top,.card-group>.card:first-child .card-header{border-top-right-radius:0}.card-group>.card:first-child .card-img-bottom,.card-group>.card:first-child .card-footer{border-bottom-right-radius:0}.card-group>.card:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:last-child .card-img-top,.card-group>.card:last-child .card-header{border-top-left-radius:0}.card-group>.card:last-child .card-img-bottom,.card-group>.card:last-child .card-footer{border-bottom-left-radius:0}.card-group>.card:only-child{border-radius:0.25rem}.card-group>.card:only-child .card-img-top,.card-group>.card:only-child .card-header{border-top-left-radius:0.25rem;border-top-right-radius:0.25rem}.card-group>.card:only-child .card-img-bottom,.card-group>.card:only-child .card-footer{border-bottom-right-radius:0.25rem;border-bottom-left-radius:0.25rem}.card-group>.card:not(:first-child):not(:last-child):not(:only-child){border-radius:0}.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-top,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-header,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-footer{border-radius:0}}.card-columns .card{margin-bottom:0.75rem}@media (min-width: 576px){.card-columns{-webkit-column-count:3;column-count:3;-webkit-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion .card{overflow:hidden}.accordion .card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion .card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion .card:first-of-type{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion .card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.accordion .card .card-header{margin-bottom:-1px}.breadcrumb{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:0.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:0.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:0.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:0.5rem;color:#7A8288;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#999}.pagination{display:-webkit-box;display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:0.25rem}.page-link{position:relative;display:block;padding:0.5rem 0.75rem;margin-left:-1px;line-height:1.25;color:#fff;background-color:transparent;border:1px solid rgba(0,0,0,0.6)}.page-link:hover{z-index:2;color:#fff;text-decoration:none;background-color:transparent;border-color:rgba(0,0,0,0.6)}.page-link:focus{z-index:2;outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25)}.page-link:not(:disabled):not(.disabled){cursor:pointer}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:0.25rem;border-bottom-left-radius:0.25rem}.page-item:last-child .page-link{border-top-right-radius:0.25rem;border-bottom-right-radius:0.25rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:transparent;border-color:rgba(0,0,0,0.6)}.page-item.disabled .page-link{color:#7A8288;pointer-events:none;cursor:auto;background-color:transparent;border-color:rgba(0,0,0,0.6)}.pagination-lg .page-link{padding:0.75rem 1.5rem;font-size:1.171875rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:0.3rem;border-bottom-left-radius:0.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:0.3rem;border-bottom-right-radius:0.3rem}.pagination-sm .page-link{padding:0.25rem 0.5rem;font-size:0.8203125rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:0.2rem;border-bottom-left-radius:0.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:0.2rem;border-bottom-right-radius:0.2rem}.badge{display:inline-block;padding:0.25em 0.4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:0.25rem}a.badge:hover,a.badge:focus{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:0.6em;padding-left:0.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#3A3F44}a.badge-primary:hover,a.badge-primary:focus{color:#fff;background-color:#232628}.badge-secondary{color:#fff;background-color:#7A8288}a.badge-secondary:hover,a.badge-secondary:focus{color:#fff;background-color:#62686d}.badge-success{color:#272B30;background-color:#62c462}a.badge-success:hover,a.badge-success:focus{color:#272B30;background-color:#42b142}.badge-info{color:#272B30;background-color:#5bc0de}a.badge-info:hover,a.badge-info:focus{color:#272B30;background-color:#31b0d5}.badge-warning{color:#272B30;background-color:#f89406}a.badge-warning:hover,a.badge-warning:focus{color:#272B30;background-color:#c67605}.badge-danger{color:#fff;background-color:#ee5f5b}a.badge-danger:hover,a.badge-danger:focus{color:#fff;background-color:#e9322d}.badge-light{color:#272B30;background-color:#e9ecef}a.badge-light:hover,a.badge-light:focus{color:#272B30;background-color:#cbd3da}.badge-dark{color:#fff;background-color:#272B30}a.badge-dark:hover,a.badge-dark:focus{color:#fff;background-color:#101214}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#1c1e22;border-radius:0.3rem}@media (min-width: 576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:0.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:0.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:3.90625rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:0.75rem 1.25rem;color:inherit}.alert-primary{color:#1e2123;background-color:#d8d9da;border-color:#c8c9cb}.alert-primary hr{border-top-color:#bbbcbf}.alert-primary .alert-link{color:#060708}.alert-secondary{color:#3f4447;background-color:#e4e6e7;border-color:#dadcde}.alert-secondary hr{border-top-color:#cdcfd2}.alert-secondary .alert-link{color:#272a2c}.alert-success{color:#336633;background-color:#e0f3e0;border-color:#d3eed3}.alert-success hr{border-top-color:#c1e7c1}.alert-success .alert-link{color:#224422}.alert-info{color:#2f6473;background-color:#def2f8;border-color:#d1edf6}.alert-info hr{border-top-color:#bce5f2}.alert-info .alert-link{color:#20454f}.alert-warning{color:#814d03;background-color:#feeacd;border-color:#fde1b9}.alert-warning hr{border-top-color:#fcd6a0}.alert-warning .alert-link{color:#4f2f02}.alert-danger{color:#7c312f;background-color:#fcdfde;border-color:#fad2d1}.alert-danger hr{border-top-color:#f8bcba}.alert-danger .alert-link{color:#572221}.alert-light{color:#797b7c;background-color:#fbfbfc;border-color:#f9fafb}.alert-light hr{border-top-color:#eaedf1}.alert-light .alert-link{color:#606162}.alert-dark{color:#141619;background-color:#d4d5d6;border-color:#c3c4c5}.alert-dark hr{border-top-color:#b6b7b8}.alert-dark .alert-link{color:black}@-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:-webkit-box;display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:0.703125rem;background-color:#1c1e22;border-radius:0.25rem}.progress-bar{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;color:#7A8288;text-align:center;white-space:nowrap;background-color:#3A3F44;-webkit-transition:width 0.6s ease;transition:width 0.6s ease}@media screen and (prefers-reduced-motion: reduce){.progress-bar{-webkit-transition:none;transition:none}}.progress-bar-striped{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:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}.media{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.media-body{-webkit-box-flex:1;-ms-flex:1;flex:1}.list-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#fff;text-align:inherit}.list-group-item-action:hover,.list-group-item-action:focus{color:#fff;text-decoration:none;background-color:#3e444c}.list-group-item-action:active{color:#aaa;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:0.75rem 1.25rem;margin-bottom:-1px;background-color:#32383e;border:1px solid rgba(0,0,0,0.6)}.list-group-item:first-child{border-top-left-radius:0.25rem;border-top-right-radius:0.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:0.25rem;border-bottom-left-radius:0.25rem}.list-group-item:hover,.list-group-item:focus{z-index:1;text-decoration:none}.list-group-item.disabled,.list-group-item:disabled{color:#52575C;pointer-events:none;background-color:#32383e}.list-group-item.active{z-index:2;color:#fff;background-color:#3e444c;border-color:rgba(0,0,0,0.6)}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush .list-group-item:last-child{margin-bottom:-1px}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{margin-bottom:0;border-bottom:0}.list-group-item-primary{color:#1e2123;background-color:#c8c9cb}.list-group-item-primary.list-group-item-action:hover,.list-group-item-primary.list-group-item-action:focus{color:#1e2123;background-color:#bbbcbf}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#1e2123;border-color:#1e2123}.list-group-item-secondary{color:#3f4447;background-color:#dadcde}.list-group-item-secondary.list-group-item-action:hover,.list-group-item-secondary.list-group-item-action:focus{color:#3f4447;background-color:#cdcfd2}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#3f4447;border-color:#3f4447}.list-group-item-success{color:#336633;background-color:#d3eed3}.list-group-item-success.list-group-item-action:hover,.list-group-item-success.list-group-item-action:focus{color:#336633;background-color:#c1e7c1}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#336633;border-color:#336633}.list-group-item-info{color:#2f6473;background-color:#d1edf6}.list-group-item-info.list-group-item-action:hover,.list-group-item-info.list-group-item-action:focus{color:#2f6473;background-color:#bce5f2}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#2f6473;border-color:#2f6473}.list-group-item-warning{color:#814d03;background-color:#fde1b9}.list-group-item-warning.list-group-item-action:hover,.list-group-item-warning.list-group-item-action:focus{color:#814d03;background-color:#fcd6a0}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#814d03;border-color:#814d03}.list-group-item-danger{color:#7c312f;background-color:#fad2d1}.list-group-item-danger.list-group-item-action:hover,.list-group-item-danger.list-group-item-action:focus{color:#7c312f;background-color:#f8bcba}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#7c312f;border-color:#7c312f}.list-group-item-light{color:#797b7c;background-color:#f9fafb}.list-group-item-light.list-group-item-action:hover,.list-group-item-light.list-group-item-action:focus{color:#797b7c;background-color:#eaedf1}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#797b7c;border-color:#797b7c}.list-group-item-dark{color:#141619;background-color:#c3c4c5}.list-group-item-dark.list-group-item-action:hover,.list-group-item-dark.list-group-item-action:focus{color:#141619;background-color:#b6b7b8}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#141619;border-color:#141619}.close{float:right;font-size:1.40625rem;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){cursor:pointer}.close:not(:disabled):not(.disabled):hover,.close:not(:disabled):not(.disabled):focus{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:0.875rem;background-color:rgba(255,255,255,0.85);background-clip:padding-box;border:1px solid rgba(0,0,0,0.1);border-radius:0.25rem;-webkit-box-shadow:0 0.25rem 0.75rem rgba(0,0,0,0.1);box-shadow:0 0.25rem 0.75rem rgba(0,0,0,0.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0}.toast:not(:last-child){margin-bottom:0.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:0.25rem 0.75rem;color:#7A8288;background-color:rgba(255,255,255,0.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,0.05)}.toast-body{padding:0.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:0.5rem;pointer-events:none}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform 0.3s ease-out;transition:-webkit-transform 0.3s ease-out;transition:transform 0.3s ease-out;transition:transform 0.3s ease-out, -webkit-transform 0.3s ease-out;-webkit-transform:translate(0, -50px);transform:translate(0, -50px)}@media screen and (prefers-reduced-motion: reduce){.modal.fade .modal-dialog{-webkit-transition:none;transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal-dialog-centered{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;min-height:calc(100% - (0.5rem * 2))}.modal-dialog-centered::before{display:block;height:calc(100vh - (0.5rem * 2));content:""}.modal-content{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#32383e;background-clip:padding-box;border:1px solid rgba(0,0,0,0.2);border-radius:0.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:0.5}.modal-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid rgba(0,0,0,0.2);border-top-left-radius:0.3rem;border-top-right-radius:0.3rem}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid rgba(0,0,0,0.2);border-bottom-right-radius:0.3rem;border-bottom-left-radius:0.3rem}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.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-centered{min-height:calc(100% - (1.75rem * 2))}.modal-dialog-centered::before{height:calc(100vh - (1.75rem * 2))}.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", 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;word-spacing:normal;white-space:normal;line-break:auto;font-size:0.8203125rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:0.9}.tooltip .arrow{position:absolute;display:block;width:0.8rem;height:0.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-top,.bs-tooltip-auto[x-placement^="top"]{padding:0.4rem 0}.bs-tooltip-top .arrow,.bs-tooltip-auto[x-placement^="top"] .arrow{bottom:0}.bs-tooltip-top .arrow::before,.bs-tooltip-auto[x-placement^="top"] .arrow::before{top:0;border-width:0.4rem 0.4rem 0;border-top-color:#000}.bs-tooltip-right,.bs-tooltip-auto[x-placement^="right"]{padding:0 0.4rem}.bs-tooltip-right .arrow,.bs-tooltip-auto[x-placement^="right"] .arrow{left:0;width:0.4rem;height:0.8rem}.bs-tooltip-right .arrow::before,.bs-tooltip-auto[x-placement^="right"] .arrow::before{right:0;border-width:0.4rem 0.4rem 0.4rem 0;border-right-color:#000}.bs-tooltip-bottom,.bs-tooltip-auto[x-placement^="bottom"]{padding:0.4rem 0}.bs-tooltip-bottom .arrow,.bs-tooltip-auto[x-placement^="bottom"] .arrow{top:0}.bs-tooltip-bottom .arrow::before,.bs-tooltip-auto[x-placement^="bottom"] .arrow::before{bottom:0;border-width:0 0.4rem 0.4rem;border-bottom-color:#000}.bs-tooltip-left,.bs-tooltip-auto[x-placement^="left"]{padding:0 0.4rem}.bs-tooltip-left .arrow,.bs-tooltip-auto[x-placement^="left"] .arrow{right:0;width:0.4rem;height:0.8rem}.bs-tooltip-left .arrow::before,.bs-tooltip-auto[x-placement^="left"] .arrow::before{left:0;border-width:0.4rem 0 0.4rem 0.4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:0.25rem 0.5rem;color:#fff;text-align:center;background-color:#000;border-radius:0.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", 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;word-spacing:normal;white-space:normal;line-break:auto;font-size:0.8203125rem;word-wrap:break-word;background-color:#32383e;background-clip:padding-box;border:1px solid rgba(0,0,0,0.2);border-radius:0.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:0.5rem;margin:0 0.3rem}.popover .arrow::before,.popover .arrow::after{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-top,.bs-popover-auto[x-placement^="top"]{margin-bottom:0.5rem}.bs-popover-top .arrow,.bs-popover-auto[x-placement^="top"] .arrow{bottom:calc((0.5rem + 1px) * -1)}.bs-popover-top .arrow::before,.bs-popover-auto[x-placement^="top"] .arrow::before,.bs-popover-top .arrow::after,.bs-popover-auto[x-placement^="top"] .arrow::after{border-width:0.5rem 0.5rem 0}.bs-popover-top .arrow::before,.bs-popover-auto[x-placement^="top"] .arrow::before{bottom:0;border-top-color:rgba(0,0,0,0.25)}.bs-popover-top .arrow::after,.bs-popover-auto[x-placement^="top"] .arrow::after{bottom:1px;border-top-color:#32383e}.bs-popover-right,.bs-popover-auto[x-placement^="right"]{margin-left:0.5rem}.bs-popover-right .arrow,.bs-popover-auto[x-placement^="right"] .arrow{left:calc((0.5rem + 1px) * -1);width:0.5rem;height:1rem;margin:0.3rem 0}.bs-popover-right .arrow::before,.bs-popover-auto[x-placement^="right"] .arrow::before,.bs-popover-right .arrow::after,.bs-popover-auto[x-placement^="right"] .arrow::after{border-width:0.5rem 0.5rem 0.5rem 0}.bs-popover-right .arrow::before,.bs-popover-auto[x-placement^="right"] .arrow::before{left:0;border-right-color:rgba(0,0,0,0.25)}.bs-popover-right .arrow::after,.bs-popover-auto[x-placement^="right"] .arrow::after{left:1px;border-right-color:#32383e}.bs-popover-bottom,.bs-popover-auto[x-placement^="bottom"]{margin-top:0.5rem}.bs-popover-bottom .arrow,.bs-popover-auto[x-placement^="bottom"] .arrow{top:calc((0.5rem + 1px) * -1)}.bs-popover-bottom .arrow::before,.bs-popover-auto[x-placement^="bottom"] .arrow::before,.bs-popover-bottom .arrow::after,.bs-popover-auto[x-placement^="bottom"] .arrow::after{border-width:0 0.5rem 0.5rem 0.5rem}.bs-popover-bottom .arrow::before,.bs-popover-auto[x-placement^="bottom"] .arrow::before{top:0;border-bottom-color:rgba(0,0,0,0.25)}.bs-popover-bottom .arrow::after,.bs-popover-auto[x-placement^="bottom"] .arrow::after{top:1px;border-bottom-color:#32383e}.bs-popover-bottom .popover-header::before,.bs-popover-auto[x-placement^="bottom"] .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-0.5rem;content:"";border-bottom:1px solid #2c3036}.bs-popover-left,.bs-popover-auto[x-placement^="left"]{margin-right:0.5rem}.bs-popover-left .arrow,.bs-popover-auto[x-placement^="left"] .arrow{right:calc((0.5rem + 1px) * -1);width:0.5rem;height:1rem;margin:0.3rem 0}.bs-popover-left .arrow::before,.bs-popover-auto[x-placement^="left"] .arrow::before,.bs-popover-left .arrow::after,.bs-popover-auto[x-placement^="left"] .arrow::after{border-width:0.5rem 0 0.5rem 0.5rem}.bs-popover-left .arrow::before,.bs-popover-auto[x-placement^="left"] .arrow::before{right:0;border-left-color:rgba(0,0,0,0.25)}.bs-popover-left .arrow::after,.bs-popover-auto[x-placement^="left"] .arrow::after{right:1px;border-left-color:#32383e}.popover-header{padding:0.5rem 0.75rem;margin-bottom:0;font-size:0.9375rem;color:inherit;background-color:#2c3036;border-bottom:1px solid #202328;border-top-left-radius:calc(0.3rem - 1px);border-top-right-radius:calc(0.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:0.5rem 0.75rem;color:#aaa}.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;-webkit-transition:-webkit-transform 0.6s ease-in-out;transition:-webkit-transform 0.6s ease-in-out;transition:transform 0.6s ease-in-out;transition:transform 0.6s ease-in-out, -webkit-transform 0.6s ease-in-out}@media screen and (prefers-reduced-motion: reduce){.carousel-item{-webkit-transition:none;transition:none}}.carousel-item.active,.carousel-item-next,.carousel-item-prev{display:block}.carousel-item-next:not(.carousel-item-left),.active.carousel-item-right{-webkit-transform:translateX(100%);transform:translateX(100%)}.carousel-item-prev:not(.carousel-item-right),.active.carousel-item-left{-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;-webkit-transition-property:opacity;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item.active,.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;-webkit-transition:0s 0.6s opacity;transition:0s 0.6s opacity}@media screen and (prefers-reduced-motion: reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{-webkit-transition:none;transition:none}}.carousel-control-prev,.carousel-control-next{position:absolute;top:0;bottom:0;z-index:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:0.5;-webkit-transition:opacity 0.15s ease;transition:opacity 0.15s ease}@media screen and (prefers-reduced-motion: reduce){.carousel-control-prev,.carousel-control-next{-webkit-transition:none;transition:none}}.carousel-control-prev:hover,.carousel-control-prev:focus,.carousel-control-next:hover,.carousel-control-next:focus{color:#fff;text-decoration:none;outline:0;opacity:0.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-prev-icon,.carousel-control-next-icon{display:inline-block;width:20px;height:20px;background:transparent no-repeat center center;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%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' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{-webkit-box-sizing:content-box;box-sizing:content-box;-webkit-box-flex:0;-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;-webkit-transition:opacity 0.6s ease;transition:opacity 0.6s ease}@media screen and (prefers-reduced-motion: reduce){.carousel-indicators li{-webkit-transition:none;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:text-bottom;border:0.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:0.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.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:#3A3F44 !important}a.bg-primary:hover,a.bg-primary:focus,button.bg-primary:hover,button.bg-primary:focus{background-color:#232628 !important}.bg-secondary{background-color:#7A8288 !important}a.bg-secondary:hover,a.bg-secondary:focus,button.bg-secondary:hover,button.bg-secondary:focus{background-color:#62686d !important}.bg-success{background-color:#62c462 !important}a.bg-success:hover,a.bg-success:focus,button.bg-success:hover,button.bg-success:focus{background-color:#42b142 !important}.bg-info{background-color:#5bc0de !important}a.bg-info:hover,a.bg-info:focus,button.bg-info:hover,button.bg-info:focus{background-color:#31b0d5 !important}.bg-warning{background-color:#f89406 !important}a.bg-warning:hover,a.bg-warning:focus,button.bg-warning:hover,button.bg-warning:focus{background-color:#c67605 !important}.bg-danger{background-color:#ee5f5b !important}a.bg-danger:hover,a.bg-danger:focus,button.bg-danger:hover,button.bg-danger:focus{background-color:#e9322d !important}.bg-light{background-color:#e9ecef !important}a.bg-light:hover,a.bg-light:focus,button.bg-light:hover,button.bg-light:focus{background-color:#cbd3da !important}.bg-dark{background-color:#272B30 !important}a.bg-dark:hover,a.bg-dark:focus,button.bg-dark:hover,button.bg-dark:focus{background-color:#101214 !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:#3A3F44 !important}.border-secondary{border-color:#7A8288 !important}.border-success{border-color:#62c462 !important}.border-info{border-color:#5bc0de !important}.border-warning{border-color:#f89406 !important}.border-danger{border-color:#ee5f5b !important}.border-light{border-color:#e9ecef !important}.border-dark{border-color:#272B30 !important}.border-white{border-color:#fff !important}.rounded{border-radius:0.25rem !important}.rounded-top{border-top-left-radius:0.25rem !important;border-top-right-radius:0.25rem !important}.rounded-right{border-top-right-radius:0.25rem !important;border-bottom-right-radius:0.25rem !important}.rounded-bottom{border-bottom-right-radius:0.25rem !important;border-bottom-left-radius:0.25rem !important}.rounded-left{border-top-left-radius:0.25rem !important;border-bottom-left-radius:0.25rem !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:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-inline-flex{display:-webkit-inline-box !important;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:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-sm-inline-flex{display:-webkit-inline-box !important;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:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-md-inline-flex{display:-webkit-inline-box !important;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:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-lg-inline-flex{display:-webkit-inline-box !important;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:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-xl-inline-flex{display:-webkit-inline-box !important;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:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-print-inline-flex{display:-webkit-inline-box !important;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 iframe,.embed-responsive embed,.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.8571428571%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-3by4::before{padding-top:133.333333333%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-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{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-grow-1{-webkit-box-flex:1 !important;-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{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-between{-webkit-box-pack:justify !important;-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{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-stretch{-webkit-box-align:stretch !important;-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{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-sm-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-sm-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-sm-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-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{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-sm-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-sm-grow-1{-webkit-box-flex:1 !important;-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{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-sm-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-sm-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-sm-between{-webkit-box-pack:justify !important;-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{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-sm-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-sm-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-sm-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-sm-stretch{-webkit-box-align:stretch !important;-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{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-md-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-md-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-md-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-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{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-md-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-md-grow-1{-webkit-box-flex:1 !important;-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{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-md-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-md-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-md-between{-webkit-box-pack:justify !important;-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{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-md-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-md-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-md-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-md-stretch{-webkit-box-align:stretch !important;-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{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-lg-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-lg-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-lg-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-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{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-lg-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-lg-grow-1{-webkit-box-flex:1 !important;-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{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-lg-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-lg-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-lg-between{-webkit-box-pack:justify !important;-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{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-lg-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-lg-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-lg-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-lg-stretch{-webkit-box-align:stretch !important;-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{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-xl-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-xl-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-xl-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-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{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-xl-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-xl-grow-1{-webkit-box-flex:1 !important;-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{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-xl-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-xl-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-xl-between{-webkit-box-pack:justify !important;-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{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-xl-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-xl-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-xl-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-xl-stretch{-webkit-box-align:stretch !important;-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}}.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;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{-webkit-box-shadow:0 0.125rem 0.25rem rgba(0,0,0,0.075) !important;box-shadow:0 0.125rem 0.25rem rgba(0,0,0,0.075) !important}.shadow{-webkit-box-shadow:0 0.5rem 1rem rgba(0,0,0,0.15) !important;box-shadow:0 0.5rem 1rem rgba(0,0,0,0.15) !important}.shadow-lg{-webkit-box-shadow:0 1rem 3rem rgba(0,0,0,0.175) !important;box-shadow:0 1rem 3rem rgba(0,0,0,0.175) !important}.shadow-none{-webkit-box-shadow:none !important;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:0.25rem !important}.mt-1,.my-1{margin-top:0.25rem !important}.mr-1,.mx-1{margin-right:0.25rem !important}.mb-1,.my-1{margin-bottom:0.25rem !important}.ml-1,.mx-1{margin-left:0.25rem !important}.m-2{margin:0.5rem !important}.mt-2,.my-2{margin-top:0.5rem !important}.mr-2,.mx-2{margin-right:0.5rem !important}.mb-2,.my-2{margin-bottom:0.5rem !important}.ml-2,.mx-2{margin-left:0.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:0.25rem !important}.pt-1,.py-1{padding-top:0.25rem !important}.pr-1,.px-1{padding-right:0.25rem !important}.pb-1,.py-1{padding-bottom:0.25rem !important}.pl-1,.px-1{padding-left:0.25rem !important}.p-2{padding:0.5rem !important}.pt-2,.py-2{padding-top:0.5rem !important}.pr-2,.px-2{padding-right:0.5rem !important}.pb-2,.py-2{padding-bottom:0.5rem !important}.pl-2,.px-2{padding-left:0.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:-0.25rem !important}.mt-n1,.my-n1{margin-top:-0.25rem !important}.mr-n1,.mx-n1{margin-right:-0.25rem !important}.mb-n1,.my-n1{margin-bottom:-0.25rem !important}.ml-n1,.mx-n1{margin-left:-0.25rem !important}.m-n2{margin:-0.5rem !important}.mt-n2,.my-n2{margin-top:-0.5rem !important}.mr-n2,.mx-n2{margin-right:-0.5rem !important}.mb-n2,.my-n2{margin-bottom:-0.5rem !important}.ml-n2,.mx-n2{margin-left:-0.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:0.25rem !important}.mt-sm-1,.my-sm-1{margin-top:0.25rem !important}.mr-sm-1,.mx-sm-1{margin-right:0.25rem !important}.mb-sm-1,.my-sm-1{margin-bottom:0.25rem !important}.ml-sm-1,.mx-sm-1{margin-left:0.25rem !important}.m-sm-2{margin:0.5rem !important}.mt-sm-2,.my-sm-2{margin-top:0.5rem !important}.mr-sm-2,.mx-sm-2{margin-right:0.5rem !important}.mb-sm-2,.my-sm-2{margin-bottom:0.5rem !important}.ml-sm-2,.mx-sm-2{margin-left:0.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:0.25rem !important}.pt-sm-1,.py-sm-1{padding-top:0.25rem !important}.pr-sm-1,.px-sm-1{padding-right:0.25rem !important}.pb-sm-1,.py-sm-1{padding-bottom:0.25rem !important}.pl-sm-1,.px-sm-1{padding-left:0.25rem !important}.p-sm-2{padding:0.5rem !important}.pt-sm-2,.py-sm-2{padding-top:0.5rem !important}.pr-sm-2,.px-sm-2{padding-right:0.5rem !important}.pb-sm-2,.py-sm-2{padding-bottom:0.5rem !important}.pl-sm-2,.px-sm-2{padding-left:0.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:-0.25rem !important}.mt-sm-n1,.my-sm-n1{margin-top:-0.25rem !important}.mr-sm-n1,.mx-sm-n1{margin-right:-0.25rem !important}.mb-sm-n1,.my-sm-n1{margin-bottom:-0.25rem !important}.ml-sm-n1,.mx-sm-n1{margin-left:-0.25rem !important}.m-sm-n2{margin:-0.5rem !important}.mt-sm-n2,.my-sm-n2{margin-top:-0.5rem !important}.mr-sm-n2,.mx-sm-n2{margin-right:-0.5rem !important}.mb-sm-n2,.my-sm-n2{margin-bottom:-0.5rem !important}.ml-sm-n2,.mx-sm-n2{margin-left:-0.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:0.25rem !important}.mt-md-1,.my-md-1{margin-top:0.25rem !important}.mr-md-1,.mx-md-1{margin-right:0.25rem !important}.mb-md-1,.my-md-1{margin-bottom:0.25rem !important}.ml-md-1,.mx-md-1{margin-left:0.25rem !important}.m-md-2{margin:0.5rem !important}.mt-md-2,.my-md-2{margin-top:0.5rem !important}.mr-md-2,.mx-md-2{margin-right:0.5rem !important}.mb-md-2,.my-md-2{margin-bottom:0.5rem !important}.ml-md-2,.mx-md-2{margin-left:0.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:0.25rem !important}.pt-md-1,.py-md-1{padding-top:0.25rem !important}.pr-md-1,.px-md-1{padding-right:0.25rem !important}.pb-md-1,.py-md-1{padding-bottom:0.25rem !important}.pl-md-1,.px-md-1{padding-left:0.25rem !important}.p-md-2{padding:0.5rem !important}.pt-md-2,.py-md-2{padding-top:0.5rem !important}.pr-md-2,.px-md-2{padding-right:0.5rem !important}.pb-md-2,.py-md-2{padding-bottom:0.5rem !important}.pl-md-2,.px-md-2{padding-left:0.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:-0.25rem !important}.mt-md-n1,.my-md-n1{margin-top:-0.25rem !important}.mr-md-n1,.mx-md-n1{margin-right:-0.25rem !important}.mb-md-n1,.my-md-n1{margin-bottom:-0.25rem !important}.ml-md-n1,.mx-md-n1{margin-left:-0.25rem !important}.m-md-n2{margin:-0.5rem !important}.mt-md-n2,.my-md-n2{margin-top:-0.5rem !important}.mr-md-n2,.mx-md-n2{margin-right:-0.5rem !important}.mb-md-n2,.my-md-n2{margin-bottom:-0.5rem !important}.ml-md-n2,.mx-md-n2{margin-left:-0.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:0.25rem !important}.mt-lg-1,.my-lg-1{margin-top:0.25rem !important}.mr-lg-1,.mx-lg-1{margin-right:0.25rem !important}.mb-lg-1,.my-lg-1{margin-bottom:0.25rem !important}.ml-lg-1,.mx-lg-1{margin-left:0.25rem !important}.m-lg-2{margin:0.5rem !important}.mt-lg-2,.my-lg-2{margin-top:0.5rem !important}.mr-lg-2,.mx-lg-2{margin-right:0.5rem !important}.mb-lg-2,.my-lg-2{margin-bottom:0.5rem !important}.ml-lg-2,.mx-lg-2{margin-left:0.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:0.25rem !important}.pt-lg-1,.py-lg-1{padding-top:0.25rem !important}.pr-lg-1,.px-lg-1{padding-right:0.25rem !important}.pb-lg-1,.py-lg-1{padding-bottom:0.25rem !important}.pl-lg-1,.px-lg-1{padding-left:0.25rem !important}.p-lg-2{padding:0.5rem !important}.pt-lg-2,.py-lg-2{padding-top:0.5rem !important}.pr-lg-2,.px-lg-2{padding-right:0.5rem !important}.pb-lg-2,.py-lg-2{padding-bottom:0.5rem !important}.pl-lg-2,.px-lg-2{padding-left:0.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:-0.25rem !important}.mt-lg-n1,.my-lg-n1{margin-top:-0.25rem !important}.mr-lg-n1,.mx-lg-n1{margin-right:-0.25rem !important}.mb-lg-n1,.my-lg-n1{margin-bottom:-0.25rem !important}.ml-lg-n1,.mx-lg-n1{margin-left:-0.25rem !important}.m-lg-n2{margin:-0.5rem !important}.mt-lg-n2,.my-lg-n2{margin-top:-0.5rem !important}.mr-lg-n2,.mx-lg-n2{margin-right:-0.5rem !important}.mb-lg-n2,.my-lg-n2{margin-bottom:-0.5rem !important}.ml-lg-n2,.mx-lg-n2{margin-left:-0.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:0.25rem !important}.mt-xl-1,.my-xl-1{margin-top:0.25rem !important}.mr-xl-1,.mx-xl-1{margin-right:0.25rem !important}.mb-xl-1,.my-xl-1{margin-bottom:0.25rem !important}.ml-xl-1,.mx-xl-1{margin-left:0.25rem !important}.m-xl-2{margin:0.5rem !important}.mt-xl-2,.my-xl-2{margin-top:0.5rem !important}.mr-xl-2,.mx-xl-2{margin-right:0.5rem !important}.mb-xl-2,.my-xl-2{margin-bottom:0.5rem !important}.ml-xl-2,.mx-xl-2{margin-left:0.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:0.25rem !important}.pt-xl-1,.py-xl-1{padding-top:0.25rem !important}.pr-xl-1,.px-xl-1{padding-right:0.25rem !important}.pb-xl-1,.py-xl-1{padding-bottom:0.25rem !important}.pl-xl-1,.px-xl-1{padding-left:0.25rem !important}.p-xl-2{padding:0.5rem !important}.pt-xl-2,.py-xl-2{padding-top:0.5rem !important}.pr-xl-2,.px-xl-2{padding-right:0.5rem !important}.pb-xl-2,.py-xl-2{padding-bottom:0.5rem !important}.pl-xl-2,.px-xl-2{padding-left:0.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:-0.25rem !important}.mt-xl-n1,.my-xl-n1{margin-top:-0.25rem !important}.mr-xl-n1,.mx-xl-n1{margin-right:-0.25rem !important}.mb-xl-n1,.my-xl-n1{margin-bottom:-0.25rem !important}.ml-xl-n1,.mx-xl-n1{margin-left:-0.25rem !important}.m-xl-n2{margin:-0.5rem !important}.mt-xl-n2,.my-xl-n2{margin-top:-0.5rem !important}.mr-xl-n2,.mx-xl-n2{margin-right:-0.5rem !important}.mb-xl-n2,.my-xl-n2{margin-bottom:-0.5rem !important}.ml-xl-n2,.mx-xl-n2{margin-left:-0.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}}.text-monospace{font-family:SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace}.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:#3A3F44 !important}a.text-primary:hover,a.text-primary:focus{color:#17191b !important}.text-secondary{color:#7A8288 !important}a.text-secondary:hover,a.text-secondary:focus{color:#565b60 !important}.text-success{color:#62c462 !important}a.text-success:hover,a.text-success:focus{color:#3b9e3b !important}.text-info{color:#5bc0de !important}a.text-info:hover,a.text-info:focus{color:#28a1c5 !important}.text-warning{color:#f89406 !important}a.text-warning:hover,a.text-warning:focus{color:#ad6704 !important}.text-danger{color:#ee5f5b !important}a.text-danger:hover,a.text-danger:focus{color:#e51d18 !important}.text-light{color:#e9ecef !important}a.text-light:hover,a.text-light:focus{color:#bdc6cf !important}.text-dark{color:#272B30 !important}a.text-dark:hover,a.text-dark:focus{color:#050506 !important}.text-body{color:#aaa !important}.text-muted{color:#7A8288 !important}.text-black-50{color:rgba(0,0,0,0.5) !important}.text-white-50{color:rgba(255,255,255,0.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-reset{color:inherit !important}.visible{visibility:visible !important}.invisible{visibility:hidden !important}@media print{*,*::before,*::after{text-shadow:none !important;-webkit-box-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}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}p,h2,h3{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 th,.table-bordered td{border:1px solid #dee2e6 !important}.table-dark{color:inherit}.table-dark th,.table-dark td,.table-dark thead th,.table-dark tbody+tbody{border-color:rgba(0,0,0,0.6)}.table .thead-dark th{color:inherit;border-color:rgba(0,0,0,0.6)}}.navbar{border:1px solid rgba(0,0,0,0.6);text-shadow:1px 1px 1px rgba(0,0,0,0.3)}.navbar .container{padding:0}.navbar .navbar-toggler{border-color:rgba(0,0,0,0.6)}.navbar-fixed-top{border-width:0 0 1px 0}.navbar-fixed-bottom{border-width:1px 0 0 0}.navbar .nav-link{padding:1rem;border-left:1px solid rgba(255,255,255,0.1);border-right:1px solid rgba(0,0,0,0.2)}.navbar .nav-link:hover,.navbar .nav-link:focus{background-image:-webkit-gradient(linear, left top, left bottom, from(#101112), color-stop(40%, #17191b), to(#1b1e20));background-image:linear-gradient(#101112, #17191b 40%, #1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-left:1px solid rgba(0,0,0,0.2)}.navbar-brand{padding:0.75rem 1rem calc(54px - 0.75rem - 30px);margin-right:0;border-right:1px solid rgba(0,0,0,0.2)}.navbar .nav-item.active .nav-link{background-color:rgba(0,0,0,0.3);border-left:1px solid rgba(0,0,0,0.2)}.navbar-nav .nav-item+.nav-item{margin-left:0}.navbar.bg-light{text-shadow:1px 1px 1px rgba(0,0,0,0.1)}.navbar.bg-light .nav-link:hover,.navbar.bg-light .nav-link:focus{background-image:-webkit-gradient(linear, left top, left bottom, from(#4e5458), color-stop(40%, #565b60), to(#5b6165));background-image:linear-gradient(#4e5458, #565b60 40%, #5b6165);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-left:1px solid rgba(0,0,0,0.2)}@media (max-width: 576px){.navbar-expand-sm .navbar-brand,.navbar-expand-sm .nav-link{border:none !important}}@media (max-width: 768px){.navbar-expand-md .navbar-brand,.navbar-expand-md .nav-link{border:none !important}}@media (max-width: 992px){.navbar-expand-lg .navbar-brand,.navbar-expand-lg .nav-link{border:none !important}}.btn{border-color:rgba(0,0,0,0.6);text-shadow:1px 1px 1px rgba(0,0,0,0.3)}.btn:not([disabled]):not(.disabled).active,.btn.disabled{border-color:rgba(0,0,0,0.6);-webkit-box-shadow:none;box-shadow:none}.btn:hover,.btn:focus,.btn:not([disabled]):not(.disabled):active,.btn:not([disabled]):not(.disabled):active:hover,.btn:not([disabled]):not(.disabled).active:hover{border-color:rgba(0,0,0,0.6)}.btn-primary{background-image:-webkit-gradient(linear, left top, left bottom, from(#484e55), color-stop(60%, #3A3F44), to(#313539));background-image:linear-gradient(#484e55, #3A3F44 60%, #313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-primary:not([disabled]):not(.disabled):hover,.btn-primary:not([disabled]):not(.disabled):focus,.btn-primary:not([disabled]):not(.disabled):active:hover,.btn-primary:not([disabled]):not(.disabled).active:hover{background-image:-webkit-gradient(linear, left top, left bottom, from(#101112), color-stop(40%, #17191b), to(#1b1e20));background-image:linear-gradient(#101112, #17191b 40%, #1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-secondary{background-image:-webkit-gradient(linear, left top, left bottom, from(#8a9196), color-stop(60%, #7A8288), to(#70787d));background-image:linear-gradient(#8a9196, #7A8288 60%, #70787d);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-secondary:not([disabled]):not(.disabled):hover,.btn-secondary:not([disabled]):not(.disabled):focus,.btn-secondary:not([disabled]):not(.disabled):active,.btn-secondary:not([disabled]):not(.disabled).active{background-image:-webkit-gradient(linear, left top, left bottom, from(#4e5458), color-stop(40%, #565b60), to(#5b6165));background-image:linear-gradient(#4e5458, #565b60 40%, #5b6165);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-success{background-image:-webkit-gradient(linear, left top, left bottom, from(#78cc78), color-stop(60%, #62c462), to(#53be53));background-image:linear-gradient(#78cc78, #62c462 60%, #53be53);background-repeat:no-repeat;-webkit-filter:none;filter:none;color:#fff}.btn-success:not([disabled]):not(.disabled):hover,.btn-success:not([disabled]):not(.disabled):focus,.btn-success:not([disabled]):not(.disabled):active,.btn-success:not([disabled]):not(.disabled).active{background-image:-webkit-gradient(linear, left top, left bottom, from(#379337), color-stop(40%, #3b9e3b), to(#3ea63e));background-image:linear-gradient(#379337, #3b9e3b 40%, #3ea63e);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-info{background-image:-webkit-gradient(linear, left top, left bottom, from(#74cae3), color-stop(60%, #5bc0de), to(#4ab9db));background-image:linear-gradient(#74cae3, #5bc0de 60%, #4ab9db);background-repeat:no-repeat;-webkit-filter:none;filter:none;color:#fff}.btn-info:not([disabled]):not(.disabled):hover,.btn-info:not([disabled]):not(.disabled):focus,.btn-info:not([disabled]):not(.disabled):active,.btn-info:not([disabled]):not(.disabled).active{background-image:-webkit-gradient(linear, left top, left bottom, from(#2596b8), color-stop(40%, #28a1c5), to(#29a8cd));background-image:linear-gradient(#2596b8, #28a1c5 40%, #29a8cd);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-warning{background-image:-webkit-gradient(linear, left top, left bottom, from(#faa123), color-stop(60%, #f89406), to(#e48806));background-image:linear-gradient(#faa123, #f89406 60%, #e48806);background-repeat:no-repeat;-webkit-filter:none;filter:none;color:#fff}.btn-warning:not([disabled]):not(.disabled):hover,.btn-warning:not([disabled]):not(.disabled):focus,.btn-warning:not([disabled]):not(.disabled):active,.btn-warning:not([disabled]):not(.disabled).active{background-image:-webkit-gradient(linear, left top, left bottom, from(#9e5f04), color-stop(40%, #ad6704), to(#b76d04));background-image:linear-gradient(#9e5f04, #ad6704 40%, #b76d04);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-danger{background-image:-webkit-gradient(linear, left top, left bottom, from(#f17a77), color-stop(60%, #ee5f5b), to(#ec4d49));background-image:linear-gradient(#f17a77, #ee5f5b 60%, #ec4d49);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-danger:not([disabled]):not(.disabled):hover,.btn-danger:not([disabled]):not(.disabled):focus,.btn-danger:not([disabled]):not(.disabled):active,.btn-danger:not([disabled]):not(.disabled).active{background-image:-webkit-gradient(linear, left top, left bottom, from(#d71c16), color-stop(40%, #e51d18), to(#e8241f));background-image:linear-gradient(#d71c16, #e51d18 40%, #e8241f);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-link,.btn-link:hover{border-color:transparent}.btn-group .btn.active,.btn-group-vertical .btn.active{border-color:rgba(0,0,0,0.6)}h1,h2,h3,h4,h5,h6{text-shadow:-1px -1px 0 rgba(0,0,0,0.3)}.table-primary,.table-secondary,.table-success,.table-info,.table-warning,.table-danger{color:#fff}.table-primary,.table-primary>th,.table-primary>td{background-color:#3A3F44}.table-secondary,.table-secondary>th,.table-secondary>td{background-color:#7A8288}.table-light,.table-light>th,.table-light>td{background-color:#e9ecef}.table-dark,.table-dark>th,.table-dark>td{background-color:#272B30}.table-success,.table-success>th,.table-success>td{background-color:#62c462}.table-info,.table-info>th,.table-info>td{background-color:#5bc0de}.table-danger,.table-danger>th,.table-danger>td{background-color:#ee5f5b}.table-warning,.table-warning>th,.table-warning>td{background-color:#f89406}.table-active,.table-active>th,.table-active>td{background-color:rgba(255,255,255,0.075)}.table-hover .table-primary:hover,.table-hover .table-primary:hover>th,.table-hover .table-primary:hover>td{background-color:#2e3236}.table-hover .table-secondary:hover,.table-hover .table-secondary:hover>th,.table-hover .table-secondary:hover>td{background-color:#6e757b}.table-hover .table-light:hover,.table-hover .table-light:hover>th,.table-hover .table-light:hover>td{background-color:#dadfe4}.table-hover .table-dark:hover,.table-hover .table-dark:hover>th,.table-hover .table-dark:hover>td{background-color:#1c1e22}.table-hover .table-success:hover,.table-hover .table-success:hover>th,.table-hover .table-success:hover>td{background-color:#4fbd4f}.table-hover .table-info:hover,.table-hover .table-info:hover>th,.table-hover .table-info:hover>td{background-color:#46b8da}.table-hover .table-danger:hover,.table-hover .table-danger:hover>th,.table-hover .table-danger:hover>td{background-color:#ec4844}.table-hover .table-warning:hover,.table-hover .table-warning:hover>th,.table-hover .table-warning:hover>td{background-color:#df8505}.table-hover .table-active:hover,.table-hover .table-active:hover>th,.table-hover .table-active:hover>td{background-color:rgba(255,255,255,0.075)}legend{color:#fff}.input-group-addon{background-image:-webkit-gradient(linear, left top, left bottom, from(#8a9196), color-stop(60%, #7A8288), to(#70787d));background-image:linear-gradient(#8a9196, #7A8288 60%, #70787d);background-repeat:no-repeat;-webkit-filter:none;filter:none;text-shadow:1px 1px 1px rgba(0,0,0,0.3);color:#fff}.nav-tabs .nav-link,.nav-tabs .nav-link:hover{color:#fff}.nav-pills .nav-link{background-image:-webkit-gradient(linear, left top, left bottom, from(#484e55), color-stop(60%, #3A3F44), to(#313539));background-image:linear-gradient(#484e55, #3A3F44 60%, #313539);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,0.6);text-shadow:1px 1px 1px rgba(0,0,0,0.3);color:#fff}.nav-pills .nav-link:hover{background-image:-webkit-gradient(linear, left top, left bottom, from(#101112), color-stop(40%, #17191b), to(#1b1e20));background-image:linear-gradient(#101112, #17191b 40%, #1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,0.6)}.nav-pills .nav-link.active,.nav-pills .nav-link:hover{background-color:transparent;background-image:-webkit-gradient(linear, left top, left bottom, from(#101112), color-stop(40%, #17191b), to(#1b1e20));background-image:linear-gradient(#101112, #17191b 40%, #1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,0.6)}.nav-pills .nav-link.disabled,.nav-pills .nav-link.disabled:hover{background-image:-webkit-gradient(linear, left top, left bottom, from(#484e55), color-stop(60%, #3A3F44), to(#313539));background-image:linear-gradient(#484e55, #3A3F44 60%, #313539);background-repeat:no-repeat;-webkit-filter:none;filter:none;color:#7A8288}.pagination .page-link{text-shadow:1px 1px 1px rgba(0,0,0,0.3);background-image:-webkit-gradient(linear, left top, left bottom, from(#484e55), color-stop(60%, #3A3F44), to(#313539));background-image:linear-gradient(#484e55, #3A3F44 60%, #313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.pagination .page-link:hover{background-image:-webkit-gradient(linear, left top, left bottom, from(#101112), color-stop(40%, #17191b), to(#1b1e20));background-image:linear-gradient(#101112, #17191b 40%, #1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;text-decoration:none}.pagination .page-item.active .page-link{background-image:-webkit-gradient(linear, left top, left bottom, from(#101112), color-stop(40%, #17191b), to(#1b1e20));background-image:linear-gradient(#101112, #17191b 40%, #1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none}.pagination .page-item.disabled .page-link{background-image:-webkit-gradient(linear, left top, left bottom, from(#484e55), color-stop(60%, #3A3F44), to(#313539));background-image:linear-gradient(#484e55, #3A3F44 60%, #313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.breadcrumb{border:1px solid rgba(0,0,0,0.6);text-shadow:1px 1px 1px rgba(0,0,0,0.3);background-color:transparent;background-image:-webkit-gradient(linear, left top, left bottom, from(#484e55), color-stop(60%, #3A3F44), to(#313539));background-image:linear-gradient(#484e55, #3A3F44 60%, #313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.breadcrumb a,.breadcrumb a:hover{color:#fff}.alert .close{color:#000;text-decoration:none}.alert{border:none;color:#fff}.alert a,.alert .alert-link{color:#fff;text-decoration:underline}.alert-primary{background-color:#3A3F44}.alert-secondary{background-color:#7A8288}.alert-success{background-color:#62c462}.alert-info{background-color:#5bc0de}.alert-warning{background-color:#f89406}.alert-danger{background-color:#ee5f5b}.alert-light{background-color:#e9ecef}.alert-dark{background-color:#272B30}.alert-light,.alert-light a:not(.btn),.alert-light .alert-link{color:#272B30}.badge-success,.badge-warning,.badge-info{color:#fff}.jumbotron{border:1px solid rgba(0,0,0,0.6)}.list-group-item:hover{background-color:#1c1e22} + */:root{--blue: #007bff;--indigo: #6610f2;--purple: #6f42c1;--pink: #e83e8c;--red: #ee5f5b;--orange: #fd7e14;--yellow: #f89406;--green: #62c462;--teal: #20c997;--cyan: #5bc0de;--white: #fff;--gray: #7A8288;--gray-dark: #3A3F44;--primary: #3A3F44;--secondary: #7A8288;--success: #62c462;--info: #5bc0de;--warning: #f89406;--danger: #ee5f5b;--light: #e9ecef;--dark: #272B30;--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", 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}*,*::before,*::after{-webkit-box-sizing:border-box;box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}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", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";font-size:0.9375rem;font-weight:400;line-height:1.5;color:#aaa;text-align:left;background-color:#272B30}[tabindex="-1"]:focus:not(:focus-visible){outline:0 !important}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:0.5rem}p{margin-top:0;margin-bottom:1rem}abbr[title],abbr[data-original-title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul,dl{margin-top:0;margin-bottom:1rem}ol ol,ul ul,ol ul,ul ol{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:#fff;text-decoration:none;background-color:transparent}a:hover{color:#d9d9d9;text-decoration:underline}a:not([href]){color:inherit;text-decoration:none}a:not([href]):hover{color:inherit;text-decoration:none}pre,code,kbd,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}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:0.75rem;padding-bottom:0.75rem;color:#7A8288;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:0.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}input,button,select,optgroup,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}button,[type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button:not(:disabled),[type="button"]:not(:disabled),[type="reset"]:not(:disabled),[type="submit"]:not(:disabled){cursor:pointer}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{padding:0;border-style:none}input[type="radio"],input[type="checkbox"]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="date"],input[type="time"],input[type="datetime-local"],input[type="month"]{-webkit-appearance:listbox}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:0.5rem;font-weight:500;line-height:1.2}h1,.h1{font-size:2.34375rem}h2,.h2{font-size:1.875rem}h3,.h3{font-size:1.640625rem}h4,.h4{font-size:1.40625rem}h5,.h5{font-size:1.171875rem}h6,.h6{font-size:0.9375rem}.lead{font-size:1.171875rem;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,0.1)}small,.small{font-size:80%;font-weight:400}mark,.mark{padding:0.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:0.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.171875rem}.blockquote-footer{display:block;font-size:80%;color:#7A8288}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:0.25rem;background-color:#272B30;border:1px solid #dee2e6;border-radius:0.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:0.5rem;line-height:1}.figure-caption{font-size:90%;color:#7A8288}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:0.2rem 0.4rem;font-size:87.5%;color:#fff;background-color:#272B30;border-radius:0.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:inherit}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width: 576px){.container{max-width:540px}}@media (min-width: 768px){.container{max-width:720px}}@media (min-width: 992px){.container{max-width:960px}}@media (min-width: 1200px){.container{max-width:1140px}}.container-fluid,.container-sm,.container-md,.container-lg,.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-sm,.container-md{max-width:720px}}@media (min-width: 992px){.container,.container-sm,.container-md,.container-lg{max-width:960px}}@media (min-width: 1200px){.container,.container-sm,.container-md,.container-lg,.container-xl{max-width:1140px}}.row{display:-webkit-box;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-1,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-10,.col-11,.col-12,.col,.col-auto,.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,.col-sm,.col-sm-auto,.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,.col-md,.col-md-auto,.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,.col-lg,.col-lg-auto,.col-xl-1,.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-10,.col-xl-11,.col-xl-12,.col-xl,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-1>*{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.row-cols-4>*{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-webkit-box-flex:0;-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-1{margin-left:8.3333333333%}.offset-2{margin-left:16.6666666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.3333333333%}.offset-5{margin-left:41.6666666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.3333333333%}.offset-8{margin-left:66.6666666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.3333333333%}.offset-11{margin-left:91.6666666667%}@media (min-width: 576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-sm-1>*{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.row-cols-sm-4>*{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-webkit-box-flex:0;-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-sm-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-sm-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-sm-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-sm-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-sm-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-sm-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-sm-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-sm-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-sm-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-sm-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-sm-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-sm-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-sm-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-sm-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-sm-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-sm-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-sm-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-sm-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-sm-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-sm-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-sm-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-sm-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-sm-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.3333333333%}.offset-sm-2{margin-left:16.6666666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.3333333333%}.offset-sm-5{margin-left:41.6666666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.3333333333%}.offset-sm-8{margin-left:66.6666666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.3333333333%}.offset-sm-11{margin-left:91.6666666667%}}@media (min-width: 768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-md-1>*{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.row-cols-md-4>*{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-webkit-box-flex:0;-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-md-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-md-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-md-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-md-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-md-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-md-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-md-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-md-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-md-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-md-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-md-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-md-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-md-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-md-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-md-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-md-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-md-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-md-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-md-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-md-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-md-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-md-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-md-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.3333333333%}.offset-md-2{margin-left:16.6666666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.3333333333%}.offset-md-5{margin-left:41.6666666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.3333333333%}.offset-md-8{margin-left:66.6666666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.3333333333%}.offset-md-11{margin-left:91.6666666667%}}@media (min-width: 992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-lg-1>*{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.row-cols-lg-4>*{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-webkit-box-flex:0;-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-lg-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-lg-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-lg-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-lg-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-lg-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-lg-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-lg-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-lg-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-lg-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-lg-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-lg-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-lg-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-lg-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-lg-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-lg-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-lg-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-lg-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-lg-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-lg-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-lg-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-lg-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-lg-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-lg-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.3333333333%}.offset-lg-2{margin-left:16.6666666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.3333333333%}.offset-lg-5{margin-left:41.6666666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.3333333333%}.offset-lg-8{margin-left:66.6666666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.3333333333%}.offset-lg-11{margin-left:91.6666666667%}}@media (min-width: 1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-xl-1>*{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.row-cols-xl-4>*{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-webkit-box-flex:0;-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-xl-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-xl-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-xl-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-xl-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-xl-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-xl-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-xl-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-xl-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-xl-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-xl-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-xl-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-xl-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-xl-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-xl-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-xl-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-xl-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-xl-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-xl-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-xl-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-xl-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-xl-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-xl-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-xl-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.3333333333%}.offset-xl-2{margin-left:16.6666666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.3333333333%}.offset-xl-5{margin-left:41.6666666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.3333333333%}.offset-xl-8{margin-left:66.6666666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.3333333333%}.offset-xl-11{margin-left:91.6666666667%}}.table{width:100%;margin-bottom:1rem;color:#fff}.table th,.table td{padding:0.75rem;vertical-align:top;border-top:1px solid rgba(0,0,0,0.6)}.table thead th{vertical-align:bottom;border-bottom:2px solid rgba(0,0,0,0.6)}.table tbody+tbody{border-top:2px solid rgba(0,0,0,0.6)}.table-sm th,.table-sm td{padding:0.3rem}.table-bordered{border:1px solid rgba(0,0,0,0.6)}.table-bordered th,.table-bordered td{border:1px solid rgba(0,0,0,0.6)}.table-bordered thead th,.table-bordered thead td{border-bottom-width:2px}.table-borderless th,.table-borderless td,.table-borderless thead th,.table-borderless tbody+tbody{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,0.05)}.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,0.075)}.table-primary,.table-primary>th,.table-primary>td{background-color:#c8c9cb}.table-primary th,.table-primary td,.table-primary thead th,.table-primary tbody+tbody{border-color:#999b9e}.table-hover .table-primary:hover{background-color:#bbbcbf}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#bbbcbf}.table-secondary,.table-secondary>th,.table-secondary>td{background-color:#dadcde}.table-secondary th,.table-secondary td,.table-secondary thead th,.table-secondary tbody+tbody{border-color:#babec1}.table-hover .table-secondary:hover{background-color:#cdcfd2}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#cdcfd2}.table-success,.table-success>th,.table-success>td{background-color:#d3eed3}.table-success th,.table-success td,.table-success thead th,.table-success tbody+tbody{border-color:#ade0ad}.table-hover .table-success:hover{background-color:#c1e7c1}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#c1e7c1}.table-info,.table-info>th,.table-info>td{background-color:#d1edf6}.table-info th,.table-info td,.table-info thead th,.table-info tbody+tbody{border-color:#aadeee}.table-hover .table-info:hover{background-color:#bce5f2}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#bce5f2}.table-warning,.table-warning>th,.table-warning>td{background-color:#fde1b9}.table-warning th,.table-warning td,.table-warning thead th,.table-warning tbody+tbody{border-color:#fbc77e}.table-hover .table-warning:hover{background-color:#fcd6a0}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#fcd6a0}.table-danger,.table-danger>th,.table-danger>td{background-color:#fad2d1}.table-danger th,.table-danger td,.table-danger thead th,.table-danger tbody+tbody{border-color:#f6acaa}.table-hover .table-danger:hover{background-color:#f8bcba}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f8bcba}.table-light,.table-light>th,.table-light>td{background-color:#f9fafb}.table-light th,.table-light td,.table-light thead th,.table-light tbody+tbody{border-color:#f4f5f7}.table-hover .table-light:hover{background-color:#eaedf1}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#eaedf1}.table-dark,.table-dark>th,.table-dark>td{background-color:#c3c4c5}.table-dark th,.table-dark td,.table-dark thead th,.table-dark tbody+tbody{border-color:#8f9193}.table-hover .table-dark:hover{background-color:#b6b7b8}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b6b7b8}.table-active,.table-active>th,.table-active>td{background-color:rgba(255,255,255,0.075)}.table-hover .table-active:hover{background-color:rgba(242,242,242,0.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(242,242,242,0.075)}.table .thead-dark th{color:#fff;background-color:#3A3F44;border-color:rgba(0,0,0,0.6)}.table .thead-light th{color:#52575C;background-color:#e9ecef;border-color:rgba(0,0,0,0.6)}.table-dark{color:#fff;background-color:#3A3F44}.table-dark th,.table-dark td,.table-dark thead th{border-color:rgba(0,0,0,0.6)}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,0.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,0.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 + 1.5rem + 2px);padding:0.75rem 1rem;font-size:0.9375rem;font-weight:400;line-height:1.5;color:#52575C;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:0.25rem;-webkit-transition:border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.form-control{-webkit-transition:none;transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #52575C}.form-control:focus{color:#52575C;background-color:#fff;border-color:#757f89;outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25)}.form-control::-webkit-input-placeholder{color:#7A8288;opacity:1}.form-control::-ms-input-placeholder{color:#7A8288;opacity:1}.form-control::placeholder{color:#7A8288;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#ccc;opacity:1}select.form-control:focus::-ms-value{color:#52575C;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(0.75rem + 1px);padding-bottom:calc(0.75rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(0.5rem + 1px);padding-bottom:calc(0.5rem + 1px);font-size:1.171875rem;line-height:1.5}.col-form-label-sm{padding-top:calc(0.25rem + 1px);padding-bottom:calc(0.25rem + 1px);font-size:0.8203125rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:0.75rem 0;margin-bottom:0;font-size:0.9375rem;line-height:1.5;color:#aaa;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-sm,.form-control-plaintext.form-control-lg{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + 0.5rem + 2px);padding:0.25rem 0.5rem;font-size:0.8203125rem;line-height:1.5;border-radius:0.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:0.5rem 1rem;font-size:1.171875rem;line-height:1.5;border-radius:0.3rem}select.form-control[size],select.form-control[multiple]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:0.25rem}.form-row{display:-webkit-box;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:0.3rem;margin-left:-1.25rem}.form-check-input[disabled] ~ .form-check-label,.form-check-input:disabled ~ .form-check-label{color:#7A8288}.form-check-label{margin-bottom:0}.form-check-inline{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:0.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:0.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:0.25rem;font-size:80%;color:#62c462}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:0.25rem 0.5rem;margin-top:.1rem;font-size:0.8203125rem;line-height:1.5;color:#fff;background-color:rgba(98,196,98,0.9);border-radius:0.25rem}.was-validated :valid ~ .valid-feedback,.was-validated :valid ~ .valid-tooltip,.is-valid ~ .valid-feedback,.is-valid ~ .valid-tooltip{display:block}.was-validated .form-control:valid,.form-control.is-valid{border-color:#62c462;padding-right:calc(1.5em + 1.5rem);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='%2362c462' 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(0.375em + 0.375rem) center;background-size:calc(0.75em + 0.75rem) calc(0.75em + 0.75rem)}.was-validated .form-control:valid:focus,.form-control.is-valid:focus{border-color:#62c462;-webkit-box-shadow:0 0 0 0.2rem rgba(98,196,98,0.25);box-shadow:0 0 0 0.2rem rgba(98,196,98,0.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + 1.5rem);background-position:top calc(0.375em + 0.375rem) right calc(0.375em + 0.375rem)}.was-validated .custom-select:valid,.custom-select.is-valid{border-color:#62c462;padding-right:calc(0.75em + 3.125rem);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='%233A3F44' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 1rem center/8px 10px,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='%2362c462' 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") #fff no-repeat center right 2rem/calc(0.75em + 0.75rem) calc(0.75em + 0.75rem)}.was-validated .custom-select:valid:focus,.custom-select.is-valid:focus{border-color:#62c462;-webkit-box-shadow:0 0 0 0.2rem rgba(98,196,98,0.25);box-shadow:0 0 0 0.2rem rgba(98,196,98,0.25)}.was-validated .form-check-input:valid ~ .form-check-label,.form-check-input.is-valid ~ .form-check-label{color:#62c462}.was-validated .form-check-input:valid ~ .valid-feedback,.was-validated .form-check-input:valid ~ .valid-tooltip,.form-check-input.is-valid ~ .valid-feedback,.form-check-input.is-valid ~ .valid-tooltip{display:block}.was-validated .custom-control-input:valid ~ .custom-control-label,.custom-control-input.is-valid ~ .custom-control-label{color:#62c462}.was-validated .custom-control-input:valid ~ .custom-control-label::before,.custom-control-input.is-valid ~ .custom-control-label::before{border-color:#62c462}.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before,.custom-control-input.is-valid:checked ~ .custom-control-label::before{border-color:#87d287;background-color:#87d287}.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before,.custom-control-input.is-valid:focus ~ .custom-control-label::before{-webkit-box-shadow:0 0 0 0.2rem rgba(98,196,98,0.25);box-shadow:0 0 0 0.2rem rgba(98,196,98,0.25)}.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before,.custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before{border-color:#62c462}.was-validated .custom-file-input:valid ~ .custom-file-label,.custom-file-input.is-valid ~ .custom-file-label{border-color:#62c462}.was-validated .custom-file-input:valid:focus ~ .custom-file-label,.custom-file-input.is-valid:focus ~ .custom-file-label{border-color:#62c462;-webkit-box-shadow:0 0 0 0.2rem rgba(98,196,98,0.25);box-shadow:0 0 0 0.2rem rgba(98,196,98,0.25)}.invalid-feedback{display:none;width:100%;margin-top:0.25rem;font-size:80%;color:#ee5f5b}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:0.25rem 0.5rem;margin-top:.1rem;font-size:0.8203125rem;line-height:1.5;color:#fff;background-color:rgba(238,95,91,0.9);border-radius:0.25rem}.was-validated :invalid ~ .invalid-feedback,.was-validated :invalid ~ .invalid-tooltip,.is-invalid ~ .invalid-feedback,.is-invalid ~ .invalid-tooltip{display:block}.was-validated .form-control:invalid,.form-control.is-invalid{border-color:#ee5f5b;padding-right:calc(1.5em + 1.5rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23ee5f5b' 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='%23ee5f5b' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(0.375em + 0.375rem) center;background-size:calc(0.75em + 0.75rem) calc(0.75em + 0.75rem)}.was-validated .form-control:invalid:focus,.form-control.is-invalid:focus{border-color:#ee5f5b;-webkit-box-shadow:0 0 0 0.2rem rgba(238,95,91,0.25);box-shadow:0 0 0 0.2rem rgba(238,95,91,0.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + 1.5rem);background-position:top calc(0.375em + 0.375rem) right calc(0.375em + 0.375rem)}.was-validated .custom-select:invalid,.custom-select.is-invalid{border-color:#ee5f5b;padding-right:calc(0.75em + 3.125rem);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='%233A3F44' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 1rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23ee5f5b' 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='%23ee5f5b' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 2rem/calc(0.75em + 0.75rem) calc(0.75em + 0.75rem)}.was-validated .custom-select:invalid:focus,.custom-select.is-invalid:focus{border-color:#ee5f5b;-webkit-box-shadow:0 0 0 0.2rem rgba(238,95,91,0.25);box-shadow:0 0 0 0.2rem rgba(238,95,91,0.25)}.was-validated .form-check-input:invalid ~ .form-check-label,.form-check-input.is-invalid ~ .form-check-label{color:#ee5f5b}.was-validated .form-check-input:invalid ~ .invalid-feedback,.was-validated .form-check-input:invalid ~ .invalid-tooltip,.form-check-input.is-invalid ~ .invalid-feedback,.form-check-input.is-invalid ~ .invalid-tooltip{display:block}.was-validated .custom-control-input:invalid ~ .custom-control-label,.custom-control-input.is-invalid ~ .custom-control-label{color:#ee5f5b}.was-validated .custom-control-input:invalid ~ .custom-control-label::before,.custom-control-input.is-invalid ~ .custom-control-label::before{border-color:#ee5f5b}.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before,.custom-control-input.is-invalid:checked ~ .custom-control-label::before{border-color:#f38c89;background-color:#f38c89}.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before,.custom-control-input.is-invalid:focus ~ .custom-control-label::before{-webkit-box-shadow:0 0 0 0.2rem rgba(238,95,91,0.25);box-shadow:0 0 0 0.2rem rgba(238,95,91,0.25)}.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before,.custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before{border-color:#ee5f5b}.was-validated .custom-file-input:invalid ~ .custom-file-label,.custom-file-input.is-invalid ~ .custom-file-label{border-color:#ee5f5b}.was-validated .custom-file-input:invalid:focus ~ .custom-file-label,.custom-file-input.is-invalid:focus ~ .custom-file-label{border-color:#ee5f5b;-webkit-box-shadow:0 0 0 0.2rem rgba(238,95,91,0.25);box-shadow:0 0 0 0.2rem rgba(238,95,91,0.25)}.form-inline{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width: 576px){.form-inline label{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-box-align:center;-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 .input-group,.form-inline .custom-select{width:auto}.form-inline .form-check{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack: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:0.25rem;margin-left:0}.form-inline .custom-control{-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#aaa;text-align:center;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:0.75rem 1rem;font-size:0.9375rem;line-height:1.5;border-radius:0.25rem;-webkit-transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.btn{-webkit-transition:none;transition:none}}.btn:hover{color:#aaa;text-decoration:none}.btn:focus,.btn.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25)}.btn.disabled,.btn:disabled{opacity:0.65}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#3A3F44;border-color:#3A3F44}.btn-primary:hover{color:#fff;background-color:#282c2f;border-color:#232628}.btn-primary:focus,.btn-primary.focus{color:#fff;background-color:#282c2f;border-color:#232628;-webkit-box-shadow:0 0 0 0.2rem rgba(88,92,96,0.5);box-shadow:0 0 0 0.2rem rgba(88,92,96,0.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#3A3F44;border-color:#3A3F44}.btn-primary:not(:disabled):not(.disabled):active,.btn-primary:not(:disabled):not(.disabled).active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#232628;border-color:#1d1f22}.btn-primary:not(:disabled):not(.disabled):active:focus,.btn-primary:not(:disabled):not(.disabled).active:focus,.show>.btn-primary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(88,92,96,0.5);box-shadow:0 0 0 0.2rem rgba(88,92,96,0.5)}.btn-secondary{color:#fff;background-color:#7A8288;border-color:#7A8288}.btn-secondary:hover{color:#fff;background-color:#686f74;border-color:#62686d}.btn-secondary:focus,.btn-secondary.focus{color:#fff;background-color:#686f74;border-color:#62686d;-webkit-box-shadow:0 0 0 0.2rem rgba(142,149,154,0.5);box-shadow:0 0 0 0.2rem rgba(142,149,154,0.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#7A8288;border-color:#7A8288}.btn-secondary:not(:disabled):not(.disabled):active,.btn-secondary:not(:disabled):not(.disabled).active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#62686d;border-color:#5c6267}.btn-secondary:not(:disabled):not(.disabled):active:focus,.btn-secondary:not(:disabled):not(.disabled).active:focus,.show>.btn-secondary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(142,149,154,0.5);box-shadow:0 0 0 0.2rem rgba(142,149,154,0.5)}.btn-success{color:#fff;background-color:#62c462;border-color:#62c462}.btn-success:hover{color:#fff;background-color:#46ba46;border-color:#42b142}.btn-success:focus,.btn-success.focus{color:#fff;background-color:#46ba46;border-color:#42b142;-webkit-box-shadow:0 0 0 0.2rem rgba(122,205,122,0.5);box-shadow:0 0 0 0.2rem rgba(122,205,122,0.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#62c462;border-color:#62c462}.btn-success:not(:disabled):not(.disabled):active,.btn-success:not(:disabled):not(.disabled).active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#42b142;border-color:#3fa73f}.btn-success:not(:disabled):not(.disabled):active:focus,.btn-success:not(:disabled):not(.disabled).active:focus,.show>.btn-success.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(122,205,122,0.5);box-shadow:0 0 0 0.2rem rgba(122,205,122,0.5)}.btn-info{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-info:hover{color:#fff;background-color:#3bb4d8;border-color:#31b0d5}.btn-info:focus,.btn-info.focus{color:#fff;background-color:#3bb4d8;border-color:#31b0d5;-webkit-box-shadow:0 0 0 0.2rem rgba(116,201,227,0.5);box-shadow:0 0 0 0.2rem rgba(116,201,227,0.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-info:not(:disabled):not(.disabled):active,.btn-info:not(:disabled):not(.disabled).active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#31b0d5;border-color:#2aaacf}.btn-info:not(:disabled):not(.disabled):active:focus,.btn-info:not(:disabled):not(.disabled).active:focus,.show>.btn-info.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(116,201,227,0.5);box-shadow:0 0 0 0.2rem rgba(116,201,227,0.5)}.btn-warning{color:#fff;background-color:#f89406;border-color:#f89406}.btn-warning:hover{color:#fff;background-color:#d37e05;border-color:#c67605}.btn-warning:focus,.btn-warning.focus{color:#fff;background-color:#d37e05;border-color:#c67605;-webkit-box-shadow:0 0 0 0.2rem rgba(249,164,43,0.5);box-shadow:0 0 0 0.2rem rgba(249,164,43,0.5)}.btn-warning.disabled,.btn-warning:disabled{color:#fff;background-color:#f89406;border-color:#f89406}.btn-warning:not(:disabled):not(.disabled):active,.btn-warning:not(:disabled):not(.disabled).active,.show>.btn-warning.dropdown-toggle{color:#fff;background-color:#c67605;border-color:#ba6f04}.btn-warning:not(:disabled):not(.disabled):active:focus,.btn-warning:not(:disabled):not(.disabled).active:focus,.show>.btn-warning.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(249,164,43,0.5);box-shadow:0 0 0 0.2rem rgba(249,164,43,0.5)}.btn-danger{color:#fff;background-color:#ee5f5b;border-color:#ee5f5b}.btn-danger:hover{color:#fff;background-color:#ea3d38;border-color:#e9322d}.btn-danger:focus,.btn-danger.focus{color:#fff;background-color:#ea3d38;border-color:#e9322d;-webkit-box-shadow:0 0 0 0.2rem rgba(241,119,116,0.5);box-shadow:0 0 0 0.2rem rgba(241,119,116,0.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#ee5f5b;border-color:#ee5f5b}.btn-danger:not(:disabled):not(.disabled):active,.btn-danger:not(:disabled):not(.disabled).active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#e9322d;border-color:#e82721}.btn-danger:not(:disabled):not(.disabled):active:focus,.btn-danger:not(:disabled):not(.disabled).active:focus,.show>.btn-danger.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(241,119,116,0.5);box-shadow:0 0 0 0.2rem rgba(241,119,116,0.5)}.btn-light{color:#272B30;background-color:#e9ecef;border-color:#e9ecef}.btn-light:hover{color:#272B30;background-color:#d3d9df;border-color:#cbd3da}.btn-light:focus,.btn-light.focus{color:#272B30;background-color:#d3d9df;border-color:#cbd3da;-webkit-box-shadow:0 0 0 0.2rem rgba(204,207,210,0.5);box-shadow:0 0 0 0.2rem rgba(204,207,210,0.5)}.btn-light.disabled,.btn-light:disabled{color:#272B30;background-color:#e9ecef;border-color:#e9ecef}.btn-light:not(:disabled):not(.disabled):active,.btn-light:not(:disabled):not(.disabled).active,.show>.btn-light.dropdown-toggle{color:#272B30;background-color:#cbd3da;border-color:#c4ccd4}.btn-light:not(:disabled):not(.disabled):active:focus,.btn-light:not(:disabled):not(.disabled).active:focus,.show>.btn-light.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(204,207,210,0.5);box-shadow:0 0 0 0.2rem rgba(204,207,210,0.5)}.btn-dark{color:#fff;background-color:#272B30;border-color:#272B30}.btn-dark:hover{color:#fff;background-color:#16181b;border-color:#101214}.btn-dark:focus,.btn-dark.focus{color:#fff;background-color:#16181b;border-color:#101214;-webkit-box-shadow:0 0 0 0.2rem rgba(71,75,79,0.5);box-shadow:0 0 0 0.2rem rgba(71,75,79,0.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#272B30;border-color:#272B30}.btn-dark:not(:disabled):not(.disabled):active,.btn-dark:not(:disabled):not(.disabled).active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#101214;border-color:#0a0b0d}.btn-dark:not(:disabled):not(.disabled):active:focus,.btn-dark:not(:disabled):not(.disabled).active:focus,.show>.btn-dark.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(71,75,79,0.5);box-shadow:0 0 0 0.2rem rgba(71,75,79,0.5)}.btn-outline-primary{color:#3A3F44;border-color:#3A3F44}.btn-outline-primary:hover{color:#fff;background-color:#3A3F44;border-color:#3A3F44}.btn-outline-primary:focus,.btn-outline-primary.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.5);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#3A3F44;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:#3A3F44;border-color:#3A3F44}.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{-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.5);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.5)}.btn-outline-secondary{color:#7A8288;border-color:#7A8288}.btn-outline-secondary:hover{color:#fff;background-color:#7A8288;border-color:#7A8288}.btn-outline-secondary:focus,.btn-outline-secondary.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(122,130,136,0.5);box-shadow:0 0 0 0.2rem rgba(122,130,136,0.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#7A8288;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:#7A8288;border-color:#7A8288}.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{-webkit-box-shadow:0 0 0 0.2rem rgba(122,130,136,0.5);box-shadow:0 0 0 0.2rem rgba(122,130,136,0.5)}.btn-outline-success{color:#62c462;border-color:#62c462}.btn-outline-success:hover{color:#fff;background-color:#62c462;border-color:#62c462}.btn-outline-success:focus,.btn-outline-success.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(98,196,98,0.5);box-shadow:0 0 0 0.2rem rgba(98,196,98,0.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#62c462;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:#62c462;border-color:#62c462}.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{-webkit-box-shadow:0 0 0 0.2rem rgba(98,196,98,0.5);box-shadow:0 0 0 0.2rem rgba(98,196,98,0.5)}.btn-outline-info{color:#5bc0de;border-color:#5bc0de}.btn-outline-info:hover{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-outline-info:focus,.btn-outline-info.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(91,192,222,0.5);box-shadow:0 0 0 0.2rem rgba(91,192,222,0.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#5bc0de;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:#5bc0de;border-color:#5bc0de}.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{-webkit-box-shadow:0 0 0 0.2rem rgba(91,192,222,0.5);box-shadow:0 0 0 0.2rem rgba(91,192,222,0.5)}.btn-outline-warning{color:#f89406;border-color:#f89406}.btn-outline-warning:hover{color:#fff;background-color:#f89406;border-color:#f89406}.btn-outline-warning:focus,.btn-outline-warning.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(248,148,6,0.5);box-shadow:0 0 0 0.2rem rgba(248,148,6,0.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#f89406;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:#fff;background-color:#f89406;border-color:#f89406}.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{-webkit-box-shadow:0 0 0 0.2rem rgba(248,148,6,0.5);box-shadow:0 0 0 0.2rem rgba(248,148,6,0.5)}.btn-outline-danger{color:#ee5f5b;border-color:#ee5f5b}.btn-outline-danger:hover{color:#fff;background-color:#ee5f5b;border-color:#ee5f5b}.btn-outline-danger:focus,.btn-outline-danger.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(238,95,91,0.5);box-shadow:0 0 0 0.2rem rgba(238,95,91,0.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#ee5f5b;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:#ee5f5b;border-color:#ee5f5b}.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{-webkit-box-shadow:0 0 0 0.2rem rgba(238,95,91,0.5);box-shadow:0 0 0 0.2rem rgba(238,95,91,0.5)}.btn-outline-light{color:#e9ecef;border-color:#e9ecef}.btn-outline-light:hover{color:#272B30;background-color:#e9ecef;border-color:#e9ecef}.btn-outline-light:focus,.btn-outline-light.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(233,236,239,0.5);box-shadow:0 0 0 0.2rem rgba(233,236,239,0.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#e9ecef;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:#272B30;background-color:#e9ecef;border-color:#e9ecef}.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{-webkit-box-shadow:0 0 0 0.2rem rgba(233,236,239,0.5);box-shadow:0 0 0 0.2rem rgba(233,236,239,0.5)}.btn-outline-dark{color:#272B30;border-color:#272B30}.btn-outline-dark:hover{color:#fff;background-color:#272B30;border-color:#272B30}.btn-outline-dark:focus,.btn-outline-dark.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(39,43,48,0.5);box-shadow:0 0 0 0.2rem rgba(39,43,48,0.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#272B30;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:#272B30;border-color:#272B30}.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{-webkit-box-shadow:0 0 0 0.2rem rgba(39,43,48,0.5);box-shadow:0 0 0 0.2rem rgba(39,43,48,0.5)}.btn-link{font-weight:400;color:#fff;text-decoration:none}.btn-link:hover{color:#d9d9d9;text-decoration:underline}.btn-link:focus,.btn-link.focus{text-decoration:underline;-webkit-box-shadow:none;box-shadow:none}.btn-link:disabled,.btn-link.disabled{color:#7A8288;pointer-events:none}.btn-lg,.btn-group-lg>.btn{padding:0.5rem 1rem;font-size:1.171875rem;line-height:1.5;border-radius:0.3rem}.btn-sm,.btn-group-sm>.btn{padding:0.25rem 0.5rem;font-size:0.8203125rem;line-height:1.5;border-radius:0.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:0.5rem}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{-webkit-transition:opacity 0.15s linear;transition:opacity 0.15s linear}@media (prefers-reduced-motion: reduce){.fade{-webkit-transition:none;transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height 0.35s ease;transition:height 0.35s ease}@media (prefers-reduced-motion: reduce){.collapsing{-webkit-transition:none;transition:none}}.dropup,.dropright,.dropdown,.dropleft{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:0.255em;vertical-align:0.255em;content:"";border-top:0.3em solid;border-right:0.3em solid transparent;border-bottom:0;border-left:0.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:0.5rem 0;margin:0.125rem 0 0;font-size:0.9375rem;color:#aaa;text-align:left;list-style:none;background-color:#3A3F44;background-clip:padding-box;border:1px solid rgba(0,0,0,0.6);border-radius:0.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:0.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:0.255em;vertical-align:0.255em;content:"";border-top:0;border-right:0.3em solid transparent;border-bottom:0.3em solid;border-left:0.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:0.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:0.255em;vertical-align:0.255em;content:"";border-top:0.3em solid transparent;border-right:0;border-bottom:0.3em solid transparent;border-left:0.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:0.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:0.255em;vertical-align:0.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:0.255em;vertical-align:0.255em;content:"";border-top:0.3em solid transparent;border-right:0.3em solid;border-bottom:0.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^="top"],.dropdown-menu[x-placement^="right"],.dropdown-menu[x-placement^="bottom"],.dropdown-menu[x-placement^="left"]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:0.5rem 0;overflow:hidden;border-top:1px solid rgba(0,0,0,0.15)}.dropdown-item{display:block;width:100%;padding:0.25rem 1.5rem;clear:both;font-weight:400;color:#aaa;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:hover,.dropdown-item:focus{color:#fff;text-decoration:none;background-color:#272B30}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#3A3F44}.dropdown-item.disabled,.dropdown-item:disabled{color:#7A8288;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:0.5rem 1.5rem;margin-bottom:0;font-size:0.8203125rem;color:#7A8288;white-space:nowrap}.dropdown-item-text{display:block;padding:0.25rem 1.5rem;color:#aaa}.btn-group,.btn-group-vertical{position:relative;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover{z-index:1}.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:1}.btn-toolbar{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn:not(:first-child),.btn-group>.btn-group:not(:first-child){margin-left:-1px}.btn-group>.btn:not(:last-child):not(.dropdown-toggle),.btn-group>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:not(:first-child),.btn-group>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:0.75rem;padding-left:0.75rem}.dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-sm+.dropdown-toggle-split,.btn-group-sm>.btn+.dropdown-toggle-split{padding-right:0.375rem;padding-left:0.375rem}.btn-lg+.dropdown-toggle-split,.btn-group-lg>.btn+.dropdown-toggle-split{padding-right:0.75rem;padding-left:0.75rem}.btn-group-vertical{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn:not(:first-child),.btn-group-vertical>.btn-group:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle),.btn-group-vertical>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:not(:first-child),.btn-group-vertical>.btn-group:not(:first-child)>.btn{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="radio"],.btn-group-toggle>.btn input[type="checkbox"],.btn-group-toggle>.btn-group>.btn input[type="radio"],.btn-group-toggle>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-control-plaintext,.input-group>.custom-select,.input-group>.custom-file{position:relative;-webkit-box-flex:1;-ms-flex:1 1 0%;flex:1 1 0%;min-width:0;margin-bottom:0}.input-group>.form-control+.form-control,.input-group>.form-control+.custom-select,.input-group>.form-control+.custom-file,.input-group>.form-control-plaintext+.form-control,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.custom-file,.input-group>.custom-select+.form-control,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.custom-file,.input-group>.custom-file+.form-control,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.custom-file{margin-left:-1px}.input-group>.form-control:focus,.input-group>.custom-select:focus,.input-group>.custom-file .custom-file-input:focus ~ .custom-file-label{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.form-control:not(:last-child),.input-group>.custom-select:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.form-control:not(:first-child),.input-group>.custom-select:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-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-prepend,.input-group-append{display:-webkit-box;display:-ms-flexbox;display:flex}.input-group-prepend .btn,.input-group-append .btn{position:relative;z-index:2}.input-group-prepend .btn:focus,.input-group-append .btn:focus{z-index:3}.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.input-group-text,.input-group-append .input-group-text+.btn{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:0.75rem 1rem;margin-bottom:0;font-size:0.9375rem;font-weight:400;line-height:1.5;color:#52575C;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:0.25rem}.input-group-text input[type="radio"],.input-group-text input[type="checkbox"]{margin-top:0}.input-group-lg>.form-control:not(textarea),.input-group-lg>.custom-select{height:calc(1.5em + 1rem + 2px)}.input-group-lg>.form-control,.input-group-lg>.custom-select,.input-group-lg>.input-group-prepend>.input-group-text,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-append>.btn{padding:0.5rem 1rem;font-size:1.171875rem;line-height:1.5;border-radius:0.3rem}.input-group-sm>.form-control:not(textarea),.input-group-sm>.custom-select{height:calc(1.5em + 0.5rem + 2px)}.input-group-sm>.form-control,.input-group-sm>.custom-select,.input-group-sm>.input-group-prepend>.input-group-text,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-append>.btn{padding:0.25rem 0.5rem;font-size:0.8203125rem;line-height:1.5;border-radius:0.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:2rem}.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text,.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.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){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:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.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){border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.40625rem;padding-left:1.5rem}.custom-control-inline{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.203125rem;opacity:0}.custom-control-input:checked ~ .custom-control-label::before{color:#fff;border-color:#3A3F44;background-color:#3A3F44}.custom-control-input:focus ~ .custom-control-label::before{-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25)}.custom-control-input:focus:not(:checked) ~ .custom-control-label::before{border-color:#757f89}.custom-control-input:not(:disabled):active ~ .custom-control-label::before{color:#fff;background-color:#9098a0;border-color:#9098a0}.custom-control-input[disabled] ~ .custom-control-label,.custom-control-input:disabled ~ .custom-control-label{color:#7A8288}.custom-control-input[disabled] ~ .custom-control-label::before,.custom-control-input:disabled ~ .custom-control-label::before{background-color:#ccc}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:0.203125rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#999 solid 1px}.custom-control-label::after{position:absolute;top:0.203125rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50% / 50% 50%}.custom-checkbox .custom-control-label::before{border-radius:0.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:#3A3F44;background-color:#3A3F44}.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(58,63,68,0.5)}.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before{background-color:rgba(58,63,68,0.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(58,63,68,0.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:0.5rem}.custom-switch .custom-control-label::after{top:calc(0.203125rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#999;border-radius:0.5rem;-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.custom-switch .custom-control-label::after{-webkit-transition:none;transition:none}}.custom-switch .custom-control-input:checked ~ .custom-control-label::after{background-color:#fff;-webkit-transform:translateX(0.75rem);transform:translateX(0.75rem)}.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before{background-color:rgba(58,63,68,0.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + 1.5rem + 2px);padding:0.75rem 2rem 0.75rem 1rem;font-size:0.9375rem;font-weight:400;line-height:1.5;color:#52575C;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='%233A3F44' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 1rem center/8px 10px;border:1px solid #ced4da;border-radius:0.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#757f89;outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25)}.custom-select:focus::-ms-value{color:#52575C;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:1rem;background-image:none}.custom-select:disabled{color:#7A8288;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #52575C}.custom-select-sm{height:calc(1.5em + 0.5rem + 2px);padding-top:0.25rem;padding-bottom:0.25rem;padding-left:0.5rem;font-size:0.8203125rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:0.5rem;padding-bottom:0.5rem;padding-left:1rem;font-size:1.171875rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + 1.5rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + 1.5rem + 2px);margin:0;opacity:0}.custom-file-input:focus ~ .custom-file-label{border-color:#757f89;-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25)}.custom-file-input[disabled] ~ .custom-file-label,.custom-file-input:disabled ~ .custom-file-label{background-color:#ccc}.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 + 1.5rem + 2px);padding:0.75rem 1rem;font-weight:400;line-height:1.5;color:#52575C;background-color:#fff;border:1px solid #ced4da;border-radius:0.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + 1.5rem);padding:0.75rem 1rem;line-height:1.5;color:#52575C;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 0.25rem 0.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:none}.custom-range:focus::-webkit-slider-thumb{-webkit-box-shadow:0 0 0 1px #272B30,0 0 0 0.2rem rgba(58,63,68,0.25);box-shadow:0 0 0 1px #272B30,0 0 0 0.2rem rgba(58,63,68,0.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #272B30,0 0 0 0.2rem rgba(58,63,68,0.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #272B30,0 0 0 0.2rem rgba(58,63,68,0.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-0.25rem;background-color:#3A3F44;border:0;border-radius:1rem;-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.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:#9098a0}.custom-range::-webkit-slider-runnable-track{width:100%;height:0.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:#3A3F44;border:0;border-radius:1rem;-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion: reduce){.custom-range::-moz-range-thumb{-webkit-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#9098a0}.custom-range::-moz-range-track{width:100%;height:0.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:0.2rem;margin-left:0.2rem;background-color:#3A3F44;border:0;border-radius:1rem;-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;appearance:none}@media (prefers-reduced-motion: reduce){.custom-range::-ms-thumb{-webkit-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#9098a0}.custom-range::-ms-track{width:100%;height:0.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:0.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:#999}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#999}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#999}.custom-control-label::before,.custom-file-label,.custom-select{-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.custom-control-label::before,.custom-file-label,.custom-select{-webkit-transition:none;transition:none}}.nav{display:-webkit-box;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:0.5rem 1rem}.nav-link:hover,.nav-link:focus{text-decoration:none}.nav-link.disabled{color:#7A8288;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid rgba(0,0,0,0.6)}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:0.25rem;border-top-right-radius:0.25rem}.nav-tabs .nav-link:hover,.nav-tabs .nav-link:focus{border-color:rgba(0,0,0,0.6)}.nav-tabs .nav-link.disabled{color:#7A8288;background-color:transparent;border-color:transparent}.nav-tabs .nav-link.active,.nav-tabs .nav-item.show .nav-link{color:#fff;background-color:#272B30;border-color:rgba(0,0,0,0.6)}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:0.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#3A3F44}.nav-fill .nav-item{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-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:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:0 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-sm,.navbar .container-md,.navbar .container-lg,.navbar .container-xl{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:0.32421875rem;padding-bottom:0.32421875rem;margin-right:1rem;font-size:1.171875rem;line-height:inherit;white-space:nowrap}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-nav{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-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:0.5rem;padding-bottom:0.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:0.25rem 0.75rem;font-size:1.171875rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:0.25rem}.navbar-toggler:hover,.navbar-toggler:focus{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width: 575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width: 576px){.navbar-expand-sm{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-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:0.5rem;padding-left:0.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-webkit-box !important;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-sm,.navbar-expand-md>.container-md,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width: 768px){.navbar-expand-md{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-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:0.5rem;padding-left:0.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-md,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-webkit-box !important;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-sm,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width: 992px){.navbar-expand-lg{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-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:0.5rem;padding-left:0.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-webkit-box !important;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-sm,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width: 1200px){.navbar-expand-xl{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-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:0.5rem;padding-left:0.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-sm,.navbar-expand>.container-md,.navbar-expand>.container-lg,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:0.5rem;padding-left:0.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-sm,.navbar-expand>.container-md,.navbar-expand>.container-lg,.navbar-expand>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-webkit-box !important;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:#3A3F44}.navbar-light .navbar-brand:hover,.navbar-light .navbar-brand:focus{color:#3A3F44}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,0.5)}.navbar-light .navbar-nav .nav-link:hover,.navbar-light .navbar-nav .nav-link:focus{color:#3A3F44}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,0.3)}.navbar-light .navbar-nav .show>.nav-link,.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .nav-link.active{color:#3A3F44}.navbar-light .navbar-toggler{color:rgba(0,0,0,0.5);border-color:rgba(0,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(0, 0, 0, 0.5)' 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,0.5)}.navbar-light .navbar-text a{color:#3A3F44}.navbar-light .navbar-text a:hover,.navbar-light .navbar-text a:focus{color:#3A3F44}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:hover,.navbar-dark .navbar-brand:focus{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,0.5)}.navbar-dark .navbar-nav .nav-link:hover,.navbar-dark .navbar-nav .nav-link:focus{color:#fff}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,0.25)}.navbar-dark .navbar-nav .show>.nav-link,.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .nav-link.active{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,0.5);border-color:rgba(255,255,255,0.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(255, 255, 255, 0.5)' 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,0.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:hover,.navbar-dark .navbar-text a:focus{color:#fff}.card{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#32383e;background-clip:border-box;border:1px solid rgba(0,0,0,0.6);border-radius:0.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:0.25rem;border-top-right-radius:0.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:0.25rem;border-bottom-left-radius:0.25rem}.card-body{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:0.75rem}.card-subtitle{margin-top:-0.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:0.75rem 1.25rem;margin-bottom:0;background-color:#515960;border-bottom:1px solid rgba(0,0,0,0.6)}.card-header:first-child{border-radius:calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:0.75rem 1.25rem;background-color:#515960;border-top:1px solid rgba(0,0,0,0.6)}.card-footer:last-child{border-radius:0 0 calc(0.25rem - 1px) calc(0.25rem - 1px)}.card-header-tabs{margin-right:-0.625rem;margin-bottom:-0.75rem;margin-left:-0.625rem;border-bottom:0}.card-header-pills{margin-right:-0.625rem;margin-left:-0.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img,.card-img-top,.card-img-bottom{-ms-flex-negative:0;flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(0.25rem - 1px);border-top-right-radius:calc(0.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(0.25rem - 1px);border-bottom-left-radius:calc(0.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width: 576px){.card-deck{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-webkit-box-flex:1;-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:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-webkit-box-flex:1;-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-img-top,.card-group>.card:not(:last-child) .card-header{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-img-bottom,.card-group>.card:not(:last-child) .card-footer{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-img-top,.card-group>.card:not(:first-child) .card-header{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-img-bottom,.card-group>.card:not(:first-child) .card-footer{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:0.75rem}@media (min-width: 576px){.card-columns{-webkit-column-count:3;column-count:3;-webkit-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.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:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:0.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:0.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:0.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:0.5rem;color:#7A8288;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#999}.pagination{display:-webkit-box;display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:0.25rem}.page-link{position:relative;display:block;padding:0.5rem 0.75rem;margin-left:-1px;line-height:1.25;color:#fff;background-color:transparent;border:1px solid rgba(0,0,0,0.6)}.page-link:hover{z-index:2;color:#fff;text-decoration:none;background-color:transparent;border-color:rgba(0,0,0,0.6)}.page-link:focus{z-index:3;outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:0.25rem;border-bottom-left-radius:0.25rem}.page-item:last-child .page-link{border-top-right-radius:0.25rem;border-bottom-right-radius:0.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:transparent;border-color:rgba(0,0,0,0.6)}.page-item.disabled .page-link{color:#7A8288;pointer-events:none;cursor:auto;background-color:transparent;border-color:rgba(0,0,0,0.6)}.pagination-lg .page-link{padding:0.75rem 1.5rem;font-size:1.171875rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:0.3rem;border-bottom-left-radius:0.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:0.3rem;border-bottom-right-radius:0.3rem}.pagination-sm .page-link{padding:0.25rem 0.5rem;font-size:0.8203125rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:0.2rem;border-bottom-left-radius:0.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:0.2rem;border-bottom-right-radius:0.2rem}.badge{display:inline-block;padding:0.25em 0.4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:0.25rem;-webkit-transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.badge{-webkit-transition:none;transition:none}}a.badge:hover,a.badge:focus{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:0.6em;padding-left:0.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#3A3F44}a.badge-primary:hover,a.badge-primary:focus{color:#fff;background-color:#232628}a.badge-primary:focus,a.badge-primary.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.5);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.5)}.badge-secondary{color:#fff;background-color:#7A8288}a.badge-secondary:hover,a.badge-secondary:focus{color:#fff;background-color:#62686d}a.badge-secondary:focus,a.badge-secondary.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(122,130,136,0.5);box-shadow:0 0 0 0.2rem rgba(122,130,136,0.5)}.badge-success{color:#fff;background-color:#62c462}a.badge-success:hover,a.badge-success:focus{color:#fff;background-color:#42b142}a.badge-success:focus,a.badge-success.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(98,196,98,0.5);box-shadow:0 0 0 0.2rem rgba(98,196,98,0.5)}.badge-info{color:#fff;background-color:#5bc0de}a.badge-info:hover,a.badge-info:focus{color:#fff;background-color:#31b0d5}a.badge-info:focus,a.badge-info.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(91,192,222,0.5);box-shadow:0 0 0 0.2rem rgba(91,192,222,0.5)}.badge-warning{color:#fff;background-color:#f89406}a.badge-warning:hover,a.badge-warning:focus{color:#fff;background-color:#c67605}a.badge-warning:focus,a.badge-warning.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(248,148,6,0.5);box-shadow:0 0 0 0.2rem rgba(248,148,6,0.5)}.badge-danger{color:#fff;background-color:#ee5f5b}a.badge-danger:hover,a.badge-danger:focus{color:#fff;background-color:#e9322d}a.badge-danger:focus,a.badge-danger.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(238,95,91,0.5);box-shadow:0 0 0 0.2rem rgba(238,95,91,0.5)}.badge-light{color:#272B30;background-color:#e9ecef}a.badge-light:hover,a.badge-light:focus{color:#272B30;background-color:#cbd3da}a.badge-light:focus,a.badge-light.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(233,236,239,0.5);box-shadow:0 0 0 0.2rem rgba(233,236,239,0.5)}.badge-dark{color:#fff;background-color:#272B30}a.badge-dark:hover,a.badge-dark:focus{color:#fff;background-color:#101214}a.badge-dark:focus,a.badge-dark.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(39,43,48,0.5);box-shadow:0 0 0 0.2rem rgba(39,43,48,0.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#1c1e22;border-radius:0.3rem}@media (min-width: 576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:0.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:0.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:3.90625rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:0.75rem 1.25rem;color:inherit}.alert-primary{color:#1e2123;background-color:#d8d9da;border-color:#c8c9cb}.alert-primary hr{border-top-color:#bbbcbf}.alert-primary .alert-link{color:#060708}.alert-secondary{color:#3f4447;background-color:#e4e6e7;border-color:#dadcde}.alert-secondary hr{border-top-color:#cdcfd2}.alert-secondary .alert-link{color:#272a2c}.alert-success{color:#336633;background-color:#e0f3e0;border-color:#d3eed3}.alert-success hr{border-top-color:#c1e7c1}.alert-success .alert-link{color:#224422}.alert-info{color:#2f6473;background-color:#def2f8;border-color:#d1edf6}.alert-info hr{border-top-color:#bce5f2}.alert-info .alert-link{color:#20454f}.alert-warning{color:#814d03;background-color:#feeacd;border-color:#fde1b9}.alert-warning hr{border-top-color:#fcd6a0}.alert-warning .alert-link{color:#4f2f02}.alert-danger{color:#7c312f;background-color:#fcdfde;border-color:#fad2d1}.alert-danger hr{border-top-color:#f8bcba}.alert-danger .alert-link{color:#572221}.alert-light{color:#797b7c;background-color:#fbfbfc;border-color:#f9fafb}.alert-light hr{border-top-color:#eaedf1}.alert-light .alert-link{color:#606162}.alert-dark{color:#141619;background-color:#d4d5d6;border-color:#c3c4c5}.alert-dark hr{border-top-color:#b6b7b8}.alert-dark .alert-link{color:black}@-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:-webkit-box;display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:0.703125rem;background-color:#1c1e22;border-radius:0.25rem}.progress-bar{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;overflow:hidden;color:#7A8288;text-align:center;white-space:nowrap;background-color:#3A3F44;-webkit-transition:width 0.6s ease;transition:width 0.6s ease}@media (prefers-reduced-motion: reduce){.progress-bar{-webkit-transition:none;transition:none}}.progress-bar-striped{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:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion: reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.media-body{-webkit-box-flex:1;-ms-flex:1;flex:1}.list-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#fff;text-align:inherit}.list-group-item-action:hover,.list-group-item-action:focus{z-index:1;color:#fff;text-decoration:none;background-color:#3e444c}.list-group-item-action:active{color:#aaa;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:0.75rem 1.25rem;background-color:#32383e;border:1px solid rgba(0,0,0,0.6)}.list-group-item:first-child{border-top-left-radius:0.25rem;border-top-right-radius:0.25rem}.list-group-item:last-child{border-bottom-right-radius:0.25rem;border-bottom-left-radius:0.25rem}.list-group-item.disabled,.list-group-item:disabled{color:#52575C;pointer-events:none;background-color:#32383e}.list-group-item.active{z-index:2;color:#fff;background-color:#3e444c;border-color:rgba(0,0,0,0.6)}.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{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.list-group-horizontal .list-group-item:first-child{border-bottom-left-radius:0.25rem;border-top-right-radius:0}.list-group-horizontal .list-group-item:last-child{border-top-right-radius:0.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{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm .list-group-item:first-child{border-bottom-left-radius:0.25rem;border-top-right-radius:0}.list-group-horizontal-sm .list-group-item:last-child{border-top-right-radius:0.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{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md .list-group-item:first-child{border-bottom-left-radius:0.25rem;border-top-right-radius:0}.list-group-horizontal-md .list-group-item:last-child{border-top-right-radius:0.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{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg .list-group-item:first-child{border-bottom-left-radius:0.25rem;border-top-right-radius:0}.list-group-horizontal-lg .list-group-item:last-child{border-top-right-radius:0.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{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl .list-group-item:first-child{border-bottom-left-radius:0.25rem;border-top-right-radius:0}.list-group-horizontal-xl .list-group-item:last-child{border-top-right-radius:0.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 .list-group-item{border-right-width:0;border-left-width:0;border-radius:0}.list-group-flush .list-group-item:first-child{border-top-width:0}.list-group-flush:last-child .list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#1e2123;background-color:#c8c9cb}.list-group-item-primary.list-group-item-action:hover,.list-group-item-primary.list-group-item-action:focus{color:#1e2123;background-color:#bbbcbf}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#1e2123;border-color:#1e2123}.list-group-item-secondary{color:#3f4447;background-color:#dadcde}.list-group-item-secondary.list-group-item-action:hover,.list-group-item-secondary.list-group-item-action:focus{color:#3f4447;background-color:#cdcfd2}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#3f4447;border-color:#3f4447}.list-group-item-success{color:#336633;background-color:#d3eed3}.list-group-item-success.list-group-item-action:hover,.list-group-item-success.list-group-item-action:focus{color:#336633;background-color:#c1e7c1}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#336633;border-color:#336633}.list-group-item-info{color:#2f6473;background-color:#d1edf6}.list-group-item-info.list-group-item-action:hover,.list-group-item-info.list-group-item-action:focus{color:#2f6473;background-color:#bce5f2}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#2f6473;border-color:#2f6473}.list-group-item-warning{color:#814d03;background-color:#fde1b9}.list-group-item-warning.list-group-item-action:hover,.list-group-item-warning.list-group-item-action:focus{color:#814d03;background-color:#fcd6a0}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#814d03;border-color:#814d03}.list-group-item-danger{color:#7c312f;background-color:#fad2d1}.list-group-item-danger.list-group-item-action:hover,.list-group-item-danger.list-group-item-action:focus{color:#7c312f;background-color:#f8bcba}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#7c312f;border-color:#7c312f}.list-group-item-light{color:#797b7c;background-color:#f9fafb}.list-group-item-light.list-group-item-action:hover,.list-group-item-light.list-group-item-action:focus{color:#797b7c;background-color:#eaedf1}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#797b7c;border-color:#797b7c}.list-group-item-dark{color:#141619;background-color:#c3c4c5}.list-group-item-dark.list-group-item-action:hover,.list-group-item-dark.list-group-item-action:focus{color:#141619;background-color:#b6b7b8}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#141619;border-color:#141619}.close{float:right;font-size:1.40625rem;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):hover,.close:not(:disabled):not(.disabled):focus{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:0.875rem;background-color:rgba(255,255,255,0.85);background-clip:padding-box;border:1px solid rgba(0,0,0,0.1);-webkit-box-shadow:0 0.25rem 0.75rem rgba(0,0,0,0.1);box-shadow:0 0.25rem 0.75rem rgba(0,0,0,0.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:0.25rem}.toast:not(:last-child){margin-bottom:0.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:0.25rem 0.75rem;color:#7A8288;background-color:rgba(255,255,255,0.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,0.05)}.toast-body{padding:0.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:0.5rem;pointer-events:none}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform 0.3s ease-out;transition:-webkit-transform 0.3s ease-out;transition:transform 0.3s ease-out;transition:transform 0.3s ease-out, -webkit-transform 0.3s ease-out;-webkit-transform:translate(0, -50px);transform:translate(0, -50px)}@media (prefers-reduced-motion: reduce){.modal.fade .modal-dialog{-webkit-transition:none;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:-webkit-box;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-header,.modal-dialog-scrollable .modal-footer{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);content:""}.modal-dialog-centered.modal-dialog-scrollable{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-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:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#32383e;background-clip:padding-box;border:1px solid rgba(0,0,0,0.2);border-radius:0.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:0.5}.modal-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid rgba(0,0,0,0.2);border-top-left-radius:calc(0.3rem - 1px);border-top-right-radius:calc(0.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;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;padding:0.75rem;border-top:1px solid rgba(0,0,0,0.2);border-bottom-right-radius:calc(0.3rem - 1px);border-bottom-left-radius:calc(0.3rem - 1px)}.modal-footer>*{margin:0.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)}.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", 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;word-spacing:normal;white-space:normal;line-break:auto;font-size:0.8203125rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:0.9}.tooltip .arrow{position:absolute;display:block;width:0.8rem;height:0.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-top,.bs-tooltip-auto[x-placement^="top"]{padding:0.4rem 0}.bs-tooltip-top .arrow,.bs-tooltip-auto[x-placement^="top"] .arrow{bottom:0}.bs-tooltip-top .arrow::before,.bs-tooltip-auto[x-placement^="top"] .arrow::before{top:0;border-width:0.4rem 0.4rem 0;border-top-color:#000}.bs-tooltip-right,.bs-tooltip-auto[x-placement^="right"]{padding:0 0.4rem}.bs-tooltip-right .arrow,.bs-tooltip-auto[x-placement^="right"] .arrow{left:0;width:0.4rem;height:0.8rem}.bs-tooltip-right .arrow::before,.bs-tooltip-auto[x-placement^="right"] .arrow::before{right:0;border-width:0.4rem 0.4rem 0.4rem 0;border-right-color:#000}.bs-tooltip-bottom,.bs-tooltip-auto[x-placement^="bottom"]{padding:0.4rem 0}.bs-tooltip-bottom .arrow,.bs-tooltip-auto[x-placement^="bottom"] .arrow{top:0}.bs-tooltip-bottom .arrow::before,.bs-tooltip-auto[x-placement^="bottom"] .arrow::before{bottom:0;border-width:0 0.4rem 0.4rem;border-bottom-color:#000}.bs-tooltip-left,.bs-tooltip-auto[x-placement^="left"]{padding:0 0.4rem}.bs-tooltip-left .arrow,.bs-tooltip-auto[x-placement^="left"] .arrow{right:0;width:0.4rem;height:0.8rem}.bs-tooltip-left .arrow::before,.bs-tooltip-auto[x-placement^="left"] .arrow::before{left:0;border-width:0.4rem 0 0.4rem 0.4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:0.25rem 0.5rem;color:#fff;text-align:center;background-color:#000;border-radius:0.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", 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;word-spacing:normal;white-space:normal;line-break:auto;font-size:0.8203125rem;word-wrap:break-word;background-color:#32383e;background-clip:padding-box;border:1px solid rgba(0,0,0,0.2);border-radius:0.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:0.5rem;margin:0 0.3rem}.popover .arrow::before,.popover .arrow::after{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-top,.bs-popover-auto[x-placement^="top"]{margin-bottom:0.5rem}.bs-popover-top>.arrow,.bs-popover-auto[x-placement^="top"]>.arrow{bottom:calc(-0.5rem - 1px)}.bs-popover-top>.arrow::before,.bs-popover-auto[x-placement^="top"]>.arrow::before{bottom:0;border-width:0.5rem 0.5rem 0;border-top-color:rgba(0,0,0,0.25)}.bs-popover-top>.arrow::after,.bs-popover-auto[x-placement^="top"]>.arrow::after{bottom:1px;border-width:0.5rem 0.5rem 0;border-top-color:#32383e}.bs-popover-right,.bs-popover-auto[x-placement^="right"]{margin-left:0.5rem}.bs-popover-right>.arrow,.bs-popover-auto[x-placement^="right"]>.arrow{left:calc(-0.5rem - 1px);width:0.5rem;height:1rem;margin:0.3rem 0}.bs-popover-right>.arrow::before,.bs-popover-auto[x-placement^="right"]>.arrow::before{left:0;border-width:0.5rem 0.5rem 0.5rem 0;border-right-color:rgba(0,0,0,0.25)}.bs-popover-right>.arrow::after,.bs-popover-auto[x-placement^="right"]>.arrow::after{left:1px;border-width:0.5rem 0.5rem 0.5rem 0;border-right-color:#32383e}.bs-popover-bottom,.bs-popover-auto[x-placement^="bottom"]{margin-top:0.5rem}.bs-popover-bottom>.arrow,.bs-popover-auto[x-placement^="bottom"]>.arrow{top:calc(-0.5rem - 1px)}.bs-popover-bottom>.arrow::before,.bs-popover-auto[x-placement^="bottom"]>.arrow::before{top:0;border-width:0 0.5rem 0.5rem 0.5rem;border-bottom-color:rgba(0,0,0,0.25)}.bs-popover-bottom>.arrow::after,.bs-popover-auto[x-placement^="bottom"]>.arrow::after{top:1px;border-width:0 0.5rem 0.5rem 0.5rem;border-bottom-color:#32383e}.bs-popover-bottom .popover-header::before,.bs-popover-auto[x-placement^="bottom"] .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-0.5rem;content:"";border-bottom:1px solid #2c3036}.bs-popover-left,.bs-popover-auto[x-placement^="left"]{margin-right:0.5rem}.bs-popover-left>.arrow,.bs-popover-auto[x-placement^="left"]>.arrow{right:calc(-0.5rem - 1px);width:0.5rem;height:1rem;margin:0.3rem 0}.bs-popover-left>.arrow::before,.bs-popover-auto[x-placement^="left"]>.arrow::before{right:0;border-width:0.5rem 0 0.5rem 0.5rem;border-left-color:rgba(0,0,0,0.25)}.bs-popover-left>.arrow::after,.bs-popover-auto[x-placement^="left"]>.arrow::after{right:1px;border-width:0.5rem 0 0.5rem 0.5rem;border-left-color:#32383e}.popover-header{padding:0.5rem 0.75rem;margin-bottom:0;font-size:0.9375rem;background-color:#2c3036;border-bottom:1px solid #202328;border-top-left-radius:calc(0.3rem - 1px);border-top-right-radius:calc(0.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:0.5rem 0.75rem;color:#aaa}.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;-webkit-transition:-webkit-transform 0.6s ease-in-out;transition:-webkit-transform 0.6s ease-in-out;transition:transform 0.6s ease-in-out;transition:transform 0.6s ease-in-out, -webkit-transform 0.6s ease-in-out}@media (prefers-reduced-motion: reduce){.carousel-item{-webkit-transition:none;transition:none}}.carousel-item.active,.carousel-item-next,.carousel-item-prev{display:block}.carousel-item-next:not(.carousel-item-left),.active.carousel-item-right{-webkit-transform:translateX(100%);transform:translateX(100%)}.carousel-item-prev:not(.carousel-item-right),.active.carousel-item-left{-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;-webkit-transition-property:opacity;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item.active,.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;-webkit-transition:opacity 0s 0.6s;transition:opacity 0s 0.6s}@media (prefers-reduced-motion: reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{-webkit-transition:none;transition:none}}.carousel-control-prev,.carousel-control-next{position:absolute;top:0;bottom:0;z-index:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:0.5;-webkit-transition:opacity 0.15s ease;transition:opacity 0.15s ease}@media (prefers-reduced-motion: reduce){.carousel-control-prev,.carousel-control-next{-webkit-transition:none;transition:none}}.carousel-control-prev:hover,.carousel-control-prev:focus,.carousel-control-next:hover,.carousel-control-next:focus{color:#fff;text-decoration:none;outline:0;opacity:0.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-prev-icon,.carousel-control-next-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50% / 100% 100%}.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:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{-webkit-box-sizing:content-box;box-sizing:content-box;-webkit-box-flex:0;-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;-webkit-transition:opacity 0.6s ease;transition:opacity 0.6s ease}@media (prefers-reduced-motion: reduce){.carousel-indicators li{-webkit-transition:none;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:text-bottom;border:0.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:0.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.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:#3A3F44 !important}a.bg-primary:hover,a.bg-primary:focus,button.bg-primary:hover,button.bg-primary:focus{background-color:#232628 !important}.bg-secondary{background-color:#7A8288 !important}a.bg-secondary:hover,a.bg-secondary:focus,button.bg-secondary:hover,button.bg-secondary:focus{background-color:#62686d !important}.bg-success{background-color:#62c462 !important}a.bg-success:hover,a.bg-success:focus,button.bg-success:hover,button.bg-success:focus{background-color:#42b142 !important}.bg-info{background-color:#5bc0de !important}a.bg-info:hover,a.bg-info:focus,button.bg-info:hover,button.bg-info:focus{background-color:#31b0d5 !important}.bg-warning{background-color:#f89406 !important}a.bg-warning:hover,a.bg-warning:focus,button.bg-warning:hover,button.bg-warning:focus{background-color:#c67605 !important}.bg-danger{background-color:#ee5f5b !important}a.bg-danger:hover,a.bg-danger:focus,button.bg-danger:hover,button.bg-danger:focus{background-color:#e9322d !important}.bg-light{background-color:#e9ecef !important}a.bg-light:hover,a.bg-light:focus,button.bg-light:hover,button.bg-light:focus{background-color:#cbd3da !important}.bg-dark{background-color:#272B30 !important}a.bg-dark:hover,a.bg-dark:focus,button.bg-dark:hover,button.bg-dark:focus{background-color:#101214 !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:#3A3F44 !important}.border-secondary{border-color:#7A8288 !important}.border-success{border-color:#62c462 !important}.border-info{border-color:#5bc0de !important}.border-warning{border-color:#f89406 !important}.border-danger{border-color:#ee5f5b !important}.border-light{border-color:#e9ecef !important}.border-dark{border-color:#272B30 !important}.border-white{border-color:#fff !important}.rounded-sm{border-radius:0.2rem !important}.rounded{border-radius:0.25rem !important}.rounded-top{border-top-left-radius:0.25rem !important;border-top-right-radius:0.25rem !important}.rounded-right{border-top-right-radius:0.25rem !important;border-bottom-right-radius:0.25rem !important}.rounded-bottom{border-bottom-right-radius:0.25rem !important;border-bottom-left-radius:0.25rem !important}.rounded-left{border-top-left-radius:0.25rem !important;border-bottom-left-radius:0.25rem !important}.rounded-lg{border-radius:0.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:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-inline-flex{display:-webkit-inline-box !important;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:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-sm-inline-flex{display:-webkit-inline-box !important;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:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-md-inline-flex{display:-webkit-inline-box !important;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:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-lg-inline-flex{display:-webkit-inline-box !important;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:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-xl-inline-flex{display:-webkit-inline-box !important;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:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-print-inline-flex{display:-webkit-inline-box !important;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 iframe,.embed-responsive embed,.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.8571428571%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-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{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-grow-1{-webkit-box-flex:1 !important;-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{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-between{-webkit-box-pack:justify !important;-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{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-stretch{-webkit-box-align:stretch !important;-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{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-sm-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-sm-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-sm-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-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{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-sm-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-sm-grow-1{-webkit-box-flex:1 !important;-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{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-sm-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-sm-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-sm-between{-webkit-box-pack:justify !important;-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{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-sm-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-sm-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-sm-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-sm-stretch{-webkit-box-align:stretch !important;-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{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-md-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-md-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-md-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-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{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-md-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-md-grow-1{-webkit-box-flex:1 !important;-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{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-md-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-md-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-md-between{-webkit-box-pack:justify !important;-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{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-md-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-md-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-md-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-md-stretch{-webkit-box-align:stretch !important;-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{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-lg-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-lg-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-lg-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-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{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-lg-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-lg-grow-1{-webkit-box-flex:1 !important;-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{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-lg-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-lg-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-lg-between{-webkit-box-pack:justify !important;-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{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-lg-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-lg-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-lg-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-lg-stretch{-webkit-box-align:stretch !important;-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{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-xl-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-xl-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-xl-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-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{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-xl-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-xl-grow-1{-webkit-box-flex:1 !important;-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{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-xl-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-xl-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-xl-between{-webkit-box-pack:justify !important;-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{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-xl-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-xl-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-xl-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-xl-stretch{-webkit-box-align:stretch !important;-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}}.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{-webkit-box-shadow:0 0.125rem 0.25rem rgba(0,0,0,0.075) !important;box-shadow:0 0.125rem 0.25rem rgba(0,0,0,0.075) !important}.shadow{-webkit-box-shadow:0 0.5rem 1rem rgba(0,0,0,0.15) !important;box-shadow:0 0.5rem 1rem rgba(0,0,0,0.15) !important}.shadow-lg{-webkit-box-shadow:0 1rem 3rem rgba(0,0,0,0.175) !important;box-shadow:0 1rem 3rem rgba(0,0,0,0.175) !important}.shadow-none{-webkit-box-shadow:none !important;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}.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)}.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:0.25rem !important}.mt-1,.my-1{margin-top:0.25rem !important}.mr-1,.mx-1{margin-right:0.25rem !important}.mb-1,.my-1{margin-bottom:0.25rem !important}.ml-1,.mx-1{margin-left:0.25rem !important}.m-2{margin:0.5rem !important}.mt-2,.my-2{margin-top:0.5rem !important}.mr-2,.mx-2{margin-right:0.5rem !important}.mb-2,.my-2{margin-bottom:0.5rem !important}.ml-2,.mx-2{margin-left:0.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:0.25rem !important}.pt-1,.py-1{padding-top:0.25rem !important}.pr-1,.px-1{padding-right:0.25rem !important}.pb-1,.py-1{padding-bottom:0.25rem !important}.pl-1,.px-1{padding-left:0.25rem !important}.p-2{padding:0.5rem !important}.pt-2,.py-2{padding-top:0.5rem !important}.pr-2,.px-2{padding-right:0.5rem !important}.pb-2,.py-2{padding-bottom:0.5rem !important}.pl-2,.px-2{padding-left:0.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:-0.25rem !important}.mt-n1,.my-n1{margin-top:-0.25rem !important}.mr-n1,.mx-n1{margin-right:-0.25rem !important}.mb-n1,.my-n1{margin-bottom:-0.25rem !important}.ml-n1,.mx-n1{margin-left:-0.25rem !important}.m-n2{margin:-0.5rem !important}.mt-n2,.my-n2{margin-top:-0.5rem !important}.mr-n2,.mx-n2{margin-right:-0.5rem !important}.mb-n2,.my-n2{margin-bottom:-0.5rem !important}.ml-n2,.mx-n2{margin-left:-0.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:0.25rem !important}.mt-sm-1,.my-sm-1{margin-top:0.25rem !important}.mr-sm-1,.mx-sm-1{margin-right:0.25rem !important}.mb-sm-1,.my-sm-1{margin-bottom:0.25rem !important}.ml-sm-1,.mx-sm-1{margin-left:0.25rem !important}.m-sm-2{margin:0.5rem !important}.mt-sm-2,.my-sm-2{margin-top:0.5rem !important}.mr-sm-2,.mx-sm-2{margin-right:0.5rem !important}.mb-sm-2,.my-sm-2{margin-bottom:0.5rem !important}.ml-sm-2,.mx-sm-2{margin-left:0.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:0.25rem !important}.pt-sm-1,.py-sm-1{padding-top:0.25rem !important}.pr-sm-1,.px-sm-1{padding-right:0.25rem !important}.pb-sm-1,.py-sm-1{padding-bottom:0.25rem !important}.pl-sm-1,.px-sm-1{padding-left:0.25rem !important}.p-sm-2{padding:0.5rem !important}.pt-sm-2,.py-sm-2{padding-top:0.5rem !important}.pr-sm-2,.px-sm-2{padding-right:0.5rem !important}.pb-sm-2,.py-sm-2{padding-bottom:0.5rem !important}.pl-sm-2,.px-sm-2{padding-left:0.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:-0.25rem !important}.mt-sm-n1,.my-sm-n1{margin-top:-0.25rem !important}.mr-sm-n1,.mx-sm-n1{margin-right:-0.25rem !important}.mb-sm-n1,.my-sm-n1{margin-bottom:-0.25rem !important}.ml-sm-n1,.mx-sm-n1{margin-left:-0.25rem !important}.m-sm-n2{margin:-0.5rem !important}.mt-sm-n2,.my-sm-n2{margin-top:-0.5rem !important}.mr-sm-n2,.mx-sm-n2{margin-right:-0.5rem !important}.mb-sm-n2,.my-sm-n2{margin-bottom:-0.5rem !important}.ml-sm-n2,.mx-sm-n2{margin-left:-0.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:0.25rem !important}.mt-md-1,.my-md-1{margin-top:0.25rem !important}.mr-md-1,.mx-md-1{margin-right:0.25rem !important}.mb-md-1,.my-md-1{margin-bottom:0.25rem !important}.ml-md-1,.mx-md-1{margin-left:0.25rem !important}.m-md-2{margin:0.5rem !important}.mt-md-2,.my-md-2{margin-top:0.5rem !important}.mr-md-2,.mx-md-2{margin-right:0.5rem !important}.mb-md-2,.my-md-2{margin-bottom:0.5rem !important}.ml-md-2,.mx-md-2{margin-left:0.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:0.25rem !important}.pt-md-1,.py-md-1{padding-top:0.25rem !important}.pr-md-1,.px-md-1{padding-right:0.25rem !important}.pb-md-1,.py-md-1{padding-bottom:0.25rem !important}.pl-md-1,.px-md-1{padding-left:0.25rem !important}.p-md-2{padding:0.5rem !important}.pt-md-2,.py-md-2{padding-top:0.5rem !important}.pr-md-2,.px-md-2{padding-right:0.5rem !important}.pb-md-2,.py-md-2{padding-bottom:0.5rem !important}.pl-md-2,.px-md-2{padding-left:0.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:-0.25rem !important}.mt-md-n1,.my-md-n1{margin-top:-0.25rem !important}.mr-md-n1,.mx-md-n1{margin-right:-0.25rem !important}.mb-md-n1,.my-md-n1{margin-bottom:-0.25rem !important}.ml-md-n1,.mx-md-n1{margin-left:-0.25rem !important}.m-md-n2{margin:-0.5rem !important}.mt-md-n2,.my-md-n2{margin-top:-0.5rem !important}.mr-md-n2,.mx-md-n2{margin-right:-0.5rem !important}.mb-md-n2,.my-md-n2{margin-bottom:-0.5rem !important}.ml-md-n2,.mx-md-n2{margin-left:-0.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:0.25rem !important}.mt-lg-1,.my-lg-1{margin-top:0.25rem !important}.mr-lg-1,.mx-lg-1{margin-right:0.25rem !important}.mb-lg-1,.my-lg-1{margin-bottom:0.25rem !important}.ml-lg-1,.mx-lg-1{margin-left:0.25rem !important}.m-lg-2{margin:0.5rem !important}.mt-lg-2,.my-lg-2{margin-top:0.5rem !important}.mr-lg-2,.mx-lg-2{margin-right:0.5rem !important}.mb-lg-2,.my-lg-2{margin-bottom:0.5rem !important}.ml-lg-2,.mx-lg-2{margin-left:0.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:0.25rem !important}.pt-lg-1,.py-lg-1{padding-top:0.25rem !important}.pr-lg-1,.px-lg-1{padding-right:0.25rem !important}.pb-lg-1,.py-lg-1{padding-bottom:0.25rem !important}.pl-lg-1,.px-lg-1{padding-left:0.25rem !important}.p-lg-2{padding:0.5rem !important}.pt-lg-2,.py-lg-2{padding-top:0.5rem !important}.pr-lg-2,.px-lg-2{padding-right:0.5rem !important}.pb-lg-2,.py-lg-2{padding-bottom:0.5rem !important}.pl-lg-2,.px-lg-2{padding-left:0.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:-0.25rem !important}.mt-lg-n1,.my-lg-n1{margin-top:-0.25rem !important}.mr-lg-n1,.mx-lg-n1{margin-right:-0.25rem !important}.mb-lg-n1,.my-lg-n1{margin-bottom:-0.25rem !important}.ml-lg-n1,.mx-lg-n1{margin-left:-0.25rem !important}.m-lg-n2{margin:-0.5rem !important}.mt-lg-n2,.my-lg-n2{margin-top:-0.5rem !important}.mr-lg-n2,.mx-lg-n2{margin-right:-0.5rem !important}.mb-lg-n2,.my-lg-n2{margin-bottom:-0.5rem !important}.ml-lg-n2,.mx-lg-n2{margin-left:-0.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:0.25rem !important}.mt-xl-1,.my-xl-1{margin-top:0.25rem !important}.mr-xl-1,.mx-xl-1{margin-right:0.25rem !important}.mb-xl-1,.my-xl-1{margin-bottom:0.25rem !important}.ml-xl-1,.mx-xl-1{margin-left:0.25rem !important}.m-xl-2{margin:0.5rem !important}.mt-xl-2,.my-xl-2{margin-top:0.5rem !important}.mr-xl-2,.mx-xl-2{margin-right:0.5rem !important}.mb-xl-2,.my-xl-2{margin-bottom:0.5rem !important}.ml-xl-2,.mx-xl-2{margin-left:0.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:0.25rem !important}.pt-xl-1,.py-xl-1{padding-top:0.25rem !important}.pr-xl-1,.px-xl-1{padding-right:0.25rem !important}.pb-xl-1,.py-xl-1{padding-bottom:0.25rem !important}.pl-xl-1,.px-xl-1{padding-left:0.25rem !important}.p-xl-2{padding:0.5rem !important}.pt-xl-2,.py-xl-2{padding-top:0.5rem !important}.pr-xl-2,.px-xl-2{padding-right:0.5rem !important}.pb-xl-2,.py-xl-2{padding-bottom:0.5rem !important}.pl-xl-2,.px-xl-2{padding-left:0.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:-0.25rem !important}.mt-xl-n1,.my-xl-n1{margin-top:-0.25rem !important}.mr-xl-n1,.mx-xl-n1{margin-right:-0.25rem !important}.mb-xl-n1,.my-xl-n1{margin-bottom:-0.25rem !important}.ml-xl-n1,.mx-xl-n1{margin-left:-0.25rem !important}.m-xl-n2{margin:-0.5rem !important}.mt-xl-n2,.my-xl-n2{margin-top:-0.5rem !important}.mr-xl-n2,.mx-xl-n2{margin-right:-0.5rem !important}.mb-xl-n2,.my-xl-n2{margin-bottom:-0.5rem !important}.ml-xl-n2,.mx-xl-n2{margin-left:-0.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}}.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:#3A3F44 !important}a.text-primary:hover,a.text-primary:focus{color:#17191b !important}.text-secondary{color:#7A8288 !important}a.text-secondary:hover,a.text-secondary:focus{color:#565b60 !important}.text-success{color:#62c462 !important}a.text-success:hover,a.text-success:focus{color:#3b9e3b !important}.text-info{color:#5bc0de !important}a.text-info:hover,a.text-info:focus{color:#28a1c5 !important}.text-warning{color:#f89406 !important}a.text-warning:hover,a.text-warning:focus{color:#ad6704 !important}.text-danger{color:#ee5f5b !important}a.text-danger:hover,a.text-danger:focus{color:#e51d18 !important}.text-light{color:#e9ecef !important}a.text-light:hover,a.text-light:focus{color:#bdc6cf !important}.text-dark{color:#272B30 !important}a.text-dark:hover,a.text-dark:focus{color:#050506 !important}.text-body{color:#aaa !important}.text-muted{color:#7A8288 !important}.text-black-50{color:rgba(0,0,0,0.5) !important}.text-white-50{color:rgba(255,255,255,0.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;overflow-wrap:break-word !important}.text-reset{color:inherit !important}.visible{visibility:visible !important}.invisible{visibility:hidden !important}@media print{*,*::before,*::after{text-shadow:none !important;-webkit-box-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}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}p,h2,h3{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 th,.table-bordered td{border:1px solid #dee2e6 !important}.table-dark{color:inherit}.table-dark th,.table-dark td,.table-dark thead th,.table-dark tbody+tbody{border-color:rgba(0,0,0,0.6)}.table .thead-dark th{color:inherit;border-color:rgba(0,0,0,0.6)}}.navbar{border:1px solid rgba(0,0,0,0.6);text-shadow:1px 1px 1px rgba(0,0,0,0.3)}.navbar .container{padding:0}.navbar .navbar-toggler{border-color:rgba(0,0,0,0.6)}.navbar-fixed-top{border-width:0 0 1px 0}.navbar-fixed-bottom{border-width:1px 0 0 0}.navbar .nav-link{padding:1rem;border-left:1px solid rgba(255,255,255,0.1);border-right:1px solid rgba(0,0,0,0.2)}.navbar .nav-link:hover,.navbar .nav-link:focus{background-image:-webkit-gradient(linear, left top, left bottom, from(#101112), color-stop(40%, #17191b), to(#1b1e20));background-image:linear-gradient(#101112, #17191b 40%, #1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-left:1px solid rgba(0,0,0,0.2)}.navbar-brand{padding:0.75rem 1rem calc(54px - 0.75rem - 30px);margin-right:0;border-right:1px solid rgba(0,0,0,0.2)}.navbar .nav-item.active .nav-link{background-color:rgba(0,0,0,0.3);border-left:1px solid rgba(0,0,0,0.2)}.navbar-nav .nav-item+.nav-item{margin-left:0}.navbar.bg-light{text-shadow:1px 1px 1px rgba(0,0,0,0.1)}.navbar.bg-light .nav-link:hover,.navbar.bg-light .nav-link:focus{background-image:-webkit-gradient(linear, left top, left bottom, from(#4e5458), color-stop(40%, #565b60), to(#5b6165));background-image:linear-gradient(#4e5458, #565b60 40%, #5b6165);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-left:1px solid rgba(0,0,0,0.2)}@media (max-width: 576px){.navbar-expand-sm .navbar-brand,.navbar-expand-sm .nav-link{border:none !important}}@media (max-width: 768px){.navbar-expand-md .navbar-brand,.navbar-expand-md .nav-link{border:none !important}}@media (max-width: 992px){.navbar-expand-lg .navbar-brand,.navbar-expand-lg .nav-link{border:none !important}}.btn{border-color:rgba(0,0,0,0.6);text-shadow:1px 1px 1px rgba(0,0,0,0.3)}.btn:not([disabled]):not(.disabled).active,.btn.disabled{border-color:rgba(0,0,0,0.6);-webkit-box-shadow:none;box-shadow:none}.btn:hover,.btn:focus,.btn:not([disabled]):not(.disabled):active,.btn:not([disabled]):not(.disabled):active:hover,.btn:not([disabled]):not(.disabled).active:hover{border-color:rgba(0,0,0,0.6)}.btn-primary{background-image:-webkit-gradient(linear, left top, left bottom, from(#484e55), color-stop(60%, #3A3F44), to(#313539));background-image:linear-gradient(#484e55, #3A3F44 60%, #313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-primary:not([disabled]):not(.disabled):hover,.btn-primary:not([disabled]):not(.disabled):focus,.btn-primary:not([disabled]):not(.disabled):active:hover,.btn-primary:not([disabled]):not(.disabled).active:hover{background-image:-webkit-gradient(linear, left top, left bottom, from(#101112), color-stop(40%, #17191b), to(#1b1e20));background-image:linear-gradient(#101112, #17191b 40%, #1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-secondary{background-image:-webkit-gradient(linear, left top, left bottom, from(#8a9196), color-stop(60%, #7A8288), to(#70787d));background-image:linear-gradient(#8a9196, #7A8288 60%, #70787d);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-secondary:not([disabled]):not(.disabled):hover,.btn-secondary:not([disabled]):not(.disabled):focus,.btn-secondary:not([disabled]):not(.disabled):active,.btn-secondary:not([disabled]):not(.disabled).active{background-image:-webkit-gradient(linear, left top, left bottom, from(#4e5458), color-stop(40%, #565b60), to(#5b6165));background-image:linear-gradient(#4e5458, #565b60 40%, #5b6165);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-success{background-image:-webkit-gradient(linear, left top, left bottom, from(#78cc78), color-stop(60%, #62c462), to(#53be53));background-image:linear-gradient(#78cc78, #62c462 60%, #53be53);background-repeat:no-repeat;-webkit-filter:none;filter:none;color:#fff}.btn-success:not([disabled]):not(.disabled):hover,.btn-success:not([disabled]):not(.disabled):focus,.btn-success:not([disabled]):not(.disabled):active,.btn-success:not([disabled]):not(.disabled).active{background-image:-webkit-gradient(linear, left top, left bottom, from(#379337), color-stop(40%, #3b9e3b), to(#3ea63e));background-image:linear-gradient(#379337, #3b9e3b 40%, #3ea63e);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-info{background-image:-webkit-gradient(linear, left top, left bottom, from(#74cae3), color-stop(60%, #5bc0de), to(#4ab9db));background-image:linear-gradient(#74cae3, #5bc0de 60%, #4ab9db);background-repeat:no-repeat;-webkit-filter:none;filter:none;color:#fff}.btn-info:not([disabled]):not(.disabled):hover,.btn-info:not([disabled]):not(.disabled):focus,.btn-info:not([disabled]):not(.disabled):active,.btn-info:not([disabled]):not(.disabled).active{background-image:-webkit-gradient(linear, left top, left bottom, from(#2596b8), color-stop(40%, #28a1c5), to(#29a8cd));background-image:linear-gradient(#2596b8, #28a1c5 40%, #29a8cd);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-warning{background-image:-webkit-gradient(linear, left top, left bottom, from(#faa123), color-stop(60%, #f89406), to(#e48806));background-image:linear-gradient(#faa123, #f89406 60%, #e48806);background-repeat:no-repeat;-webkit-filter:none;filter:none;color:#fff}.btn-warning:not([disabled]):not(.disabled):hover,.btn-warning:not([disabled]):not(.disabled):focus,.btn-warning:not([disabled]):not(.disabled):active,.btn-warning:not([disabled]):not(.disabled).active{background-image:-webkit-gradient(linear, left top, left bottom, from(#9e5f04), color-stop(40%, #ad6704), to(#b76d04));background-image:linear-gradient(#9e5f04, #ad6704 40%, #b76d04);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-danger{background-image:-webkit-gradient(linear, left top, left bottom, from(#f17a77), color-stop(60%, #ee5f5b), to(#ec4d49));background-image:linear-gradient(#f17a77, #ee5f5b 60%, #ec4d49);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-danger:not([disabled]):not(.disabled):hover,.btn-danger:not([disabled]):not(.disabled):focus,.btn-danger:not([disabled]):not(.disabled):active,.btn-danger:not([disabled]):not(.disabled).active{background-image:-webkit-gradient(linear, left top, left bottom, from(#d71c16), color-stop(40%, #e51d18), to(#e8241f));background-image:linear-gradient(#d71c16, #e51d18 40%, #e8241f);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-link,.btn-link:hover{border-color:transparent}.btn-group .btn.active,.btn-group-vertical .btn.active{border-color:rgba(0,0,0,0.6)}h1,h2,h3,h4,h5,h6{text-shadow:-1px -1px 0 rgba(0,0,0,0.3)}.table-primary,.table-secondary,.table-success,.table-info,.table-warning,.table-danger{color:#fff}.table-primary,.table-primary>th,.table-primary>td{background-color:#3A3F44}.table-secondary,.table-secondary>th,.table-secondary>td{background-color:#7A8288}.table-light,.table-light>th,.table-light>td{background-color:#e9ecef}.table-dark,.table-dark>th,.table-dark>td{background-color:#272B30}.table-success,.table-success>th,.table-success>td{background-color:#62c462}.table-info,.table-info>th,.table-info>td{background-color:#5bc0de}.table-danger,.table-danger>th,.table-danger>td{background-color:#ee5f5b}.table-warning,.table-warning>th,.table-warning>td{background-color:#f89406}.table-active,.table-active>th,.table-active>td{background-color:rgba(255,255,255,0.075)}.table-hover .table-primary:hover,.table-hover .table-primary:hover>th,.table-hover .table-primary:hover>td{background-color:#2e3236}.table-hover .table-secondary:hover,.table-hover .table-secondary:hover>th,.table-hover .table-secondary:hover>td{background-color:#6e757b}.table-hover .table-light:hover,.table-hover .table-light:hover>th,.table-hover .table-light:hover>td{background-color:#dadfe4}.table-hover .table-dark:hover,.table-hover .table-dark:hover>th,.table-hover .table-dark:hover>td{background-color:#1c1e22}.table-hover .table-success:hover,.table-hover .table-success:hover>th,.table-hover .table-success:hover>td{background-color:#4fbd4f}.table-hover .table-info:hover,.table-hover .table-info:hover>th,.table-hover .table-info:hover>td{background-color:#46b8da}.table-hover .table-danger:hover,.table-hover .table-danger:hover>th,.table-hover .table-danger:hover>td{background-color:#ec4844}.table-hover .table-warning:hover,.table-hover .table-warning:hover>th,.table-hover .table-warning:hover>td{background-color:#df8505}.table-hover .table-active:hover,.table-hover .table-active:hover>th,.table-hover .table-active:hover>td{background-color:rgba(255,255,255,0.075)}legend{color:#fff}.input-group-addon{background-image:-webkit-gradient(linear, left top, left bottom, from(#8a9196), color-stop(60%, #7A8288), to(#70787d));background-image:linear-gradient(#8a9196, #7A8288 60%, #70787d);background-repeat:no-repeat;-webkit-filter:none;filter:none;text-shadow:1px 1px 1px rgba(0,0,0,0.3);color:#fff}.nav-tabs .nav-link{background-image:-webkit-gradient(linear, left top, left bottom, from(#101112), color-stop(40%, #17191b), to(#1b1e20));background-image:linear-gradient(#101112, #17191b 40%, #1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,0.6)}.nav-tabs .nav-link:not([disabled]):not(.disabled):hover,.nav-tabs .nav-link:not([disabled]):not(.disabled):focus,.nav-tabs .nav-link:not([disabled]):not(.disabled):active,.nav-tabs .nav-link:not([disabled]):not(.disabled).active{background-image:-webkit-gradient(linear, left top, left bottom, from(#484e55), color-stop(60%, #3A3F44), to(#313539));background-image:linear-gradient(#484e55, #3A3F44 60%, #313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.nav-tabs .nav-link.disabled{border:1px solid rgba(0,0,0,0.6)}.nav-tabs .nav-link,.nav-tabs .nav-link:hover{color:#fff}.nav-pills .nav-link{background-image:-webkit-gradient(linear, left top, left bottom, from(#484e55), color-stop(60%, #3A3F44), to(#313539));background-image:linear-gradient(#484e55, #3A3F44 60%, #313539);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,0.6);text-shadow:1px 1px 1px rgba(0,0,0,0.3);color:#fff}.nav-pills .nav-link:hover{background-image:-webkit-gradient(linear, left top, left bottom, from(#101112), color-stop(40%, #17191b), to(#1b1e20));background-image:linear-gradient(#101112, #17191b 40%, #1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,0.6)}.nav-pills .nav-link.active,.nav-pills .nav-link:hover{background-color:transparent;background-image:-webkit-gradient(linear, left top, left bottom, from(#101112), color-stop(40%, #17191b), to(#1b1e20));background-image:linear-gradient(#101112, #17191b 40%, #1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,0.6)}.nav-pills .nav-link.disabled,.nav-pills .nav-link.disabled:hover{background-image:-webkit-gradient(linear, left top, left bottom, from(#484e55), color-stop(60%, #3A3F44), to(#313539));background-image:linear-gradient(#484e55, #3A3F44 60%, #313539);background-repeat:no-repeat;-webkit-filter:none;filter:none;color:#7A8288}.pagination .page-link{text-shadow:1px 1px 1px rgba(0,0,0,0.3);background-image:-webkit-gradient(linear, left top, left bottom, from(#484e55), color-stop(60%, #3A3F44), to(#313539));background-image:linear-gradient(#484e55, #3A3F44 60%, #313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.pagination .page-link:hover{background-image:-webkit-gradient(linear, left top, left bottom, from(#101112), color-stop(40%, #17191b), to(#1b1e20));background-image:linear-gradient(#101112, #17191b 40%, #1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;text-decoration:none}.pagination .page-item.active .page-link{background-image:-webkit-gradient(linear, left top, left bottom, from(#101112), color-stop(40%, #17191b), to(#1b1e20));background-image:linear-gradient(#101112, #17191b 40%, #1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none}.pagination .page-item.disabled .page-link{background-image:-webkit-gradient(linear, left top, left bottom, from(#484e55), color-stop(60%, #3A3F44), to(#313539));background-image:linear-gradient(#484e55, #3A3F44 60%, #313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.breadcrumb{border:1px solid rgba(0,0,0,0.6);text-shadow:1px 1px 1px rgba(0,0,0,0.3);background-color:transparent;background-image:-webkit-gradient(linear, left top, left bottom, from(#484e55), color-stop(60%, #3A3F44), to(#313539));background-image:linear-gradient(#484e55, #3A3F44 60%, #313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.breadcrumb a,.breadcrumb a:hover{color:#fff}.alert .close{color:#000;text-decoration:none}.alert{border:none;color:#fff}.alert a,.alert .alert-link{color:#fff;text-decoration:underline}.alert-primary{background-color:#3A3F44}.alert-secondary{background-color:#7A8288}.alert-success{background-color:#62c462}.alert-info{background-color:#5bc0de}.alert-warning{background-color:#f89406}.alert-danger{background-color:#ee5f5b}.alert-light{background-color:#e9ecef}.alert-dark{background-color:#272B30}.alert-light,.alert-light a:not(.btn),.alert-light .alert-link{color:#272B30}.badge-success,.badge-warning,.badge-info{color:#fff}.jumbotron{border:1px solid rgba(0,0,0,0.6)}.list-group-item:hover{background-color:#1c1e22} diff --git a/frontend/static/js/angular-route.min.js b/frontend/static/js/angular-route.min.js index d73d03c4..d2622160 100644 --- a/frontend/static/js/angular-route.min.js +++ b/frontend/static/js/angular-route.min.js @@ -1,11 +1,11 @@ /* - AngularJS v1.7.5 + AngularJS v1.7.9 (c) 2010-2018 Google, Inc. http://angularjs.org License: MIT */ (function(I,b){'use strict';function z(b,h){var d=[],c=b.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)(\*\?|[?*])?/g,function(b,c,h,k){b="?"===k||"*?"===k;k="*"===k||"*?"===k;d.push({name:h,optional:b});c=c||"";return(b?"(?:"+c:c+"(?:")+(k?"(.+?)":"([^/]+)")+(b?"?)?":")")}).replace(/([/$*])/g,"\\$1");h.ignoreTrailingSlashes&&(c=c.replace(/\/+$/,"")+"/*");return{keys:d,regexp:new RegExp("^"+c+"(?:[?#]|$)",h.caseInsensitiveMatch?"i":"")}}function A(b){p&&b.get("$route")}function v(u,h,d){return{restrict:"ECA", terminal:!0,priority:400,transclude:"element",link:function(c,f,g,l,k){function q(){r&&(d.cancel(r),r=null);m&&(m.$destroy(),m=null);s&&(r=d.leave(s),r.done(function(b){!1!==b&&(r=null)}),s=null)}function C(){var g=u.current&&u.current.locals;if(b.isDefined(g&&g.$template)){var g=c.$new(),l=u.current;s=k(g,function(g){d.enter(g,null,s||f).done(function(d){!1===d||!b.isDefined(w)||w&&!c.$eval(w)||h()});q()});m=l.scope=g;m.$emit("$viewContentLoaded");m.$eval(p)}else q()}var m,s,r,w=g.autoscroll,p=g.onload|| -"";c.$on("$routeChangeSuccess",C);C()}}}function x(b,h,d){return{restrict:"ECA",priority:-400,link:function(c,f){var g=d.current,l=g.locals;f.html(l.$template);var k=b(f.contents());if(g.controller){l.$scope=c;var q=h(g.controller,l);g.controllerAs&&(c[g.controllerAs]=q);f.data("$ngControllerController",q);f.children().data("$ngControllerController",q)}c[g.resolveAs||"$resolve"]=l;k(c)}}}var D,E,F,G,y=b.module("ngRoute",[]).info({angularVersion:"1.7.5"}).provider("$route",function(){function u(d, +"";c.$on("$routeChangeSuccess",C);C()}}}function x(b,h,d){return{restrict:"ECA",priority:-400,link:function(c,f){var g=d.current,l=g.locals;f.html(l.$template);var k=b(f.contents());if(g.controller){l.$scope=c;var q=h(g.controller,l);g.controllerAs&&(c[g.controllerAs]=q);f.data("$ngControllerController",q);f.children().data("$ngControllerController",q)}c[g.resolveAs||"$resolve"]=l;k(c)}}}var D,E,F,G,y=b.module("ngRoute",[]).info({angularVersion:"1.7.9"}).provider("$route",function(){function u(d, c){return b.extend(Object.create(d),c)}D=b.isArray;E=b.isObject;F=b.isDefined;G=b.noop;var h={};this.when=function(d,c){var f;f=void 0;if(D(c)){f=f||[];for(var g=0,l=c.length;g';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.5"});c.module("ngSanitize").filter("linky",["$sanitize",function(f){var h=/((s?ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i, +(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/frontend/static/js/angular.min.js b/frontend/static/js/angular.min.js index 97b56ad2..f6bf3370 100644 --- a/frontend/static/js/angular.min.js +++ b/frontend/static/js/angular.min.js @@ -1,347 +1,350 @@ /* - AngularJS v1.7.5 + AngularJS v1.7.9 (c) 2010-2018 Google, Inc. http://angularjs.org License: MIT */ -(function(B){'use strict';function oe(a){if(F(a))v(a.objectMaxDepth)&&(Vb.objectMaxDepth=Wb(a.objectMaxDepth)?a.objectMaxDepth:NaN),v(a.urlErrorParamsEnabled)&&Fa(a.urlErrorParamsEnabled)&&(Vb.urlErrorParamsEnabled=a.urlErrorParamsEnabled);else return Vb}function Wb(a){return ba(a)&&0c)return"...";var d=b.$$hashKey,f;if(I(a)){f=0;for(var g=a.length;f

").append(a).html();try{return a[0].nodeType===Oa?O(b):b.match(/^(<[^>]+>)/)[1].replace(/^<([\w-]+)/,function(a,b){return"<"+O(b)})}catch(d){return O(b)}}function Sc(a){try{return decodeURIComponent(a)}catch(b){}}function fc(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=Sc(e),v(e)&&(f=v(f)?Sc(f):!0,sa.call(b,e)?I(b[e])?b[e].push(f):b[e]=[b[e],f]:b[e]=f))});return b}function ve(a){var b=[];r(a,function(a,c){I(a)?r(a,function(a){b.push(ca(c,!0)+(!0===a?"":"="+ca(a,!0)))}):b.push(ca(c,!0)+(!0===a?"":"="+ca(a,!0)))});return b.length?b.join("&"):""}function gc(a){return ca(a,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function ca(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 we(a,b){var d,c,e=Pa.length;for(c=0;c protocol indicates an extension, document.location.href does not match.")}catch(f){}}function Tc(a,b,d){F(d)||(d={});d=R({strictDi:!1},d);var c=function(){a=y(a);if(a.injector()){var c=a[0]===B.document?"document":ya(a);throw Ia("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!/;B&&e.test(B.name)&&(d.debugInfoEnabled=!0,B.name=B.name.replace(e,""));if(B&&!f.test(B.name))return c();B.name=B.name.replace(f,"");ea.resumeBootstrap=function(a){r(a,function(a){b.push(a)});return c()};z(ea.resumeDeferredBootstrap)&& -ea.resumeDeferredBootstrap()}function ze(){B.name="NG_ENABLE_DEBUG_INFO!"+B.name;B.location.reload()}function Ae(a){a=ea.element(a).injector();if(!a)throw Ia("test");return a.get("$$testability")}function Uc(a,b){b=b||"_";return a.replace(Be,function(a,c){return(c?b:"")+a.toLowerCase()})}function Ce(){var a;if(!Vc){var b=qb();(rb=x(b)?B.jQuery:b?B[b]:void 0)&&rb.fn.on?(y=rb,R(rb.fn,{scope:Wa.scope,isolateScope:Wa.isolateScope,controller:Wa.controller,injector:Wa.injector,inheritedData:Wa.inheritedData})): -y=W;a=y.cleanData;y.cleanData=function(b){for(var c,e=0,f;null!=(f=b[e]);e++)(c=(y._data(f)||{}).events)&&c.$destroy&&y(f).triggerHandler("$destroy");a(b)};ea.element=y;Vc=!0}}function gb(a,b,d){if(!a)throw Ia("areq",b||"?",d||"required");return a}function sb(a,b,d){d&&I(a)&&(a=a[a.length-1]);gb(z(a),b,"not a function, got "+(a&&"object"===typeof a?a.constructor.name||"Object":typeof a));return a}function Qa(a,b){if("hasOwnProperty"===a)throw Ia("badname",b);}function De(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 W(a){if(a instanceof W)return a;var b;C(a)&&(a=T(a),b=!0);if(!(this instanceof W)){if(b&&"<"!==a.charAt(0))throw mc("nosel");return new W(a)}if(b){b= -B.document;var d;a=(d=kg.exec(a))?[b.createElement(d[1])]:(d=dd(a,b))?d.childNodes:[];nc(this,a)}else z(a)?ed(a):nc(this,a)}function oc(a){return a.cloneNode(!0)}function yb(a,b){!b&&kc(a)&&y.cleanData([a]);a.querySelectorAll&&y.cleanData(a.querySelectorAll("*"))}function fd(a){for(var b in a)return!1;return!0}function gd(a){var b=a.ng339,d=b&&Ja[b],c=d&&d.events,d=d&&d.data;d&&!fd(d)||c&&!fd(c)||(delete Ja[b],a.ng339=void 0)}function hd(a,b,d,c){if(v(c))throw mc("offargs");var e=(c=zb(a))&&c.events, -f=c&&c.handle;if(f){if(b){var g=function(b){var c=e[b];v(d)&&cb(c||[],d);v(d)&&c&&0l&&this.remove(n.key);return b}},get:function(a){if(l";b=Ea.firstChild.attributes;var d=b[0];b.removeNamedItem(d.name);d.value=c;a.attributes.setNamedItem(d)}function ra(a,b){try{a.addClass(b)}catch(c){}} -function fa(a,b,c,d,e){a instanceof y||(a=y(a));var f=Xa(a,b,a,c,d,e);fa.$$addScopeClass(a);var g=null;return function(b,c,d){if(!a)throw aa("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"!==ta(d)&&ma.call(d).match(/SVG/)?"svg":"html":"html");d="html"!==g?y(ja(g,y("
").append(a).html())):c?Wa.clone.call(a): -a;if(k)for(var l in k)d.data("$"+l+"Controller",k[l].instance);fa.$$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,q,n,G;if(J)for(G=Array(c.length),m=0;mt.priority)break;if(N=t.scope)t.templateUrl||(F(N)?(ca("new/isolated scope",s||G,t,H),s=t):ca("new/isolated scope",s,t,H)),G=G||t;K=t.name;if(!na&&(t.replace&&(t.templateUrl||t.template)||t.transclude&&!t.$$tlb)){for(N=ra+1;na=a[N++];)if(na.transclude&&!na.$$tlb||na.replace&&(na.templateUrl||na.template)){Ib=!0;break}na=!0}!t.templateUrl&&t.controller&&(u=u||S(),ca("'"+K+"' controller", -u[K],t,H),u[K]=t);if(N=t.transclude)if(E=!0,t.$$tlb||(ca("transclusion",L,t,H),L=t),"element"===N)P=!0,n=t.priority,X=H,H=d.$$element=y(fa.$$createComment(K,d[K])),b=H[0],pa(f,Ga.call(X,0),b),Q=Y(Ib,X,e,n,g&&g.name,{nonTlbTranscludeDirective:L});else{var ka=S();if(F(N)){X=B.document.createDocumentFragment();var Xa=S(),M=S();r(N,function(a,b){var c="?"===a.charAt(0);a=c?a.substring(1):a;Xa[a]=b;ka[b]=null;M[b]=c});r(H.contents(),function(a){var b=Xa[va(ta(a))];b?(M[b]=!0,ka[b]=ka[b]||B.document.createDocumentFragment(), -ka[b].appendChild(a)):X.appendChild(a)});r(M,function(a,b){if(!a)throw aa("reqslot",b);});for(var O in ka)ka[O]&&(Q=y(ka[O].childNodes),ka[O]=Y(Ib,Q,e));X=y(X.childNodes)}else X=y(oc(b)).contents();H.empty();Q=Y(Ib,X,e,void 0,void 0,{needsNewScope:t.$$isolateScope||t.$$newScope});Q.$$slots=ka}if(t.template)if(D=!0,ca("template",w,t,H),w=t,N=z(t.template)?t.template(H,d):t.template,N=Na(N),t.replace){g=t;X=lc.test(N)?qd(ja(t.templateNamespace,T(N))):[];b=X[0];if(1!==X.length||1!==b.nodeType)throw aa("tplrt", -K,"");pa(f,H,b);C={$attr:{}};N=rc(b,[],C);var zg=a.splice(ra+1,a.length-(ra+1));(s||G)&&da(N,s,G);a=a.concat(N).concat(zg);ha(d,C);C=a.length}else H.html(N);if(t.templateUrl)D=!0,ca("template",w,t,H),w=t,t.replace&&(g=t),q=ia(a.splice(ra,a.length-ra),H,d,f,E&&Q,h,k,{controllerDirectives:u,newScopeDirective:G!==t&&G,newIsolateScopeDirective:s,templateDirective:w,nonTlbTranscludeDirective:L}),C=a.length;else if(t.compile)try{p=t.compile(H,d,Q);var V=t.$$originalDirective||t;z(p)?m(null,Va(V,p),A,ib): -p&&m(Va(V,p.pre),Va(V,p.post),A,ib)}catch(ea){c(ea,ya(H))}t.terminal&&(q.terminal=!0,n=Math.max(n,t.priority))}q.scope=G&&!0===G.scope;q.transcludeOnThisElement=E;q.templateOnThisElement=D;q.transclude=Q;l.hasElementTranscludeDirective=P;return q}function $(a,b,c,d){var e;if(C(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 aa("ctreq",b,a);}else if(I(b))for(e=[],g=0,f=b.length;gc.priority)&&-1!==c.restrict.indexOf(e)){k&&(c=$b(c,{$$start:k,$$end:l}));if(!c.$$bindings){var J=m=c,G=c.name,u={isolateScope:null,bindToController:null};F(J.scope)&&(!0===J.bindToController?(u.bindToController=d(J.scope,G,!0),u.isolateScope={}):u.isolateScope=d(J.scope,G,!1));F(J.bindToController)&&(u.bindToController=d(J.bindToController, -G,!0));if(u.bindToController&&!J.controller)throw aa("noctrl",G);m=m.$$bindings=u;F(m.isolateScope)&&(c.$$isolateBindings=m.isolateScope)}b.push(c);m=c}}return m}function ea(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 wa(a,b){if("srcdoc"===b)return P.HTML;if("src"===b||"ngSrc"===b)return-1===["img","video","audio","source","track"].indexOf(a)?P.RESOURCE_URL:P.MEDIA_URL;if("xlinkHref"===b)return"image"===a?P.MEDIA_URL: -"a"===a?P.URL:P.RESOURCE_URL;if("form"===a&&"action"===b||"base"===a&&"href"===b||"link"===a&&"href"===b)return P.RESOURCE_URL;if("a"===a&&("href"===b||"ngHref"===b))return P.URL}function xa(a,b){var c=b.toLowerCase();return w[a+"|"+c]||w["*|"+c]}function Da(a){return na(P.valueOf(a),"ng-prop-srcset")}function Ha(a,b,c,d){if(m.test(d))throw aa("nodomevents");a=ta(a);var e=xa(a,d),f=Ta;"srcset"!==d||"img"!==a&&"source"!==a?e&&(f=P.getTrusted.bind(P,e)):f=Da;b.push({priority:100,compile:function(a, -b){var e=q(b[c]),g=q(b[c],function(a){return P.valueOf(a)});return{pre:function(a,b){function c(){var g=e(a);b.prop(d,f(g))}c();a.$watch(g,c)}}}})}function Ia(a,c,d,e,f){var g=ta(a),k=wa(g,e),l=h[e]||f,n=b(d,!f,k,l);if(n){if("multiple"===e&&"select"===g)throw aa("selmulti",ya(a));if(m.test(e))throw aa("nodomevents");c.push({priority:100,compile:function(){return{pre:function(a,c,f){c=f.$$observers||(f.$$observers=S());var g=f[e];g!==d&&(n=g&&b(g,!0,k,l),d=g);n&&(f[e]=n(a),(c[e]||(c[e]=[])).$$inter= -!0,(f.$$observers&&f.$$observers[e].$$scope||a).$watch(n,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===Oa&&""===d.nodeValue.trim())&&Bg.call(a,b,1)}return a}function xg(a,b){if(b&&C(b))return b;if(C(a)){var d=td.exec(a);if(d)return d[3]}}function Bf(){var a={};this.has=function(b){return a.hasOwnProperty(b)};this.register=function(b,d){Qa(b,"controller");F(b)?R(a, -b):a[b]=d};this.$get=["$injector",function(b){function d(a,b,d,g){if(!a||!F(a.$scope))throw M("$controller")("noscp",g,b);a.$scope[b]=d}return function(c,e,f,g){var k,h,l;f=!0===f;g&&C(g)&&(l=g);if(C(c)){g=c.match(td);if(!g)throw ud("ctrlfmt",c);h=g[1];l=l||g[3];c=a.hasOwnProperty(h)?a[h]:De(e.$scope,h,!0);if(!c)throw ud("ctrlreg",h);sb(c,h,!0)}if(f)return f=(I(c)?c[c.length-1]:c).prototype,k=Object.create(f||null),l&&d(e,l,k,h||c.name),R(function(){var a=b.invoke(c,k,e,h);a!==k&&(F(a)||z(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 Cf(){this.$get=["$window",function(a){return y(a.document)}]}function Df(){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 Ef(){this.$get=["$log",function(a){return function(b,d){a.error.apply(a,arguments)}}]} -function tc(a){return F(a)?ha(a)?a.toISOString():eb(a):a}function Kf(){this.$get=function(){return function(a){if(!a)return"";var b=[];Nc(a,function(a,c){null===a||x(a)||z(a)||(I(a)?r(a,function(a){b.push(ca(c)+"="+ca(tc(a)))}):b.push(ca(c)+"="+ca(tc(a))))});return b.join("&")}}}function Lf(){this.$get=function(){return function(a){function b(a,e,f){I(a)?r(a,function(a,c){b(a,e+"["+(F(a)?c:"")+"]")}):F(a)&&!ha(a)?Nc(a,function(a,c){b(a,e+(f?"":"[")+c+(f?"":"]"))}):(z(a)&&(a=a()),d.push(ca(e)+"="+ -(null==a?"":ca(tc(a)))))}if(!a)return"";var d=[];b(a,"",!0);return d.join("&")}}}function uc(a,b){if(C(a)){var d=a.replace(Cg,"").trim();if(d){var c=b("Content-Type"),c=c&&0===c.indexOf(vd),e;(e=c)||(e=(e=d.match(Dg))&&Eg[e[0]].test(d));if(e)try{a=Qc(d)}catch(f){if(!c)return a;throw Kb("baddata",a,f);}}}return a}function wd(a){var b=S(),d;C(a)?r(a.split("\n"),function(a){d=a.indexOf(":");var e=O(T(a.substr(0,d)));a=T(a.substr(d+1));e&&(b[e]=b[e]?b[e]+", "+a:a)}):F(a)&&r(a,function(a,d){var f=O(d), -g=T(a);f&&(b[f]=b[f]?b[f]+", "+g:g)});return b}function xd(a){var b;return function(d){b||(b=wd(a));return d?(d=b[O(d)],void 0===d&&(d=null),d):b}}function yd(a,b,d,c){if(z(c))return c(a,b,d);r(c,function(c){a=c(a,b,d)});return a}function Jf(){var a=this.defaults={transformResponse:[uc],transformRequest:[function(a){return F(a)&&"[object File]"!==ma.call(a)&&"[object Blob]"!==ma.call(a)&&"[object FormData]"!==ma.call(a)?eb(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},post:oa(vc), -put:oa(vc),patch:oa(vc)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",paramSerializer:"$httpParamSerializer",jsonpCallbackParam:"callback"},b=!1;this.useApplyAsync=function(a){return v(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,q){function n(b){function c(a,b){for(var d=0,e=b.length;da?b:l.reject(b)}if(!F(b))throw M("$http")("badreq",b);if(!C(q.valueOf(b.url)))throw M("$http")("badreq",b.url);var g=R({method:"get",transformRequest:a.transformRequest,transformResponse:a.transformResponse,paramSerializer:a.paramSerializer,jsonpCallbackParam:a.jsonpCallbackParam}, -b);g.headers=function(b){var c=a.headers,e=R({},b.headers),f,g,h,c=R({},c.common,c[O(b.method)]);a:for(f in c){g=O(f);for(h in e)if(O(h)===g)continue a;e[f]=c[f]}return d(e,oa(b))}(b);g.method=ub(g.method);g.paramSerializer=C(g.paramSerializer)?m.get(g.paramSerializer):g.paramSerializer;e.$$incOutstandingRequestCount("$http");var h=[],k=[];b=l.resolve(g);r(w,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=yd(b.data,xd(c),void 0,b.transformRequest);x(d)&&r(c,function(a,b){"content-type"===O(b)&&delete c[b]});x(b.withCredentials)&&!x(a.withCredentials)&&(b.withCredentials=a.withCredentials);return s(b,d).then(f,f)});b=c(b,k);return b=b.finally(function(){e.$$completeOutstandingRequest(A,"$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)}Q&&(200<=a&&300>a?Q.put(N,[a,c,wd(d),e,f]):Q.remove(N));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:xd(d),config:c,statusText:e,xhrStatus:f})}function s(a){m(a.data,a.status,oa(a.headers()),a.statusText,a.xhrStatus)}function w(){var a=n.pendingRequests.indexOf(c);-1!==a&&n.pendingRequests.splice(a,1)}var L=l.defer(),P=L.promise,Q,p,na=c.headers,y="jsonp"===O(c.method), -N=c.url;y?N=q.getTrustedResourceUrl(N):C(N)||(N=q.valueOf(N));N=t(N,c.paramSerializer(c.params));y&&(N=u(N,c.jsonpCallbackParam));n.pendingRequests.push(c);P.then(w,w);!c.cache&&!a.cache||!1===c.cache||"GET"!==c.method&&"JSONP"!==c.method||(Q=F(c.cache)?c.cache:F(a.cache)?a.cache:D);Q&&(p=Q.get(N),v(p)?p&&z(p.then)?p.then(s,s):I(p)?m(p[1],p[0],oa(p[2]),p[3],p[4]):m(p,200,{},"OK","complete"):Q.put(N,P));x(p)&&((p=ic(c.url)?g()[c.xsrfCookieName||a.xsrfCookieName]:void 0)&&(na[c.xsrfHeaderName||a.xsrfHeaderName]= -p),f(c.method,N,d,k,na,c.timeout,c.withCredentials,c.responseType,e(c.eventHandlers),e(c.uploadEventHandlers)));return P}function t(a,b){0=h&&(u.resolve(s), -f(D.$$intervalId));t||c.$apply()},k,u,t);return D}}}]}function zd(a,b){var d=la(a);b.$$protocol=d.protocol;b.$$host=d.hostname;b.$$port=da(d.port)||Ig[d.protocol]||null}function Ad(a,b,d){if(Jg.test(a))throw jb("badpath",a);var c="/"!==a.charAt(0);c&&(a="/"+a);a=la(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=fc(a.search);b.$$hash=decodeURIComponent(a.hash); -b.$$path&&"/"!==b.$$path.charAt(0)&&(b.$$path="/"+b.$$path)}function wc(a,b){return a.slice(0,b.length)===b}function wa(a,b){if(wc(b,a))return b.substr(a.length)}function qa(a){var b=a.indexOf("#");return-1===b?a:a.substr(0,b)}function xc(a,b,d){this.$$html5=!0;d=d||"";zd(a,this);this.$$parse=function(a){var d=wa(b,a);if(!C(d))throw jb("ipthprfx",a,b);Ad(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;v(f=wa(a,c))?(g=f,g=d&&v(f=wa(d,f))?b+(wa("/",f)||f):a+g):v(f=wa(b,c))?g=b+f:b===c+"/"&&(g=b);g&&this.$$parse(g);return!!g}}function yc(a,b,d){zd(a,this);this.$$parse=function(c){var e=wa(a,c)||wa(b,c),f;x(e)||"#"!==e.charAt(0)?this.$$html5?f=e:(f="",x(e)&&(a=c,this.replace())):(f=wa(d,e),x(f)&&(f=e));Ad(f,this,!1);c=this.$$path;var e=a,g=/^\/[A-Z]:(\/.*)/;wc(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 qa(a)===qa(b)?(this.$$parse(b),!0):!1}}function Bd(a,b,d){this.$$html5=!0;yc.apply(this,arguments);this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;a===qa(c)?f=c:(g=wa(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 Cd(a,b){return function(d){if(x(d))return this[a]; -this[a]=b(d);this.$$compose();return this}}function Pf(){var a="!",b={enabled:!1,requireBase:!0,rewriteLinks:!0};this.hashPrefix=function(b){return v(b)?(a=b,this):a};this.html5Mode=function(a){if(Fa(a))return b.enabled=a,this;if(F(a)){Fa(a.enabled)&&(b.enabled=a.enabled);Fa(a.requireBase)&&(b.requireBase=a.requireBase);if(Fa(a.rewriteLinks)||C(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||la(a).href===la(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,q;q=c.baseHref();var n=c.url(),s;if(b.enabled){if(!q&&b.requireBase)throw jb("nobase");s=n.substring(0,n.indexOf("/",n.indexOf("//")+2))+(q||"/");q=e.history?xc:Bd}else s=qa(n),q=yc;var t=s.substr(0,qa(s).lastIndexOf("/")+1);m=new q(s, -t,"#"+a);m.$$parseLinkUrl(n,n);m.$$state=c.state();var u=/^\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=y(a.target);"a"!==ta(g[0]);)if(g[0]===f[0]||!(g=g.parent())[0])return;if(!C(e)||!x(g.attr(e))){var e=g.prop("href"),h=g.attr("href")||g.attr("xlink:href");F(e)&&"[object SVGAnimatedString]"===e.toString()&&(e=la(e.animVal).href);u.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 D=!0;c.onUrlChange(function(a,b){wc(a,t)?(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)):(D=!1,l(c,e)))}),d.$$phase||d.$digest()):g.location.href=a});d.$watch(function(){if(D||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(D||n)D=!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 Qf(){var a=!0,b=this;this.debugEnabled=function(b){return v(b)?(a=b,this):a};this.$get=["$window",function(d){function c(a){bc(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||A;return function(){var a=[];r(arguments,function(b){a.push(c(b))});return Function.prototype.apply.call(e,b,a)}}var f=Aa||/\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 Kg(a){return a+""}function Lg(a,b){return"undefined"!==typeof a?a:b}function Dd(a,b){return"undefined"===typeof a?b:"undefined"===typeof b?a:a+b}function Mg(a,b){switch(a.type){case p.MemberExpression:if(a.computed)return!1;break;case p.UnaryExpression:return 1;case p.BinaryExpression:return"+"!==a.operator?1:!1;case p.CallExpression:return!1}return void 0===b?Ed:b}function Y(a,b,d){var c,e,f=a.isPure=Mg(a,d);switch(a.type){case p.Program:c=!0;r(a.body,function(a){Y(a.expression,b,f);c=c&& -a.expression.constant});a.constant=c;break;case p.Literal:a.constant=!0;a.toWatch=[];break;case p.UnaryExpression:Y(a.argument,b,f);a.constant=a.argument.constant;a.toWatch=a.argument.toWatch;break;case p.BinaryExpression:Y(a.left,b,f);Y(a.right,b,f);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.left.toWatch.concat(a.right.toWatch);break;case p.LogicalExpression:Y(a.left,b,f);Y(a.right,b,f);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.constant?[]:[a];break;case p.ConditionalExpression:Y(a.test, -b,f);Y(a.alternate,b,f);Y(a.consequent,b,f);a.constant=a.test.constant&&a.alternate.constant&&a.consequent.constant;a.toWatch=a.constant?[]:[a];break;case p.Identifier:a.constant=!1;a.toWatch=[a];break;case p.MemberExpression:Y(a.object,b,f);a.computed&&Y(a.property,b,f);a.constant=a.object.constant&&(!a.computed||a.property.constant);a.toWatch=a.constant?[]:[a];break;case p.CallExpression:c=d=a.filter?!b(a.callee.name).$stateful:!1;e=[];r(a.arguments,function(a){Y(a,b,f);c=c&&a.constant;e.push.apply(e, -a.toWatch)});a.constant=c;a.toWatch=d?e:[a];break;case p.AssignmentExpression:Y(a.left,b,f);Y(a.right,b,f);a.constant=a.left.constant&&a.right.constant;a.toWatch=[a];break;case p.ArrayExpression:c=!0;e=[];r(a.elements,function(a){Y(a,b,f);c=c&&a.constant;e.push.apply(e,a.toWatch)});a.constant=c;a.toWatch=e;break;case p.ObjectExpression:c=!0;e=[];r(a.properties,function(a){Y(a.value,b,f);c=c&&a.value.constant;e.push.apply(e,a.value.toWatch);a.computed&&(Y(a.key,b,!1),c=c&&a.key.constant,e.push.apply(e, -a.key.toWatch))});a.constant=c;a.toWatch=e;break;case p.ThisExpression:a.constant=!1;a.toWatch=[];break;case p.LocalsExpression:a.constant=!1,a.toWatch=[]}}function Fd(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 Gd(a){return a.type===p.Identifier||a.type===p.MemberExpression}function Hd(a){if(1===a.body.length&&Gd(a.body[0].expression))return{type:p.AssignmentExpression,left:a.body[0].expression,right:{type:p.NGValueParameter},operator:"="}} -function Id(a){this.$filter=a}function Jd(a){this.$filter=a}function Mb(a,b,d){this.ast=new p(a,d);this.astCompiler=d.csp?new Jd(b):new Id(b)}function zc(a){return z(a.valueOf)?a.valueOf():Ng.call(a)}function Rf(){var a=S(),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(t), -d=(new Mb(d,e,t)).parse(b),a[f]=q(d)),s(d,c);case "function":return s(b,c);default:return s(A,c)}}function g(a,b,c){return null==a||null==b?a===b:"object"!==typeof a||(a=zc(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&&zc(b));return h},b,c,e)}for(var l=[],m=[],n=0,q=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)sa.call(e,b)||(s--,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&&(x=4-r,E[x]|| -(E[x]=[]),E[x].push({msg:z(a.exp)?"fn: "+(a.exp.name||a.exp.toString()):a.exp,newVal:g,oldVal:h}));else if(a===c){s=!1;break a}}catch(B){f(B)}if(!(n=!p.$$suspended&&p.$$watchersCount&&p.$$childHead||p!==D&&p.$$nextSibling))for(;p!==D&&!(n=p.$$nextSibling);)p=p.$parent}while(p=n);if((s||v.length)&&!r--)throw w.$$phase=null,d("infdig",b,E);}while(s||v.length);for(w.$$phase=null;Gc)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;JAa)throw Da("iequirks");var c=oa(U);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(U,function(a,b){var d=O(b);c[("parse_as_"+d).replace(Bc,wb)]=function(b){return e(a,b)};c[("get_trusted_"+d).replace(Bc,wb)]=function(b){return f(a,b)};c[("trust_as_"+d).replace(Bc,wb)]=function(b){return g(a,b)}}); -return c}]}function Xf(){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=da((/android (\d+)/.exec(O((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&&Aa)return!1;if(x(d[a])){var b=g.createElement("div");d[a]="on"+a in b}return d[a]},csp:pa(),transitions:h,animations:l,android:e}}]}function Yf(){this.$get=ia(function(a){return new Pg(a)})}function Pg(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(q){a.error(q)}}};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 $f(){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(!C(k)|| -x(d.get(k)))k=f.getTrustedResourceUrl(k);var l=c.defaults&&c.defaults.transformResponse;I(l)?l=l.filter(function(a){return a!==uc}):l===uc&&(l=null);return c.get(k,R({cache:d,transformResponse:l},a)).finally(function(){g.totalPendingRequests--}).then(function(a){return d.put(k,a.data)},function(a){h||(a=Qg("tpload",k,a.status,a.statusText),b(a));return e.reject(a)})}g.totalPendingRequests=0;return g}]}function ag(){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=ea.element(a).data("$binding");c&&r(c,function(c){d?(new RegExp("(^|\\s)"+Ld(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)===Dc;e++);if(e===(g=a.length))d=[0],c=1;else{for(g--;a.charAt(g)===Dc;)g--;c-=e;d=[];for(f=0;e<=g;e++,f++)d[f]=+a.charAt(e)}c>Vd&&(d=d.splice(0,Vd-1),b=c-1,c=1);return{d:d,e:b,i:c}}function Zg(a,b,d,c){var e=a.d,f=e.length-a.i;b=x(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 Wd(a){var b=(new Date(a,0,1)).getDay();return new Date(a,0,(4>=b?5:12)-b)}function Xd(a){return function(b){var d=Wd(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 Ec(a,b){return 0>=a.getFullYear()?b.ERAS[0]:b.ERAS[1]}function Qd(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=da(b[9]+b[10]),g=da(b[9]+b[11]));k.call(a,da(b[1]),da(b[2])-1,da(b[3]));f=da(b[4]||0)-f;g=da(b[5]||0)-g;k=da(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;C(c)&&(c=$g.test(c)?da(c):b(c));ba(c)&&(c=new Date(c));if(!ha(c)||!isFinite(c.getTime()))return c;for(;d;)(l=ah.exec(d))?(k=db(k,l,1),d=k.pop()):(k.push(d),d=null);var m=c.getTimezoneOffset();f&&(m=dc(f,m),c=ec(c,f,!0));r(k,function(b){h=bh[b];g+=h?h(c,a.DATETIME_FORMATS,m):"''"===b?"'":b.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function Tg(){return function(a,b){x(b)&&(b=2);return eb(a,b)}}function Ug(){return function(a,b,d){b=Infinity===Math.abs(Number(b))?Number(b):da(b);if(V(b))return a; -ba(a)&&(a=a.toString());if(!xa(a))return a;d=!d||isNaN(d)?0:da(d);d=0>d?Math.max(0,a.length+d):d;return 0<=b?Fc(a,d,d+b):0===d?Fc(a,b,a.length):Fc(a,Math.max(0,d+b),d)}}function Fc(a,b,d){return C(a)?a.slice(b,d):Ga.call(a,b,d)}function Sd(a){function b(b){return b.map(function(b){var c=1,d=Ta;if(z(b))d=b;else if(C(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&&(F(h)&&(h=a.index),F(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(be[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(C(d)){'"'===d.charAt(0)&&'"'===d.charAt(d.length-1)&&(d=d.substring(1,d.length-1));if(ch.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){function q(a){return a&&!(a.getTime&&a.getTime()!==a.getTime())}function n(a){return v(a)&&!ha(a)?s(a)||void 0:a}function s(a,b){var c=k.$options.getOption("timezone");p&&p!==c&&(b=Rc(b,dc(p)));var e=d(a,b);!isNaN(e)&&c&&(e=ec(e,c));return e}Hc(e,f,g,k,a);Ra(e,f,g,k,h,l);var t="time"===a||"datetimelocal"===a,u,p;k.$parsers.push(function(c){if(k.$isEmpty(c))return null; -if(b.test(c))return s(c,u);k.$$parserName=a});k.$formatters.push(function(a){if(a&&!ha(a))throw ob("datefmt",a);if(q(a)){u=a;var b=k.$options.getOption("timezone");b&&(p=b,u=ec(u,b,!0));var d=c;t&&C(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}p=u=null;return""});if(v(g.min)||g.ngMin){var r;k.$validators.min= -function(a){return!q(a)||x(r)||d(a)>=r};g.$observe("min",function(a){r=n(a);k.$validate()})}if(v(g.max)||g.ngMax){var y;k.$validators.max=function(a){return!q(a)||x(y)||d(a)<=y};g.$observe("max",function(a){y=n(a);k.$validate()})}}}function Hc(a,b,d,c,e){(c.$$hasNativeValidators=F(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 ce(a){a.$parsers.push(function(b){if(a.$isEmpty(b))return null;if(dh.test(b))return parseFloat(b); -a.$$parserName="number"});a.$formatters.push(function(b){if(!a.$isEmpty(b)){if(!ba(b))throw ob("numfmt",b);b=b.toString()}return b})}function Sa(a){v(a)&&!ba(a)&&(a=parseFloat(a));return V(a)?void 0:a}function Ic(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 de(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?Ic(a):0,k=e?Ic(b):0,h=f?Ic(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 ee(a,b,d,c,e){if(v(c)){a=a(c);if(!a.constant)throw ob("constexpr",d,c);return a(b)}return e}function Jc(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>|)$/,lc=/<|&#?\w+;/,ig=/<([\w:-]+)/,jg=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,ja={option:[1,'"],thead:[1,"","
"],col:[2,"", -"
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ja.optgroup=ja.option;ja.tbody=ja.tfoot=ja.colgroup=ja.caption=ja.thead;ja.th=ja.td;var qg=B.Node.prototype.contains||function(a){return!!(this.compareDocumentPosition(a)&16)},Wa=W.prototype={ready:ed,toString:function(){var a=[];r(this,function(b){a.push(""+b)});return"["+a.join(", ")+"]"},eq:function(a){return 0<=a?y(this[a]):y(this[this.length+a])},length:0, -push:gh,sort:[].sort,splice:[].splice},Gb={};r("multiple selected checked disabled readOnly required open".split(" "),function(a){Gb[O(a)]=a});var ld={};r("input select option textarea button form details".split(" "),function(a){ld[a]=!0});var sd={ngMinlength:"minlength",ngMaxlength:"maxlength",ngMin:"min",ngMax:"max",ngPattern:"pattern",ngStep:"step"};r({data:qc,removeData:pc,hasData:function(a){for(var b in Ja[a.ng339])return!0;return!1},cleanData:function(a){for(var b=0,d=a.length;b/,tg=/^[^(]*\(\s*([^)]*)\)/m, -jh=/,/,kh=/^\s*(_?)(\S+?)\1\s*$/,rg=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,za=M("$injector");fb.$$annotate=function(a,b,d){var c;if("function"===typeof a){if(!(c=a.$inject)){c=[];if(a.length){if(b)throw C(d)&&d||(d=a.name||ug(a)),za("strictdi",d);b=nd(a);r(b[1].split(jh),function(a){a.replace(kh,function(a,b,d){c.push(d)})})}a.$inject=c}}else I(a)?(b=a.length-1,sb(a[b],"fn"),c=a.slice(0,b)):sb(a,"fn",!0);return c};var ge=M("$animate"),vf=function(){this.$get=A},wf=function(){var a=new Hb,b=[];this.$get= -["$$AnimateRunner","$rootScope",function(d,c){function e(a,b,c){var d=!1;b&&(b=C(b)?b.split(" "):I(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=vg(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:A,on:A,off:A,pin:A,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}}}]},tf=["$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 ge("notcsel",c);var g=c+"-animation";b.$$registeredAnimations[c.substr(1)]=g;a.factory(g,d)};this.customFilter=function(a){1===arguments.length&&(c=z(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,ge("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){Tb[a]=!0});var nh={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=v(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","<=",">=");)a={type:p.BinaryExpression,operator:b.text,left:a,right:this.additive()};return a},additive:function(){for(var a= -this.multiplicative(),b;b=this.expect("+","-");)a={type:p.BinaryExpression,operator:b.text,left:a,right:this.multiplicative()};return a},multiplicative:function(){for(var a=this.unary(),b;b=this.expect("*","/","%");)a={type:p.BinaryExpression,operator:b.text,left:a,right:this.unary()};return a},unary:function(){var a;return(a=this.expect("+","-","!"))?{type:p.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=Ha(this.selfReferential[this.consume().text]):this.options.literals.hasOwnProperty(this.peek().text)?a={type:p.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:p.CallExpression,callee:a,arguments:this.parseArguments()},this.consume(")")):"["===b.text?(a={type:p.MemberExpression,object:a,property:this.expression(),computed:!0},this.consume("]")):"."===b.text?a={type:p.MemberExpression,object:a,property:this.identifier(),computed:!1}:this.throwError("IMPOSSIBLE");return a},filter:function(a){a=[a];for(var b={type:p.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:p.Identifier,name:a.text}},constant:function(){return{type:p.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:p.ArrayExpression, -elements:a}},object:function(){var a=[],b;if("}"!==this.peekToken().text){do{if(this.peek("}"))break;b={type:p.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:p.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:p.ThisExpression},$locals:{type:p.LocalsExpression}}};var Ed=2;Id.prototype={compile:function(a){var b=this;this.state={nextId:0,filters:{},fn:{vars:[],body:[],own:{}},assign:{vars:[], -body:[],own:{}},inputs:[]};Y(a,b.$filter);var d="",c;this.stage="assign";if(c=Hd(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=Fd(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,Kg,Lg,Dd);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,q;c=c||A;if(!f&&v(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 p.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 p.Literal:m=this.escape(a.value);this.assign(b,m);c(b||m);break;case p.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 p.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 p.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 p.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 p.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 p.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 p.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 p.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 p.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 p.ObjectExpression:l=[];q=!1;r(a.properties,function(a){a.computed&&(q=!0)});q?(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===p.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===p.Identifier?b.key.name:""+b.key.value)+":"+a)})}),m="{"+l.join(",")+"}",this.assign(b,m));c(b||m);break;case p.ThisExpression:this.assign(b,"s");c(b||"s");break;case p.LocalsExpression:this.assign(b,"l");c(b||"l");break;case p.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(C(a))return"'"+a.replace(this.stringEscapeRegex,this.stringEscapeFn)+"'";if(ba(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]}};Jd.prototype={compile:function(a){var b=this;Y(a,b.$filter);var d,c;if(d=Hd(a))c=this.recurse(d);d=Fd(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?A: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 p.Literal:return this.value(a.value,b);case p.UnaryExpression:return e=this.recurse(a.argument),this["unary"+a.operator](e,b);case p.BinaryExpression:return c=this.recurse(a.left),e=this.recurse(a.right),this["binary"+ -a.operator](c,e,b);case p.LogicalExpression:return c=this.recurse(a.left),e=this.recurse(a.right),this["binary"+a.operator](c,e,b);case p.ConditionalExpression:return this["ternary?:"](this.recurse(a.test),this.recurse(a.alternate),this.recurse(a.consequent),b);case p.Identifier:return f.identifier(a.name,b,d);case p.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 p.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 q=[],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===p.Literal||d.body[0].expression.type===p.ArrayExpression||d.body[0].expression.type===p.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 Da=M("$sce"),U={HTML:"html", -CSS:"css",MEDIA_URL:"mediaUrl",URL:"url",RESOURCE_URL:"resourceUrl",JS:"js"},Bc=/_([a-z])/g,Qg=M("$templateRequest"),Rg=M("$timeout"),$=B.document.createElement("a"),Nd=la(B.location.href),La;Od.$inject=["$document"];cd.$inject=["$provide"];var Vd=22,Ud=".",Dc="0";Pd.$inject=["$locale"];Rd.$inject=["$locale"];var bh={yyyy:ga("FullYear",4,0,!1,!0),yy:ga("FullYear",2,0,!0,!0),y:ga("FullYear",1,0,!1,!0),MMMM:kb("Month"),MMM:kb("Month",!0),MM:ga("Month",2,1),M:ga("Month",1,1),LLLL:kb("Month",!1,!0),dd:ga("Date", -2),d:ga("Date",1),HH:ga("Hours",2),H:ga("Hours",1),hh:ga("Hours",2,-12),h:ga("Hours",1,-12),mm:ga("Minutes",2),m:ga("Minutes",1),ss:ga("Seconds",2),s:ga("Seconds",1),sss:ga("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]}}, -ah=/((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))([\s\S]*)/,$g=/^-?\d+$/;Qd.$inject=["$locale"];var Vg=ia(O),Wg=ia(ub);Sd.$inject=["$parse"];var Je=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]"===ma.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=va("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(sd,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(fh))){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=va("ng-"+a);vb[b]= -function(){return{priority:99,link:function(d,c,e){var f=a,g=a;"href"===a&&"[object SVGAnimatedString]"===ma.call(c.prop("href"))&&(g="xlinkHref",e.$attr[g]="xlink:href",f=null);e.$observe(b,function(b){b?(e.$set(g,b),Aa&&f&&c.prop(f,e[g])):"href"===a&&e.$set(g,null)})}}}});var lb={$addControl:A,$getControls:ia([]),$$renameControl:function(a,b){a.$name=b},$removeControl:A,$setValidity:A,$setDirty:A,$setPristine:A,$setSubmitted:A,$$setSubmitted:A};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){Qa(a.$name,"input");this.$$controls.push(a);a.$name&&(this[a.$name]=a);a.$$parentForm=this},$getControls:function(){return oa(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,Ub);this.$dirty=!0;this.$pristine=!1;this.$$parentForm.$setDirty()},$setPristine:function(){this.$$animate.setClass(this.$$element,Za,Ub+ -" 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()})}};$d({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 he=function(a){return["$timeout","$parse",function(b,d){function c(a){return""===a?d('this[""]').assign:d(a).assign||A}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 q=f[0];if(!("action"in e)){var n=function(b){a.$apply(function(){q.$commitViewValue(); -q.$setSubmitted()});b.preventDefault()};d[0].addEventListener("submit",n);d.on("$destroy",function(){b(function(){d[0].removeEventListener("submit",n)},0,!1)})}(f[1]||q.$$parentForm).$addControl(q);var s=g?c(q.$name):A;g&&(s(a,q),e.$observe(g,function(b){q.$name!==b&&(s(a,void 0),q.$$parentForm.$$renameControl(q,b),s=c(q.$name),s(a,q))}));d.on("$destroy",function(){q.$$parentForm.$removeControl(q);s(a,void 0);R(q,lb)})}}}}}]},Ke=he(),We=he(!0),ch=/^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)$/, -oh=/^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i,ph=/^(?=.{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])?)*$/,dh=/^\s*(-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/,ie=/^(\d{4,})-(\d{2})-(\d{2})$/,je=/^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,Lc=/^(\d{4,})-W(\d\d)$/,ke=/^(\d{4,})-(\d\d)$/, -le=/^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,be=S();r(["date","datetime-local","month","time","week"],function(a){be[a]=!0});var me={text:function(a,b,d,c,e,f){Ra(a,b,d,c,e,f);Gc(c)},date:nb("date",ie,Qb(ie,["yyyy","MM","dd"]),"yyyy-MM-dd"),"datetime-local":nb("datetimelocal",je,Qb(je,"yyyy MM dd HH mm ss sss".split(" ")),"yyyy-MM-ddTHH:mm:ss.sss"),time:nb("time",le,Qb(le,["HH","mm","ss","sss"]),"HH:mm:ss.sss"),week:nb("week",Lc,function(a,b){if(ha(a))return a;if(C(a)){Lc.lastIndex=0;var d=Lc.exec(a); -if(d){var c=+d[1],e=+d[2],f=d=0,g=0,k=0,h=Wd(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",ke,Qb(ke,["yyyy","MM"]),"yyyy-MM"),number:function(a,b,d,c,e,f){Hc(a,b,d,c,"number");ce(c);Ra(a,b,d,c,e,f);var g,k;if(v(d.min)||d.ngMin)c.$validators.min=function(a,b){return c.$isEmpty(b)||x(g)||b>=g},d.$observe("min",function(a){g=Sa(a);c.$validate()});if(v(d.max)||d.ngMax)c.$validators.max= -function(a,b){return c.$isEmpty(b)||x(k)||b<=k},d.$observe("max",function(a){k=Sa(a);c.$validate()});if(v(d.step)||d.ngStep){var h;c.$validators.step=function(a,b){return c.$isEmpty(b)||x(h)||de(b,g||0,h)};d.$observe("step",function(a){h=Sa(a);c.$validate()})}},url:function(a,b,d,c,e,f){Ra(a,b,d,c,e,f);Gc(c);c.$validators.url=function(a,b){var d=a||b;return c.$isEmpty(d)||oh.test(d)}},email:function(a,b,d,c,e,f){Ra(a,b,d,c,e,f);Gc(c);c.$validators.email=function(a,b){var d=a||b;return c.$isEmpty(d)|| -ph.test(d)}},radio:function(a,b,d,c){var e=!d.ngTrim||"false"!==T(d.ngTrim);x(d.name)&&b.attr("name",++pb);b.on("change",function(a){var g;b[0].checked&&(g=d.value,e&&(g=T(g)),c.$setViewValue(g,a&&a.type))});c.$render=function(){var a=d.value;e&&(a=T(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]);d.$observe(a,c)}function k(a){q=Sa(a);V(c.$modelValue)||(m?(a=b.val(),q>a&&(a=q,b.val(a)),c.$setViewValue(a)):c.$validate())} -function h(a){n=Sa(a);V(c.$modelValue)||(m?(a=b.val(),n=q},g("min",k));e&&(c.$validators.max=m?function(){return!0}:function(a,b){return c.$isEmpty(b)||x(n)||b<=n},g("max",h));f&&(c.$validators.step=m?function(){return!p.stepMismatch}:function(a,b){return c.$isEmpty(b)||x(s)||de(b,q||0,s)},g("step",l))},checkbox:function(a,b,d,c,e,f,g,k){var h=ee(k,a,"ngTrueValue",d.ngTrueValue,!0),l=ee(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 ua(a,h)});c.$parsers.push(function(a){return a?h:l})},hidden:A,button:A,submit:A,reset:A,file:A},Xc=["$browser","$sniffer","$filter","$parse",function(a,b,d,c){return{restrict:"E",require:["?ngModel"],link:{pre:function(e,f,g,k){k[0]&&(me[O(g.type)]||me.text)(e,f,g,k[0],b,a,d,c)}}}}],qh=/^(true|false|\d+)$/,pf=function(){function a(a,d,c){var e=v(c)?c:9===Aa?"":null; -a.prop("value",e);d.$set("value",c)}return{restrict:"A",priority:100,compile:function(b,d){return qh.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)})}}}},Oe=["$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=hc(a)})}}}}],Qe=["$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=x(a)?"":a})}}}}],Pe=["$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)||"")})}}}}],of=ia({restrict:"A", -require:"ngModel",link:function(a,b,d,c){c.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),Re=Jc("",!0),Te=Jc("Odd",0),Se=Jc("Even",1),Ue=Na({compile:function(a,b){b.$set("ngCloak",void 0);a.removeClass("ng-cloak")}}),Ve=[function(){return{restrict:"A",scope:!0,controller:"@",priority:500}}],bd={},rh={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= -va("ng-"+a);bd[b]=["$parse","$rootScope","$exceptionHandler",function(d,c,e){return pd(d,c,e,b,a,rh[a])}]});var Ye=["$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))})}}}],Ze=["$templateRequest","$anchorScroll","$animate",function(a,b,d){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",controller:ea.noop,compile:function(c,e){var f=e.ngInclude||e.src,g=e.onload||"",k=e.autoscroll;return function(c,e,m,q,n){var s=0,p,r,D,w=function(){r&&(r.remove(),r=null);p&&(p.$destroy(),p=null);D&&(d.leave(D).done(function(a){!1!==a&&(r=null)}),r=D,D=null)};c.$watch(f,function(f){var m=function(a){!1===a||!v(k)||k&&!c.$eval(k)||b()}, -r=++s;f?(a(f,!0).then(function(a){if(!c.$$destroyed&&r===s){var b=c.$new();q.template=a;a=n(b,function(a){w();d.enter(a,null,e).done(m)});p=b;D=a;p.$emit("$includeContentLoaded",f);c.$eval(g)}},function(){c.$$destroyed||r!==s||(w(),c.$emit("$includeContentError",f))}),c.$emit("$includeContentRequested",f)):(w(),q.template=null)})}}}}],rf=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(b,d,c,e){ma.call(d[0]).match(/SVG/)?(d.empty(),a(dd(e.template,B.document).childNodes)(b, -function(a){d.append(a)},{futureParentElement:d})):(d.html(e.template),a(d.contents())(b))}}}],$e=Na({priority:450,compile:function(){return{pre:function(a,b,d){a.$eval(d.ngInit)}}}}),nf=function(){return{restrict:"A",priority:100,require:"ngModel",link:function(a,b,d,c){var e=d.ngList||", ",f="false"!==d.ngTrim,g=f?T(e):e;c.$parsers.push(function(a){if(!x(a)){var b=[];a&&r(a.split(g),function(a){a&&b.push(f?T(a):a)});return b}});c.$formatters.push(function(a){if(I(a))return a.join(e)});c.$isEmpty= -function(a){return!a||!a.length}}}},mb="ng-valid",Zd="ng-invalid",Za="ng-pristine",Ub="ng-dirty",ob=M("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);z(c)&&(c=a(b));return c};this.$$ngModelSet= -function(a,c){z(this.$$parsedNgModel(a))?b(a,{$$$p:c}):this.$$parsedNgModelAssign(a,c)}}else if(!this.$$parsedNgModel.assign)throw ob("nonassign",this.$$attr.ngModel,ya(this.$$element));},$render:A,$isEmpty:function(a){return x(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,Ub);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,Ub);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(!V(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||!z(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)},A):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(x(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=x(a)?void 0:!0;this.$setValidity(this.$$parserName,null);this.$$parserName="parse";if(this.$$parserValid)for(var d=0;dCa)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;de||c.$isEmpty(b)|| -b.length<=e}}}}},$c=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){if(c){var e=0;d.$observe("minlength",function(a){e=da(a)||0;c.$validate()});c.$validators.minlength=function(a,b){return c.$isEmpty(b)||b.length>=e}}}}};B.angular.bootstrap?B.console&&console.log("WARNING: Tried to load AngularJS more than once."):(Ce(),Ge(ea),ea.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"}})}]),y(function(){xe(B.document,Tc)}))})(window);!window.angular.$$csp().noInlineStyle&&window.angular.element(document.head).prepend(''); +a&&e.emptyOption?(e.removeUnknownOption(),e.selectEmptyOption()):e.unknownOption.parent().length?e.updateUnknownOption(a):e.renderUnknownOption(a)};var g=!1,k=!1;e.registerOption=function(a,b,f,g,k){if(f.$attr.ngValue){var q,r;f.$observe("value",function(a){var d,f=b.prop("selected");w(r)&&(e.removeOption(q),delete e.selectValueMap[r],d=!0);r=La(a);q=a;e.selectValueMap[r]=a;e.addOption(a,b);b.attr("value",r);d&&f&&c()})}else g?f.$observe("value",function(a){e.readValue();var d,f=b.prop("selected"); +w(q)&&(e.removeOption(q),d=!0);q=a;e.addOption(a,b);d&&f&&c()}):k?a.$watch(k,function(a,d){f.$set("value",a);var g=b.prop("selected");d!==a&&e.removeOption(d);e.addOption(a,b);d&&g&&c()}):e.addOption(f.value,b);f.$observe("disabled",function(a){if("true"===a||a&&b.prop("selected"))e.multiple?c(!0):(e.ngModelCtrl.$setViewValue(null),e.ngModelCtrl.$render())});b.on("$destroy",function(){var a=e.readValue(),b=f.value;e.removeOption(b);d();(e.multiple&&a&&-1!==a.indexOf(b)||a===b)&&c(!0)})}}],Pe=function(){return{restrict:"E", +require:["select","?ngModel"],controller:Bh,priority:1,link:{pre:function(a,b,d,c){var e=c[0],f=c[1];if(f){if(e.ngModelCtrl=f,b.on("change",function(){e.removeUnknownOption();a.$apply(function(){f.$setViewValue(e.readValue())})}),d.multiple){e.multiple=!0;e.readValue=function(){var a=[];r(b.find("option"),function(b){b.selected&&!b.disabled&&(b=b.value,a.push(b in e.selectValueMap?e.selectValueMap[b]:b))});return a};e.writeValue=function(a){r(b.find("option"),function(b){var c=!!a&&(-1!==Array.prototype.indexOf.call(a, +b.value)||-1!==Array.prototype.indexOf.call(a,e.selectValueMap[b.value]));c!==b.selected&&Oa(x(b),c)})};var g,k=NaN;a.$watch(function(){k!==f.$viewValue||va(g,f.$viewValue)||(g=ja(f.$viewValue),f.$render());k=f.$viewValue});f.$isEmpty=function(a){return!a||0===a.length}}}else e.registerOption=E},post:function(a,b,d,c){var e=c[1];if(e){var f=c[0];e.$render=function(){f.writeValue(e.$viewValue)}}}}}},Qe=["$interpolate",function(a){return{restrict:"E",priority:100,compile:function(b,d){var c,e;w(d.ngValue)|| +(w(d.value)?c=a(d.value,!0):(e=a(b.text(),!0))||d.$set("value",b.text()));return function(a,b,d){var h=b.parent();(h=h.data("$selectController")||h.parent().data("$selectController"))&&h.registerOption(a,b,d,c,e)}}}}],$c=["$parse",function(a){return{restrict:"A",require:"?ngModel",link:function(b,d,c,e){if(e){var f=c.hasOwnProperty("required")||a(c.ngRequired)(b);c.ngRequired||(c.required=!0);e.$validators.required=function(a,b){return!f||!e.$isEmpty(b)};c.$observe("required",function(a){f!==a&&(f= +a,e.$validate())})}}}}],Zc=["$parse",function(a){return{restrict:"A",require:"?ngModel",compile:function(b,d){var c,e;d.ngPattern&&(c=d.ngPattern,e="/"===d.ngPattern.charAt(0)&&ie.test(d.ngPattern)?function(){return d.ngPattern}:a(d.ngPattern));return function(a,b,d,h){if(h){var l=d.pattern;d.ngPattern?l=e(a):c=d.pattern;var m=he(l,c,b);d.$observe("pattern",function(a){var d=m;m=he(a,c,b);(d&&d.toString())!==(m&&m.toString())&&h.$validate()});h.$validators.pattern=function(a,b){return h.$isEmpty(b)|| +z(m)||m.test(b)}}}}}}],bd=["$parse",function(a){return{restrict:"A",require:"?ngModel",link:function(b,d,c,e){if(e){var f=c.maxlength||a(c.ngMaxlength)(b),g=Tb(f);c.$observe("maxlength",function(a){f!==a&&(g=Tb(a),f=a,e.$validate())});e.$validators.maxlength=function(a,b){return 0>g||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/frontend/static/js/jquery.min.js b/frontend/static/js/jquery.min.js index 105d00e6..a1c07fd8 100644 --- a/frontend/static/js/jquery.min.js +++ b/frontend/static/js/jquery.min.js @@ -1,4 +1,2 @@ -/*! jQuery v3.2.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/parseXML,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-event/ajax,-effects,-effects/Tween,-effects/animatedSelector | (c) JS Foundation and other contributors | jquery.org/license */ -!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.2.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/parseXML,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-event/ajax,-effects,-effects/Tween,-effects/animatedSelector",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext;function B(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()}var C=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,D=/^.[^:#\[\.,]*$/;function E(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):D.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(E(this,a||[],!1))},not:function(a){return this.pushStack(E(this,a||[],!0))},is:function(a){return!!E(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var F,G=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,H=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||F,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:G.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),C.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};H.prototype=r.fn,F=r(d);var I=/^(?:parents|prev(?:Until|All))/,J={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function K(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return K(a,"nextSibling")},prev:function(a){return K(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return B(a,"iframe")?a.contentDocument:(B(a,"template")&&(a=a.content||a),r.merge([],a.childNodes))}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(J[a]||r.uniqueSort(e),I.test(a)&&e.reverse()),this.pushStack(e)}});var L=/[^\x20\t\r\n\f]+/g;function M(a){var b={};return r.each(a.match(L)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?M(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=e||a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function N(a){return a}function O(a){throw a}function P(a,b,c,d){var e;try{a&&r.isFunction(e=a.promise)?e.call(a).done(b).fail(c):a&&r.isFunction(e=a.then)?e.call(a,b,c):b.apply(void 0,[a].slice(d))}catch(a){c.apply(void 0,[a])}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==O&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:N,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:N)),c[2][3].add(g(0,a,r.isFunction(d)?d:O))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(P(a,g.done(h(c)).resolve,g.reject,!b),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)P(e[c],h(c),g.reject);return g.promise()}});var Q=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&Q.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var R=r.Deferred();r.fn.ready=function(a){return R.then(a)["catch"](function(a){r.readyException(a); -}),this},r.extend({isReady:!1,readyWait:1,ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||R.resolveWith(d,[r]))}}),r.ready.then=R.then;function S(){d.removeEventListener("DOMContentLoaded",S),a.removeEventListener("load",S),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",S),a.addEventListener("load",S));var T=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)T(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){X.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=W.get(a,b),c&&(!d||Array.isArray(c)?d=W.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return W.get(a,c)||W.access(a,c,{empty:r.Callbacks("once memory").add(function(){W.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,la=/^$|\/(?:java|ecma)script/i,ma={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ma.optgroup=ma.option,ma.tbody=ma.tfoot=ma.colgroup=ma.caption=ma.thead,ma.th=ma.td;function na(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&B(a,b)?r.merge([a],c):c}function oa(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=na(l.appendChild(f),"script"),j&&oa(g),c){k=0;while(f=g[k++])la.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var ra=d.documentElement,sa=/^key/,ta=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ua=/^([^.]*)(?:\.(.+)|)/;function va(){return!0}function wa(){return!1}function xa(){try{return d.activeElement}catch(a){}}function ya(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ya(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=wa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(ra,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(L)||[""],j=b.length;while(j--)h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.hasData(a)&&W.get(a);if(q&&(i=q.events)){b=(b||"").match(L)||[""],j=b.length;while(j--)if(h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&W.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(W.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i\x20\t\r\n\f]*)[^>]*)\/>/gi,Aa=/\s*$/g;function Ea(a,b){return B(a,"table")&&B(11!==b.nodeType?b:b.firstChild,"tr")?r(">tbody",a)[0]||a:a}function Fa(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Ga(a){var b=Ca.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ha(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(W.hasData(a)&&(f=W.access(a),g=W.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c1&&"string"==typeof q&&!o.checkClone&&Ba.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ja(f,b,c,d)});if(m&&(e=qa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(na(e,"script"),Fa),i=h.length;l")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=na(h),f=na(a),d=0,e=f.length;d0&&oa(g,!i&&na(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(U(c)){if(b=c[W.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[W.expando]=void 0}c[X.expando]&&(c[X.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ka(this,a,!0)},remove:function(a){return Ka(this,a)},text:function(a){return T(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.appendChild(a)}})},prepend:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(na(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return T(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!Aa.test(a)&&!ma[(ka.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c1)}}),r.fn.delay=function(b,c){return b=r.fx?r.fx.speeds[b]||b:b,c=c||"fx",this.queue(c,function(c,d){var e=a.setTimeout(c,b);d.stop=function(){a.clearTimeout(e)}})},function(){var a=d.createElement("input"),b=d.createElement("select"),c=b.appendChild(d.createElement("option"));a.type="checkbox",o.checkOn=""!==a.value,o.optSelected=c.selected,a=d.createElement("input"),a.value="t",a.type="radio",o.radioValue="t"===a.value}();var _a,ab=r.expr.attrHandle;r.fn.extend({attr:function(a,b){return T(this,r.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?_a:void 0)),void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&B(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(L);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),_a={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=ab[b]||r.find.attr;ab[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=ab[g],ab[g]=e,e=null!=c(a,b,d)?g:null,ab[g]=f),e}});var bb=/^(?:input|select|textarea|button)$/i,cb=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return T(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function db(a){var b=a.match(L)||[];return b.join(" ")}function eb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,eb(this)))});if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=eb(c),d=1===c.nodeType&&" "+db(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=db(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,eb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=eb(c),d=1===c.nodeType&&" "+db(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=db(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,eb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(L)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=eb(this),b&&W.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":W.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+db(eb(c))+" ").indexOf(b)>-1)return!0;return!1}});var fb=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":Array.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(fb,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:db(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(Array.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var gb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!gb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,gb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(W.get(h,"events")||{})[b.type]&&W.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&U(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!U(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.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(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=W.access(d,b);e||d.addEventListener(a,c,!0),W.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=W.access(d,b)-1;e?W.access(d,b,e):(d.removeEventListener(a,c,!0),W.remove(d,b))}}});var hb=/\[\]$/,ib=/\r?\n/g,jb=/^(?:submit|button|image|reset|file)$/i,kb=/^(?:input|select|textarea|keygen)/i;function lb(a,b,c,d){var e;if(Array.isArray(b))r.each(b,function(b,e){c||hb.test(a)?d(a,e):lb(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d); -});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)lb(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(Array.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)lb(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&kb.test(this.nodeName)&&!jb.test(a)&&(this.checked||!ja.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:Array.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(ib,"\r\n")}}):{name:b.name,value:c.replace(ib,"\r\n")}}).get()}}),r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},o.createHTMLDocument=function(){var a=d.implementation.createHTMLDocument("").body;return a.innerHTML="
",2===a.childNodes.length}(),r.parseHTML=function(a,b,c){if("string"!=typeof a)return[];"boolean"==typeof b&&(c=b,b=!1);var e,f,g;return b||(o.createHTMLDocument?(b=d.implementation.createHTMLDocument(""),e=b.createElement("base"),e.href=d.location.href,b.head.appendChild(e)):b=d),f=C.exec(a),g=!c&&[],f?[b.createElement(f[1])]:(f=qa([a],b,g),g&&g.length&&r(g).remove(),r.merge([],f.childNodes))},r.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=r.css(a,"position"),l=r(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=r.css(a,"top"),i=r.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),r.isFunction(b)&&(b=b.call(a,c,r.extend({},h))),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},r.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){r.offset.setOffset(this,a,b)});var b,c,d,e,f=this[0];if(f)return f.getClientRects().length?(d=f.getBoundingClientRect(),b=f.ownerDocument,c=b.documentElement,e=b.defaultView,{top:d.top+e.pageYOffset-c.clientTop,left:d.left+e.pageXOffset-c.clientLeft}):{top:0,left:0}},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===r.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),B(a[0],"html")||(d=a.offset()),d={top:d.top+r.css(a[0],"borderTopWidth",!0),left:d.left+r.css(a[0],"borderLeftWidth",!0)}),{top:b.top-d.top-r.css(c,"marginTop",!0),left:b.left-d.left-r.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent;while(a&&"static"===r.css(a,"position"))a=a.offsetParent;return a||ra})}}),r.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c="pageYOffset"===b;r.fn[a]=function(d){return T(this,function(a,d,e){var f;return r.isWindow(a)?f=a:9===a.nodeType&&(f=a.defaultView),void 0===e?f?f[b]:a[d]:void(f?f.scrollTo(c?f.pageXOffset:e,c?e:f.pageYOffset):a[d]=e)},a,d,arguments.length)}}),r.each(["top","left"],function(a,b){r.cssHooks[b]=Pa(o.pixelPosition,function(a,c){if(c)return c=Oa(a,b),Ma.test(c)?r(a).position()[b]+"px":c})}),r.each({Height:"height",Width:"width"},function(a,b){r.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){r.fn[d]=function(e,f){var g=arguments.length&&(c||"boolean"!=typeof e),h=c||(e===!0||f===!0?"margin":"border");return T(this,function(b,c,e){var f;return r.isWindow(b)?0===d.indexOf("outer")?b["inner"+a]:b.document.documentElement["client"+a]:9===b.nodeType?(f=b.documentElement,Math.max(b.body["scroll"+a],f["scroll"+a],b.body["offset"+a],f["offset"+a],f["client"+a])):void 0===e?r.css(b,c,h):r.style(b,c,e,h)},b,g?e:void 0,g)}})}),r.fn.extend({bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}}),r.holdReady=function(a){a?r.readyWait++:r.ready(!0)},r.isArray=Array.isArray,r.parseJSON=JSON.parse,r.nodeName=B,"function"==typeof define&&define.amd&&define("jquery",[],function(){return r});var mb=a.jQuery,nb=a.$;return r.noConflict=function(b){return a.$===r&&(a.$=nb),b&&a.jQuery===r&&(a.jQuery=mb),r},b||(a.jQuery=a.$=r),r}); +/*! jQuery v3.4.1 | (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(C,e){"use strict";var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).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 w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|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(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.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,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){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 H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.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--)b.attrHandle[n[r]]=t}function pe(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 de(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 d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.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}},b.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[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.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},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),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(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.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)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(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===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(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?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(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 G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.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=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(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 j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.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 k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?: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}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;nx",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.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),m(t)&&(t=t.call(e,n,k.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):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.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"===k.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"===k.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===k.css(e,"position"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.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)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.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?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.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){k.fn[n]=function(e,t){return 0 Date: Thu, 16 Jan 2020 18:18:26 +0100 Subject: [PATCH 0129/1637] sync: avoid depending on database when importing files --- admin/sync/file.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/admin/sync/file.go b/admin/sync/file.go index 5b88b63a..4e0d82f3 100644 --- a/admin/sync/file.go +++ b/admin/sync/file.go @@ -150,11 +150,6 @@ func ImportFile(i Importer, URI string, next func(string, string) (interface{}, } } - // Ensure no more file is registered with this path - if f, err := fic.GetFileByPath(dest); err == nil { - f.Delete() - } - if err := os.MkdirAll(path.Dir(dest), 0755); err != nil { return nil, err } From 769158a9d7d7d9bffe343a8f892c06a8689b625b Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 16 Jan 2020 18:53:27 +0100 Subject: [PATCH 0130/1637] repochecker: add new option -skipfiledigests to speed up the checks and avoid downloading lots of content --- admin/sync/exercice_files.go | 34 +++++++++++++++++++++++++++++----- admin/sync/exercice_keys.go | 6 +++--- repochecker/main.go | 14 ++++++++++++-- 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/admin/sync/exercice_files.go b/admin/sync/exercice_files.go index 83bb5f30..4d7e240c 100644 --- a/admin/sync/exercice_files.go +++ b/admin/sync/exercice_files.go @@ -11,7 +11,7 @@ import ( "srs.epita.fr/fic-server/libfic" ) -func buildFilesListInto(i Importer, exercice fic.Exercice, into string) (files []string, digests map[string][]byte, errs []string) { +func BuildFilesListInto(i Importer, exercice fic.Exercice, into string) (files []string, digests map[string][]byte, errs []string) { // If no files directory, don't display error if !i.exists(path.Join(exercice.Path, into)) { return @@ -69,9 +69,33 @@ func buildFilesListInto(i Importer, exercice fic.Exercice, into string) (files [ return } +// CheckExerciceFilesPresence limits remote checks to presence, don't get it to check digest. +func CheckExerciceFilesPresence(i Importer, exercice fic.Exercice) (files []string, errs []string) { + flist, digests, berrs := BuildFilesListInto(i, exercice, "files") + errs = append(errs, berrs...) + + for _, fname := range flist { + if !i.exists(path.Join(exercice.Path, "files", fname)) { + errs = append(errs, fmt.Sprintf("%q: unable to read file %q: No such file or directory", path.Base(exercice.Path), fname)) + } else if _, ok := digests[fname]; !ok { + errs = append(errs, fmt.Sprintf("%q: unable to import file %q: No digest given", path.Base(exercice.Path), fname)) + } else { + files = append(files, fname) + } + } + + for fname := range digests { + if !i.exists(path.Join(exercice.Path, "files", fname)) { + errs = append(errs, fmt.Sprintf("%q: unable to read file %q: No such file or directory. Check your DIGESTS.txt for legacy entries.", path.Base(exercice.Path), fname)) + } + } + + return +} + // CheckExerciceFiles checks that remote files have the right digest. -func CheckExerciceFiles(i Importer, exercice fic.Exercice) (files []fic.EFile, errs []string) { - flist, digests, berrs := buildFilesListInto(i, exercice, "files") +func CheckExerciceFiles(i Importer, exercice fic.Exercice) (files []string, errs []string) { + flist, digests, berrs := BuildFilesListInto(i, exercice, "files") errs = append(errs, berrs...) for _, fname := range flist { @@ -84,7 +108,7 @@ func CheckExerciceFiles(i Importer, exercice fic.Exercice) (files []fic.EFile, e errs = append(errs, fmt.Sprintf("%q: %s: %s", path.Base(exercice.Path), fname, err)) } - files = append(files, fic.EFile{Name: fname}) + files = append(files, fname) } return } @@ -96,7 +120,7 @@ func SyncExerciceFiles(i Importer, exercice fic.Exercice) (errs []string) { errs = append(errs, err.Error()) } - files, digests, berrs := buildFilesListInto(i, exercice, "files") + files, digests, berrs := BuildFilesListInto(i, exercice, "files") errs = append(errs, berrs...) // Import standard files diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index ee81e56f..ac518a53 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -341,7 +341,7 @@ func buildExerciceFlags(i Importer, exercice fic.Exercice) (flags map[int64]impo } // CheckExerciceFlags checks if all flags for the given challenge are correct. -func CheckExerciceFlags(i Importer, exercice fic.Exercice, files []fic.EFile) (rf []fic.Flag, errs []string) { +func CheckExerciceFlags(i Importer, exercice fic.Exercice, files []string) (rf []fic.Flag, errs []string) { flags, flagsids, berrs := buildExerciceFlags(i, exercice) errs = append(errs, berrs...) @@ -358,7 +358,7 @@ func CheckExerciceFlags(i Importer, exercice fic.Exercice, files []fic.EFile) (r for _, lf := range flag.FilesDeps { found := false for _, f := range files { - if f.Name == lf { + if f == lf { found = true break } @@ -452,7 +452,7 @@ func ApiGetRemoteExerciceFlags(ps httprouter.Params, _ []byte) (interface{}, err if theme != nil { exercice, _, _, _, errs := BuildExercice(GlobalImporter, *theme, path.Join(theme.Path, ps.ByName("exid")), nil) if exercice != nil { - flags, errs := CheckExerciceFlags(GlobalImporter, *exercice, []fic.EFile{}) + flags, errs := CheckExerciceFlags(GlobalImporter, *exercice, []string{}) if flags != nil { return flags, nil } else { diff --git a/repochecker/main.go b/repochecker/main.go index 283cc75a..81a8a38b 100644 --- a/repochecker/main.go +++ b/repochecker/main.go @@ -13,15 +13,24 @@ import ( "srs.epita.fr/fic-server/libfic" ) +var skipFileChecks = false + func checkExercice(theme fic.Theme, edir string, dmap *map[int64]fic.Exercice) (errs []string) { e, _, eid, _, berrs := sync.BuildExercice(sync.GlobalImporter, theme, path.Join(theme.Path, edir), dmap) errs = append(errs, berrs...) if e != nil { // Files - files, cerrs := sync.CheckExerciceFiles(sync.GlobalImporter, *e) + var files []string + var cerrs []string + if !skipFileChecks { + files, cerrs = sync.CheckExerciceFiles(sync.GlobalImporter, *e) + log.Printf("%d files checked.\n", len(files)) + } else { + files, cerrs = sync.CheckExerciceFilesPresence(sync.GlobalImporter, *e) + log.Printf("%d files presents but not checked (please check digest yourself).\n", len(files)) + } errs = append(errs, cerrs...) - log.Printf("%d files checked.\n", len(files)) // Flags flags, cerrs := sync.CheckExerciceFlags(sync.GlobalImporter, *e, files) @@ -66,6 +75,7 @@ func main() { 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(&skipFileChecks, "skipfiledigests", skipFileChecks, "Don't perform DIGESTS checks on file to speed up the checks") flag.Parse() log.SetPrefix("[repochecker] ") From b35a8a9200133951a63b44f638662778e5342587 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 16 Jan 2020 23:29:56 +0100 Subject: [PATCH 0131/1637] unbound: fix missing options for containers --- fickit-pkg/unbound/etc/unbound/unbound.conf | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fickit-pkg/unbound/etc/unbound/unbound.conf b/fickit-pkg/unbound/etc/unbound/unbound.conf index aefec065..122e74fc 100644 --- a/fickit-pkg/unbound/etc/unbound/unbound.conf +++ b/fickit-pkg/unbound/etc/unbound/unbound.conf @@ -26,6 +26,9 @@ server: # 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 @@ -45,7 +48,7 @@ server: # file to read root hints from. # get one from https://www.internic.net/domain/named.cache # root-hints: "" - root-hints: "root.hints" + root-hints: "/usr/share/dns-root-hints/named.root" # enable to not answer id.server and hostname.bind queries. # hide-identity: no @@ -113,7 +116,7 @@ server: # 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: "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. From 9c9d4edd7490171158c21398e600a8a94663ba45 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 17 Jan 2020 03:13:18 +0100 Subject: [PATCH 0132/1637] fickit: fix iptables script on frontend --- configs/nsenter_iptables.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/nsenter_iptables.sh b/configs/nsenter_iptables.sh index 78cf11c2..2f3b6c82 100755 --- a/configs/nsenter_iptables.sh +++ b/configs/nsenter_iptables.sh @@ -2,8 +2,8 @@ if [ -d /containers/onboot/004-admin-ip-setup ]; then LOWER=/containers/onboot/004-admin-ip-setup/lower -elif [ -d /containers/onboot/004-nginx-ip-setup ]; then - LOWER=/containers/onboot/004-nginx-ip-setup/lower +elif [ -d /containers/onboot/004-frontal-ip-setup ]; then + LOWER=/containers/onboot/004-frontal-ip-setup/lower else nsenter -t 1 -a "$0" $@ exit $? From f251d3016274f242accf803766de71eb64de7477 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 17 Jan 2020 07:00:40 +0100 Subject: [PATCH 0133/1637] settings: reload also on file creation (when rsync do atomic moves) --- settings/settings.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings/settings.go b/settings/settings.go index 2161ddc8..58691a59 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -156,7 +156,7 @@ func LoadAndWatchSettings(settingsPath string, reload func (FICSettings)) { for { select { case ev := <-watcher.Events: - if path.Base(ev.Name) == SettingsFile && ev.Op & fsnotify.Write == fsnotify.Write { + if path.Base(ev.Name) == SettingsFile && ev.Op & (fsnotify.Write | fsnotify.Create) != 0 { log.Println("Settings file changes, reloading it!") go tryReload(settingsPath, reload) } From a6e799a63525f54a1a83a2beb8ae30ac5022c801 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 17 Jan 2020 07:04:32 +0100 Subject: [PATCH 0134/1637] backend: also create .tmp directory required by rsync --- backend/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/main.go b/backend/main.go index 0058b83e..d41631f3 100644 --- a/backend/main.go +++ b/backend/main.go @@ -101,8 +101,8 @@ func main() { launchWorkers() log.Println("Creating submission directory...") - if _, err := os.Stat(SubmissionDir); os.IsNotExist(err) { - if err := os.MkdirAll(SubmissionDir, 0777); err != nil { + if _, err := os.Stat(path.Join(SubmissionDir, ".tmp")); os.IsNotExist(err) { + if err := os.MkdirAll(path.Join(SubmissionDir, ".tmp"), 0777); err != nil { log.Fatal("Unable to create submission directory: ", err) } } From 4f6480d7f8da14d65f4c770082755afc2efe2c5e Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 17 Jan 2020 07:20:00 +0100 Subject: [PATCH 0135/1637] sync: add some precision around Empty flags detection --- admin/sync/exercice_keys.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index ac518a53..89be3b60 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -126,7 +126,7 @@ func buildKeyFlag(exercice fic.Exercice, flag ExerciceFlag, flagline int, defaul hashedFlag, err := fic.ComputeHashedFlag([]byte(raw), !flag.CaseSensitive, validatorRegexp(flag.ValidatorRe)) if err != nil { - errs = append(errs, err.Error()) + errs = append(errs, fmt.Sprintf("%q: flag #%d: %s", path.Base(exercice.Path), flagline, err.Error())) return } fl := fic.Flag(fic.FlagKey{ From 91cc8b9314b46ccb60518986e6e03bbb4d38f892 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 17 Jan 2020 07:20:36 +0100 Subject: [PATCH 0136/1637] nginx: fix redirection from HTTP --- configs/nginx-demo.conf | 2 +- configs/nginx-prod.conf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/nginx-demo.conf b/configs/nginx-demo.conf index 7ae029bd..17cea230 100644 --- a/configs/nginx-demo.conf +++ b/configs/nginx-demo.conf @@ -4,7 +4,7 @@ proxy_connect_timeout 1s; server { listen 80 default; - rewrite ^ https://$server_name$request_uri permanent; + rewrite ^ https://$host$request_uri permanent; } server { diff --git a/configs/nginx-prod.conf b/configs/nginx-prod.conf index a0e9c416..afab54e3 100644 --- a/configs/nginx-prod.conf +++ b/configs/nginx-prod.conf @@ -4,7 +4,7 @@ proxy_connect_timeout 1s; server { listen 80 default; - rewrite ^ https://$server_name$request_uri permanent; + rewrite ^ https://$host$request_uri permanent; } server { From ccf32f8a4873b11d44474bc605c2ad564887ab33 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 17 Jan 2020 07:34:36 +0100 Subject: [PATCH 0137/1637] configs: DHCPd config now indicates also default route --- configs/dhcpd.conf | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/configs/dhcpd.conf b/configs/dhcpd.conf index a45e3488..581dac56 100644 --- a/configs/dhcpd.conf +++ b/configs/dhcpd.conf @@ -7,8 +7,12 @@ 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; -option rfc3442-classless-static-routes 32, 163, 5, 55, 58, 172, 23, 42, 1; -option ms-classless-static-routes 32, 163, 5, 55, 58, 172, 23, 42, 1; +# Provide Internet +option rfc3442-classless-static-routes 32, 163, 5, 55, 58, 172, 23, 42, 1, 0, 172, 23, 42, 254; +option ms-classless-static-routes 32, 163, 5, 55, 58, 172, 23, 42, 1, 0, 172, 23, 42, 254; +# Don't provide Internet +#option rfc3442-classless-static-routes 32, 163, 5, 55, 58, 172, 23, 42, 1; +#option ms-classless-static-routes 32, 163, 5, 55, 58, 172, 23, 42, 1; subnet 172.23.42.0 netmask 255.255.255.0 { range 172.23.42.10 172.23.42.253; } From 22c48358752cd43acd5c2ade94b065161b77fd5f Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 17 Jan 2020 14:45:34 +0100 Subject: [PATCH 0138/1637] admin: use default bootstrap theme, even when served with frontend --- admin/index.go | 4 ++-- admin/static/index.html | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/admin/index.go b/admin/index.go index 595d0b2f..8999403b 100644 --- a/admin/index.go +++ b/admin/index.go @@ -5,8 +5,8 @@ const indextpl = ` Challenge Forensic - Administration - - + + From 7f691779f7696eaa9d292963df4ea7dbea3c58da Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 28 Jan 2020 16:21:23 +0100 Subject: [PATCH 0182/1637] Hardenize nginx config --- configs/nginx-demo.conf | 4 +++- configs/nginx-prod.conf | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/configs/nginx-demo.conf b/configs/nginx-demo.conf index cfd45ce5..d072a5ed 100644 --- a/configs/nginx-demo.conf +++ b/configs/nginx-demo.conf @@ -1,6 +1,8 @@ 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; @@ -34,7 +36,7 @@ server { 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'; camera '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'"; + 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; diff --git a/configs/nginx-prod.conf b/configs/nginx-prod.conf index 9f5be5bc..1ced4fa3 100644 --- a/configs/nginx-prod.conf +++ b/configs/nginx-prod.conf @@ -1,6 +1,8 @@ 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; @@ -34,7 +36,7 @@ server { 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'; camera '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'"; + 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; From 32fe61f557b383009895037af1e41eed3d9c1908 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 28 Jan 2020 17:00:37 +0100 Subject: [PATCH 0183/1637] admin: refresh claims list each 10s --- admin/static/js/app.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 374d1bf1..6e819d82 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -1113,9 +1113,15 @@ angular.module("FICApp") refresh(); $interval(refresh, 10000); }) - .controller("ClaimsListController", function($scope, Claim, ClaimAssignee, Teams, $location) { - $scope.claims = Claim.query(); - $scope.assignees = ClaimAssignee.query(); + .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"]; From 23b6b2b005e8d03f2e81286ad477644a3f65f380 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 28 Jan 2020 18:47:36 +0100 Subject: [PATCH 0184/1637] admin: handle case insensitive ucq --- admin/sync/exercice_keys.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index 89be3b60..e865d3a1 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -170,7 +170,7 @@ func buildKeyFlag(exercice fic.Exercice, flag ExerciceFlag, flagline int, defaul Value: val, }) - if val == raw { + if val == raw || (!flag.CaseSensitive && val == strings.ToLower(raw)) { hasOne = true } } From 05a795ad497946d00d6270c89675ca4ccc72623e Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 28 Jan 2020 18:49:59 +0100 Subject: [PATCH 0185/1637] frontend: add hint on special SE page --- frontend/static/views/defi-SE.html | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/frontend/static/views/defi-SE.html b/frontend/static/views/defi-SE.html index c2bc7730..62bc3774 100644 --- a/frontend/static/views/defi-SE.html +++ b/frontend/static/views/defi-SE.html @@ -29,6 +29,18 @@ #{{ tag }}

+
+

+

+ + + + b2sum : {{ hint.content }} + Débloquer cet indice vous fera perdre . + + +

+

From 6f17fc0760e2e1558f4f7fad4a3c1a4f6cae0bf1 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 28 Jan 2020 18:50:26 +0100 Subject: [PATCH 0186/1637] frontend: pluralize points on index --- frontend/static/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/static/index.html b/frontend/static/index.html index 2190d40a..bd55882a 100644 --- a/frontend/static/index.html +++ b/frontend/static/index.html @@ -113,7 +113,7 @@
- {{ my.score | number }} points – {{ teams[my.team_id].rank }}e sur {{ teams_count }} + {{ my.score | number }} – {{ teams[my.team_id].rank }}e sur {{ teams_count }}

{{ my.name }} From a0b19f618440bf22a1b9449f83ca44ca683700bf Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 28 Jan 2020 19:49:46 +0100 Subject: [PATCH 0187/1637] backend: handle SIGUSR1 to retreat all files left in submissions directory and SIGUSR2 to dump statistics --- backend/main.go | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/backend/main.go b/backend/main.go index 63306e23..f03f2000 100644 --- a/backend/main.go +++ b/backend/main.go @@ -6,9 +6,11 @@ import ( "log" "math/rand" "os" + "os/signal" "path" "strconv" "strings" + "syscall" "time" "srs.epita.fr/fic-server/libfic" @@ -43,6 +45,24 @@ func watchsubdir(watcher *fsnotify.Watcher, pathname string) error { } } +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 @@ -127,10 +147,24 @@ func main() { log.Fatal(err) } + // 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) + watchedNotify := fsnotify.Create for { select { + case <-interrupt1: + log.Println("SIGUSR1 received, retreating all files in queue...") + walkAndTreat(SubmissionDir) + log.Println("SIGUSR1 treated.") + case <-interrupt2: + inQueueMutex.Lock() + log.Printf("SIGUSR2 received, dumping statistics:\n parallelJobs: %d\n genTeamQueue size: %d\n genQueue: %d\n Teams in queue: %v\n Challenge started: %v\n Last regeneration: %v\n", parallelJobs, len(genTeamQueue), len(genQueue), inGenQueue, ChStarted, lastRegeneration) + inQueueMutex.Unlock() 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 From 007efc6118a88cfa410cd8b7eff6969d835fcf39 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 29 Jan 2020 11:35:01 +0100 Subject: [PATCH 0188/1637] health: done --- admin/api/health.go | 25 +++++++++++++++++++++---- admin/pki/team.go | 4 ++++ admin/static/js/app.js | 12 ++++++++++++ admin/static/views/home.html | 32 +++++++++++++++++++++----------- 4 files changed, 58 insertions(+), 15 deletions(-) diff --git a/admin/api/health.go b/admin/api/health.go index 43f3da77..f3cf2e27 100644 --- a/admin/api/health.go +++ b/admin/api/health.go @@ -8,6 +8,8 @@ import ( "strings" "time" + "srs.epita.fr/fic-server/admin/pki" + "github.com/julienschmidt/httprouter" ) @@ -30,17 +32,32 @@ func init() { router.GET("/api/health.json", apiHandler(GetHealth)) } -func getHealth(pathname string) (ret []string) { +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, fmt.Sprintf("%s: unable to ReadDir: %s", strings.TrimPrefix(pathname, TimestampCheck), err)) + 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 { - ret = append(ret, fmt.Sprintf("%s/%s: existing untreated file.", strings.TrimPrefix(pathname, TimestampCheck), d.Name())) + } else if !d.IsDir() && d.Mode()&os.ModeSymlink == 0 && time.Since(d.ModTime()) > 2 * time.Second { + 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 diff --git a/admin/pki/team.go b/admin/pki/team.go index 08b9eef8..8168be64 100644 --- a/admin/pki/team.go +++ b/admin/pki/team.go @@ -16,6 +16,10 @@ 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 diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 6e819d82..128e03dd 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -136,6 +136,9 @@ angular.module("FICApp") .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' }) }) @@ -416,6 +419,15 @@ angular.module("FICApp") $scope.$on('$destroy', function () { $interval.cancel(myinterval); }); }) + .controller("HealthController", function($scope, $interval, Health) { + var refresh = function() { + $scope.health = Health.query(); + } + refresh(); + var myinterval = $interval(refresh, 2500); + $scope.$on('$destroy', function () { $interval.cancel(myinterval); }); + }) + .controller("MonitorController", function($scope, Monitor) { $scope.monitor = Monitor.get(); }) diff --git a/admin/static/views/home.html b/admin/static/views/home.html index c5d49dd7..3544a9c8 100644 --- a/admin/static/views/home.html +++ b/admin/static/views/home.html @@ -1,14 +1,24 @@ From 5df1cc6e936967303eaa7f26e8e35496dd3adf77 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 29 Jan 2020 15:57:34 +0100 Subject: [PATCH 0189/1637] admin: add some stats about exercices --- admin/api/exercice.go | 31 ++++++++++++++++++++++++--- admin/api/theme.go | 21 +++++++++++++++++++ admin/static/js/app.js | 16 +++++++++----- admin/static/views/exercice.html | 8 +++++++ libfic/exercice.go | 36 ++++++++++++++++++++++++++++++++ 5 files changed, 104 insertions(+), 8 deletions(-) diff --git a/admin/api/exercice.go b/admin/api/exercice.go index 2a4eeb27..babf5919 100644 --- a/admin/api/exercice.go +++ b/admin/api/exercice.go @@ -23,6 +23,7 @@ func init() { router.DELETE("/api/exercices/:eid", apiHandler(exerciceHandler(deleteExercice))) router.GET("/api/exercices/:eid/stats.json", apiHandler(exerciceHandler(getExerciceStats))) + router.GET("/api/exercices_stats.json", apiHandler(getExercicesStats)) router.GET("/api/exercices/:eid/history.json", apiHandler(exerciceHandler(getExerciceHistory))) router.PATCH("/api/exercices/:eid/history.json", apiHandler(exerciceHandler(updateExerciceHistory))) @@ -170,9 +171,12 @@ func getExerciceHistory(exercice fic.Exercice, body []byte) (interface{}, error) } type exerciceStats struct { - TeamTries int64 `json:"team_tries"` - TotalTries int64 `json:"total_tries"` - SolvedCount int64 `json:"solved_count"` + 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"` } func getExerciceStats(e fic.Exercice, body []byte) (interface{}, error) { @@ -180,9 +184,30 @@ func getExerciceStats(e fic.Exercice, body []byte) (interface{}, error) { TeamTries: e.TriedTeamCount(), TotalTries: e.TriedCount(), SolvedCount: e.SolvedCount(), + FlagSolved: e.FlagSolved(), + MCQSolved: e.MCQSolved(), }, nil } +func getExercicesStats(_ httprouter.Params, body []byte) (interface{}, error) { + if exercices, err := fic.GetExercices(); err != nil { + return nil, err + } else { + 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(), + }) + } + return ret, nil + } +} + type uploadedExerciceHistory struct { IdTeam int64 `json:"team_id"` Kind string diff --git a/admin/api/theme.go b/admin/api/theme.go index 97832c77..8df3b5ac 100644 --- a/admin/api/theme.go +++ b/admin/api/theme.go @@ -25,6 +25,8 @@ func init() { router.GET("/api/themes/:thid/exercices", apiHandler(themeHandler(listThemedExercices))) router.POST("/api/themes/:thid/exercices", apiHandler(themeHandler(createExercice))) + router.GET("/api/themes/:thid/exercices_stats.json", apiHandler(themeHandler(getThemedExercicesStats))) + router.GET("/api/themes/:thid/exercices/:eid", apiHandler(exerciceHandler(showExercice))) router.PUT("/api/themes/:thid/exercices/:eid", apiHandler(exerciceHandler(updateExercice))) router.DELETE("/api/themes/:thid/exercices/:eid", apiHandler(exerciceHandler(deleteExercice))) @@ -205,3 +207,22 @@ func updateTheme(theme fic.Theme, body []byte) (interface{}, error) { func deleteTheme(theme fic.Theme, _ []byte) (interface{}, error) { return theme.Delete() } + +func getThemedExercicesStats(theme fic.Theme, body []byte) (interface{}, error) { + if exercices, err := theme.GetExercices(); err != nil { + return nil, err + } else { + 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(), + }) + } + return ret, nil + } +} diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 128e03dd..bf91546c 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -247,6 +247,9 @@ angular.module("FICApp") .factory("ExerciceHistory", function($resource) { return $resource("/api/exercices/:exerciceId/history.json", { exerciceId: '@id' }) }) + .factory("ExercicesStats", function($resource) { + return $resource("/api/themes/:themeId/exercices_stats.json", { themeId: '@id' }) + }) .factory("ExerciceStats", function($resource) { return $resource("/api/exercices/:exerciceId/stats.json", { exerciceId: '@id' }) }) @@ -519,7 +522,6 @@ angular.module("FICApp") var refreshSyncReport = function() { needRefreshSyncReportWhenReady = false; $http.get("full_import_report.json").then(function(response) { - console.log(response.data); $scope.syncReport = response.data; }) }; @@ -1404,12 +1406,12 @@ angular.module("FICApp") $scope.syncHints = true; $scope.syncFlags = true; }) - .controller("ExercicesListController", function($scope, ThemedExercice, $routeParams, $location, $rootScope, $http) { - $scope.exercices = ThemedExercice.query({ themeId: $routeParams.themeId }); + .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/" + $routeParams.themeId + "/exercices/" + id); + $location.url("/themes/" + $scope.theme.id + "/exercices/" + id); }; $scope.inSync = false; @@ -1420,7 +1422,7 @@ angular.module("FICApp") method: "POST" }).then(function(response) { $scope.inSync = false; - $scope.exercices = ThemedExercice.query({ themeId: $routeParams.themeId }); + $scope.exercices = ThemedExercice.query({ themeId: $scope.theme.id }); $rootScope.staticFilesNeedUpdate++; if (response.data) $rootScope.newBox('warning', null, response.data, -1); @@ -1500,6 +1502,10 @@ angular.module("FICApp") } }) + .controller("ExercicesStatsController", function($scope, ExercicesStats) { + $scope.exercices = ExercicesStats.query({ themeId: $scope.theme.id }); + }) + .controller("ExerciceStatsController", function($scope, ExerciceStats, $routeParams) { $scope.stats = ExerciceStats.get({ exerciceId: $routeParams.exerciceId }); }) diff --git a/admin/static/views/exercice.html b/admin/static/views/exercice.html index 2f0e2b93..6821814b 100644 --- a/admin/static/views/exercice.html +++ b/admin/static/views/exercice.html @@ -64,6 +64,14 @@
Défi validé par
+ +
Drapeaux validés
+
{{ stats.flag_solved.length }}
+
aucun
+ +
QCM validés
+
{{ stats.mcq_solved.length }}
+
aucun

diff --git a/libfic/exercice.go b/libfic/exercice.go index 3709272c..47b3c3dc 100644 --- a/libfic/exercice.go +++ b/libfic/exercice.go @@ -360,6 +360,42 @@ func (e Exercice) TriedCount() int64 { } } +// FlagSolved returns the list of flags solved. +func (e Exercice) FlagSolved() (res []int64) { + if rows, err := DBQuery("SELECT F.id_flag FROM flag_found F INNER JOIN exercice_flags E ON E.id_flag = F.id_flag WHERE E.id_exercice = ? GROUP BY id_flag", e.Id); err != nil { + return + } else { + defer rows.Close() + + for rows.Next() { + var n int64 + if err := rows.Scan(&n); err != nil { + return + } + res = append(res, n) + } + return + } +} + +// MCQSolved returns the list of mcqs solved. +func (e Exercice) MCQSolved() (res []int64) { + if rows, err := DBQuery("SELECT F.id_mcq FROM mcq_found F INNER JOIN exercice_mcq E ON E.id_mcq = F.id_mcq WHERE E.id_exercice = ? GROUP BY id_mcq", e.Id); err != nil { + return + } else { + defer rows.Close() + + for rows.Next() { + var n int64 + if err := rows.Scan(&n); err != nil { + return + } + res = append(res, n) + } + return + } +} + // CheckResponse, given both flags and MCQ responses, figures out if thoses are correct (or if they are previously solved). // In the meanwhile, CheckResponse registers good answers given (but it does not mark the challenge as solved at the end). func (e Exercice) CheckResponse(cksum []byte, respflags map[int64]string, respmcq map[int64]bool, t Team) (bool, error) { From 4f237677e2b552159c4c28b59607bcc41e9d1446 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 29 Jan 2020 15:59:20 +0100 Subject: [PATCH 0190/1637] admin: version bump --- admin/api/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/api/version.go b/admin/api/version.go index 2e8f8d26..7025acb6 100644 --- a/admin/api/version.go +++ b/admin/api/version.go @@ -9,5 +9,5 @@ func init() { } func showVersion(_ httprouter.Params, body []byte) (interface{}, error) { - return map[string]interface{}{"version": 0.8}, nil + return map[string]interface{}{"version": 0.9}, nil } From cb97af2f8a208d213623747bab47102af94b697e Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 29 Jan 2020 16:01:11 +0100 Subject: [PATCH 0191/1637] admin: redesign home page --- admin/index.go | 35 ++++++++++++++++++++ admin/static.go | 3 ++ admin/static/index.html | 35 ++++++++++++++++++++ admin/static/js/app.js | 10 ++++++ admin/static/views/home.html | 63 ++++++++++++++++++++++++++++++++++-- 5 files changed, 143 insertions(+), 3 deletions(-) diff --git a/admin/index.go b/admin/index.go index d46fc70c..444157a9 100644 --- a/admin/index.go +++ b/admin/index.go @@ -26,6 +26,41 @@ const indextpl = ` .bg-wchoices { background-color: #c07bdf !important; } + .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 a { + transform: skew(-45deg,0deg) rotate(45deg); + position: absolute; + bottom: 40px; + left: -35px; + display: inline-block; + width: 110px; + text-align: left; + text-overflow: ellipsis; + } diff --git a/admin/static.go b/admin/static.go index 160ff758..69052426 100644 --- a/admin/static.go +++ b/admin/static.go @@ -63,6 +63,9 @@ func init() { api.Router().GET("/files/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { http.ServeFile(w, r, path.Join(fic.FilesDir, strings.TrimPrefix(r.URL.Path, "/files"))) }) + api.Router().GET("/submissions/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + http.ServeFile(w, r, path.Join(api.TimestampCheck, strings.TrimPrefix(r.URL.Path, "/submissions"))) + }) api.Router().GET("/vids/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { if importer, ok := sync.GlobalImporter.(sync.LocalImporter); ok { http.ServeFile(w, r, path.Join(importer.Base, strings.TrimPrefix(r.URL.Path, "/vids"))) diff --git a/admin/static/index.html b/admin/static/index.html index 463c5105..0096007b 100644 --- a/admin/static/index.html +++ b/admin/static/index.html @@ -24,6 +24,41 @@ .bg-wchoices { background-color: #c07bdf !important; } + .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 a { + transform: skew(-45deg,0deg) rotate(45deg); + position: absolute; + bottom: 40px; + left: -35px; + display: inline-block; + width: 110px; + text-align: left; + text-overflow: ellipsis; + } diff --git a/admin/static/js/app.js b/admin/static/js/app.js index bf91546c..13df1b7d 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -2000,6 +2000,16 @@ angular.module("FICApp") 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(); diff --git a/admin/static/views/home.html b/admin/static/views/home.html index 3544a9c8..eea45d14 100644 --- a/admin/static/views/home.html +++ b/admin/static/views/home.html @@ -6,19 +6,76 @@ Version de l'API : {{ v.version }}

- Latence frontend-backend :
+ Latence frontend-backend :
Dernière synchronisation du frontend : {{ t.frontend | date:"mediumTime" }}

+ Problèmes dans les fichiers :
+ +
+
+
+

Progression

+
+ + + + + + + + + + + + + + + + + + + +
Challenge {{ lvl }} + + {{ exercice.solved_count }} + {{ exercice.team_tries }} + +
+    Résolus + Total résolus
+    Tentatives +
+ {{ mystats.themes[tid].solved }}
+ {{ mystats.themes[tid].tries }} +
+
+ +
+
+

Classement

+
+ + + + + + + + +
{{ team.rank }}{{ team.name }}{{ team.score | number:0 }}
+
+
From 7c84301c047e4a05ad8aaf3eea745cde86ec8b37 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 29 Jan 2020 18:18:25 +0100 Subject: [PATCH 0192/1637] admin: implement Enter keypress on search --- admin/static/js/app.js | 48 +++++++++++++++++++++++++++ admin/static/views/exercice-list.html | 2 +- admin/static/views/team-list.html | 2 +- admin/static/views/theme-list.html | 2 +- 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 13df1b7d..98d8e848 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -1284,6 +1284,22 @@ angular.module("FICApp") $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); }; @@ -1343,6 +1359,22 @@ angular.module("FICApp") $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 @@ -1833,6 +1865,22 @@ angular.module("FICApp") $scope.teams = Team.query(); $scope.fields = ["id", "name"]; + $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(); diff --git a/admin/static/views/exercice-list.html b/admin/static/views/exercice-list.html index 03e7254a..1ef934dc 100644 --- a/admin/static/views/exercice-list.html +++ b/admin/static/views/exercice-list.html @@ -20,7 +20,7 @@
-

+

diff --git a/admin/static/views/team-list.html b/admin/static/views/team-list.html index d1ace909..a6306916 100644 --- a/admin/static/views/team-list.html +++ b/admin/static/views/team-list.html @@ -6,7 +6,7 @@ -

+

diff --git a/admin/static/views/theme-list.html b/admin/static/views/theme-list.html index 241f9166..aed9b4ca 100644 --- a/admin/static/views/theme-list.html +++ b/admin/static/views/theme-list.html @@ -4,7 +4,7 @@ -

+

From 35bd9083743fcb5130d950708fe0df6a5cd427f8 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 29 Jan 2020 18:20:09 +0100 Subject: [PATCH 0193/1637] admin: add graphique in public --- admin/static/js/app.js | 1 + 1 file changed, 1 insertion(+) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 98d8e848..42771667 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -765,6 +765,7 @@ angular.module("FICApp") "exercice": "Exercice", "table": "Tableau", "rank": "Classement", + "graph": "Graphique", }; $scope.typeside = { "welcome": "Messages de bienvenue", From 0ab637faedec31a3b0d29fc6f3f065961c69fb6b Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 30 Jan 2020 01:06:16 +0100 Subject: [PATCH 0194/1637] dashboard: don't get legacy /stats.json --- dashboard/static/js/dashboard.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/dashboard/static/js/dashboard.js b/dashboard/static/js/dashboard.js index b526f0b6..d0966693 100644 --- a/dashboard/static/js/dashboard.js +++ b/dashboard/static/js/dashboard.js @@ -111,9 +111,6 @@ angular.module("FICApp", ["ngSanitize", "ngAnimate"]) $scope.my = response.data; }); - $http.get("/stats.json").then(function(response) { - $scope.stats = response.data; - }); $http.get("/settings.json").then(function(response) { $rootScope.recvTime(response); response.data.start = new Date(response.data.start); From d66de6fb3c6f5f1a623caec6704e722ac1366c78 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 30 Jan 2020 03:54:18 +0100 Subject: [PATCH 0195/1637] libfic: avoid infinite loop in db --- libfic/exercice.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libfic/exercice.go b/libfic/exercice.go index 47b3c3dc..26894368 100644 --- a/libfic/exercice.go +++ b/libfic/exercice.go @@ -283,7 +283,9 @@ func (e Exercice) GetLevel() (int, error) { nb := 1 for dep != nil { nb += 1 - if edep, err := GetExercice(*dep); err != nil { + if nb > 10 || *dep == e.Id { + return nb, errors.New("Exceed number of levels") + } else if edep, err := GetExercice(*dep); err != nil { return nb, err } else { dep = edep.Depend From b9fa5accff8759bfdf783e76a9acaa119120c248 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 30 Jan 2020 04:17:53 +0100 Subject: [PATCH 0196/1637] dashboard: add graph score --- admin/static/js/app.js | 46 +++++++++++- admin/static/views/public.html | 27 ++++++- dashboard/static.go | 5 +- dashboard/static/index.html | 23 +++++- dashboard/static/js/dashboard.js | 123 +++++++++++++++++++++++++++++++ 5 files changed, 216 insertions(+), 8 deletions(-) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 42771667..a4841b8f 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -871,9 +871,53 @@ angular.module("FICApp") 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: 400, 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: { }, + }, + ]; + } + else if (scene == "summary2") { + $scope.display.scenes = [ + { + type: "graph", + params: { teams: [], height: 400, legend: false }, + }, { type: "rank", - params: { limit: 10, which: "general" }, + 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: { }, + }, + ]; + } + 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 = [ diff --git a/admin/static/views/public.html b/admin/static/views/public.html index 17f762e3..45bb5b71 100644 --- a/admin/static/views/public.html +++ b/admin/static/views/public.html @@ -12,6 +12,8 @@ Lancement Résumé + Résumé 2 + Résumé 3 Free Hint Quarter Happy Hour @@ -67,7 +69,7 @@ -
+
@@ -161,7 +163,7 @@
-
+
+
+
+ +
+
+
+
+ +
+
+
+
@@ -222,7 +243,7 @@ -
+
diff --git a/dashboard/static.go b/dashboard/static.go index 3f272339..5e21583e 100644 --- a/dashboard/static.go +++ b/dashboard/static.go @@ -93,12 +93,11 @@ func init() { http.ServeFile(w, r, path.Join(TeamsDir, "public", "my.json")) } }) - api.Router().GET("/stats.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - w.Header().Set("Cache-Control", "no-cache") + api.Router().GET("/api/teams/:tid/score-grid.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { if forwarder != nil { fwd_request(w, r, *forwarder) } else { - http.ServeFile(w, r, path.Join(TeamsDir, "public", "stats.json")) + fwd_request(w, r, "http://127.0.0.1:8081/") } }) api.Router().GET("/api/teams/:tid/stats.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { diff --git a/dashboard/static/index.html b/dashboard/static/index.html index feb51ded..c9f75867 100644 --- a/dashboard/static/index.html +++ b/dashboard/static/index.html @@ -13,6 +13,17 @@ +
@@ -166,13 +177,22 @@
- +
{{ r.rank }}ere{{ r.name }}   {{ r.name }} {{ r.score | number:0 }}
+
+
+

+

+
+
+
+
+

@@ -560,6 +580,7 @@ + diff --git a/dashboard/static/js/dashboard.js b/dashboard/static/js/dashboard.js index d0966693..07eb3e5e 100644 --- a/dashboard/static/js/dashboard.js +++ b/dashboard/static/js/dashboard.js @@ -167,4 +167,127 @@ angular.module("FICApp", ["ngSanitize", "ngAnimate"]) $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) { + 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)"; + }); + } }); + }) From 66a72633d65b34c003a68ef64da8217450c2e36f Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 30 Jan 2020 18:43:44 +0100 Subject: [PATCH 0197/1637] dashboard: generate a special teams.json with members for trophee scene --- admin/api/team.go | 6 +++++- backend/generation.go | 18 +++++++++++++++++- dashboard/static.go | 2 +- libfic/team_export.go | 10 +++++++++- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/admin/api/team.go b/admin/api/team.go index 28d4ee5e..f23f3539 100644 --- a/admin/api/team.go +++ b/admin/api/team.go @@ -15,7 +15,11 @@ import ( func init() { router.GET("/api/teams.json", apiHandler( func(httprouter.Params, []byte) (interface{}, error) { - return fic.ExportTeams() + return fic.ExportTeams(false) + })) + router.GET("/api/teams-members.json", apiHandler( + func(httprouter.Params, []byte) (interface{}, error) { + return fic.ExportTeams(true) })) router.GET("/api/teams-binding", apiHandler( func(httprouter.Params, []byte) (interface{}, error) { diff --git a/backend/generation.go b/backend/generation.go index dcc5f7d4..88a1092a 100644 --- a/backend/generation.go +++ b/backend/generation.go @@ -169,6 +169,14 @@ func genMyPublicFile() error { 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 } @@ -187,7 +195,7 @@ func genEventsFile() error { // Generate general teams.json file func genTeamsFile() error { - if teams, err := fic.ExportTeams(); err != nil { + if teams, err := fic.ExportTeams(false); err != nil { return err } else if j, err := json.Marshal(teams); err != nil { return err @@ -195,6 +203,14 @@ func genTeamsFile() error { 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 } diff --git a/dashboard/static.go b/dashboard/static.go index 5e21583e..bccfb3bf 100644 --- a/dashboard/static.go +++ b/dashboard/static.go @@ -121,7 +121,7 @@ func init() { if forwarder != nil { fwd_request(w, r, *forwarder) } else { - http.ServeFile(w, r, path.Join(TeamsDir, "teams.json")) + http.ServeFile(w, r, path.Join(TeamsDir, "public", "teams.json")) } }) api.Router().GET("/themes.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { diff --git a/libfic/team_export.go b/libfic/team_export.go index 4fa474f4..250a8e60 100644 --- a/libfic/team_export.go +++ b/libfic/team_export.go @@ -10,10 +10,11 @@ type exportedTeam struct { Color string `json:"color"` Rank int `json:"rank"` Points float64 `json:"score"` + Members []Member `json:"members,omitempty"` } // Exportedteam creates the structure to respond as teams.json. -func ExportTeams() (ret map[string]exportedTeam, err error) { +func ExportTeams(includeMembers bool) (ret map[string]exportedTeam, err error) { var teams []Team var rank map[int64]int @@ -25,11 +26,18 @@ func ExportTeams() (ret map[string]exportedTeam, err error) { ret = map[string]exportedTeam{} for _, team := range teams { points, _ := team.GetPoints() + var members []Member + if includeMembers { + if members, err = team.GetMembers(); err != nil { + return + } + } ret[fmt.Sprintf("%d", team.Id)] = exportedTeam{ team.Name, fmt.Sprintf("#%x", team.Color), rank[team.Id], points, + members, } } From 83ba3b88a54e226588ba4a04882dfbdf27141ccc Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 30 Jan 2020 18:47:46 +0100 Subject: [PATCH 0198/1637] dashboard: add trophee scene --- admin/static/js/app.js | 43 ++++++++++++++++++++++++++++++++++ admin/static/views/public.html | 5 ++-- dashboard/static/index.html | 43 +++++++++++++++++++++++++++++++++- 3 files changed, 88 insertions(+), 3 deletions(-) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index a4841b8f..0eeeef56 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -798,6 +798,7 @@ angular.module("FICApp") }; $scope.rank_types = { "general": "Classement général", + "final": "Classement final", }; $scope.rank_types_side = { "carousel": "Classement général carousel", @@ -865,6 +866,42 @@ angular.module("FICApp") }, ]; } + 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 = [ { @@ -886,6 +923,8 @@ angular.module("FICApp") params: { }, }, ]; + $scope.display.hideEvents = false; + $scope.display.hideCountdown = false; } else if (scene == "summary2") { $scope.display.scenes = [ @@ -908,6 +947,8 @@ angular.module("FICApp") params: { }, }, ]; + $scope.display.hideEvents = false; + $scope.display.hideCountdown = false; } else if (scene == "summary3") { $scope.display.scenes = [ @@ -926,6 +967,8 @@ angular.module("FICApp") params: { }, }, ]; + $scope.display.hideEvents = false; + $scope.display.hideCountdown = false; } else if (scene == "happyhour") { $scope.display.customCountdown = { diff --git a/admin/static/views/public.html b/admin/static/views/public.html index 45bb5b71..791551a6 100644 --- a/admin/static/views/public.html +++ b/admin/static/views/public.html @@ -10,6 +10,7 @@ Enregistrement équipes Accueil public Lancement + Fin/Récompenses Résumé Résumé 2 @@ -125,14 +126,14 @@
-
+
-
+
diff --git a/dashboard/static/index.html b/dashboard/static/index.html index c9f75867..19b2f5cd 100644 --- a/dashboard/static/index.html +++ b/dashboard/static/index.html @@ -175,12 +175,53 @@ - + {{ 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 }})
  • +
+ + + +
From 5849648c70fe8bcc7d1ae735f13060ba546b7cbf Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 30 Jan 2020 18:48:28 +0100 Subject: [PATCH 0199/1637] admin: fix handling of description in claims --- admin/static/js/app.js | 11 ++++++----- admin/static/views/claim.html | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 0eeeef56..ad468e75 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -1266,7 +1266,8 @@ angular.module("FICApp") else $scope.fields = ["subject", "priority", "id_exercice", "id_assignee", "id_team", "creation", "state"]; $scope.assignees = ClaimAssignee.query(); - $scope.whoami = Math.floor(getCookie("myassignee")); + $scope.comm = { ndescription: ""}; + $scope.whoami = Math.floor(getCookie("myassignee")); $scope.teams = Teams.get(); $scope.exercices = Exercice.query(); $scope.namedFields = { @@ -1326,7 +1327,7 @@ angular.module("FICApp") method: "POST", data: { "id_assignee": $scope.whoami, - "content": $scope.ndescription + "content": $scope.comm.ndescription } }).then(function(response) { $location.url("/claims/" + $scope.claim.id + "/"); @@ -1342,7 +1343,7 @@ angular.module("FICApp") if (this.claim.id) { this.claim.$update(function(v) { v.id_team = "" + v.id_team; - if ($scope.ndescription) + if ($scope.comm.ndescription) $scope.saveDescription(); else if (backToList) $location.url("/claims/"); @@ -1355,8 +1356,8 @@ angular.module("FICApp") }); } else { this.claim.$save(function() { - if (!$scope.ndescription) - $scope.ndescription = "Création de la tâche"; + if (!$scope.comm.ndescription) + $scope.comm.ndescription = "Création de la tâche"; $scope.saveDescription(); }, function(response) { $rootScope.newBox('danger', 'An error occurs when trying to save claim:', response.data.errmsg); diff --git a/admin/static/views/claim.html b/admin/static/views/claim.html index c18876e4..8b2ffdb5 100644 --- a/admin/static/views/claim.html +++ b/admin/static/views/claim.html @@ -54,10 +54,10 @@
- +
-
+
From e017e11f68d4310cbe9581da61299ce6f756d915 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 30 Jan 2020 18:50:10 +0100 Subject: [PATCH 0200/1637] dashboard: add graph on side --- admin/static/js/app.js | 1 + dashboard/static/index.html | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index ad468e75..8c89dcd9 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -773,6 +773,7 @@ angular.module("FICApp") "exercice_follow": "Dernier exercice des événements", "exercice": "Exercice", "rank": "Classement", + "graph": "Graphique", "message": "Message", "panel": "Boîte", }; diff --git a/dashboard/static/index.html b/dashboard/static/index.html index 19b2f5cd..43568d15 100644 --- a/dashboard/static/index.html +++ b/dashboard/static/index.html @@ -431,6 +431,15 @@
+
+
+

+

+
+
+
+
+
From f1c5681a376ed5e17bed9a56b0060a4eda365abe Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 30 Jan 2020 18:50:36 +0100 Subject: [PATCH 0201/1637] dashboard: improve display --- dashboard/static/index.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dashboard/static/index.html b/dashboard/static/index.html index 43568d15..0057ba1d 100644 --- a/dashboard/static/index.html +++ b/dashboard/static/index.html @@ -142,10 +142,10 @@ - + @@ -153,7 +153,7 @@ + + From ce2f42cdc8da9ee30370e0ff5250dca354a3b50a Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 9 Sep 2020 21:19:33 +0200 Subject: [PATCH 0233/1637] qa: Fix new line formating in comments --- qa/static/views/exercice.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qa/static/views/exercice.html b/qa/static/views/exercice.html index 87e488af..c52e13ba 100644 --- a/qa/static/views/exercice.html +++ b/qa/static/views/exercice.html @@ -92,7 +92,7 @@
{{ team.rank }}ere
{{ team.name }}
{{ team.rank }}ere — {{ team.score | number:0 }} points
{{ team.name }}
- {{ mystats.themes[tid].solved }}/{{ mystats.themes[tid].total }} - ({{ mystats.themes[tid].tries }}) + {{ mystats.themes[tid].solved }}/{{ mystats.themes[tid].total }} + ({{ mystats.themes[tid].tries }})
   Résolus - Total résolus
+    Total résolus
   Tentatives
@@ -366,7 +366,7 @@ -
-
- - -
    -
  • -
- - -
-
-
+
+ +
+ diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 8c89dcd9..b4a4c4d7 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -127,7 +127,54 @@ angular.module("FICApp") }); } } - }]); + }]) + + .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'); + this.yesFunc = function() { + $element.children(0).toast('dispose'); + if (this.onyes) + this.onyes(); + } + this.noFunc = function() { + $element.children(0).toast('dispose'); + if (this.onno) + this.onno(); + } + }, + template: `` + }); angular.module("FICApp") .factory("Version", function($resource) { @@ -395,15 +442,27 @@ angular.module("FICApp") refresh(); $interval(refresh, 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.regenerateStaticFiles = function() { Settings.get().$promise.then(function(config) { config.generation = (new Date()).toISOString(); config.$update(function() { $rootScope.staticFilesNeedUpdate = 0; - $rootScope.newBox('success', "Regeneration in progress..."); + $scope.addToast('success', "Regeneration in progress..."); }, function (response) { - $rootScope.newBox('success', 'An error occurs when saving settings:', response.data.errmsg); + $scope.addToast('success', 'An error occurs when saving settings:', response.data.errmsg); }) }) } @@ -435,76 +494,6 @@ angular.module("FICApp") $scope.monitor = Monitor.get(); }) - .controller("DIWEBoxController", function($scope, $rootScope, $interval, $timeout) { - function updBox() { - while ($rootScope._newBoxes.length > 0) { - var b = $rootScope._newBoxes.shift(); - $scope.boxes.unshift(b); - var id = $scope.boxes.length - 1; - b.cancel = function() { - $scope.boxes.pop($scope.boxes.indexOf(b)); - } - if (b.timeout >= 0) - $timeout(function() { b.cancel(); }, b.timeout); - } - } - - $rootScope._newBoxes = new Array(); - $rootScope.newBox = function(kind, title, msg, tmout) { - if (kind === undefined) { kind = 'default'; } - if (msg === undefined) { msg = ''; } - if (tmout === undefined) { tmout = 5000; } - - var mybox = { - 'kind': kind, - 'title': title, - 'timeout': tmout, - }; - - if (Array.isArray(msg)) - mybox['list'] = msg; - else - mybox['msg'] = msg; - - $rootScope._newBoxes.push(mybox); - }; - $rootScope.newYesNoBox = function(kind, title, msg, yesFunc, noFunc, tmout) { - if (kind === undefined) { kind = 'default'; } - if (msg === undefined) { msg = ''; } - if (tmout === undefined) { tmout = 5000; } - - var mybox; - - var yesFn = function() { - mybox.cancel(); - if (yesFunc !== undefined) { - yesFunc(); - } - } - var noFn = function() { - mybox.cancel(); - if (noFunc !== undefined) { - noFunc(); - } - } - - mybox = { - 'kind': kind, - 'title': title, - 'msg': msg, - 'yes': yesFn, - 'no': noFn, - 'timeout': tmout, - }; - - $rootScope._newBoxes.push(mybox); - }; - $scope.boxes = new Array(); - - updBox(); - $interval(updBox, 750); - }) - .controller("SettingsController", function($scope, $rootScope, Settings, ROSettings, $location, $http, $interval) { $scope.displayDangerousActions = false; $scope.config = Settings.get(); @@ -555,14 +544,14 @@ angular.module("FICApp") var state = this.config.enableExerciceDepend; this.config.unlockedChallengeDepth = (this.config.enableExerciceDepend?this.config.unlockedChallengeDepth:-1) this.config.$update(function(response) { - $rootScope.newBox('success', msg); + $scope.addToast('success', msg); response.enableExerciceDepend = response.unlockedChallengeDepth >= 0; $rootScope.settings.start = new Date(nStart); $rootScope.settings.end = new Date(nEnd); $rootScope.settings.generation = new Date(nGen); $rootScope.settings.activateTime = new Date(aTime); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when saving settings:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when saving settings:', response.data.errmsg); }); } $scope.regenerate = function() { @@ -577,7 +566,7 @@ angular.module("FICApp") var f = new Date(ts + 120000 + this.duration * 60000); this.config.end = f.toISOString(); - $rootScope.newYesNoBox('info', 'Challenge ready to start,', 'propagate the changes?', + $scope.addToast('info', 'Challenge ready to start,', 'propagate the changes?', function() { $scope.saveSettings(); }); @@ -593,14 +582,14 @@ angular.module("FICApp") "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.", } - $rootScope.newYesNoBox('warning', txts[type], 'Êtes-vous sûr de vouloir continuer ?', + $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) { - $rootScope.newBox('success', type + 'reseted'); + $scope.addToast('success', type + 'reseted'); $location.url("/"); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when reseting ' + type + ':', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when reseting ' + type + ':', response.data.errmsg); }); } @@ -615,28 +604,28 @@ angular.module("FICApp") question = 'Faire une synchronisation intégrale ?' url = "/api/sync/deep" } - $rootScope.newYesNoBox('warning', question, '', + $scope.addToast('warning', question, '', function() { $scope.deepSyncInProgress = true; $http.post(url).then(function() { $scope.deepSyncInProgress = false; - $rootScope.newBox('success', 'Synchronisation intégrale terminée.', 'Voir le rapport.', 15000); + $scope.addToast('success', 'Synchronisation intégrale terminée.', 'Voir le rapport.', 15000); }, function(response) { $scope.deepSyncInProgress = false; - $rootScope.newBox('warning', 'Synchronisation intégrale terminée.', 'Voir le rapport.', 15000); + $scope.addToast('warning', 'Synchronisation intégrale terminée.', 'Voir le rapport.', 15000); }); }); }; $scope.speedyDeepSync = function() { - $rootScope.newYesNoBox('warning', 'Faire une synchronisation profonde rapide, sans s\'occuper des fichiers ?', '', + $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; - $rootScope.newBox('success', 'Synchronisation profonde rapide terminée.', 'Voir le rapport.', 15000); + $scope.addToast('success', 'Synchronisation profonde rapide terminée.', 'Voir le rapport.', 15000); }, function(response) { $scope.deepSyncInProgress = false; - $rootScope.newBox('warning', 'Synchronisation profinde rapide terminée.', 'Voir le rapport.', 15000); + $scope.addToast('warning', 'Synchronisation profinde rapide terminée.', 'Voir le rapport.', 15000); }); }); }; @@ -667,7 +656,7 @@ angular.module("FICApp") }); }); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when trying to associate certificate:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when trying to associate certificate:', response.data.errmsg); } ); } @@ -703,7 +692,7 @@ angular.module("FICApp") $scope.certificates = Certificate.query(); $scope.selectedTeam = null; }, function(response) { - $rootScope.newBox('danger', 'An error occurs when trying to associate certificate:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when trying to associate certificate:', response.data.errmsg); } ); }; @@ -712,7 +701,7 @@ angular.module("FICApp") $http.post("/api/ca/new", $scope.newca).then(function() { $scope.ca = CACertificate.get(); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when generating CA:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when generating CA:', response.data.errmsg); }); }; $scope.renewCA = function() { @@ -723,21 +712,21 @@ angular.module("FICApp") $http.post("/api/certs").then(function() { $scope.certificates = Certificate.query(); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when generating certificate:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when generating certificate:', response.data.errmsg); }); }; $scope.generateHtpasswd = function() { $http.post("/api/htpasswd").then(function() { - $rootScope.newBox('success', 'Fichier htpasswd généré avec succès'); + $scope.addToast('success', 'Fichier htpasswd généré avec succès'); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when generating htpasswd file:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when generating htpasswd file:', response.data.errmsg); }); }; $scope.removeHtpasswd = function() { $http.delete("/api/htpasswd").then(function() { - $rootScope.newBox('success', 'Fichier htpasswd supprimé avec succès'); + $scope.addToast('success', 'Fichier htpasswd supprimé avec succès'); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when deleting htpasswd file:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when deleting htpasswd file:', response.data.errmsg); }); }; }) @@ -1006,9 +995,9 @@ angular.module("FICApp") $scope.someUpdt = false; var prms = Scene.update({ screenId: $scope.screenid }, $scope.display); prms.$promise.then(function() { - $rootScope.newBox('success', 'Scene successfully published!'); + $scope.addToast('success', 'Scene successfully published!'); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when saving scene:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when saving scene:', response.data.errmsg); }); }; $scope.addSide = function() { @@ -1361,7 +1350,7 @@ angular.module("FICApp") $scope.comm.ndescription = "Création de la tâche"; $scope.saveDescription(); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when trying to save claim:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when trying to save claim:', response.data.errmsg); }); } } @@ -1404,12 +1393,12 @@ angular.module("FICApp") $scope.themes = Theme.query(); $rootScope.staticFilesNeedUpdate++; if (response.data) - $rootScope.newBox('danger', response.data); + $scope.addToast('danger', response.data); else - $rootScope.newBox('success', 'Synchronisation de la liste des thèmes terminée avec succès.'); + $scope.addToast('success', 'Synchronisation de la liste des thèmes terminée avec succès.'); }, function(response) { $scope.inSync = false; - $rootScope.newBox('danger', 'An error occurs when synchronizing theme list:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when synchronizing theme list:', response.data.errmsg); }); }; }) @@ -1432,7 +1421,7 @@ angular.module("FICApp") $rootScope.staticFilesNeedUpdate++; $location.url("/themes/"); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when trying to delete theme:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when trying to delete theme:', response.data.errmsg); }); } }) @@ -1479,7 +1468,7 @@ angular.module("FICApp") }) $scope.exercice = {}; $rootScope.staticFilesNeedUpdate++; - $rootScope.newBox('success', 'Édition de masse terminée avec succès'); + $scope.addToast('success', 'Édition de masse terminée avec succès'); } $scope.show = function(id) { @@ -1493,7 +1482,7 @@ angular.module("FICApp") var work = []; var go = function() { if (!work.length) { - $rootScope.newBox('info', "Synchronisation des exercices terminée."); + $scope.addToast('info', "Synchronisation des exercices terminée."); $scope.inSync = false; return; } @@ -1547,12 +1536,12 @@ angular.module("FICApp") $scope.exercices = ThemedExercice.query({ themeId: $scope.theme.id }); $rootScope.staticFilesNeedUpdate++; if (response.data) - $rootScope.newBox('warning', null, response.data, -1); + $scope.addToast('warning', null, response.data, -1); else - $rootScope.newBox('success', 'Synchronisation de la liste des exercices terminée avec succès.'); + $scope.addToast('success', 'Synchronisation de la liste des exercices terminée avec succès.'); }, function(response) { $scope.inSync = false; - $rootScope.newBox('danger', 'An error occurs when synchrinizing exercices:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when synchrinizing exercices:', response.data.errmsg); }); }; }) @@ -1591,12 +1580,12 @@ angular.module("FICApp") $scope.exercice = Exercice.get({ exerciceId: $routeParams.exerciceId }); $rootScope.staticFilesNeedUpdate++; if (response.data) - $rootScope.newBox('danger', response.data); + $scope.addToast('danger', response.data); else - $rootScope.newBox('success', "Synchronisation de l'exercice terminée avec succès."); + $scope.addToast('success', "Synchronisation de l'exercice terminée avec succès."); }, function(response) { $scope.inSync = false; - $rootScope.newBox('danger', 'An error occurs when synchronizing exercice:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when synchronizing exercice:', response.data.errmsg); }); }; @@ -1606,7 +1595,7 @@ angular.module("FICApp") $rootScope.staticFilesNeedUpdate++; $location.url("/themes/" + tid); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when trying to delete exercice:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when trying to delete exercice:', response.data.errmsg); }); } $scope.saveExercice = function() { @@ -1618,7 +1607,7 @@ angular.module("FICApp") $rootScope.staticFilesNeedUpdate++; $location.url("/themes/" + $scope.exercice.idTheme + "/exercices/" + $scope.exercice.id); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when trying to save exercice:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when trying to save exercice:', response.data.errmsg); }); } } @@ -1684,7 +1673,7 @@ angular.module("FICApp") $scope.history = ExerciceHistory.query({ exerciceId: $routeParams.exerciceId }); $('#updHistory').modal('hide'); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when updating history item: ', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when updating history item: ', response.data.errmsg); }); } } @@ -1697,7 +1686,7 @@ angular.module("FICApp") $rootScope.staticFilesNeedUpdate++; $scope.history = ExerciceHistory.query({ exerciceId: $routeParams.exerciceId }); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when removing history item: ', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when removing history item: ', response.data.errmsg); }); } }) @@ -1719,7 +1708,7 @@ angular.module("FICApp") }).then(function(response) { $scope.files = ExerciceFile.query({ exerciceId: $routeParams.exerciceId }); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when removing file dep:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when removing file dep:', response.data.errmsg); }); $rootScope.staticFilesNeedUpdate++; return false; @@ -1739,12 +1728,12 @@ angular.module("FICApp") $rootScope.staticFilesNeedUpdate++; $scope.files = ExerciceFile.query({ exerciceId: $routeParams.exerciceId }); if (response.data) - $rootScope.newBox('danger', response.data); + $scope.addToast('danger', response.data); else - $rootScope.newBox('success', "Synchronisation de la liste de fichiers terminée avec succès."); + $scope.addToast('success', "Synchronisation de la liste de fichiers terminée avec succès."); }, function(response) { $scope.inSync = false; - $rootScope.newBox('danger', 'An error occurs when synchronizing flags list:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when synchronizing flags list:', response.data.errmsg); }); }; }) @@ -1760,7 +1749,7 @@ angular.module("FICApp") $scope.hints = ExerciceHint.query({ exerciceId: $routeParams.exerciceId }); $rootScope.staticFilesNeedUpdate++; }, function(response) { - $rootScope.newBox('danger', 'An error occurs when trying to delete hint:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when trying to delete hint:', response.data.errmsg); }); } $scope.saveHint = function() { @@ -1782,12 +1771,12 @@ angular.module("FICApp") $scope.hints = ExerciceHint.query({ exerciceId: $routeParams.exerciceId }); $rootScope.staticFilesNeedUpdate++; if (response.data) - $rootScope.newBox('danger', response.data); + $scope.addToast('danger', response.data); else - $rootScope.newBox('success', "Synchronisation de la liste d'indices terminée avec succès."); + $scope.addToast('success', "Synchronisation de la liste d'indices terminée avec succès."); }, function(response) { $scope.inSync = false; - $rootScope.newBox('danger', 'An error occurs when synchronizing hints list:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when synchronizing hints list:', response.data.errmsg); }); }; }) @@ -1819,7 +1808,7 @@ angular.module("FICApp") $scope.flags = ExerciceFlag.query({ exerciceId: $routeParams.exerciceId }); $rootScope.staticFilesNeedUpdate++; }, function(response) { - $rootScope.newBox('danger', 'An error occurs when trying to delete flag:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when trying to delete flag:', response.data.errmsg); }); } $scope.saveFlag = function() { @@ -1845,10 +1834,10 @@ angular.module("FICApp") method: "POST" }).then(function(response) { flag.test_str = ""; - $rootScope.newBox('success', "Flag Ok !"); + $scope.addToast('success', "Flag Ok !"); }, function(response) { flag.test_str = ""; - $rootScope.newBox('danger', 'An error occurs: ', response.data.errmsg); + $scope.addToast('danger', 'An error occurs: ', response.data.errmsg); }); } } @@ -1863,12 +1852,12 @@ angular.module("FICApp") $scope.flags = ExerciceFlag.query({ exerciceId: $routeParams.exerciceId }); $rootScope.staticFilesNeedUpdate++; if (response.data) - $rootScope.newBox('danger', response.data); + $scope.addToast('danger', response.data); else - $rootScope.newBox('success', "Synchronisation de la liste de drapeaux terminée avec succès."); + $scope.addToast('success', "Synchronisation de la liste de drapeaux terminée avec succès."); }, function(response) { $scope.inSync = false; - $rootScope.newBox('danger', 'An error occurs when synchronizing flags list:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when synchronizing flags list:', response.data.errmsg); }); }; }) @@ -1925,7 +1914,7 @@ angular.module("FICApp") $scope.quiz = ExerciceMCQFlag.query({ exerciceId: $routeParams.exerciceId }); $rootScope.staticFilesNeedUpdate++; }, function(response) { - $rootScope.newBox('danger', 'An error occurs when trying to delete flag:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when trying to delete flag:', response.data.errmsg); }); } $scope.saveQuiz = function() { @@ -1975,7 +1964,7 @@ angular.module("FICApp") $http.post("/api/disableinactiveteams").then(function() { $scope.teams = Team.query(); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when disabling inactive teams:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when disabling inactive teams:', response.data.errmsg); }); } $scope.show = function(id) { @@ -2022,8 +2011,8 @@ angular.module("FICApp") } $scope.deleteTeam = function() { backName = this.team.name; - this.team.$remove(function() { $rootScope.newBox('success', 'Team ' + backName + ' successfully removed.'); $location.url("/teams/"); $rootScope.staticFilesNeedUpdate++; }, - function(response) { $rootScope.newBox('danger', 'An error occurs during suppression of the team:', response.data.errmsg); }); + 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"); @@ -2053,9 +2042,9 @@ angular.module("FICApp") certificate.serial = parseInt(certificate.id).toString(16); }); }); - $rootScope.newBox('success', 'Certificate successfully dissociated!'); + $scope.addToast('success', 'Certificate successfully dissociated!'); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when dissociating certiticate:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when dissociating certiticate:', response.data.errmsg); }); } }) @@ -2071,7 +2060,7 @@ angular.module("FICApp") $scope.associations = TeamAssociation.query({ teamId: $routeParams.teamId }); }, function(response) { if (response.data) - $rootScope.newBox('danger', 'An error occurs when creating user association: ', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when creating user association: ', response.data.errmsg); }); } } @@ -2081,7 +2070,7 @@ angular.module("FICApp") function() { $scope.associations = TeamAssociation.query({ teamId: $routeParams.teamId }); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when removing user association: ', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when removing user association: ', response.data.errmsg); }); } }) @@ -2105,7 +2094,7 @@ angular.module("FICApp") $scope.history = TeamHistory.query({ teamId: $routeParams.teamId }); $('#updHistory').modal('hide'); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when updating history item: ', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when updating history item: ', response.data.errmsg); }); } } @@ -2118,7 +2107,7 @@ angular.module("FICApp") $rootScope.staticFilesNeedUpdate++; $scope.history = TeamHistory.query({ teamId: $routeParams.teamId }); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when removing history item: ', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when removing history item: ', response.data.errmsg); }); } }) From adb424ea035e31aba37f8747d2e2750e7579f21f Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 15 Apr 2020 07:39:38 +0200 Subject: [PATCH 0214/1637] Use fmt.Errorf --- admin/api/exercice.go | 24 ++++++++++++------------ admin/api/file.go | 5 ++--- admin/sync/exercice_files.go | 13 ++++++------- admin/sync/exercice_hints.go | 7 +++---- admin/sync/exercice_keys.go | 12 +++++------- admin/sync/exercices.go | 11 +++++------ admin/sync/file.go | 5 ++--- admin/sync/themes.go | 7 +++---- backend/generation.go | 9 ++++----- libfic/flag_key.go | 2 +- libfic/hint.go | 15 +++++++-------- 11 files changed, 50 insertions(+), 60 deletions(-) diff --git a/admin/api/exercice.go b/admin/api/exercice.go index babf5919..a4dcfa33 100644 --- a/admin/api/exercice.go +++ b/admin/api/exercice.go @@ -118,7 +118,7 @@ func exportResolutionMovies(_ httprouter.Params, body []byte) (interface{}, erro } } -func loadFlags(n func () ([]fic.Flag, error)) (interface{}, error) { +func loadFlags(n func() ([]fic.Flag, error)) (interface{}, error) { if flags, err := n(); err != nil { return nil, err } else { @@ -138,7 +138,7 @@ func loadFlags(n func () ([]fic.Flag, error)) (interface{}, error) { ret = append(ret, m) } } else { - return nil, errors.New(fmt.Sprintf("Flag type %T not implemented for this flag.", f)) + return nil, fmt.Errorf("Flag type %T not implemented for this flag.", f) } } @@ -181,11 +181,11 @@ type exerciceStats struct { func getExerciceStats(e fic.Exercice, body []byte) (interface{}, error) { return exerciceStats{ - TeamTries: e.TriedTeamCount(), - TotalTries: e.TriedCount(), + TeamTries: e.TriedTeamCount(), + TotalTries: e.TriedCount(), SolvedCount: e.SolvedCount(), - FlagSolved: e.FlagSolved(), - MCQSolved: e.MCQSolved(), + FlagSolved: e.FlagSolved(), + MCQSolved: e.MCQSolved(), }, nil } @@ -196,12 +196,12 @@ func getExercicesStats(_ httprouter.Params, body []byte) (interface{}, error) { ret := []exerciceStats{} for _, e := range exercices { ret = append(ret, exerciceStats{ - IdExercice: e.Id, - TeamTries: e.TriedTeamCount(), - TotalTries: e.TriedCount(), + IdExercice: e.Id, + TeamTries: e.TriedTeamCount(), + TotalTries: e.TriedCount(), SolvedCount: e.SolvedCount(), - FlagSolved: e.FlagSolved(), - MCQSolved: e.MCQSolved(), + FlagSolved: e.FlagSolved(), + MCQSolved: e.MCQSolved(), }) } return ret, nil @@ -406,7 +406,7 @@ type uploadedFlag struct { ValidatorRe *string `json:"validator_regexp"` Flag string Value []byte - ChoicesCost int64 `json:"choices_cost"` + ChoicesCost int64 `json:"choices_cost"` } func createExerciceFlag(exercice fic.Exercice, body []byte) (interface{}, error) { diff --git a/admin/api/file.go b/admin/api/file.go index 5b173bc7..b35e2c74 100644 --- a/admin/api/file.go +++ b/admin/api/file.go @@ -3,7 +3,6 @@ package api import ( "encoding/hex" "encoding/json" - "errors" "fmt" "srs.epita.fr/fic-server/admin/sync" @@ -77,7 +76,7 @@ func genFileList(in []fic.EFile, e error) (out []APIFile, err error) { g.Depends = append(g.Depends, m) } else { - err = errors.New(fmt.Sprintf("Unknown type %T to handle file dependancy", k)) + err = fmt.Errorf("Unknown type %T to handle file dependancy", k) return } } @@ -148,6 +147,6 @@ func deleteFileDep(file fic.EFile, depid int64, _ []byte) (interface{}, error) { return true, file.DeleteDepend(fic.FlagKey{Id: depid}) } -func checkFile(file fic.EFile, _ []byte) (interface{}, error) { +func checkFile(file fic.EFile, _ []byte) (interface{}, error) { return true, file.CheckFileOnDisk() } diff --git a/admin/sync/exercice_files.go b/admin/sync/exercice_files.go index f4baed39..5057b0d3 100644 --- a/admin/sync/exercice_files.go +++ b/admin/sync/exercice_files.go @@ -3,7 +3,6 @@ package sync import ( "bufio" "encoding/hex" - "errors" "fmt" "path" "strings" @@ -154,20 +153,20 @@ func ApiGetRemoteExerciceFiles(ps httprouter.Params, _ []byte) (interface{}, err fPath := path.Join(exercice.Path, "files", fname) fSize, _ := getFileSize(GlobalImporter, fPath) ret = append(ret, fic.EFile{ - Path: fPath, - Name: fname, + Path: fPath, + Name: fname, Checksum: digests[fname], - Size: fSize, + Size: fSize, }) } return ret, nil } else { - return nil, errors.New(fmt.Sprintf("%q", errs)) + return nil, fmt.Errorf("%q", errs) } } else { - return nil, errors.New(fmt.Sprintf("%q", errs)) + return nil, fmt.Errorf("%q", errs) } } else { - return nil, errors.New(fmt.Sprintf("%q", errs)) + return nil, fmt.Errorf("%q", errs) } } diff --git a/admin/sync/exercice_hints.go b/admin/sync/exercice_hints.go index d485e7fb..f3bc2158 100644 --- a/admin/sync/exercice_hints.go +++ b/admin/sync/exercice_hints.go @@ -4,7 +4,6 @@ import ( "bufio" "crypto" "encoding/hex" - "errors" "fmt" "io" "os" @@ -150,12 +149,12 @@ func ApiGetRemoteExerciceHints(ps httprouter.Params, _ []byte) (interface{}, err if hints != nil { return hints, nil } else { - return hints, errors.New(fmt.Sprintf("%q", errs)) + return hints, fmt.Errorf("%q", errs) } } else { - return exercice, errors.New(fmt.Sprintf("%q", errs)) + return exercice, fmt.Errorf("%q", errs) } } else { - return nil, errors.New(fmt.Sprintf("%q", errs)) + return nil, fmt.Errorf("%q", errs) } } diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index e865d3a1..ece36066 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -1,7 +1,6 @@ package sync import ( - "errors" "fmt" "math/rand" "path" @@ -282,8 +281,8 @@ func buildExerciceFlag(i Importer, exercice fic.Exercice, flag ExerciceFlag, nli } ret = append(ret, importFlag{ - Line: nline + 1, - Flag: addedFlag, + Line: nline + 1, + Flag: addedFlag, }) } return @@ -392,7 +391,6 @@ func ExerciceFlagsMap(i Importer, exercice fic.Exercice) (kmap map[int64]fic.Fla return } - // SyncExerciceFlags imports all kind of flags for the given challenge. func SyncExerciceFlags(i Importer, exercice fic.Exercice) (kmap map[int64]fic.Flag, errs []string) { if _, err := exercice.WipeFlags(); err != nil { @@ -456,12 +454,12 @@ func ApiGetRemoteExerciceFlags(ps httprouter.Params, _ []byte) (interface{}, err if flags != nil { return flags, nil } else { - return flags, errors.New(fmt.Sprintf("%q", errs)) + return flags, fmt.Errorf("%q", errs) } } else { - return exercice, errors.New(fmt.Sprintf("%q", errs)) + return exercice, fmt.Errorf("%q", errs) } } else { - return nil, errors.New(fmt.Sprintf("%q", errs)) + return nil, fmt.Errorf("%q", errs) } } diff --git a/admin/sync/exercices.go b/admin/sync/exercices.go index 4b7116eb..d0908549 100644 --- a/admin/sync/exercices.go +++ b/admin/sync/exercices.go @@ -1,7 +1,6 @@ package sync import ( - "errors" "fmt" "path" "strconv" @@ -71,13 +70,13 @@ func buildDependancyMap(i Importer, theme fic.Theme) (dmap map[int64]fic.Exercic func parseExerciceDirname(edir string) (eid int, ename string, err error) { edir_splt := strings.SplitN(edir, "-", 2) if len(edir_splt) != 2 { - err = errors.New(fmt.Sprintf("%q is not a valid exercice directory: missing id prefix", edir)) + 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 = errors.New(fmt.Sprintf("%q: invalid exercice identifier: %s", edir, err)) + err = fmt.Errorf("%q: invalid exercice identifier: %s", edir, err) return } @@ -270,7 +269,7 @@ func ApiListRemoteExercices(ps httprouter.Params, _ []byte) (interface{}, error) if theme != nil { return GetExercices(GlobalImporter, *theme) } else { - return nil, errors.New(fmt.Sprintf("%q", errs)) + return nil, fmt.Errorf("%q", errs) } } @@ -282,9 +281,9 @@ func ApiGetRemoteExercice(ps httprouter.Params, _ []byte) (interface{}, error) { if exercice != nil { return exercice, nil } else { - return exercice, errors.New(fmt.Sprintf("%q", errs)) + return exercice, fmt.Errorf("%q", errs) } } else { - return nil, errors.New(fmt.Sprintf("%q", errs)) + return nil, fmt.Errorf("%q", errs) } } diff --git a/admin/sync/file.go b/admin/sync/file.go index 4e0d82f3..bc3749fe 100644 --- a/admin/sync/file.go +++ b/admin/sync/file.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "encoding/base32" - "errors" "fmt" "io" "os" @@ -75,7 +74,7 @@ func getFileSize(i Importer, URI string) (size int64, err error) { } } - return size, errors.New(fmt.Sprintf("%q: no such file or directory", URI)) + 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. @@ -110,7 +109,7 @@ func getFile(i Importer, URI string, writer *bufio.Writer) error { } } - return errors.New(fmt.Sprintf("%q: no such file or directory", URI)) + return fmt.Errorf("%q: no such file or directory", URI) } // getFileContent retrieves the content of the given text file. diff --git a/admin/sync/themes.go b/admin/sync/themes.go index ce30240f..9e4f3704 100644 --- a/admin/sync/themes.go +++ b/admin/sync/themes.go @@ -1,7 +1,6 @@ package sync import ( - "errors" "fmt" "image" "image/jpeg" @@ -183,8 +182,8 @@ func SyncThemes(i Importer) []string { btheme.Image = strings.TrimPrefix(filePath, fic.FilesDir) return nil, nil }); err != nil { - errs = append(errs, fmt.Sprintf("%q: unable to import heading image: %s", tdir, err)) - } + errs = append(errs, fmt.Sprintf("%q: unable to import heading image: %s", tdir, err)) + } } var theme fic.Theme @@ -217,7 +216,7 @@ func ApiListRemoteThemes(_ httprouter.Params, _ []byte) (interface{}, error) { func ApiGetRemoteTheme(ps httprouter.Params, _ []byte) (interface{}, error) { r, errs := BuildTheme(GlobalImporter, ps.ByName("thid")) if r == nil { - return r, errors.New(fmt.Sprintf("%q", errs)) + return r, fmt.Errorf("%q", errs) } else { return r, nil } diff --git a/backend/generation.go b/backend/generation.go index 88a1092a..837dfd73 100644 --- a/backend/generation.go +++ b/backend/generation.go @@ -2,7 +2,6 @@ package main import ( "encoding/json" - "errors" "fmt" "io/ioutil" "log" @@ -103,7 +102,7 @@ func genTeamIssuesFile(team fic.Team) error { if s, err := os.Stat(dirPath); os.IsNotExist(err) { os.MkdirAll(dirPath, 0777) } else if !s.IsDir() { - return errors.New(fmt.Sprintf("%s is not a directory", dirPath)) + return fmt.Errorf("%s is not a directory", dirPath) } if my, err := team.MyIssueFile(); err != nil { @@ -124,7 +123,7 @@ func genTeamMyFile(team *fic.Team) error { if s, err := os.Stat(dirPath); os.IsNotExist(err) { os.MkdirAll(dirPath, 0777) } else if !s.IsDir() { - return errors.New(fmt.Sprintf("%s is not a directory", dirPath)) + return fmt.Errorf("%s is not a directory", dirPath) } if my, err := fic.MyJSONTeam(team, true); err != nil { @@ -156,7 +155,7 @@ func genMyPublicFile() error { if s, err := os.Stat(dirPath); os.IsNotExist(err) { os.MkdirAll(dirPath, 0777) } else if !s.IsDir() { - return errors.New(fmt.Sprintf("%s is not a directory", dirPath)) + return fmt.Errorf("%s is not a directory", dirPath) } if my, err := fic.MyJSONTeam(nil, true); err != nil { @@ -237,7 +236,7 @@ func genAll() { log.Println("Team retrieval error: ", err) } else { for _, team := range teams { - myteam := team // team is reused, we need to create a new variable here to store the value + myteam := team // team is reused, we need to create a new variable here to store the value genTeamQueue <- &myteam } } diff --git a/libfic/flag_key.go b/libfic/flag_key.go index 6186f3c1..e1d71608 100644 --- a/libfic/flag_key.go +++ b/libfic/flag_key.go @@ -210,7 +210,7 @@ func (k FlagKey) AddDepend(j Flag) (err error) { } else if d, ok := j.(MCQ); ok { _, err = DBExec("INSERT INTO exercice_flags_omcq_deps (id_flag, id_mcq_dep) VALUES (?, ?)", k.Id, d.Id) } else { - err = errors.New(fmt.Sprintf("Dependancy type for key (%T) not implemented for this flag.", j)) + err = fmt.Errorf("Dependancy type for key (%T) not implemented for this flag.", j) } return } diff --git a/libfic/hint.go b/libfic/hint.go index b0eb4de4..42b53954 100644 --- a/libfic/hint.go +++ b/libfic/hint.go @@ -1,7 +1,6 @@ package fic import ( - "errors" "fmt" "path" "strings" @@ -12,18 +11,18 @@ var HintCoefficient = 1.0 // EHint represents a challenge hint. type EHint struct { - Id int64 `json:"id"` + Id int64 `json:"id"` // IdExercice is the identifier of the underlying challenge - IdExercice int64 `json:"idExercice"` + IdExercice int64 `json:"idExercice"` // Title is the hint name displayed to players - Title string `json:"title"` + Title string `json:"title"` // Content is the actual content of small text hints (mutually exclusive with File field) // When File is filled, Content contains the hexadecimal file's hash. - Content string `json:"content"` + Content string `json:"content"` // File is path, relative to FilesDir where the file hint is stored (mutually exclusive with Content field) - File string `json:"file"` + File string `json:"file"` // Cost is the amount of points the player will loose if it unlocks the hint - Cost int64 `json:"cost"` + Cost int64 `json:"cost"` } // treatHintContent reads Content to detect if this is a hint file in order to convert to such hint. @@ -138,7 +137,7 @@ func (h EHint) AddDepend(f Flag) (err error) { } else if d, ok := f.(MCQ); ok { _, err = DBExec("INSERT INTO exercice_hints_omcq_deps (id_hint, id_mcq_dep) VALUES (?, ?)", h.Id, d.Id) } else { - err = errors.New(fmt.Sprintf("Dependancy type for key (%T) not implemented for this flag.", f)) + err = fmt.Errorf("Dependancy type for key (%T) not implemented for this flag.", f) } return } From 21cc875cc0620d806967aae2543d66adcbe21f88 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 15 Apr 2020 07:39:57 +0200 Subject: [PATCH 0215/1637] Update ficicon --- admin/static/img/fic.png | Bin 25186 -> 162706 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/admin/static/img/fic.png b/admin/static/img/fic.png index b44e05121b8345b2552d5b93f595a81ad92fa235..848424e54ae4f0aa7e31d13a30e94c9c04adbdfe 100644 GIT binary patch literal 162706 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

1qBjP~bx+uFNo1_T9+_Utd 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

jS{;nRGwB;4o-` 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

mpQBNbNm`(q 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 z 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 EANf;ISO5S3 literal 25186 zcmX_o1z1#Vv^7Xb$IvMZLwC1`)X?1xA`;R_O2-U6bR*p*Eg{_@siL5Olz^lNiuk{L z_ul_eU^o-!oHzDfd#$xW>S(DD;?d%vp`j6~!WH$<(9n^0UpUy{Na)&JFZh9Nr>>%i z_V4cBtKP~qa0J%_Zt9JO#>aB^h3;Q2_W~Te@1v@zbbkwjmW-EGSF3;x9HQ}2GVxJx zfBxLT%?C}v%fZ&i;R%zUlg~3I6;(|g<8UG>G&CkORYiFN|JB2SfM5&Xu77g7`KGgn zs%$}Q`?)`Y^7vXc%_rHQuW|7BIO5h1BA9*zcEtrMoz{%^@_qe*ML~{99)-uN`s<~; zLY~;v6K%E$*WKyBkWhjRW3@ENG)c#u>FM*3-rupQ8as7G$LGOCyXOMq_v%@S?+*zE zbCPE~BEgi0jnz2{)(-bS`F;HTwP(|>9wD3<+r1hxO!;T@%U_n>%dKf{+>u@>G<=vJ70!eMmBUn+Cx&@4;Y5t{59oYTfYakdFo{g)&#f7}bXif{EC@Y%6Jejt z>%#z!p9$AwU-0W)fD@wr*$Hx@n7~Iwa1a#3O<{2GhzU=HLl1%r0gn)a5WJ62d2mWh zUH`|->i_$^K1XUCCN8EtiQ)zxmim9se&{)Gv@|Z`fsp-$fLEjBG?vI`w05%YKIF!( zXPPu`k{H|uNi>#vWf(XKHUA?U&bYg?airdT*GpnEiGzX%IB~GM_oSENLU6%v&;Xcx zH5+(fXMwtV9YS#Z`u*kp%n{3?uMgMVf%pyTm$lO4x-f8Bd5c^=>vTo%@(5}b*p2Sq zY;qK1&z}r=DLQcOVe^yNt#?SlV5BYe{~jPIftU!YWi;MB`w3T>wW(R6;O-(ekA`7L zu?BDjh*ReOP7J@hES&$YDdN8qF#UJNmA52f8kc z#h<6c4f}a)O%m+kh~h=6k*;~1Rb>_laoT7(4NU_~Bv=bXwS^-Vm5km6*wenW0xfW2 zjLJe^Ey1~xY)8QslVnYS-s^wFnD!D6sYf?%Nscr>S~2O4Ap2-n6&)C*To_!yDe*aUL@w!XaKZ}>Fuc_We1zC==LD~=?4(x=&aWm0 zNLN2pz`=HntgXyF-I`KYWg@q;%|VxY(?d>}p)5rM18?un_9gh?!ZFC6C1oJ`iCeKQ zkkZ;*KZ1CsgLBhaoddr-4+o0Nmq%o6ZkgcBEz9RTfYuq1Ng*76aW&9K>OWDD55C120i(81DJMq(EaKG?g#yc1u3D|M*;eJG0b^sT ztx%$*uNg$)ZdzdfjrTnY4y?v7)|jt&ln}EiAx83e$^)}82>Ofx$pq2<^7ZkYP_}8^ zSa2o!xjLbVj`KSWEUGEAB2z2Ow8D`u<`_O{Pq(^W1%T13YPep~yWg;+gUQS+_|DO< z12OcMIiD`^(PY>@3T59^H;7nXmN7Cu{p!0)lEAKYeX_e4^2SuS$D;&H4BF?ecMUY* z5^}TyE(P~q_#jR96^TY*hu zlgo%NnS1=zP&axTNu2@q(TScpiDOKgu&^0I&A*Emk@YDe`#|LDjwxkb)i>cET`*CDd1WZqT?A^Hbr_7@{nT>#tNX9+&5$(iCii_uJ$xB$pGUcRtyOYV;k|jF8E3Z3~%R;#{WJmvHZA(6$U9V|ZijTE~<8J1>B7WP+t`~&Ibe)%y$~1XXGor-o?$X^j zE;aVmuwCr;_Ak3CES;g0a!C>Ol)_1qah)1HtLuJ7gHXEse58V$V1DO_`$$s zu*8d{uWK<<;pHmdLF=ArkM*{5g*OZ7=eI)`55SATa-+pgQpHgypqZLXbgw3G_Q?_9 zhjdK0584{=+b}}x{B5ZI=XE53nFFa_EGsJ~Ci+8>57--9-{g+X5eehCwBCP}?dW;r z^YifSkRa}tHDL`g2*^dKe*y#o9m8R5qB68DnoUzD8-fSabyWg(jvZAK_HTUKH4WV> ztm#jkCs*>AKnfkn(JdR>_GPQ97c9=!mgQ~fTPagTaR_^Z$gSEo7y85X6%*YyNV6qq zRV@ci?W0kIZ89}H`)l{Oxe^2pXfRn;)g;$U+{3lXbB#ejk;}iBPgDdWejSanKZ#5% zuWI4;`$$M1X=o_Z{sJdgau&)G&s|y7LPeSt_TT~a{NY0ac@Dy#s^lca*-ADG)s$ZwVzIuK~-rcmT zzS~aIy2IcOC)-kju%Yit}dCaFriQwLZE z2I15L(^V69x1Til7aV_fgJ>Cth{Ci{$wJT_Wc-vdJJ>wk)8#MW+Rd+!PJbIsd>Knp zW%(I(2qQ!5Z*-};Mz2HV7ZXsfGbUp%OQ4*6=4%CIM)b>2bG5>`>o1gVT2 zue@7Ue1rXhCYB@iU^3Mg4;{wI6Zz7hP;|6UYxmspT=Xyg)YoXG-^Ovx+Elf3bw40Y zP}^goBZ^=`QJ1b%3{^;xHRL-RJvT`-TcSG9eZE}$spwYyxq0OobikXy$l&zD52MB} zuLesFc|Qx0$0&Xx1re{gbr50hXf8PCG`_!OVm!1pk$F*I9E2g*nxrV8ut9jOoD~S& z5O^K;tEBbajdKKtE9zO5$Y!s%YZd>>?#QXgN-jAmE>m-OeJh-&QDD&7X2bYZ<(6GO z+qQbLbPqsYp3GRnK2@tVLw{Y=PJW8A2C$Gkf?8G>L~H9;R{3YOCOI@!s1;nlC!$!0 z1lhHh0;eQJ5e&rD$vx>?M{k-;W7_lYAM8$}UPkFH8#4;Vq;VNs)-@Wczz&tS-M^rN zY~X+Jn7@d)0gS;*LZzB?)u;HX_q2irKg_e1eB`x(mATSZPVjQLc{f)rAs~g3Dh8KW zuKE(GIAbudrZ0>Dj3q(3ROX$cuwGENe)uMUYo-Ey=7s<4MSPsdyTq|1KGFx)Phyh{ zfAk0vc$g9+9?mMe% zypI!9;|SGg?F-_^^zkwdYUD+>-ywW12uQFc3m2NO+VL zCy4teNWsLo)YWid)GVz8N@{qOKk{rYm+4F!W`ccnR%6Fg|1KnslRnMk6@L_4N7&z9 z<|xtpFVB8}O^TbP8O!;CVYPR%EI-}qdTcy7^6q^P1?DRDWf7g2t^R`_R+(RQsXr1( zrQB~oERP%(E zJr+NRMvE}|yj|TmHLSoA+K^X#_-yefA6dxD>AV-#5wr!nO3IlUw`UgAw3ck3I{cbE ze40_gEX8@v1_6M5jB-`@*6-zp#ETb5u-+teA4NLleva6@+i)g2Plr*r7s`1=ae~9; ziqUy`ORGLGNI=Y5zn!LNvZilE$8`0V1;(*;Sv_MSG@MF`_nEc!h% z{MSi+0845!#c{Zo z3yn(txEJmQr-m;|P8dTkCB4}z_Li}vQwQbTW!4;y-dPfCAjdP=_ zn!gL^mp7N1uSQ<&7we|H{d6VLjir&}sDB|$R|Dae6C{jxE9ZGQ`qjjyZ!t{aZ!3SX zC@BA3>qIxs-dy}*m42yv_C1w*vg=Gqd@0u;o(azM4H>miIX*6ElJS)D{CXa-N%^Jg zD~8dtyMim(m2YUKr7o^4|C2Ij+_*GKXKR3ga?Ag!9|t#zAP_^a=Z9{eY14d0;Z?jY z?osJvNF(bu3}R-VGdTATkQfC^!4O=GF`G9E*V7+LRa7WCUW_|}b#b<7?WPfA^wTMN z#05|bdSoh87AzXRdm9E`X_>Llju09>1CG687ToU<9)RcM$!{dY1pi6N zqI5zc9+c;|MlSHpLBh_{-6p|1bjxmHem>B?-uqrT-b6jtEWa)8v#2S_z<|TqXZ>2Y zTvotN2wGf+r5!6?(|JsVRLshR&D?@DyH(2tsv!Y*5Z-!mh=Wp5@w3Ndu7!3+ z!Swyj@@7n6339j^zKknDsFERuJ)5+HI4dz;PU8QokZP&e$sqqcg+v!y=Hy;V#mr|y zjmx0~JSHF3d}A(R)O6dn9v6=PS=Fx97jZOfW8zstErtKMY31RVD-*C zj-kj-DV&4Ql&@^853E7H{Zo_U+~7%0KIZ4~$?7Ov1=Y<3z(eTX2>bc!*GTd!J>Gwh zT7IM&r5S%@_xmsqC@K?(A z1ydD6ts)jC$rrH#JX}R;oepA3WH~#RU1B40EsIo2?2>PHD&Yj&`7VV@+(WIBOAqu| zJ3g66U#IJ9=&AGos>caQg`%C}PKaB5t52?r1{JJ2k(k)dkg>dOE$vp6tOJ2UC|s3= z?#ek91wLg7gc9+hfa$PQmTpNMlqSd5~BQb`{?8B$1FzEG|j9ow)pZ0knb zwbwq6GfTvMJhiAZQF&yd4%=N5=JoiPu6@6V9&a(FIG7UoFk0ORAeY^e`wuO(2%-gH z9M)Pr4`&DuL-X;gyn6h2UssA;C%_ZIIGgWQku~>zrYs}NpM%azmB_|BL+7*0lz;tR zrDe3B^?TZ4K8(O?nV6cUcgwRc#c#X^Xpg6+r2bj~q(sUvq4Q}!)E@CDd81jic1u7! z{MD#(^U%1R=f`gaax9*>`~}92+gA-alp6dcny%#mzck{x@|Mv~oK;Aezb8(*qtL=y zxD?4;TsXGVFMVl}UVUl|ZV+FCA78J8aS?{nV+4CTkn#j(^7TdFw-Zn6vp33r^s z-h)v^z^gVo6gk-)o*4~P*!zkWWirI`(;M>=TRO~dtWyx3`Rbw7hL2DgQ7X*Uc#Cy( zVO`vxh^b^!w6ktJ<1dlwCep9Im3_Vhm(1}YFe;P0+RYJ0A7OAAdsg)gWHhdLO>wS2 z!3v~Y)87qf)gUJq2O(1P(NGA(I!E9==Q2@n#e8no1{t0y2aB!WFA;)0{#v~?nZ(mA zwt_Q5g(7~uFKnvgcHNCT;0}$^FbG;xqY6X3vl<--iDs2R!ZW34(x)#2jdv#~!g8J4teSy8KX%^8W2HOtXkb0;Mx#&@er1`y%P#H;1QM8S@Qk z5Zn$l)s5+-aQh~y8Ei3YiKMrE4eO(<0BICuW!SKp_KrUV6_OucQ|)hYE13t6vWiL& z5oEYOb344)x1WeMeK2qw#BfnR`LMiw)xk=n8Qciy1bxQmlvFZbXmwZ(C1XsBNa8qO za8+xKI*7{rurf-|{HZ1cSl(No+)F5)9&E{Z-RxK>e9>ce0)$k6ln($_{NrcvwQyU> z#KH|TGRo98Ly3;R@+pN_6IBMC?h}n;F~Tv}%_hQl*}j9$B-p!NUl>(DX{tZ7)kmRZ zV;HgmXqw7_$I)C6dndwjPij*hp2ZdHvO7bDxZ4i~uxi^_t9-o03KP zjp8tQh?pc>^KXqTg_q2SI$a0t*^Z|+rNYn6QNd5_>v643ObH4m@#e*72jpf^SSs@I zQGqk&A4BI~69~*X^#g7ZT(!~H1DCkw>e=+A8XNn1F)KB|;w2G0bHDvboy@>s_%hn* zEA{7&5*%^;fWy4cChh~W+;IlUEB26+w8&D#Xm$6SS4vRSicW}9XT@mQ70>goX^w@h zyQ21YE?#iQJQA>+wps)5S%*a@wg(N$W#VH_I!{(ko2ZRtk1%1uh=(FyV!E@a2F5Ae zj`EWnPI4TGg8jo~Stz3B@=t3w&y}I5$f{GQ&;d2il+8>Kpb$Upte(WzPM!{FmUp_v zV2#{-<5wwFE=DMz?48T|SQ%I5#ZV#9F!sK9`liD9^P1hQw!uv=mXOwJg7f?wPF`X#)gJ#4&TBuT zwPsfiMSbJqlG80rouW*~n5te0THwyvf@&IIhaCyRn%?oL8TzLgd-IzUA)RZVIlsOJ zg}i@-Wid5_PXPu=$;>RJ7-C^*a}GbC&BB?22OWHY7*jm*Vm^z2yhR z`?X<#9w$4RgfRVpdy->2i(ep$QSP%z+}(sYO*T#-rnnOPzr_jla1Y&UuGjBZ7tujL zk?MjgJ>@>|PkTa|&Xf&cCw5LFGy(ZjBlMKD?T%<@Ob$RjKe+MG82rIVn)RNO+lH|Xn#nv2q~_5KcwgeS0zpmG=}ET@I0 zMm_eqto57`X$~d{TjtWTBu?2R$YDi*N=}=jiIm*+lCV~mPPVkf?5SGuz@idm0{I?J~Xh2Iu2XQAQT`LOMgE z#A;Mwdg?A5ypZz(Yza;JX({Xj3pSBASNS=y&sSI(pNZ0YvwJ^Jr$pKnj{L`bWQdZ{ zL+q~R7#P8xulB!4`;OmLynu*NV~L5P&i#BxP)i))vZPTIg8FV#2*b$u9B3>k`g7zpzai#%U_5EjBXjpAhf3pkzXv$i~Tz;o^dpE8KhdYcVo%4y2Jj6Q-!HC z@)-yF-U4o2&d_P-^=!ishD$+iJSC`2h%TZ%e3%j_wW%%pIMSC0GSQu%gJ~DGQ|&^> zS4$MwZ(V%PtlG~Kt9+^^Z7Ae$LG$#CDHW%09xQ0fkOn+VG!X+QZ8m)i&w45 z^8gVfGoIFL&M$5xZo14UT&=u;Xl7Ke67Yzv&gg6Er8-@u0QME=g%>Tc@R$#?enmyH0G$gB6}>0+far1JPQAq_ zNG66OTWzz4at1SQx}K<}@1{Jw;x^WeQgj!Vw;X+|GEwdgzFaG$jUq2F;jx%J?4mAMmr0qq>;qw4tk3gG$RkWWln zMA6>I#ZMt9k7{J3;auM}|9i~G32IkA+=y{8k&3?)MBPboftbVa@W>$4^;qwg+1hlb zwvh2Odh2SGhLSGB<#vZiWqJN)p~~?;gmjD_AG5dm6QZ!s%;zj`O_KV5bJ_>?ud1*^Iea@~b2Q znDh5v7f~Y8uI1t+LmxMAzEiYM0|v1sL8F%%x4%e>{VB!g?h+hIS#@`!j1LQx!=)-M zUv?F~6~n8Rd&)&OAs((wQT0H(@rx(%8lEf$bjk|`Im-X@0`NgVRBNI&xe^wc^S5@# z`PCzZ2r?#jn8xl-p_EBx{Aq`u^AiQH`U{;@L8ZXHwz4MxB>|K)70>BLOF!~{Wx+p; zdvWmH1ly&?-*v*~C^%Fgrcs=D1Mp0p4(${?3j8P^C%VM1#V=U^vFsxRhb?Vi=urP1 zFj=XiNUs1vZG{LORIC2OpD(LvD7Pd8sf|9v-CsU`s$|FG{Bkp$`2KIj;6Nu*P}T*e zX+%7;7zBFg&Gt{UnQ{Q!-^I~=7jbtmqXr6yf4%;r4!O5RM*l@T69Z>JC)&ctdl2pZ z&`!q#G`DQ0k>}p^K--#9noU$Z3q&JBMpZ1*+RY+|Uy%&`a3hs|Gn`*Hl{GcXM_Od! zYV5OuJ%pBJc<$dPnv!83HxmA~@5fjnBk2S4XJdf)MVy|-Ze49T$pJMN4T~5CF}1A= zP(R>A3zbNE*^4CHEB(t%K6t*FJnUH|mk^^CZ5iaEX-b9^{M^UU*Sr`Rqs9mXScQye zJsm1YqPqWv1xcvesbk%{Fu)6D0gc+N7c4nj~|@ z6Ul}jR_3xiG8emuc_O7_&Dj0i04b7GN2)M>H$g+u~iUQnQAIk z?*}(me6R7}`Xb<+)Z(;H&389X4dSiI#;D^l_zQ_QGlU|lnSegXuC~BM+z}b5c%Z@t|@C6RJp0<}mZ>{-ohJMCTlDkjNNho`;q%P+Qpy>Z; z{XV(ke2|&Wx}(D1C^A^%Xxp0U_m0XmPfnkfEy zh*Z1Z?AAB+us40{lW#iQ;?7#WmA zcissuF&ix;qdtA$6T87@?o2W7V~6el4st+XHc@$;tw0efr&}A~(mSOHE$+t)bnHO* z%`|a@JO?~jd~lV>58!~HBU#U%9`z=ukh-X9-q&$Zovvzl?E&6+ z!|%3}2an=QrXDDKdKLx6D)CSR1Y~h59Jn`h+;-kQC`DjZqoP0taBnh>-r>dWe?v=F z_;9OInuE~l0Vow87|Q%w-`hObgRvz{r!)nE{rD*CNBt>K+X?6~;vb$q1f~DgAf)RZ zga)Gy~`nDc>bfz#JZ zrMuF^Aolgz>4A$T41zTXpu};u+o;3xO*uEs^+aW^vEk`mP9%$G1t9EXu#3qI-xXU~ z9uWmn#s5M|4ydNwg1#Bd$cWZ}Sl?7OPIIRG5WG5{i|gu_fM!T}RLK zZ}!DNWjvzEglh?C$f8e8%xs5;va&YYyV^UBL%)$?2|rZt-Qi_?EBk}hyG6w5MSQk? zNnP}0Djo~Ha%S$3SH7cr?dc7bw7PhROeqlLfGQg!7SGsmh--PJXa{1n6RitpJ&NzHyd&iJ zf~LaB4vAG&A+d9=rHn5eF*c^yVpyCG@Rh}LGwY_EF$dsQCU98YGH+iw(2Z1mGNVl= z=U4>jNd_O2q3IMc8s|UcUcB87J*SV%842X!61E$GV+zOV@vGTVef`gGdG(HLtB59LSH6JvZG!aqc)_Io>e|Lfxh+(2zW?YsjN1y8dv05*t} z%|AGad}K%fM;>u)3e0zJc8vA<*Ni0lX^C%Dm>Om4k;EtSG?k7$80uU9?Dl<$Vw12= z?c7L|*lO%i*-pJkVjLgFZH`oA7YHkQH!zK|qe(X$`g zng2N)_(chepNsf)x8fVXFoQk%H0R|nWX4_eBzX$D0P_pOse=yH~ySP>mq$^hI`2U%9H{aBIoKk249-rC!Ls=or)kwHt+D3ADq!y z!XW#QB-Ia#ymtBp3k|(&5Tp!Qtn|N?tc-}L=LPpbK=b&Q3rWvO(>BVtd%w}%Wps0^ z==Qs(IXW;>ygOr#jIUOJ%MEIh{a!hsxE@f;y$BwIbVzI+&M=)r^`BMDo?jD%bw+i* z{-sxH=P8G&zv&L@tLAj^i|$e4Zs&FQ@I;C(^<;>}{!ys$QPrPQ{5_bW>4|oytjKga zDw4IYVGL0B^{ug}*O{<2qcou?1)cNE7f99q2DfdN-k*Go1PZ(2hPvZsX8R>E?OTzy z!S2UR6~9rSEa+IY<)w=$a}m06Ohw}@?x;&68m}kZxUS0(i6`oB+4qxu=FCMIw7&DLtOq;e{YCQc{|9x|*#_->-u@o>5vV zj+$oZsUkgF;D(j>P((K$D@m~14rXY`6LL9T{>Z|Dt3TG#YZ_3L3!rxZ!D*80T&qPP zJn$GuejIEN03>>4?MiAAe>1eO5x6UVcJJY7Rw73|6u%qM8L~6{%T<9j z*-JR~*2b-@4BzU%d%IIXDQl**V*#87Aj_hCVy)uN)Je(!MB16w{iciNr zce}x*B8OeWwnYQ_=_KpD_tsyX6DVnjeRP+uZTB(Z-H>2?SZCdQ>HUpPNDbn#N;V4g zL`}DFY$698dOUf|)D6P1`EQgSBk9O^K2XGgjIWq>SC?CeF@R(#dGxUatKAppwPFcC zx!|ph?f2)Y$|he6#syHm?QIrv#NwbUb2&a8?6T1V0zlnd4i_BYwb(z98FMltQ;i5^ zDs3o=4Os2W#!fH&{*>HZO`BHuW$5~&IUy50x8WLR7Rq#5o$WHdbB#qQhfjmFgw?t`PW3e^^Ke|;EsG&QIQfX=GNdGG;07M(JK zwz+vWYI(V5^N&k(L@IVT7_QGUE}J<1FOZD0pt%Qz`r!sbHL5n1M#A3a0o&3fV5=^WM76|HR&jDNakU z<{4!-M>rt}b*LiQY}bX5_R;xLE2W$?M0GV@-(|5}WEi0codVSt=L|^u5@zp6xz3#( zZ%vo1ub{?W-WEq)WRFUmU(ZONhQNlV>a zBrzirao6-#zj`-5T?D(Jy_NZH*yGGaw&}rqw7$oR&&N0HWak-@t_XF6cLcb|th~>? zb5bDH`$5U(*6D#_QCt0-MZ&H{+n%UwNQRgQ`SAo(Su3D6ZC51J9R;^gteG+Q0^G~i zZY)ideY!6DlKsI56vfIGSiCOvDejl{#gt6s+%q`+Xm-MNk%|T%q@A8xPZ{b->Xp}_ zH&AEa)H7H$nSdE_^^f}mXEs?`;3$!8Hu5hseUL8>d+gxVIj<0JyHWstj;p5VVUmJU}U1g4?5HRhZ6_<2^Wm0b`> z?ZEq(@jTDk!YyiOJoTk$ae$Q*vE7SiMq~Eo8dEFVe2*|K=1;NjCppT0A;?v~@kn6u?wXJ#qTClF0pH2D=xx6e*=YJM&9tamq>4 z*9#>fnoYMG4^9(R6$NtG$bX(GkO%q&`y=#NG7YH($EwGCVnYVeFSl&JQUmTQpN;6} z$nTDFa-v_c9-VSgTag?2aDIzvP{J=#YVkl0Tr{q>bsL|S)@bDZnc-PsUu(^T*;u=~ ziM&sR!|u2TKFA_HGOub;g+*LA@jtdS40wS6#H6q_8!ws6L<{p6 z2}e9T<_0zz zk7o%{r~szbc6wTf5|lZTt>X*|oC*S^Cdn;Bq*4Q1ce(@vzAJ#je4 zFBaN-+1K4F^l~LDVfXdhN6^tKKKQ;~C%a=WoU2$-DFgH2B+8FH$!Rh)^?|+*gK)`I zJ;--gP;mGm%FD+gY9sH2dwGGlo2rV*GT?QT?pCspyo4+vB#S7A(czt4AzsxAnY9NX z)k^wWr^3gX_|QMCMYtB_8YtdDNre6^=W_|lPTLBK<02P~tS#P>+8TdA&~5Guqu-Q| zG$IMzcz-^8rbNy6Z8Wg9uBvT>Y`{s|EdwtNK3?{bmx~Vq7~lXcg~`I3Ve=fTvN6Xj zNx+035~Kj0Jd8vEAQNWh%Xac`)v-ahC%R9b=sK%i7;v=piFpv*$q{!zV^uogh5J+P z4H0HMDuPG9Ps2%FkKR3H3e+^aQm#fJxtfT9GJ^&H9h??E*^hT?61Xk_!3+$s z4Q{z0u?bKTYBV35oNPO&#^XKg8b${;O<`K~IiqaJU?&I9g&AiwKz4mtObdz}%l<1{ za?#I6-0S(|j>`WmqyBNe0@UeOzb~1ky3$OXLZ9Za@c!{tRa>e(22P@GbAw>wFnfp? z4|lCUFft6Fb_!R(29uMM!PDJ{X>(N}G`A+U)3-c3kl-0TUEX2#aTz9WTpHexJ}9Va z9XFS_og2_~b(*XHixb0)f+MSmWEuNG4UT(n7=ORWdCMiQ!S4k>Ub}yD~4>f?vRUP zH^2GtgHrgmX>=2bT?35EfRX+z^eOHqpZ=t9l4I81sr5ktV9ph&3x|2`LGDM>)Sjt9 zDd!3ZzvVyb;Up(lEgR)+slV!%31FedKuKI)M%@+j0}n|B^Ok_sM>^hTl)*^A#W6km2D;7FVQplmZ#%_tjzdPTHHZr*%gZBI z$w!4fg-)m`UYLm2vzOLMk%~#@YAPh3 zy1o8w?%wN|YqNFg>e&y2gvk|MZfQ%p=KELiM+I3k3}+crynQY<;$K(4G5@1&bFR`! zS#_mTv4)cmJye=({92)u=6xCyH44ro)c>7}@i>RKk)M?fUGB47D%I?p$j1cM?u=>^ zd&j*k&jI+Y%u`l4{6Q(YxCorzC_nOZo*z~}QXITVLji@~9o-`tBFMI3-W_uqouTx4 zht-?MKZSX!MVV_h47ghYe(XJ>{)f7+g-4*U&-OU7u3^&w7ZdH~GD_SX>mIyp zo^e$?pzZo# zQ5+MLI83^ix%@hBknhN!%0?#hw@>~4c;tpYw=*zmBNX)v`tt$=A&wn(tM|9BH}i4EZs73{}XvD2e<#QaSx`Yl^8d~k7CyC|`f z1{_2uHSrUMSph4H(C+zML=x+RWye$CgL-tu;@PuAIqYanlj5-dg(|_;(Kw`Bjx&`> zghJ^=XovsiXM#2GMtr$i!+ijHeRR{xx;&IojPX;6G^#IF?0DPiGF*@1p488 z{@6b|SAMsjXeGq;CM1S+-=;VU>*XzlF0hp6eq9r;7x0=WMT{i{;;&ck=~&|cg25!U z%&rR1CtW#H)|_E9MoRC z6Y2qm6h+Nlw4bD~f+Z$?nOYBR8LRY4O3&)=BOV{jWByeAmM!tQZ9K`^NV*l0Ak04e*YxX6D&wpz(2cie>iek-FixPm>a?YH((~^$x#qH7nS2Z! z?1HlEuUk!}=I}Sr##WD-Ye~xlm4uD)M`xQ6iN4h{KWEDmt-u(Rf?kHxFLdgs1J`0F z^l9^NpRWjB?LC(6>zzdkMVDqak5~0^ToXjqGu&Il*&<1RODq)0WQb@ht7&t#Pjp%l zQuG||2VHCMAs$$=61D&@!!I$3w4MCrr}EY4a_a`UOGWJ493`pRwB`8^5}%aK%4MPK zF$8O2-q2?C(R@GP5a`lVkF-2|q@DsN7kQBFIJ8R__e3ejl!$;TGbrytnOlk7XsNz&VEEfb5JLwk$=oJ+a+o9CQ9xZFqMxuM{J`P$4^zOR) z;<5HklC8S_kq}tzbE4!de1!Ine!uA^yhfid#Q1svAo3=79#{R9OP!RGg~|5odIk6@ zlVq(4ahB%&4tFn>hub|*+TK~C4kDDH#ezRf1OmeM&QzK~AIGACEohLlfg|2J=r7hd zZoJ+X03D|e+sZ&Y?@zHS8x*|Dt~iIZT29#KBVc5g%J}Hh-kH5p4ypEMN z_YQUBmwPriAkmepx_$u#qk&T@=wC*StBYw1ZjJt4!i18i-N{?=yO9z_-=e<*oe*$L zPTJI=?u7j%j?KlHW59)@^E@Mzd(Q;h`q_V^=)CyE-v}qAKp4L3YR|lCau1J)Sq!w( zYjRjl-+3pDKyxV_1}boLV2#nb@&?hu|0Z3CVFN}09LSn4n;DBN?6mipKQAy27is-r zU%R4232qRTbNx-_6+&2fz7j7w4P7QgP| z685C|tJU}dMui0Q+qIU0+1kZN%Gls98tkZdXds67a>qS6rn$hfzw^y%aXir{&4M^o z1iewZ^W5ZBj(^h02Z+BOct17`zVAmsX!z^29w4JEu%Gad=T}UxRCm7A&1Xo0tp$O& z1okr=c)#)DK))yJ%v>_)4@@{dVu<0d3ua`a&bV`4fo{NoxBQ-AX{TBNb3EK2nLPVd z(SFTQnfHlBr$LpErFi&uQ86)dmOusaXz+qR2X0|xKUk{LqgwgNLAC&3NChm4)`b^;7hn%i}$gCy2EJmio=IwKe)Y>Wu4|0>%*~I|;?Vq+Tdk zKg%e4tFz$i9R5nT7xE!n8O%JCXXcMlu_~@nP zKEWy?np(gYGofW)L2sT>zODQb7+T3=@aHyvmWceH7vRX-5q}Yxy`X9O4V&gKN$MTZ zq?jR#BYAV0POLteSf!n6Y-MDt;bidC0*bC<1#6{JMME}RKY|g(7Z#j!|GgYlTkae3 zr<)-NixtmeR{bde=3)<55&x{O1P)aXS%SVzyzF-T?2gcV5 zihWNvSU_lG{@)A;nfQLvkBFcYQ|wC^6g3mHzobJAusav;_l%8-)ovZ@y3oAnH*%1> z?vqbanEAALk*OuX98f&lpuP%v^jbw#}Y<*DEY;|DEGBZC@`Ixp{b+LTOv@Sh{$C|3X|3cof=b<6-Uf{{6nv*$ySaWE0Bcbl&CH{UR!7S=M6Qxom;sX*sx333YXWev(kZ+7FM!<29bb zNOwIn7NV$DR%Ry>OAJ@Jkxwhq;LmcOoR^GLVRl&OiGK9L9ww(rJ7yg>U0zxB`C2`! zN;$zc*XA<(qW2>&?1;eF{O->ZXbv4@1z~MxF*ENm|ae#GNv`HFW+?%$%Nv-p{8kLXr}L|LyX_tHcnAdq=RP>#&| z?DO$7Tp&Vd!8oxk-HVo3vITzm)44G}FfSopzs)df$s`pd6lpuXv#e*py<}QyC~GLh zw)b<=X9|Gm!>rSL-F9)NnQi{XwhMwE46=gTdOO(5x{gDhN(kAOf9^uzg!oR!_o01! z4Kg&-1WzmtcGY{EWblU`lmb%E)@%id1i3=B9sK;fP)&3ub+yIMHtqNth1oU#70~0A znwM@bQO)I?AOry>3!8h+Fx+c?|Mc@mv>w~4cZP|1E($`h8;7wVnn6@3hNKU5q46D`#9Zb~an~vq8y#+BIp)50A{r z!j$GV*=Tf!|6fmM9Tipg^>Mlp7`g@+x}>{PLPEMzKpLcD=#&^zDQN*I>5!5}I;C5r zOBx6GU7oex_n*6F&76Dgxpz+Oy}$c&KQ>tmua0Fhy&jf^$e8#?`e-S7%nXPgry*2V zZ&}VU=jl)K=6qKI%htKorIrNqC`YN zUm*OfHECH1k)z1_;C1NaIGtxt9h=|J=VysM$RO@M44dHa0IVHA*C05Xb!HUmhX8fU zRr?j65Zr)PCpwXpq~XO7VQeN9k!}o*_L?wX|GTcTbY1jz#M%HS|0)sve5l0OZ7t)D zh(A`I#P6t#v{Q*H2zNege#z5-zE}F>v}zw_1Z|{`@tJ7IAJ2t1!GD9$0jE+!f+E(I z*&>iWcEy)knh6+FKvHl1*HcMO1`FLTO5Gx47U~0qDNVe18Jf$`H+v{jJ>JYH(S%`* z7?Q@AMvf)cu6~?GuYa|7LCmIk@qJ6LV0Y&3eKatt*3PP6u?OMo%dImAjhZr`8pPoe!H8_cpIrZ1OK?Mg+}mesi$~5 z_Bj{}S(JQOMhGB(w)9BhHE|VAY%@F1`1ygJ9Pr(~68yx-c|o-etbKS#mqWbRqm09& zRGsx}u~;5|NsGu*-EO+jCP_z~bo!BeU3U)`Y7S9#Brc%3PkQTDdA&Za_d0ri|L)gW zw&3|lUU7eDdelpvApB?&$F+Wev7{qOiX+EZ(}%)UhFkqRfMB8>tf@mAB7c9d`|9@u z=ouRk>NQ>rrD~OL6(NurBEUfKJ&F<9MymCGi!b$X0XysDa?vrRMabW3rP*@Skccld z;4iJ-Db+acmoV=z;|WDTnAs#fThxC{tBpjEX-_b=H7-!qa@@xrJm2#35>#$(i%e5- zIE=gUbqgn)`?a!eo2_m2<3=crNkE$l2m#5!Ds;J_Opqig8m|>3|VoXJa)=Kd;?~249Hp(YrrPEu1&F+sVwGFaf*{ zn)q)*?7Q#!J-Vdrb(VFVJy4_`|HVdg-_!=^Jc#L^+(#b_%Fe0vEdw+Z)O&_E@<8I# zu^bE2W?Mki`6QYjWwW%^Ko2P(c4@8uj|`)k==3i83g^Vc0&xEBEGGht2tc5~4Iw|M zy6ZcPcAK*{j~s_GRO}A>-U~RLIQQn6US+j^RDGatIh|h*ah^|QeK2#|_u4aTA?Y1g z=_5z8d2rF8SrWC3tvvjCYOy_VV_~d}3V0~Ufe5(1y7FV8|8*rzXw3bWj{q`dePO@* zhC3rOGLwUBnvy49=m$ky0Y4~=9;B#dL8NL7v%Gc; zMhy-eiO3UKhLTL?E13w2#vc4YpUZ|-mKxwp_`kVB1uNZLIIb_x`FYt zdlr`Ww?N-odmPX{w+B}fADVpCEkl90!w0=#9NK*iGvsm-w+lt)h{2aTX+aZhC*D*m zZiHE-qy0h4w@@xR>NvX3U~a^kPaB!#_8Gt27(1`xzP)Y%agiTPt<=}|Nqq^|`qqGMENZg-r|@{4Wve@8PbLUlKh2a z^3axoQrxHOjnAx@^3TKh^f3Tk^6$^+vm(?_YApEM?rZOQrUodP;p8JjAx$V!pq>tm zK#eA8`I71%+js(-Z6iW3c6@7jax^Z1HU5*}W+AD>I~f`UkUUc%hU#?scd}HX+Kmhx zU|)ahe}UiG`inKd+6N~QRd6n|X1w1T(6>rWpaa(O_})z(z>T1u83Tgv%&QlzM9&>* zQ6h&RlqFWdec82(F5ro-TYs3V_|Z9gJx>B{$igx_><`t+alZf z?JC|j@ObN_p8AJbZ!P>J79RON!A~pvm)xudWI>p+5|wCrAE|GpjF8F33jIs~kLhzb zoU4j$ARYo8?)qj&?o`hA45T<-V=;5Vw(@B%%c}AQ!XM>q;Ox>2o_@)@!Q)Xkl=a2q z?wX8DkEh3v$aRpI77vQ+^>bYDCQsyhuVF*Gy)8m>SWJIGrd{bv4yzMBspN-?r`s)8 zEf59*z5z+-J#kF}Jt(~wiU24Sh+BiAP)mTJ4UFo=`b8nwA@;!8P6=J0X94y6>uflF zA&}*LV)S+pKo|)Nfw}=17C_2!b7A1!pEj!qRe+v9Ii8Mg7qLtTKQ51f7sp(@J)++V zNh{G(fsim%gj4WQWu#^DL(0nDRWvnzAgMKSMdcD_X*5!h*3?WhU}s?n$*0=HM?#UM z6~b(-zY%$$@3;Cn?v%1f-!^f>B{_m*88O>!QToai- zXng5HNa@IPJ<{dA)cAHia?XRMKS-D8)H=rA`R&a+;zxQ=rWf(cuCu4SJg_nBMti!= z6KrY%N~_Kl3*T*$Nmas6ibKg*g8j2;I}V#FHS0?^pvKXoaqR!8$POke*&-5tnKEa^ zP|=@}U4qctAOsa%cj#p-LLRea&Ki9|=F}CW%+4q)AB9Cgk1dnNf$}hGWo8XlqzuPX zZ?G)=1llP)5)}8`3wRrNCXAwZttG)2r)QxsPG!~+%^vGiZB2^VI_(-HgrISmT0SalfBj+s? zD&eC3qL=flWzPJL2AMNXQ`%m#&-lg_hkN8qDBZG@?b8P}a3mhsEYg`L7ECjd(71!I zOJ&o@`eyLHA4;j+5|=BzwTDwxEdj+t`>V_lMy4Etz^B#H<kN)AGVRF z4E>#RhxUTGW~Z`q&@{WhoAxvZarq?~j|_V+ZpRK8KI?V%d3y8W`IsNFw|$SC^ zz=y^5n#G~!evFD|&(|))a`tt5(CLNnQUAB=*aU&3hxUZIKk^6Mc@v8WsbFNmjsb@m z;r-^@q|#c&MQ04LO`|kVaQiC;kc^6pE4iJGE;F(dD-X)3fd0YJZCM$vqec@Qr%G77 z0c+Hta&je!+(v=NFLG*O->LWq0oxphhMxUs1^Z>u+i7RYW`vPbe=LQ+Zr>6a5BoHZ z!kA$4p``!B+N^&4e8`c22x_!u*Z+=ci+}gkZuO%HpU(P2wfl-juS{nU&0K$$uG7O3gM=5e*;HmDF@Lgozv3{TnkC9m zn#rHHGoKB#kb_lItRFOU3+v_QRz6<0j{Y}Qe2=LNkc5aWpXZuQ&HZ{dx`MUcu z#G-WL$s%&sOZjkExu#>zA?_)(#I5~t`sP`Iop@;aQ2<-ny5#vrGOI<&{Z!+X<1xbH&Mcx1Xx_mc>ojaKIF8u{fC&P%3uHaXCIa^UVN zbD7wO&USdmkLqS}68x`g(?+hBgxSYsYlHbJNY8i5DDNJ|1kJ6EtKLX@R@zB%?3WUV zW1##P)j2Wf>!jctUOwWtjL=UK`8@RJ4B~11XDc;G4l4Pp_FtEcgqi4*{aQ1&d2m6@ zs>jqd;HSE!<9idukXT-f6EbEu6i3@W*fYJYd2C4vfw8frFiQU8q4AA@V|T0V zx*q{=G6G@h+aOcN*bSI`1!|`8!Ud$NX&gqO@FCwkn3swvs-p=4 zg7^7KgniPRXiG|S%f>~%d!}zDQnB(Od>ncoLf@<`)@QeGD6lXBgc=M5@0572*G6A3 zfK@WBNfSW=pMVF4B(N>2ktEqPH4)~^R=a9i2RpWva8b#N56pFB=44sv&Vp9cNMM^g zv}^Z%02<=XOyb_Y_CY%#_Z-4Nhg3YMl>C{Y*Q-O8V9S<>LzSIA8AJ1gRU~R5xTD{p z{${+?Z5r?1fZ2#s>G8l+FS~9^D0BkLwD*dWto_kXFFU;T!jiz3x%f4N;0I3ZM+-Op zX3}#*o~}mbmxJWIgmTGW(q-uO;l1L(e4Ul*>?XV~i3UPe^Z#8P96MJcvg^_=v?tm{ zcG{PvHSX}O^!~-4tDLUICr(F>o@8zm^XrMT+}&3&XM_L(`xP^J_tf;}HMCePf&oXH zq3Pl8t9C|=#1MMD4?RZMt3wDf7#ju7j*D^IhzsJ|L3zyMrnY;A*MjBvBeg=Ec&@vY zbH5@G9rLV6zZiqh959JNu?RNO#bohH85aD{C!-G{j7C_bjq)#{g0~6bg#rIXw7s0HHYXrb{h`hDY8g%CslJ1R2p;IV=e z+er@Tlm@4wdJ4`qKW~}%D?&i;lVDl$hvjM;l9 z4OmYf@)Eq!&>cou%J?^L8L`Q}J0p(t4&Fd)#_|^PqLcY*c)W87j&6iABpxeHPpYpp`qF=fl z&u(LXjQF0%9;-0s}6%P~+8NNq&y%R$6a=PJ~)8_cw&EV;s#^G+G9>Iwe*1=wA}=4^lK zXRtVm4#jvboc~Tt$WeWbFzGfEZs6x!y>6yjz>Cz#2UdXV&8HirHgzVfjM^_u#nw^1 zFC~OguOx1|uJ+nHQggqkc)xq+`P_RWP$Dt5`B%m!dl_W|xmiiXPmLlMyDfqf$lKUn z-?pwHF;_96QnT99e{?a6%?R!qv3G2_CTSP_2b;uvXyXWcT-eW;fPj#!2~QT_B#9hB zh-!2z-)6cMsUKUePGd#_W~wfBIsFHiyB&Z*_`B)R(xv0}Zsq$h_i-!9r{=1xqhg$H zmEcrFjp~5pdLuT3g_KvDu0B%B`-u*+2pyFqmvv+W)`GOtZ$pO5!h4)T`W!<0Y(p1- zqeIw&L)3y**n(4p97m`rN2DnvQidQju9hP0)d0Pe9X3NGUMl)c$k&f@WW$L1c!C4 z= zz2!9)+cwaDkk@I4rNUif-~IIyw$G^pU=aKx z<>>G141ZIpDL2Hu=Qz80KU_~%>%%_VCg~mEMCuZZs9M7D*YA~NXox(7*4H8fEA`%A z5L4OJ!^+(2-}{tOwgHd?Oh%D9_jb=P;&gT6<3s15&_-F46v%Td#>G)q=Yp|}vb>;L zJg%l_*+5mx7s>c93x}US{cv&hCkF+baNWF)Zi?)U)DM0W7K>vyxBXf<($XVp5@5uT zT8arLng&ras`AGC{GJH9@r0g6!b+njYs+gpg6n($(`Z`AD_=#0t=%xL9NSTH8?kX{{86=2hX(OQI==m!;cDs z8J`&QaX@~D;8r;vW?NrB^V1D5XOKz1ila@5kbKM=7)p=xzZ3wvVu4eSQ=q_sVl!VR zC6H#B_ayvS=oq#2gS0)q$l;o|GFuq$pYEF=F@0d%hJ+&qmGN5otB{per6Q`so5cr! z;c8F@REoLfr1$#a(yNOc=C_x?eW?;f2YG>JgMx!aeEFVn)QWY~3X=nZ`PvF|6v7h^ z;qkO$E&uXC`=}ID>n$dHB=EN>$dpt7{jytgH-Uo07>( zSZ)1E3<+DWnrAY(O)0I|wd*NXcM|1fPF8413&ZTIfdX~n1U4I#59tmjAmw&Rbi21$ zaQijTijXJAfRn)scf$hXr}3<;*bBqvO}8h4Ti#k4C&{%n6$ruG9M{@{%DE`X@~8C0 zcl;t}+6m-h!)h&)Dmy$eCxl-wM!o%B!KE9__+BJr5%J42=LSLomdBd0VY;jPm(y%y#c2uLG3$Th3zyZ)a7mee`>~h zpjcWP{V$jo#D!1J##MabdZ`$ai`jv=)!G9-{_JHEtjNyD)e4R#AR~IKDI##Bq7BMH zPtO~2l0=UJOGyo?Lo6>V%ry;VbggOR>s)PKQv?DwP>7|pJ6ccCivKwhNu|+NU0fEh zovK^8V^JBCj%1_QC1x{88h*FsUns3PARvQpO1Ea)eUxIY#Qrkkdm$Ml^+gpFZCn1{ zH}IEKFLN!@GkzB%?5s`+!=B=q=dKT^%mDT~0CWW4;IYq#|1jY)z6uO|bVymK&nS2{ z23f?2lk@6k1O&vA=XV6~68u-vUEqB!LKjQlC8t1Ii7Id=TsBNrp68ibUuZq6MfYE! zDIN+&05uIT`>{d95H@1uRHrs~h%RWNT_WKh0y0O|=_j&oxLt&gg# z$jm4Jm#TTmIi=1F)W2~BG4TiKyJeIl0`&=_hG{eWwPlJg-lxY1!!T*FnshmSTWIrN z$qUE|2_^{Z`6R*^^;i^4Q~>nvhACgF5Ek*=7+(~mJz2vS!$V9MVS5GKEnW4eXD%IQ zKP!MIiy?j*DwIrH@chvvbVGqbi34aAk_MG8DJBRTsPYE^IxH3?7N{};&%!Nutui9` z>|x#CrgVCiYdBR7WlQUO9B@yM`#deOzZ0+xMYSST!tv+)(H$poN-3I(Y%>O zsDMIFN^&R|#F(^%nD~s?GGVe2@{t&RX2LhY2vZJzW)j#PWWcK*5R?_v<*Q{a!u|(q CpbPf^ From 16abc95b4f88e765b2aaeaa825f2032da68488ca Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 16 May 2020 03:49:27 +0200 Subject: [PATCH 0216/1637] Fix DB issues --- libfic/db.go | 9 ++++----- libfic/exercice.go | 4 +++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/libfic/db.go b/libfic/db.go index 407dd5b6..8a66087d 100644 --- a/libfic/db.go +++ b/libfic/db.go @@ -2,10 +2,10 @@ package fic import ( "database/sql" + _ "github.com/go-sql-driver/mysql" "log" "os" "time" - _ "github.com/go-sql-driver/mysql" ) // db stores the connection to the database @@ -44,7 +44,7 @@ func DSNGenerator() string { // DBInit establishes the connection to the database func DBInit(dsn string) (err error) { - if db, err = sql.Open("mysql", dsn + "?parseTime=true&foreign_key_checks=1"); err != nil { + if db, err = sql.Open("mysql", dsn+"?parseTime=true&foreign_key_checks=1"); err != nil { return } @@ -409,16 +409,15 @@ CREATE TABLE IF NOT EXISTS claims( CREATE TABLE IF NOT EXISTS claim_descriptions( id_description INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, id_claim INTEGER NOT NULL, - id_assignee INTEGER NOT NULL, + id_assignee INTEGER, date TIMESTAMP NOT NULL, content TEXT NOT NULL, publish BOOLEAN NOT NULL DEFAULT 0, - FOREIGN KEY(id_assignee) REFERENCES claim_assignees(id_assignee), FOREIGN KEY(id_claim) REFERENCES claims(id_claim) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err -} + } if _, err := db.Exec("CREATE OR REPLACE VIEW exercice_distinct_tries AS SELECT id_exercice, id_team, MAX(time) AS time, cksum, nbdiff FROM exercice_tries GROUP BY id_team, id_exercice, cksum;"); err != nil { return err diff --git a/libfic/exercice.go b/libfic/exercice.go index 26894368..a428ca38 100644 --- a/libfic/exercice.go +++ b/libfic/exercice.go @@ -235,7 +235,9 @@ func (e Exercice) Delete() (int64, error) { func (e Exercice) DeleteCascade() (int64, error) { if _, err := DBExec("UPDATE exercices SET id_depend = NULL WHERE id_depend = ?", e.Id); err != nil { return 0, err - } else if _, err := DBExec("DELETE FROM exercice_files_deps WHERE id_file IN (SELECT id_file FROM exercice_files WHERE id_exercice = ?)", e.Id); err != nil { + } else if _, err := DBExec("DELETE FROM exercice_files_okey_deps WHERE id_file IN (SELECT id_file FROM exercice_files WHERE id_exercice = ?)", e.Id); err != nil { + return 0, err + } else if _, err := DBExec("DELETE FROM exercice_files_omcq_deps WHERE id_file IN (SELECT id_file FROM exercice_files WHERE id_exercice = ?)", e.Id); err != nil { return 0, err } else if _, err := DBExec("DELETE FROM exercice_files WHERE id_exercice = ?", e.Id); err != nil { return 0, err From 5d3ef96f3f90d3c0ca5e372bc9da5cdb4fd5a592 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 16 May 2020 03:50:12 +0200 Subject: [PATCH 0217/1637] Animate lighter the clock before start --- frontend/static/css/fic.css | 24 +++++++++++++++++++++++- frontend/static/index.html | 2 +- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/frontend/static/css/fic.css b/frontend/static/css/fic.css index 1231345f..fc12b00d 100644 --- a/frontend/static/css/fic.css +++ b/frontend/static/css/fic.css @@ -90,13 +90,20 @@ body { .navbar .clock { font-size: 70px; } -.clock:not(.expired) .point, .clock.expired { +.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; } @@ -121,6 +128,21 @@ keyframes clockanim { 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; diff --git a/frontend/static/index.html b/frontend/static/index.html index bd55882a..ae3ead34 100644 --- a/frontend/static/index.html +++ b/frontend/static/index.html @@ -24,7 +24,7 @@

Chargement...
-
+
{{ time.hours | time }} : From 64b9e9a251870e8a392ef6a712e080059fb6a44c Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 16 May 2020 03:51:36 +0200 Subject: [PATCH 0218/1637] New option to disallow team creation: join only --- admin/main.go | 1 + admin/static/views/settings.html | 7 ++++ backend/main.go | 1 + backend/registration.go | 3 ++ frontend/static/views/register.html | 15 ++++++-- settings/settings.go | 56 +++++++++++++++-------------- 6 files changed, 53 insertions(+), 30 deletions(-) diff --git a/admin/main.go b/admin/main.go index 4e34af15..31d18408 100644 --- a/admin/main.go +++ b/admin/main.go @@ -178,6 +178,7 @@ func main() { WChoiceCurCoefficient: 1, AllowRegistration: false, CanJoinTeam: false, + DenyTeamCreation: false, DenyNameChange: false, AcceptNewIssue: true, EnableResolutionRoute: false, diff --git a/admin/static/views/settings.html b/admin/static/views/settings.html index 352ec296..44883487 100644 --- a/admin/static/views/settings.html +++ b/admin/static/views/settings.html @@ -129,6 +129,13 @@
+
+ +
+
-
+

- Félicitations ! vous êtes maintenant authentifié auprès de + Félicitations ! vous êtes maintenant authentifié·e auprès de notre serveur !

@@ -76,9 +76,18 @@

-

+

+ Félicitations ! vous êtes maintenant authentifié·e auprès de + notre serveur ! +

+

Si votre équipe est déjà créée, rejoignez-là !

+

+ 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 : +

diff --git a/settings/settings.go b/settings/settings.go index ade9d998..b0b30afb 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -8,8 +8,8 @@ import ( "os" "os/signal" "path" - "time" "syscall" + "time" "gopkg.in/fsnotify.v1" ) @@ -23,52 +23,54 @@ var SettingsDir string = "./SETTINGS" // FICSettings represents the settings panel. type FICSettings struct { // Title is the displayed name of the challenge. - Title string `json:"title"` + Title string `json:"title"` // Authors is the group name of people making the challenge. - Authors string `json:"authors"` + Authors string `json:"authors"` // VideoLink is the link to explaination videos when the challenge is over. - VideosLink string `json:"videoslink"` + VideosLink string `json:"videoslink"` // Start is the departure time (expected or effective). - Start time.Time `json:"start"` + Start time.Time `json:"start"` // End is the expected end time. - End time.Time `json:"end"` + End time.Time `json:"end"` // Generation is a value used to regenerate static files. - Generation time.Time `json:"generation"` + Generation time.Time `json:"generation"` // ActivateTime is the time when the current file should be proceed. - ActivateTime time.Time `json:"activateTime"` + ActivateTime time.Time `json:"activateTime"` // FirstBlood is the coefficient applied to each first team who solve a challenge. - FirstBlood float64 `json:"firstBlood"` + FirstBlood float64 `json:"firstBlood"` // SubmissionCostBase is a complex number representing the cost of each attempts. - SubmissionCostBase float64 `json:"submissionCostBase"` + SubmissionCostBase float64 `json:"submissionCostBase"` // ExerciceCurrentCoefficient is the current coefficient applied globaly to exercices. - ExerciceCurCoefficient float64 `json:"exerciceCurrentCoefficient"` + ExerciceCurCoefficient float64 `json:"exerciceCurrentCoefficient"` // HintCurrentCoefficient is the current coefficient applied to hint discovery. - HintCurCoefficient float64 `json:"hintCurrentCoefficient"` + HintCurCoefficient float64 `json:"hintCurrentCoefficient"` // WChoiceCurCoefficient is the current coefficient applied to wanted choices. - WChoiceCurCoefficient float64 `json:"wchoiceCurrentCoefficient"` + WChoiceCurCoefficient float64 `json:"wchoiceCurrentCoefficient"` // AllowRegistration permits unregistered Team to register themselves. - AllowRegistration bool `json:"allowRegistration"` + AllowRegistration bool `json:"allowRegistration"` // CanJoinTeam permits unregistered account to join an already existing team. - CanJoinTeam bool `json:"canJoinTeam"` + CanJoinTeam bool `json:"canJoinTeam"` + // DenyTeamCreation forces unregistered account to join a team, it's not possible to create a new team. + DenyTeamCreation bool `json:"denyTeamCreation"` // DenyNameChange disallow Team to change their name. - DenyNameChange bool `json:"denyNameChange"` + DenyNameChange bool `json:"denyNameChange"` // AcceptNewIssue enables the reporting system. - AcceptNewIssue bool `json:"acceptNewIssue"` + AcceptNewIssue bool `json:"acceptNewIssue"` // EnableResolutionRoute activates the route displaying resolution movies. - EnableResolutionRoute bool `json:"enableResolutionRoute"` + EnableResolutionRoute bool `json:"enableResolutionRoute"` // PartialValidation validates each correct given answers, don't expect Team to give all correct answer in a try. - PartialValidation bool `json:"partialValidation"` + PartialValidation bool `json:"partialValidation"` // UnlockedChallengeDepth don't show (or permit to solve) to team challenges they are not unlocked through dependancies. - UnlockedChallengeDepth int `json:"unlockedChallengeDepth"` + UnlockedChallengeDepth int `json:"unlockedChallengeDepth"` // SubmissionUniqueness don't count multiple times identical tries. - SubmissionUniqueness bool `json:"submissionUniqueness"` + SubmissionUniqueness bool `json:"submissionUniqueness"` // DisplayAllFlags doesn't respect the predefined constraint existing between flags. - DisplayAllFlags bool `json:"displayAllFlags"` + DisplayAllFlags bool `json:"displayAllFlags"` // EventKindness will ask browsers to delay notification interval. - EventKindness bool `json:"eventKindness"` + EventKindness bool `json:"eventKindness"` } // ExistsSettings checks if the settings file can by found at the given path. @@ -125,7 +127,7 @@ func ForceRegeneration() error { // Giving the location and a callback, this function will first call your reload function // before returning (if the file can be parsed); then it starts watching modifications made to // this file. Your callback is then run each time the file is modified. -func LoadAndWatchSettings(settingsPath string, reload func (FICSettings)) { +func LoadAndWatchSettings(settingsPath string, reload func(FICSettings)) { // First load of configuration if it exists if _, err := os.Stat(settingsPath); !os.IsNotExist(err) { if config, err := ReadSettings(settingsPath); err != nil { @@ -138,7 +140,7 @@ func LoadAndWatchSettings(settingsPath string, reload func (FICSettings)) { // Register SIGHUP c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGHUP) - go func(){ + go func() { for range c { log.Println("SIGHUP received, reloading settings...") go tryReload(settingsPath, reload) @@ -158,7 +160,7 @@ func LoadAndWatchSettings(settingsPath string, reload func (FICSettings)) { for { select { case ev := <-watcher.Events: - if path.Base(ev.Name) == SettingsFile && ev.Op & (fsnotify.Write | fsnotify.Create) != 0 { + if path.Base(ev.Name) == SettingsFile && ev.Op&(fsnotify.Write|fsnotify.Create) != 0 { log.Println("Settings file changes, reloading it!") go tryReload(settingsPath, reload) } @@ -170,7 +172,7 @@ func LoadAndWatchSettings(settingsPath string, reload func (FICSettings)) { } } -func tryReload(settingsPath string, reload func (FICSettings)) { +func tryReload(settingsPath string, reload func(FICSettings)) { if config, err := ReadSettings(settingsPath); err != nil { log.Println("ERROR: Unable to read challenge settings:", err) } else if time.Until(config.ActivateTime) > 0 { From 5b84e4bfdbd79f45bfa1097f3bc9b375ae6a77dd Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 16 May 2020 03:52:11 +0200 Subject: [PATCH 0219/1637] Fix exercices' theme loading in admin --- admin/static/js/app.js | 2 +- admin/static/views/theme.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index b4a4c4d7..fb60bfc9 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -273,7 +273,7 @@ angular.module("FICApp") }) }) .factory("ThemedExercice", function($resource) { - return $resource("/api/themes/:themeId/exercices/:exerciceId", { exerciceId: '@id' }, { + return $resource("/api/themes/:themeId/exercices/:exerciceId", { themeId: '@id', exerciceId: '@idExercice' }, { update: {method: 'PUT'} }) }) diff --git a/admin/static/views/theme.html b/admin/static/views/theme.html index 86a00357..04ad8f74 100644 --- a/admin/static/views/theme.html +++ b/admin/static/views/theme.html @@ -16,7 +16,7 @@
-
+

Exercices ({{ exercices.length }}) From 130bb92dc88b31192a94451befef9f41c4d3f29f Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 16 May 2020 03:53:07 +0200 Subject: [PATCH 0220/1637] admin: Fix some toast unreadable --- admin/static/js/app.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index fb60bfc9..e5ce31b4 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -460,9 +460,9 @@ angular.module("FICApp") config.generation = (new Date()).toISOString(); config.$update(function() { $rootScope.staticFilesNeedUpdate = 0; - $scope.addToast('success', "Regeneration in progress..."); + $rootScope.addToast('success', "Regeneration in progress..."); }, function (response) { - $scope.addToast('success', 'An error occurs when saving settings:', response.data.errmsg); + $rootScope.addToast('success', 'An error occurs when saving settings:', response.data.errmsg); }) }) } @@ -1393,7 +1393,7 @@ angular.module("FICApp") $scope.themes = Theme.query(); $rootScope.staticFilesNeedUpdate++; if (response.data) - $scope.addToast('danger', 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) { @@ -1536,7 +1536,7 @@ angular.module("FICApp") $scope.exercices = ThemedExercice.query({ themeId: $scope.theme.id }); $rootScope.staticFilesNeedUpdate++; if (response.data) - $scope.addToast('warning', null, response.data, -1); + $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) { @@ -1580,7 +1580,7 @@ angular.module("FICApp") $scope.exercice = Exercice.get({ exerciceId: $routeParams.exerciceId }); $rootScope.staticFilesNeedUpdate++; if (response.data) - $scope.addToast('danger', 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) { @@ -1728,7 +1728,7 @@ angular.module("FICApp") $rootScope.staticFilesNeedUpdate++; $scope.files = ExerciceFile.query({ exerciceId: $routeParams.exerciceId }); if (response.data) - $scope.addToast('danger', 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) { @@ -1771,7 +1771,7 @@ angular.module("FICApp") $scope.hints = ExerciceHint.query({ exerciceId: $routeParams.exerciceId }); $rootScope.staticFilesNeedUpdate++; if (response.data) - $scope.addToast('danger', 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) { @@ -1852,7 +1852,7 @@ angular.module("FICApp") $scope.flags = ExerciceFlag.query({ exerciceId: $routeParams.exerciceId }); $rootScope.staticFilesNeedUpdate++; if (response.data) - $scope.addToast('danger', 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) { From 90151ce498ded54150516d3b965d0c45c626e35e Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 16 May 2020 03:53:32 +0200 Subject: [PATCH 0221/1637] frontend: fix error on registration validated --- frontend/static/js/challenge.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/static/js/challenge.js b/frontend/static/js/challenge.js index 5bc47192..3c7d1143 100644 --- a/frontend/static/js/challenge.js +++ b/frontend/static/js/challenge.js @@ -155,7 +155,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) $rootScope.issues_nb_responses += issue.texts.length; if (issue.state == 'need-info') $rootScope.issues_need_info++; }) - }); + }, function(error) {}); } var refreshThemesInterval @@ -812,7 +812,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) $scope.$watch("my", function(my){ if (my) - $location.url("."); + $location.url("/"); }); }) .controller("TagController", function($scope, $rootScope, $routeParams, $location) { From 2d6d09852b2eabb88ac390b517ec9d283a2cf9b6 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 16 May 2020 03:54:18 +0200 Subject: [PATCH 0222/1637] dashboard: make title more explicit and avoid leaving the page --- dashboard/static/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dashboard/static/index.html b/dashboard/static/index.html index 0057ba1d..7053d94b 100644 --- a/dashboard/static/index.html +++ b/dashboard/static/index.html @@ -2,7 +2,7 @@ - Challenge Forensic + Tableau de bord du challenge forensic @@ -47,7 +47,7 @@

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

From c9932cdaf65072fc821db4bcfb4fd6f8c2a8eed1 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 6 Sep 2020 12:14:19 +0200 Subject: [PATCH 0223/1637] admin: change exercice's Delete button to cascade deletion --- admin/api/exercice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/api/exercice.go b/admin/api/exercice.go index a4dcfa33..f5496aa1 100644 --- a/admin/api/exercice.go +++ b/admin/api/exercice.go @@ -235,7 +235,7 @@ func delExerciceHistory(exercice fic.Exercice, body []byte) (interface{}, error) } func deleteExercice(exercice fic.Exercice, _ []byte) (interface{}, error) { - return exercice.Delete() + return exercice.DeleteCascade() } func updateExercice(exercice fic.Exercice, body []byte) (interface{}, error) { From 7ad36f614165808e613898541594509b00225386 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 6 Sep 2020 12:26:19 +0200 Subject: [PATCH 0224/1637] libfic: fix exercice delete cascade: bad db column name and dependancy --- libfic/exercice.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libfic/exercice.go b/libfic/exercice.go index a428ca38..0a4b91fa 100644 --- a/libfic/exercice.go +++ b/libfic/exercice.go @@ -233,7 +233,7 @@ func (e Exercice) Delete() (int64, error) { // DeleteCascade the challenge from the database, including inner content but not player content. func (e Exercice) DeleteCascade() (int64, error) { - if _, err := DBExec("UPDATE exercices SET id_depend = NULL WHERE id_depend = ?", e.Id); err != nil { + if _, err := DBExec("UPDATE exercices SET depend = NULL WHERE depend = ?", e.Id); err != nil { return 0, err } else if _, err := DBExec("DELETE FROM exercice_files_okey_deps WHERE id_file IN (SELECT id_file FROM exercice_files WHERE id_exercice = ?)", e.Id); err != nil { return 0, err @@ -241,11 +241,13 @@ func (e Exercice) DeleteCascade() (int64, error) { return 0, err } else if _, err := DBExec("DELETE FROM exercice_files WHERE id_exercice = ?", e.Id); err != nil { return 0, err + } else if _, err := DBExec("DELETE FROM exercice_hints_okey_deps WHERE id_hint IN (SELECT id_hint FROM exercice_hints WHERE id_exercice = ?)", e.Id); err != nil { + return 0, err } else if _, err := DBExec("DELETE FROM exercice_hints WHERE id_exercice = ?", e.Id); err != nil { return 0, err } else if _, err := DBExec("DELETE FROM exercice_flags_deps WHERE id_flag IN (SELECT id_flag FROM exercice_flags WHERE id_exercice = ?)", e.Id); err != nil { return 0, err - } else if _, err := DBExec("DELETE FROM exercice_flags_choices WHERE id_flag IN (SELECT id_flag FROM exercice_flags WHERE id_exercice = ?)", e.Id); err != nil { + } else if _, err := DBExec("DELETE FROM flag_choices WHERE id_flag IN (SELECT id_flag FROM exercice_flags WHERE id_exercice = ?)", e.Id); err != nil { return 0, err } else if _, err := DBExec("DELETE FROM exercice_flags WHERE id_exercice = ?", e.Id); err != nil { return 0, err From eca814ca4be2839b8b607f23bd856242dde79e61 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 6 Sep 2020 14:56:36 +0200 Subject: [PATCH 0225/1637] Add drone CI --- .drone.yml | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 .drone.yml diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 00000000..40fce85a --- /dev/null +++ b/.drone.yml @@ -0,0 +1,98 @@ +--- +kind: pipeline +type: docker +name: default + +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 -v -d srs.epita.fr/fic-server/admin + - go get -v -d srs.epita.fr/fic-server/backend + - go get -v -d srs.epita.fr/fic-server/frontend + - go get -v -d srs.epita.fr/fic-server/dashboard + + - name: build admin + image: golang:alpine + commands: + - go build -v -o admin/admin srs.epita.fr/fic-server/admin + + - name: docker admin + image: plugins/docker + settings: + username: + from_secret: docker_username + password: + from_secret: docker_password + repo: nemunaire/fic-admin + tags: latest + dockerfile: Dockerfile-admin + when: + branch: + - master + + - name: build backend + image: golang:alpine + commands: + - go build -v -o backend/backend srs.epita.fr/fic-server/backend + + - name: docker backend + image: plugins/docker + settings: + username: + from_secret: docker_username + password: + from_secret: docker_password + repo: nemunaire/fic-backend + tags: latest + dockerfile: Dockerfile-backend + when: + branch: + - master + + - name: build frontend + image: golang:alpine + commands: + - go build -v -o frontend/frontend srs.epita.fr/fic-server/frontend + + - name: docker frontend + image: plugins/docker + settings: + username: + from_secret: docker_username + password: + from_secret: docker_password + repo: nemunaire/fic-frontend + tags: latest + dockerfile: Dockerfile-frontend + when: + branch: + - master + + - name: build dashboard + image: golang:alpine + commands: + - go build -v -o dashboard/dashboard srs.epita.fr/fic-server/dashboard + + - name: docker dashboard + image: plugins/docker + settings: + username: + from_secret: docker_username + password: + from_secret: docker_password + repo: nemunaire/fic-dashboard + tags: latest + dockerfile: Dockerfile-dashboard + when: + branch: + - master From a0155c6debb3f4f726fbeba58e197b7cff25340c Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 7 Sep 2020 19:33:09 +0200 Subject: [PATCH 0226/1637] Replace old Help term by Placeholder --- admin/api/exercice.go | 6 +++--- admin/static/views/exercice-flags.html | 4 ++-- admin/static/views/exercice.html | 2 +- admin/sync/README.md | 8 ++++---- admin/sync/exercice_defines.go | 4 ++-- admin/sync/exercice_keys.go | 2 +- frontend/static/js/challenge.js | 2 +- frontend/static/js/common.js | 6 +++--- libfic/flag_key.go | 18 +++++++++--------- libfic/team_my.go | 4 ++-- 10 files changed, 28 insertions(+), 28 deletions(-) diff --git a/admin/api/exercice.go b/admin/api/exercice.go index f5496aa1..ef597be2 100644 --- a/admin/api/exercice.go +++ b/admin/api/exercice.go @@ -400,7 +400,7 @@ func deleteExerciceHint(hint fic.EHint, _ []byte) (interface{}, error) { type uploadedFlag struct { Label string - Help string + Placeholder string IgnoreCase bool Multiline bool ValidatorRe *string `json:"validator_regexp"` @@ -424,7 +424,7 @@ func createExerciceFlag(exercice fic.Exercice, body []byte) (interface{}, error) vre = uk.ValidatorRe } - return exercice.AddRawFlagKey(uk.Label, uk.Help, uk.IgnoreCase, uk.Multiline, vre, []byte(uk.Flag), uk.ChoicesCost) + return exercice.AddRawFlagKey(uk.Label, uk.Placeholder, uk.IgnoreCase, uk.Multiline, vre, []byte(uk.Flag), uk.ChoicesCost) } func showExerciceFlag(flag fic.FlagKey, _ fic.Exercice, body []byte) (interface{}, error) { @@ -464,7 +464,7 @@ func updateExerciceFlag(flag fic.FlagKey, exercice fic.Exercice, body []byte) (i flag.Label = uk.Label } - flag.Help = uk.Help + flag.Placeholder = uk.Placeholder flag.IgnoreCase = uk.IgnoreCase flag.Multiline = uk.Multiline if len(uk.Flag) > 0 { diff --git a/admin/static/views/exercice-flags.html b/admin/static/views/exercice-flags.html index 3b701808..adcd7f60 100644 --- a/admin/static/views/exercice-flags.html +++ b/admin/static/views/exercice-flags.html @@ -29,8 +29,8 @@
- - + +
diff --git a/admin/static/views/exercice.html b/admin/static/views/exercice.html index 6821814b..3b598d32 100644 --- a/admin/static/views/exercice.html +++ b/admin/static/views/exercice.html @@ -174,7 +174,7 @@
- +
diff --git a/admin/sync/README.md b/admin/sync/README.md index 1a342516..70b2d017 100644 --- a/admin/sync/README.md +++ b/admin/sync/README.md @@ -24,7 +24,7 @@ Tous les textes doivent utiliser l'encodage UTF8. * `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 ; - * `help = "Indication"` : (facultatif) chaîne de caractères placée sous le champ du formulaire, idéale pour donner une indication de format ; + * `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 : @@ -34,13 +34,13 @@ Tous les textes doivent utiliser l'encodage UTF8. * `[[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), - + `help = "Flag correspondant"` : (facultatif) indication affichée dans le champ de texte des QCM justifiés ; + + `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) ; - * `help = "Indication"` : (facultatif, uniquement si `displayAs = select`) chaîne de caractères placée sous le champ du formulaire ; + * `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 : @@ -144,7 +144,7 @@ id = 2 [[flag]] label = "Date d'exfiltration" -help= "Format : yyyy-mm" +placeholder = "Format : yyyy-mm" raw = '2015-12' [[flag]] diff --git a/admin/sync/exercice_defines.go b/admin/sync/exercice_defines.go index fab93625..f1336f16 100644 --- a/admin/sync/exercice_defines.go +++ b/admin/sync/exercice_defines.go @@ -39,7 +39,7 @@ type ExerciceFlag struct { Ordered bool `toml:",omitempty"` CaseSensitive bool `toml:",omitempty"` ValidatorRe string `toml:"validator_regexp,omitempty"` - Help string `toml:",omitempty"` + Placeholder string `toml:",omitempty"` ChoicesCost int64 `toml:"choices_cost,omitempty"` Choice []ExerciceFlagChoice LockedFile []ExerciceUnlockFile `toml:"unlock_file,omitempty"` @@ -93,7 +93,7 @@ func getExerciceParams(i Importer, exercice fic.Exercice) (params ExerciceParams Type: "ucq", Raw: flag.Raw, ValidatorRe: flag.ValidatorRe, - Help: flag.Help, + Placeholder: flag.Placeholder, ChoicesCost: flag.ChoicesCost, Choice: flag.Choice, LockedFile: flag.LockedFile, diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index ece36066..43be18de 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -131,7 +131,7 @@ func buildKeyFlag(exercice fic.Exercice, flag ExerciceFlag, flagline int, defaul fl := fic.Flag(fic.FlagKey{ IdExercice: exercice.Id, Label: flag.Label, - Help: flag.Help, + Placeholder: flag.Placeholder, IgnoreCase: !flag.CaseSensitive, Multiline: flag.Type == "text", ValidatorRegexp: validatorRegexp(flag.ValidatorRe), diff --git a/frontend/static/js/challenge.js b/frontend/static/js/challenge.js index 3c7d1143..e6c5f497 100644 --- a/frontend/static/js/challenge.js +++ b/frontend/static/js/challenge.js @@ -349,7 +349,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) if (!this[cid].justification.label) { this[cid].justification.label = "Flag correspondant"; - this[cid].justification.help2 = "Trouvez et validez les choix du QCM pour avoir des indications supplémentaires"; + this[cid].justification.help = "Trouvez et validez les choix du QCM pour avoir des indications supplémentaires"; } if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].mcqs[mid] && $scope.my.exercices[eid].mcqs[mid].choices[cid] && $scope.my.exercices[eid].mcqs[mid].choices[cid].justification) { diff --git a/frontend/static/js/common.js b/frontend/static/js/common.js index 6f3a0371..fe6fbe94 100644 --- a/frontend/static/js/common.js +++ b/frontend/static/js/common.js @@ -196,8 +196,8 @@ angular.module("FICApp")
- - + +
- +
` diff --git a/libfic/flag_key.go b/libfic/flag_key.go index e1d71608..be27441b 100644 --- a/libfic/flag_key.go +++ b/libfic/flag_key.go @@ -17,8 +17,8 @@ type FlagKey struct { IdExercice int64 `json:"idExercice"` // Label is the title of the flag as displayed to players Label string `json:"label"` - // Help is a small piece of text that aims to add useful information like flag format, ... - Help string `json:"help"` + // Placeholder is a small piece of text that aims to add useful information like flag format, ... + Placeholder string `json:"placeholder"` // IgnoreCase indicates if the case is sensitive to case or not IgnoreCase bool `json:"ignorecase"` // Multiline indicates if the flag is stored on multiple lines @@ -43,7 +43,7 @@ func (e Exercice) GetFlagKeys() ([]FlagKey, error) { var k FlagKey k.IdExercice = e.Id - if err := rows.Scan(&k.Id, &k.IdExercice, &k.Label, &k.Help, &k.IgnoreCase, &k.Multiline, &k.ValidatorRegexp, &k.Checksum, &k.ChoicesCost); err != nil { + if err := rows.Scan(&k.Id, &k.IdExercice, &k.Label, &k.Placeholder, &k.IgnoreCase, &k.Multiline, &k.ValidatorRegexp, &k.Checksum, &k.ChoicesCost); err != nil { return nil, err } @@ -59,13 +59,13 @@ func (e Exercice) GetFlagKeys() ([]FlagKey, error) { // GetFlagKey returns a list of flags comming with the challenge. func GetFlagKey(id int64) (k FlagKey, err error) { - err = DBQueryRow("SELECT id_flag, id_exercice, type, help, ignorecase, multiline, validator_regexp, cksum, choices_cost FROM exercice_flags WHERE id_flag = ?", id).Scan(&k.Id, &k.IdExercice, &k.Label, &k.Help, &k.IgnoreCase, &k.Multiline, &k.ValidatorRegexp, &k.Checksum, &k.ChoicesCost) + err = DBQueryRow("SELECT id_flag, id_exercice, type, help, ignorecase, multiline, validator_regexp, cksum, choices_cost FROM exercice_flags WHERE id_flag = ?", id).Scan(&k.Id, &k.IdExercice, &k.Label, &k.Placeholder, &k.IgnoreCase, &k.Multiline, &k.ValidatorRegexp, &k.Checksum, &k.ChoicesCost) return } // GetFlagKeyByLabel returns a flag matching the given label. func (e Exercice) GetFlagKeyByLabel(label string) (k FlagKey, err error) { - err = DBQueryRow("SELECT id_flag, id_exercice, type, help, ignorecase, multiline, validator_regexp, cksum, choices_cost FROM exercice_flags WHERE type LIKE ? AND id_exercice = ?", label, e.Id).Scan(&k.Id, &k.IdExercice, &k.Label, &k.Help, &k.IgnoreCase, &k.Multiline, &k.ValidatorRegexp, &k.Checksum, &k.ChoicesCost) + err = DBQueryRow("SELECT id_flag, id_exercice, type, help, ignorecase, multiline, validator_regexp, cksum, choices_cost FROM exercice_flags WHERE type LIKE ? AND id_exercice = ?", label, e.Id).Scan(&k.Id, &k.IdExercice, &k.Label, &k.Placeholder, &k.IgnoreCase, &k.Multiline, &k.ValidatorRegexp, &k.Checksum, &k.ChoicesCost) return } @@ -105,7 +105,7 @@ func ExecValidatorRegexp(vre string, val []byte, ignorecase bool) ([]byte, error } // AddRawFlagKey creates and fills a new struct FlagKey, from a non-hashed flag, and registers it into the database. -func (e Exercice) AddRawFlagKey(name string, help string, ignorecase bool, multiline bool, validator_regexp *string, raw_value []byte, choicescost int64) (f FlagKey, err error) { +func (e Exercice) AddRawFlagKey(name string, placeholder string, ignorecase bool, multiline bool, validator_regexp *string, raw_value []byte, choicescost int64) (f FlagKey, err error) { hash, errr := ComputeHashedFlag(raw_value, ignorecase, validator_regexp) if errr != nil { return f, err @@ -113,7 +113,7 @@ func (e Exercice) AddRawFlagKey(name string, help string, ignorecase bool, multi f = FlagKey{ Label: name, - Help: help, + Placeholder: placeholder, IgnoreCase: ignorecase, Multiline: multiline, ValidatorRegexp: validator_regexp, @@ -148,7 +148,7 @@ func (k FlagKey) Create(e Exercice) (Flag, error) { } } - if res, err := DBExec("INSERT INTO exercice_flags (id_exercice, type, help, ignorecase, multiline, validator_regexp, cksum, choices_cost) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", e.Id, k.Label, k.Help, k.IgnoreCase, k.Multiline, k.ValidatorRegexp, k.Checksum, k.ChoicesCost); err != nil { + if res, err := DBExec("INSERT INTO exercice_flags (id_exercice, type, help, ignorecase, multiline, validator_regexp, cksum, choices_cost) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", e.Id, k.Label, k.Placeholder, k.IgnoreCase, k.Multiline, k.ValidatorRegexp, k.Checksum, k.ChoicesCost); err != nil { return k, err } else if kid, err := res.LastInsertId(); err != nil { return k, err @@ -173,7 +173,7 @@ func (k FlagKey) Update() (int64, error) { } } - if res, err := DBExec("UPDATE exercice_flags SET id_exercice = ?, type = ?, help = ?, ignorecase = ?, multiline = ?, validator_regexp = ?, cksum = ?, choices_cost = ? WHERE id_flag = ?", k.IdExercice, k.Label, k.Help, k.IgnoreCase, k.Multiline, k.ValidatorRegexp, k.Checksum, k.ChoicesCost, k.Id); err != nil { + if res, err := DBExec("UPDATE exercice_flags SET id_exercice = ?, type = ?, help = ?, ignorecase = ?, multiline = ?, validator_regexp = ?, cksum = ?, choices_cost = ? WHERE id_flag = ?", k.IdExercice, k.Label, k.Placeholder, k.IgnoreCase, k.Multiline, k.ValidatorRegexp, k.Checksum, k.ChoicesCost, k.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err diff --git a/libfic/team_my.go b/libfic/team_my.go index b267f5e0..e6582b50 100644 --- a/libfic/team_my.go +++ b/libfic/team_my.go @@ -28,7 +28,7 @@ type myTeamHint struct { } type myTeamFlag struct { Label string `json:"label"` - Help string `json:"help,omitempty"` + Placeholder string `json:"placeholder,omitempty"` Separator string `json:"separator,omitempty"` NbLines uint64 `json:"nb_lines,omitempty"` IgnoreOrder bool `json:"ignore_order,omitempty"` @@ -221,7 +221,7 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { // Fill more information if the flag is displaied if flag.Solved == nil { - flag.Help = k.Help + flag.Placeholder = k.Placeholder if choices, err := k.GetChoices(); err != nil { return nil, err } else if t == nil || WChoiceCoefficient < 0 || k.ChoicesCost == 0 || t.SeeChoices(k) { From a237936febec7e3a250c30787c9da158d8fa942d Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 8 Sep 2020 12:50:41 +0200 Subject: [PATCH 0227/1637] qa: New service to handle QA testing by students --- htdocs-qa | 1 + libfic/db.go | 31 ++ libfic/qa.go | 183 ++++++++++++ qa/.gitignore | 1 + qa/api/exercice.go | 36 +++ qa/api/handler.go | 111 +++++++ qa/api/qa.go | 140 +++++++++ qa/api/router.go | 11 + qa/api/theme.go | 64 +++++ qa/api/version.go | 13 + qa/main.go | 120 ++++++++ qa/static.go | 66 +++++ qa/static/css/bootstrap.min.css | 1 + qa/static/css/fic.css | 1 + qa/static/css/glyphicon.css | 1 + qa/static/favicon.ico | 1 + qa/static/fonts | 1 + qa/static/img/comcyber.png | 1 + qa/static/img/epita.png | 1 + qa/static/img/fic.png | 1 + qa/static/img/srs.png | 1 + qa/static/index.html | 70 +++++ qa/static/js/angular-resource.min.js | 15 + qa/static/js/angular-route.min.js | 1 + qa/static/js/angular-sanitize.min.js | 1 + qa/static/js/angular.min.js | 1 + qa/static/js/bootstrap.min.js | 1 + qa/static/js/common.js | 1 + qa/static/js/d3.v3.min.js | 1 + qa/static/js/i18n | 1 + qa/static/js/jquery.min.js | 1 + qa/static/js/qa.js | 414 +++++++++++++++++++++++++++ qa/static/views/exercice-list.html | 27 ++ qa/static/views/exercice.html | 105 +++++++ qa/static/views/home.html | 7 + qa/static/views/theme-list.html | 19 ++ qa/static/views/theme.html | 25 ++ 37 files changed, 1476 insertions(+) create mode 120000 htdocs-qa create mode 100644 libfic/qa.go create mode 100644 qa/.gitignore create mode 100644 qa/api/exercice.go create mode 100644 qa/api/handler.go create mode 100644 qa/api/qa.go create mode 100644 qa/api/router.go create mode 100644 qa/api/theme.go create mode 100644 qa/api/version.go create mode 100644 qa/main.go create mode 100644 qa/static.go create mode 120000 qa/static/css/bootstrap.min.css create mode 120000 qa/static/css/fic.css create mode 120000 qa/static/css/glyphicon.css create mode 120000 qa/static/favicon.ico create mode 120000 qa/static/fonts create mode 120000 qa/static/img/comcyber.png create mode 120000 qa/static/img/epita.png create mode 120000 qa/static/img/fic.png create mode 120000 qa/static/img/srs.png create mode 100644 qa/static/index.html create mode 100644 qa/static/js/angular-resource.min.js create mode 120000 qa/static/js/angular-route.min.js create mode 120000 qa/static/js/angular-sanitize.min.js create mode 120000 qa/static/js/angular.min.js create mode 120000 qa/static/js/bootstrap.min.js create mode 120000 qa/static/js/common.js create mode 120000 qa/static/js/d3.v3.min.js create mode 120000 qa/static/js/i18n create mode 120000 qa/static/js/jquery.min.js create mode 100644 qa/static/js/qa.js create mode 100644 qa/static/views/exercice-list.html create mode 100644 qa/static/views/exercice.html create mode 100644 qa/static/views/home.html create mode 100644 qa/static/views/theme-list.html create mode 100644 qa/static/views/theme.html diff --git a/htdocs-qa b/htdocs-qa new file mode 120000 index 00000000..2c4757cb --- /dev/null +++ b/htdocs-qa @@ -0,0 +1 @@ +qa/static \ No newline at end of file diff --git a/libfic/db.go b/libfic/db.go index 8a66087d..46bd2c67 100644 --- a/libfic/db.go +++ b/libfic/db.go @@ -415,6 +415,37 @@ CREATE TABLE IF NOT EXISTS claim_descriptions( publish BOOLEAN NOT NULL DEFAULT 0, FOREIGN KEY(id_claim) REFERENCES claims(id_claim) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; +`); err != nil { + return err + } + if _, err := db.Exec(` +CREATE TABLE IF NOT EXISTS exercices_qa( + id_qa INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, + id_exercice INTEGER NOT NULL, + id_team INTEGER NOT NULL, + authuser VARCHAR(255) NOT NULL, + subject VARCHAR(255) NOT NULL, + creation TIMESTAMP NOT NULL, + state VARCHAR(255) NOT NULL, + solved TIMESTAMP NULL, + closed TIMESTAMP NULL, + FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice), + FOREIGN KEY(id_team) REFERENCES teams(id_team) +) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; +`); err != nil { + return err + } + if _, err := db.Exec(` +CREATE TABLE IF NOT EXISTS qa_comments( + id_comment INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, + id_qa INTEGER NOT NULL, + id_team INTEGER NOT NULL, + authuser VARCHAR(255) NOT NULL, + date TIMESTAMP NOT NULL, + content TEXT NOT NULL, + FOREIGN KEY(id_qa) REFERENCES exercices_qa(id_qa), + FOREIGN KEY(id_team) REFERENCES teams(id_team) +) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } diff --git a/libfic/qa.go b/libfic/qa.go new file mode 100644 index 00000000..126e61e4 --- /dev/null +++ b/libfic/qa.go @@ -0,0 +1,183 @@ +package fic + +import ( + "database/sql" + "time" +) + +// QAQuery represents a QA query. +type QAQuery struct { + Id int64 `json:"id"` + IdExercice int64 `json:"id_exercice"` + IdTeam int64 `json:"id_team"` + User string `json:"user"` + Creation time.Time `json:"creation"` + State string `json:"state"` + Subject string `json:"subject"` + Solved *time.Time `json:"solved,omitempty"` + Closed *time.Time `json:"closed,omitempty"` +} + +// GetQAQuery retrieves the query with the given identifier. +func GetQAQuery(id int64) (q QAQuery, err error) { + err = DBQueryRow("SELECT id_qa, id_exercice, id_team, authuser, creation, state, subject, solved, closed FROM exercices_qa WHERE id_qa = ?", id).Scan(&q.Id, &q.IdExercice, &q.IdTeam, &q.User, &q.Creation, &q.State, &q.Subject, &q.Solved, &q.Closed) + return +} + +// GetQAQueries returns a list of all QAQuery registered in the database. +func GetQAQueries() (res []QAQuery, err error) { + var rows *sql.Rows + if rows, err = DBQuery("SELECT id_qa, id_exercice, id_team, authuser, creation, state, subject, solved, closed FROM exercices_qa"); err != nil { + return + } + defer rows.Close() + + for rows.Next() { + var q QAQuery + if err = rows.Scan(&q.Id, &q.IdExercice, &q.IdTeam, &q.User, &q.Creation, &q.State, &q.Subject, &q.Solved, &q.Closed); err != nil { + return + } + res = append(res, q) + } + err = rows.Err() + + return +} + +// GetQAQueries returns a list of all QAQuery registered for the Exercice. +func (e Exercice) GetQAQueries() (res []QAQuery, err error) { + var rows *sql.Rows + if rows, err = DBQuery("SELECT id_qa, id_exercice, id_team, authuser, creation, state, subject, solved, closed FROM exercices_qa WHERE id_exercice = ?", e.Id); err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + var q QAQuery + if err = rows.Scan(&q.Id, &q.IdExercice, &q.IdTeam, &q.User, &q.Creation, &q.State, &q.Subject, &q.Solved, &q.Closed); err != nil { + return + } + res = append(res, q) + } + err = rows.Err() + + return +} + +// GetQAQuery retrieves the query with the given identifier. +func (e Exercice) GetQAQuery(id int64) (q QAQuery, err error) { + err = DBQueryRow("SELECT id_qa, id_exercice, id_team, authuser, creation, state, subject, solved, closed FROM exercices_qa WHERE id_qa = ? AND id_exercice = ?", id, e.Id).Scan(&q.Id, &q.IdExercice, &q.IdTeam, &q.User, &q.Creation, &q.State, &q.Subject, &q.Solved, &q.Closed) + return +} + +// NewQAQuery creates and fills a new struct QAQuery and registers it into the database. +func (e Exercice) NewQAQuery(subject string, teamId int64, user string, state string) (QAQuery, error) { + if res, err := DBExec("INSERT INTO exercices_qa (id_exercice, id_team, authuser, creation, state, subject) VALUES (?, ?, ?, ?, ?, ?)", e.Id, teamId, user, time.Now(), state, subject); err != nil { + return QAQuery{}, err + } else if qid, err := res.LastInsertId(); err != nil { + return QAQuery{}, err + } else { + return QAQuery{qid, e.Id, teamId, user, time.Now(), state, subject, nil, nil}, nil + } +} + +// Update applies modifications back to the database. +func (q QAQuery) Update() (int64, error) { + if res, err := DBExec("UPDATE exercices_qa SET subject = ?, id_team = ?, authuser = ?, id_exercice = ?, creation = ?, state = ?, solved = ?, closed = ? WHERE id_qa = ?", q.Subject, q.IdTeam, q.User, q.IdExercice, q.Creation, q.State, q.Solved, q.Closed, q.Id); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } +} + +// Delete the query from the database. +func (q QAQuery) Delete() (int64, error) { + if res, err := DBExec("DELETE FROM exercices_qa WHERE id_qa = ?", q.Id); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } +} + +// ClearQAQueries removes all queries from database. +func ClearQAQueries() (int64, error) { + if res, err := DBExec("DELETE FROM exercices_qa"); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } +} + +// QAComment represents some text describing a QAQuery. +type QAComment struct { + Id int64 `json:"id"` + IdTeam int64 `json:"id_team"` + User string `json:"user"` + Date time.Time `json:"date"` + Content string `json:"content"` +} + +// GetComments returns a list of all descriptions stored in the database for the QAQuery. +func (q QAQuery) GetComments() (res []QAComment, err error) { + var rows *sql.Rows + if rows, err = DBQuery("SELECT id_comment, id_team, authuser, date, content FROM qa_comments WHERE id_qa = ?", q.Id); err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + var c QAComment + if err = rows.Scan(&c.Id, &c.IdTeam, &c.User, &c.Date, &c.Content); err != nil { + return + } + res = append(res, c) + } + err = rows.Err() + + return +} + +// GetComment returns the comment stored in the database for the QAQuery. +func (q QAQuery) GetComment(id int64) (c QAComment, err error) { + err = DBQueryRow("SELECT id_comment, id_team, authuser, date, content FROM qa_comments WHERE id_comment = ? AND id_qa = ?", id, q.Id).Scan(&c.Id, &c.IdTeam, &c.User, &c.Date, &c.Content) + return +} + +// AddComment append in the database a new description; then returns the corresponding structure. +func (q QAQuery) AddComment(content string, teamId int64, user string) (QAComment, error) { + if res, err := DBExec("INSERT INTO qa_comments (id_qa, id_team, authuser, date, content) VALUES (?, ?, ?, ?, ?)", q.Id, teamId, user, time.Now(), content); err != nil { + return QAComment{}, err + } else if cid, err := res.LastInsertId(); err != nil { + return QAComment{}, err + } else { + return QAComment{cid, teamId, user, time.Now(), content}, nil + } +} + +// Update applies modifications back to the database +func (c QAComment) Update() (int64, error) { + if res, err := DBExec("UPDATE qa_comments SET id_team = ?, authuser = ?, date = ?, content = ? WHERE id_comment = ?", c.IdTeam, c.User, c.Date, c.Content, c.Id); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } +} + +// Delete the comment in the database. +func (c QAComment) Delete() (int64, error) { + if res, err := DBExec("DELETE FROM qa_comments WHERE id_comment = ?", c.Id); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } +} diff --git a/qa/.gitignore b/qa/.gitignore new file mode 100644 index 00000000..a483eac5 --- /dev/null +++ b/qa/.gitignore @@ -0,0 +1 @@ +qa \ No newline at end of file diff --git a/qa/api/exercice.go b/qa/api/exercice.go new file mode 100644 index 00000000..523f4d6d --- /dev/null +++ b/qa/api/exercice.go @@ -0,0 +1,36 @@ +package api + +import ( + "strconv" + + "srs.epita.fr/fic-server/libfic" + + "github.com/julienschmidt/httprouter" +) + +func init() { + router.GET("/api/exercices/", apiHandler(listExercices)) + + router.GET("/api/exercices/:eid", apiHandler(exerciceHandler(showExercice))) +} + +func exerciceHandler(f func(QAUser, fic.Exercice, []byte) (interface{}, error)) func(QAUser, httprouter.Params, []byte) (interface{}, error) { + return func(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) { + if eid, err := strconv.ParseInt(string(ps.ByName("eid")), 10, 64); err != nil { + return nil, err + } else if exercice, err := fic.GetExercice(eid); err != nil { + return nil, err + } else { + return f(u, exercice, body) + } + } +} + +func listExercices(_ QAUser, _ httprouter.Params, body []byte) (interface{}, error) { + // List all exercices + return fic.GetExercices() +} + +func showExercice(_ QAUser, exercice fic.Exercice, body []byte) (interface{}, error) { + return exercice, nil +} diff --git a/qa/api/handler.go b/qa/api/handler.go new file mode 100644 index 00000000..1d76e57f --- /dev/null +++ b/qa/api/handler.go @@ -0,0 +1,111 @@ +package api + +import ( + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "os" + "path" + "strconv" + "time" + + "github.com/julienschmidt/httprouter" +) + +var Simulator string +var TeamsDir string + +type QAUser struct { + User string `json:"name"` + TeamId int64 `json:"id_team"` +} + +type DispatchFunction func(QAUser, httprouter.Params, []byte) (interface{}, error) + +func apiHandler(f DispatchFunction) func(http.ResponseWriter, *http.Request, httprouter.Params) { + return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + ficteam := Simulator + if t := r.Header.Get("X-FIC-Team"); t != "" { + ficteam = t + } + + var teamid int64 + var err error + + if ficteam == "" { + log.Printf("%s 401 \"%s %s\" [%s]\n", r.RemoteAddr, r.Method, r.URL.Path, r.UserAgent()) + w.Header().Set("Content-Type", "application/json") + http.Error(w, fmt.Sprintf("{errmsg:\"Need to authenticate.\"}"), http.StatusUnauthorized) + return + } else if teamid, err = strconv.ParseInt(ficteam, 10, 64); err != nil { + if lnk, err := os.Readlink(path.Join(TeamsDir, ficteam)); err != nil { + log.Printf("[ERR] Unable to readlink %q: %s\n", path.Join(TeamsDir, ficteam), 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 + } + } + + log.Printf("%s \"%s %s\" [%s]\n", r.RemoteAddr, r.Method, r.URL.Path, r.UserAgent()) + + // Read the body + if r.ContentLength < 0 || r.ContentLength > 6553600 { + http.Error(w, fmt.Sprintf("{errmsg:\"Request too large or request size unknown\"}"), http.StatusRequestEntityTooLarge) + return + } + var body []byte + if r.ContentLength > 0 { + tmp := make([]byte, 1024) + for { + n, err := r.Body.Read(tmp) + for j := 0; j < n; j++ { + body = append(body, tmp[j]) + } + if err != nil || n <= 0 { + break + } + } + } + + var ret interface{} + + ret, err = f(QAUser{ficteam, teamid}, ps, body) + + // Format response + resStatus := http.StatusOK + if err != nil { + ret = map[string]string{"errmsg": err.Error()} + resStatus = http.StatusBadRequest + log.Println(r.RemoteAddr, resStatus, err.Error()) + } + + if ret == nil { + ret = map[string]string{"errmsg": "Page not found"} + resStatus = http.StatusNotFound + } + + w.Header().Set("X-FIC-Time", fmt.Sprintf("%f", float64(time.Now().UnixNano()/1000)/1000000)) + + if str, found := ret.(string); found { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(resStatus) + io.WriteString(w, str) + } else if bts, found := ret.([]byte); found { + w.Header().Set("Content-Type", "application/octet-stream") + w.Header().Set("Content-Disposition", "attachment") + w.Header().Set("Content-Transfer-Encoding", "binary") + w.WriteHeader(resStatus) + w.Write(bts) + } else if j, err := json.Marshal(ret); err != nil { + w.Header().Set("Content-Type", "application/json") + http.Error(w, fmt.Sprintf("{\"errmsg\":%q}", err), http.StatusInternalServerError) + } else { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(resStatus) + w.Write(j) + } + } +} diff --git a/qa/api/qa.go b/qa/api/qa.go new file mode 100644 index 00000000..d8814ac4 --- /dev/null +++ b/qa/api/qa.go @@ -0,0 +1,140 @@ +package api + +import ( + "encoding/json" + "errors" + "strconv" + + "srs.epita.fr/fic-server/libfic" + + "github.com/julienschmidt/httprouter" +) + +func init() { + router.GET("/api/qa/:eid", apiHandler(exerciceHandler(getExerciceQA))) + router.POST("/api/qa/:eid", apiHandler(exerciceHandler(createExerciceQA))) + + router.PUT("/api/qa/:eid/:qid", apiHandler(qaHandler(updateExerciceQA))) + router.DELETE("/api/qa/:eid/:qid", apiHandler(qaHandler(deleteExerciceQA))) + + router.GET("/api/qa/:eid/:qid/comments", apiHandler(qaHandler(getQAComments))) + router.POST("/api/qa/:eid/:qid/comments", apiHandler(qaHandler(createQAComment))) + + router.DELETE("/api/qa/:eid/:qid/comments/:cid", apiHandler(qaCommentHandler(deleteQAComment))) +} + +func qaHandler(f func(QAUser, fic.QAQuery, fic.Exercice, []byte) (interface{}, error)) func(QAUser, httprouter.Params, []byte) (interface{}, error) { + return func(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) { + return exerciceHandler(func(u QAUser, exercice fic.Exercice, _ []byte) (interface{}, error) { + if qid, err := strconv.ParseInt(string(ps.ByName("qid")), 10, 64); err != nil { + return nil, err + } else if query, err := exercice.GetQAQuery(qid); err != nil { + return nil, err + } else { + return f(u, query, exercice, body) + } + })(u, ps, body) + } +} + +func qaCommentHandler(f func(QAUser, fic.QAComment, fic.QAQuery, fic.Exercice, []byte) (interface{}, error)) func(QAUser, httprouter.Params, []byte) (interface{}, error) { + return func(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) { + return qaHandler(func(u QAUser, query fic.QAQuery, exercice fic.Exercice, _ []byte) (interface{}, error) { + if cid, err := strconv.ParseInt(string(ps.ByName("cid")), 10, 64); err != nil { + return nil, err + } else if comment, err := query.GetComment(cid); err != nil { + return nil, err + } else { + return f(u, comment, query, exercice, body) + } + })(u, ps, body) + } +} + +func getExerciceQA(_ QAUser, exercice fic.Exercice, body []byte) (interface{}, error) { + return exercice.GetQAQueries() +} + +func createExerciceQA(u QAUser, exercice fic.Exercice, body []byte) (interface{}, error) { + // Create a new query + var uq fic.QAQuery + if err := json.Unmarshal(body, &uq); err != nil { + return nil, err + } + + if len(uq.State) == 0 { + return nil, errors.New("State not filled") + } + + if len(uq.Subject) == 0 { + return nil, errors.New("Subject not filled") + } + + if qa, err := exercice.NewQAQuery(uq.Subject, u.TeamId, u.User, uq.State); err != nil { + return nil, err + } else { + var uc fic.QAComment + if err := json.Unmarshal(body, &uc); err != nil { + return nil, err + } + + if uc.Content != "" { + _, err = qa.AddComment(uc.Content, u.TeamId, u.User) + } + + return qa, err + } +} + +func updateExerciceQA(u QAUser, query fic.QAQuery, exercice fic.Exercice, body []byte) (interface{}, error) { + var uq fic.QAQuery + if err := json.Unmarshal(body, &uq); err != nil { + return nil, err + } + + uq.Id = query.Id + + if uq.User != query.User && (uq.IdExercice != query.IdExercice || uq.IdTeam != query.IdTeam || uq.User != query.User || uq.Creation != query.Creation || uq.State != query.State || uq.Subject != query.Subject || uq.Closed != query.Closed) { + return nil, errors.New("You can only update your own entry.") + } + + if _, err := uq.Update(); err != nil { + return nil, err + } else { + return uq, err + } +} + +func deleteExerciceQA(u QAUser, query fic.QAQuery, exercice fic.Exercice, body []byte) (interface{}, error) { + if u.User != query.User { + return nil, errors.New("You can only delete your own entry.") + } + + return query.Delete() +} + +func getQAComments(_ QAUser, query fic.QAQuery, exercice fic.Exercice, body []byte) (interface{}, error) { + return query.GetComments() +} + +func createQAComment(u QAUser, query fic.QAQuery, exercice fic.Exercice, body []byte) (interface{}, error) { + // Create a new query + var uc fic.QAComment + if err := json.Unmarshal(body, &uc); err != nil { + return nil, err + } + + if len(uc.Content) == 0 { + return nil, errors.New("Empty comment") + } + + return query.AddComment(uc.Content, u.TeamId, u.User) +} + +func deleteQAComment(u QAUser, comment fic.QAComment, query fic.QAQuery, exercice fic.Exercice, body []byte) (interface{}, error) { + if u.User != comment.User { + return nil, errors.New("You can only delete your own comment.") + } + + return comment.Delete() +} diff --git a/qa/api/router.go b/qa/api/router.go new file mode 100644 index 00000000..a6bd873b --- /dev/null +++ b/qa/api/router.go @@ -0,0 +1,11 @@ +package api + +import ( + "github.com/julienschmidt/httprouter" +) + +var router = httprouter.New() + +func Router() *httprouter.Router { + return router +} diff --git a/qa/api/theme.go b/qa/api/theme.go new file mode 100644 index 00000000..6931b073 --- /dev/null +++ b/qa/api/theme.go @@ -0,0 +1,64 @@ +package api + +import ( + "strconv" + + "srs.epita.fr/fic-server/libfic" + + "github.com/julienschmidt/httprouter" +) + +func init() { + router.GET("/api/themes", apiHandler(listThemes)) + router.GET("/api/themes.json", apiHandler(exportThemes)) + + router.GET("/api/themes/:thid", apiHandler(themeHandler(showTheme))) + + router.GET("/api/themes/:thid/exercices", apiHandler(themeHandler(listThemedExercices))) + + router.GET("/api/themes/:thid/exercices/:eid", apiHandler(exerciceHandler(showExercice))) +} + +func themeHandler(f func(QAUser, fic.Theme, []byte) (interface{}, error)) func(QAUser, httprouter.Params, []byte) (interface{}, error) { + return func(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) { + if thid, err := strconv.ParseInt(string(ps.ByName("thid")), 10, 64); err != nil { + return nil, err + } else if theme, err := fic.GetTheme(thid); err != nil { + return nil, err + } else { + return f(u, theme, body) + } + } +} + +func getExercice(args []string) (fic.Exercice, error) { + if tid, err := strconv.ParseInt(string(args[0]), 10, 64); err != nil { + return fic.Exercice{}, err + } else if theme, err := fic.GetTheme(tid); err != nil { + return fic.Exercice{}, err + } else if eid, err := strconv.Atoi(string(args[1])); err != nil { + return fic.Exercice{}, err + } else { + return theme.GetExercice(eid) + } +} + +func listThemes(_ QAUser, _ httprouter.Params, _ []byte) (interface{}, error) { + return fic.GetThemes() +} + +func exportThemes(_ QAUser, _ httprouter.Params, _ []byte) (interface{}, error) { + return fic.ExportThemes() +} + +func showTheme(_ QAUser, theme fic.Theme, _ []byte) (interface{}, error) { + return theme, nil +} + +func listThemedExercices(_ QAUser, theme fic.Theme, _ []byte) (interface{}, error) { + return theme.GetExercices() +} + +func showThemedExercice(_ QAUser, theme fic.Theme, exercice fic.Exercice, body []byte) (interface{}, error) { + return exercice, nil +} diff --git a/qa/api/version.go b/qa/api/version.go new file mode 100644 index 00000000..c351b81e --- /dev/null +++ b/qa/api/version.go @@ -0,0 +1,13 @@ +package api + +import ( + "github.com/julienschmidt/httprouter" +) + +func init() { + router.GET("/api/version", apiHandler(showVersion)) +} + +func showVersion(u QAUser, _ httprouter.Params, body []byte) (interface{}, error) { + return map[string]interface{}{"version": 0.1, "auth": u}, nil +} diff --git a/qa/main.go b/qa/main.go new file mode 100644 index 00000000..5cc80a76 --- /dev/null +++ b/qa/main.go @@ -0,0 +1,120 @@ +package main + +import ( + "context" + "flag" + "fmt" + "log" + "net/http" + "net/url" + "os" + "os/signal" + "path" + "path/filepath" + "strings" + "syscall" + + "srs.epita.fr/fic-server/libfic" + "srs.epita.fr/fic-server/qa/api" +) + +var StaticDir string + +type ResponseWriterPrefix struct { + real http.ResponseWriter + prefix string +} + +func (r ResponseWriterPrefix) Header() http.Header { + return r.real.Header() +} + +func (r ResponseWriterPrefix) WriteHeader(s int) { + if v, exists := r.real.Header()["Location"]; exists { + r.real.Header().Set("Location", r.prefix+v[0]) + } + r.real.WriteHeader(s) +} + +func (r ResponseWriterPrefix) Write(z []byte) (int, error) { + return r.real.Write(z) +} + +func StripPrefix(prefix string, h http.Handler) http.Handler { + if prefix == "" { + return h + } + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if prefix != "/" && r.URL.Path == "/" { + http.Redirect(w, r, prefix+"/", http.StatusFound) + } else if p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) { + r2 := new(http.Request) + *r2 = *r + r2.URL = new(url.URL) + *r2.URL = *r.URL + r2.URL.Path = p + h.ServeHTTP(ResponseWriterPrefix{w, prefix}, r2) + } else { + h.ServeHTTP(w, r) + } + }) +} + +func main() { + // Read parameters from command line + var bind = flag.String("bind", "127.0.0.1:8083", "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(&StaticDir, "static", "./htdocs-qa/", "Directory containing static files") + flag.StringVar(&api.TeamsDir, "teams", "./TEAMS", "Base directory where save teams JSON files") + flag.StringVar(&api.Simulator, "simulator", "", "Auth string to simulate (for development only)") + flag.Parse() + + log.SetPrefix("[qa] ") + + // Sanitize options + var err error + log.Println("Checking paths...") + if StaticDir, err = filepath.Abs(StaticDir); err != nil { + log.Fatal(err) + } + if BaseURL != "/" { + BaseURL = path.Clean(BaseURL) + } else { + BaseURL = "" + } + if api.Simulator != "" { + if _, err := os.Stat(path.Join(api.TeamsDir, api.Simulator)); os.IsNotExist(err) { + log.Fatal(err) + } + } + + // Database connection + log.Println("Opening database...") + if err = fic.DBInit(*dsn); err != nil { + log.Fatal("Cannot open the database: ", err) + } + defer fic.DBClose() + + // Prepare graceful shutdown + interrupt := make(chan os.Signal, 1) + signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM) + + srv := &http.Server{ + Addr: *bind, + Handler: StripPrefix(BaseURL, api.Router()), + } + + // Serve content + go func() { + log.Fatal(srv.ListenAndServe()) + }() + log.Println(fmt.Sprintf("Ready, listening on %s", *bind)) + + // Wait shutdown signal + <-interrupt + + log.Print("The service is shutting down...") + srv.Shutdown(context.Background()) + log.Println("done") +} diff --git a/qa/static.go b/qa/static.go new file mode 100644 index 00000000..0d5e479b --- /dev/null +++ b/qa/static.go @@ -0,0 +1,66 @@ +package main + +import ( + "bytes" + "io" + "io/ioutil" + "log" + "net/http" + "os" + "path" + + "srs.epita.fr/fic-server/qa/api" + + "github.com/julienschmidt/httprouter" +) + +var BaseURL = "/" + +var indexTmpl []byte + +func getIndexHtml(w io.Writer) { + if len(indexTmpl) == 0 { + if file, err := os.Open(path.Join(StaticDir, "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 init() { + api.Router().GET("/", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + getIndexHtml(w) + }) + + api.Router().GET("/exercices/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + getIndexHtml(w) + }) + api.Router().GET("/themes/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + getIndexHtml(w) + }) + + api.Router().GET("/css/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + http.ServeFile(w, r, path.Join(StaticDir, r.URL.Path)) + }) + api.Router().GET("/fonts/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + http.ServeFile(w, r, path.Join(StaticDir, r.URL.Path)) + }) + api.Router().GET("/img/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + http.ServeFile(w, r, path.Join(StaticDir, r.URL.Path)) + }) + api.Router().GET("/js/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + http.ServeFile(w, r, path.Join(StaticDir, r.URL.Path)) + }) + api.Router().GET("/views/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + http.ServeFile(w, r, path.Join(StaticDir, r.URL.Path)) + }) +} diff --git a/qa/static/css/bootstrap.min.css b/qa/static/css/bootstrap.min.css new file mode 120000 index 00000000..8dd0a066 --- /dev/null +++ b/qa/static/css/bootstrap.min.css @@ -0,0 +1 @@ +../../../admin/static/css/bootstrap.min.css \ No newline at end of file diff --git a/qa/static/css/fic.css b/qa/static/css/fic.css new file mode 120000 index 00000000..5f8e4077 --- /dev/null +++ b/qa/static/css/fic.css @@ -0,0 +1 @@ +../../../frontend/static/css/fic.css \ No newline at end of file diff --git a/qa/static/css/glyphicon.css b/qa/static/css/glyphicon.css new file mode 120000 index 00000000..6d65cb92 --- /dev/null +++ b/qa/static/css/glyphicon.css @@ -0,0 +1 @@ +../../../frontend/static/css/glyphicon.css \ No newline at end of file diff --git a/qa/static/favicon.ico b/qa/static/favicon.ico new file mode 120000 index 00000000..060d10ef --- /dev/null +++ b/qa/static/favicon.ico @@ -0,0 +1 @@ +../../frontend/static/favicon.ico \ No newline at end of file diff --git a/qa/static/fonts b/qa/static/fonts new file mode 120000 index 00000000..5431051e --- /dev/null +++ b/qa/static/fonts @@ -0,0 +1 @@ +../../frontend/static/fonts/ \ No newline at end of file diff --git a/qa/static/img/comcyber.png b/qa/static/img/comcyber.png new file mode 120000 index 00000000..0df83a18 --- /dev/null +++ b/qa/static/img/comcyber.png @@ -0,0 +1 @@ +../../../frontend/static/img/comcyber.png \ No newline at end of file diff --git a/qa/static/img/epita.png b/qa/static/img/epita.png new file mode 120000 index 00000000..110ea62c --- /dev/null +++ b/qa/static/img/epita.png @@ -0,0 +1 @@ +../../../frontend/static/img/epita.png \ No newline at end of file diff --git a/qa/static/img/fic.png b/qa/static/img/fic.png new file mode 120000 index 00000000..00ba2748 --- /dev/null +++ b/qa/static/img/fic.png @@ -0,0 +1 @@ +../../../frontend/static/img/fic.png \ No newline at end of file diff --git a/qa/static/img/srs.png b/qa/static/img/srs.png new file mode 120000 index 00000000..a599744d --- /dev/null +++ b/qa/static/img/srs.png @@ -0,0 +1 @@ +../../../frontend/static/img/srs.png \ No newline at end of file diff --git a/qa/static/index.html b/qa/static/index.html new file mode 100644 index 00000000..e86d9086 --- /dev/null +++ b/qa/static/index.html @@ -0,0 +1,70 @@ + + + + + Challenge Forensic - QA + + + + + + + + +
+
+
+ +
+ +
+ +
+ + + + + + + + + + + diff --git a/qa/static/js/angular-resource.min.js b/qa/static/js/angular-resource.min.js new file mode 100644 index 00000000..8b924c37 --- /dev/null +++ b/qa/static/js/angular-resource.min.js @@ -0,0 +1,15 @@ +/* + 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 +
+    + + just now + +
+
+
+ + +
+
` + }); + +angular.module("FICApp") + .factory("Version", function($resource) { + return $resource("/api/version") + }) + .factory("Team", function($resource) { + return $resource("/api/teams/:teamId", { teamId: '@id' }, { + 'update': {method: 'PUT'}, + }) + }) + .factory("Teams", function($resource) { + return $resource("/api/teams.json") + }) + .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("ExerciceQA", function($resource) { + return $resource("/api/qa/:exerciceId/:qaId", { exerciceId: '@idExercice', qaId: '@id' }, { + update: {method: 'PUT'}, + patch: {method: 'PATCH'} + }) + }); + +angular.module("FICApp") + .filter("toColor", function() { + return function(num) { + num >>>= 0; + var b = num & 0xFF, + g = (num & 0xFF00) >>> 8, + r = (num & 0xFF0000) >>> 16, + a = ( (num & 0xFF000000) >>> 24 ) / 255 ; + return "#" + r.toString(16) + g.toString(16) + b.toString(16); + } + }) + .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 + } + }) + + .directive('color', function() { + return { + require: 'ngModel', + link: function(scope, ele, attr, ctrl){ + ctrl.$formatters.unshift(function(num){ + num >>>= 0; + var b = num & 0xFF, + g = (num & 0xFF00) >>> 8, + r = (num & 0xFF0000) >>> 16, + a = ( (num & 0xFF000000) >>> 24 ) / 255 ; + return "#" + r.toString(16) + g.toString(16) + b.toString(16); + }); + 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) { + $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, + }); + } + }) + + .controller("VersionController", function($scope, Version) { + $scope.v = Version.get(); + }) + + .controller("ThemesListController", function($scope, Theme, $location, $rootScope, $http) { + $scope.themes = Theme.query(); + $scope.fields = ["name", "authors", "headline"]; + + $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); + }; + }) + .controller("ThemeController", function($scope, Theme, $routeParams, $location, $rootScope, $http) { + $scope.theme = Theme.get({ themeId: $routeParams.themeId }); + $scope.fields = ["name", "urlid", "authors", "headline", "intro", "image"]; + }) + + .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.show = function(id) { + $location.url("/exercices/" + id); + }; + }) + .controller("ExercicesListController", function($scope, ThemedExercice, $location, $rootScope, $http) { + $scope.exercices = ThemedExercice.query({ themeId: $scope.theme.id }); + $scope.fields = ["title", "headline"]; + + $scope.show = function(id) { + $location.url("/themes/" + $scope.theme.id + "/exercices/" + id); + }; + }) + + .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 }); + } + $http({ + url: "/api/themes.json", + method: "GET" + }).then(function(response) { + $scope.themes = response.data + var last_exercice = null; + angular.forEach($scope.themes[$scope.exercice.id_theme].exercices, function(exercice, k) { + if (last_exercice != null) { + $scope.themes[$scope.exercice.id_theme].exercices[last_exercice].next = k; + exercice.previous = last_exercice; + } + last_exercice = k; + exercice.id = k; + }); + }); + $scope.exercices = Exercice.query(); + }) + + .controller("ExerciceQAController", function($scope, $rootScope, ExerciceQA, $routeParams, $location, $http) { + $scope.queries = ExerciceQA.query({ exerciceId: $routeParams.exerciceId }); + $scope.fields = ["state", "subject", "user", "creation"]; + $scope.namedFields = { + "state": "État", + "subject": "Sujet", + "content": "Description", + }; + $scope.states = { + "ok": "OK", + "orthograph": "Orthographe et grammaire", + "issue-statement": "Pas compris", + "issue-flag": "Problème de flag", + "issue-mcq": "Problème de QCM/QCU", + "issue-hint": "Problème d'indice", + "issue-file": "Problème de fichier", + "issue": "Problème autre", + "suggest": "Suggestion", + "too-hard": "Trop dur", + "too-easy": "Trop facile", + }; + + $scope.newQuery = new ExerciceQA(); + + $scope.query_comments = null + $scope.query_selected = null + $scope.showComments = function(qid) { + if ($scope.query_selected == qid) { + $scope.query_selected = null + $scope.queries_comments = null + } else { + $scope.query_selected = qid + $http({ + url: "/api/qa/" + $routeParams.exerciceId + "/" + $scope.queries[$scope.query_selected].id + "/comments" + }).then(function(response) { + $scope.queries_comments = response.data + }) + } + } + + $scope.newComment = {content: ""} + $scope.addComment = function() { + $http({ + url: "/api/qa/" + $routeParams.exerciceId + "/" + $scope.queries[$scope.query_selected].id + "/comments", + method: "POST", + data: $scope.newComment, + }).then(function(response) { + $scope.newComment = {content: ""} + $http({ + url: "/api/qa/" + $routeParams.exerciceId + "/" + $scope.queries[$scope.query_selected].id + "/comments" + }).then(function(response) { + $scope.queries_comments = response.data + }) + }, function(response) { + $scope.addToast('danger', 'An error occurs when trying to respond to QA entry:', response.data.errmsg); + }) + } + + $scope.updateQA = function(qid) { + $scope.newQuery = $scope.queries[$scope.query_selected] + } + + $scope.deleteQA = function(qid) { + var myq = $scope.queries[$scope.query_selected] + myq.$delete( + { exerciceId: $routeParams.exerciceId, qaId: qid }, + function() { + $scope.queries = ExerciceQA.query({ exerciceId: $routeParams.exerciceId }); + $scope.query_selected = null + }, function(response) { + $scope.addToast('danger', 'An error occurs when trying to delete QA query:', response.data.errmsg); + } + ) + } + + $scope.solveQA = function(qid) { + var myq = $scope.queries[$scope.query_selected] + myq.solved = (new Date()).toISOString() + myq.$update({ exerciceId: $routeParams.exerciceId, qaId: qid }) + } + + $scope.closeQA = function(qid) { + var myq = $scope.queries[$scope.query_selected] + myq.closed = (new Date()).toISOString() + myq.$update({ exerciceId: $routeParams.exerciceId, qaId: qid }) + } + + $scope.saveQuery = function() { + if (this.newQuery.id) { + this.newQuery.$update({ exerciceId: $routeParams.exerciceId, qaId: this.newQuery.id }); + } else { + this.newQuery.$save({ exerciceId: $routeParams.exerciceId }, function() { + //$scope.saveComment(); + $scope.addToast('success', 'QA query created!'); + $scope.queries = ExerciceQA.query({ exerciceId: $routeParams.exerciceId }); + $scope.newQuery = new ExerciceQA(); + }, function(response) { + $scope.addToast('danger', 'An error occurs when trying to create QA query:', response.data.errmsg); + }); + } + } + }); diff --git a/qa/static/views/exercice-list.html b/qa/static/views/exercice-list.html new file mode 100644 index 00000000..dc3855f5 --- /dev/null +++ b/qa/static/views/exercice-list.html @@ -0,0 +1,27 @@ +

+ Défis +

+ +
+

+ + + + + + + + + + + + + +
+ {{ field }} + + Scénario +
+ {{ themes[exercice.id_theme].name }} +
+
diff --git a/qa/static/views/exercice.html b/qa/static/views/exercice.html new file mode 100644 index 00000000..7152b459 --- /dev/null +++ b/qa/static/views/exercice.html @@ -0,0 +1,105 @@ +

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

+ +
+
+
+
+ +
+
+
+ Qu'avez-vous pensé de ce défi ? +
+
+
+ +
+ + + +
+
+ +
+
+ + + + + + + + + + + + + + + + + +
+ {{ field }} +
Aucun requête enregistrée
+ +
+
+

{{ queries[query_selected].subject }}

+
+
+
+
+
Qui ?
+
{{ queries[query_selected].user }} (team #{{ queries[query_selected].id_team}})
+ +
État
+
{{ queries[query_selected].state }}
+ +
Date de création
+
{{ queries[query_selected].creation }}
+ +
Date de résolution
+
{{ queries[query_selected].solved }}
+ +
Date de clôture
+
{{ queries[query_selected].closed }}
+
+
+ + + +
+
+ + + + +
+ Le {{ comment.date }}, {{ comment.user }} a écrit : {{ comment.content }} +
+
+ Répondre : + + + +
+
+
diff --git a/qa/static/views/home.html b/qa/static/views/home.html new file mode 100644 index 00000000..39ee5004 --- /dev/null +++ b/qa/static/views/home.html @@ -0,0 +1,7 @@ +
+

Interface QA du challenge

+
+
+
+
+
diff --git a/qa/static/views/theme-list.html b/qa/static/views/theme-list.html new file mode 100644 index 00000000..daf7ff81 --- /dev/null +++ b/qa/static/views/theme-list.html @@ -0,0 +1,19 @@ +

+ Scénarios +

+ +

+ + + + + + + + + + + +
+ {{ field }} +
diff --git a/qa/static/views/theme.html b/qa/static/views/theme.html new file mode 100644 index 00000000..88086d3b --- /dev/null +++ b/qa/static/views/theme.html @@ -0,0 +1,25 @@ +

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

+ +
+ +
+

+ Défis ({{ exercices.length }}) +

+ +

+ + + + + + + + + + + +
+ {{ field }} +
+
From 42d594ccac83b0e8752492384bebdc96f9f02507 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 8 Sep 2020 13:30:28 +0200 Subject: [PATCH 0228/1637] qa: Add todo list on home page --- libfic/db.go | 11 ++++++++ libfic/qa.go | 55 +++++++++++++++++++++++++++++++++++++++ qa/api/todo.go | 49 ++++++++++++++++++++++++++++++++++ qa/static/js/qa.js | 27 +++++++++++++++++++ qa/static/views/home.html | 18 ++++++++++++- 5 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 qa/api/todo.go diff --git a/libfic/db.go b/libfic/db.go index 46bd2c67..fc49decf 100644 --- a/libfic/db.go +++ b/libfic/db.go @@ -446,6 +446,17 @@ CREATE TABLE IF NOT EXISTS qa_comments( FOREIGN KEY(id_qa) REFERENCES exercices_qa(id_qa), FOREIGN KEY(id_team) REFERENCES teams(id_team) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; +`); err != nil { + return err + } + if _, err := db.Exec(` +CREATE TABLE IF NOT EXISTS teams_qa_todo( + id_todo INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, + id_team INTEGER NOT NULL, + id_exercice INTEGER NOT NULL, + FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice), + FOREIGN KEY(id_team) REFERENCES teams(id_team) +) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } diff --git a/libfic/qa.go b/libfic/qa.go index 126e61e4..c01e3717 100644 --- a/libfic/qa.go +++ b/libfic/qa.go @@ -64,6 +64,26 @@ func (e Exercice) GetQAQueries() (res []QAQuery, err error) { return } +// GetQAQueries returns a list of all QAQuery registered for the Exercice. +func (t Team) GetQAQueries() (res []QAQuery, err error) { + var rows *sql.Rows + if rows, err = DBQuery("SELECT id_qa, id_exercice, id_team, authuser, creation, state, subject, solved, closed FROM exercices_qa WHERE id_team = ?", t.Id); err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + var q QAQuery + if err = rows.Scan(&q.Id, &q.IdExercice, &q.IdTeam, &q.User, &q.Creation, &q.State, &q.Subject, &q.Solved, &q.Closed); err != nil { + return + } + res = append(res, q) + } + err = rows.Err() + + return +} + // GetQAQuery retrieves the query with the given identifier. func (e Exercice) GetQAQuery(id int64) (q QAQuery, err error) { err = DBQueryRow("SELECT id_qa, id_exercice, id_team, authuser, creation, state, subject, solved, closed FROM exercices_qa WHERE id_qa = ? AND id_exercice = ?", id, e.Id).Scan(&q.Id, &q.IdExercice, &q.IdTeam, &q.User, &q.Creation, &q.State, &q.Subject, &q.Solved, &q.Closed) @@ -181,3 +201,38 @@ func (c QAComment) Delete() (int64, error) { return nb, err } } + +type QATodo struct { + Id int64 `json:"id"` + IdTeam int64 `json:"id_team,omitempty"` + IdExercice int64 `json:"id_exercice"` +} + +func (t Team) GetQATodo() (res []QATodo, err error) { + var rows *sql.Rows + if rows, err = DBQuery("SELECT id_todo, id_exercice FROM teams_qa_todo WHERE id_team = ?", t.Id); err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + var t QATodo + if err = rows.Scan(&t.Id, &t.IdExercice); err != nil { + return + } + res = append(res, t) + } + err = rows.Err() + + return +} + +func (t Team) NewQATodo(idExercice int64) (QATodo, error) { + if res, err := DBExec("INSERT INTO teams_qa_todo (id_team, id_exercice) VALUES (?, ?)", t.Id, idExercice); err != nil { + return QATodo{}, err + } else if tid, err := res.LastInsertId(); err != nil { + return QATodo{}, err + } else { + return QATodo{tid, t.Id, idExercice}, nil + } +} diff --git a/qa/api/todo.go b/qa/api/todo.go new file mode 100644 index 00000000..94222e93 --- /dev/null +++ b/qa/api/todo.go @@ -0,0 +1,49 @@ +package api + +import ( + "encoding/json" + "errors" + + "srs.epita.fr/fic-server/libfic" + + "github.com/julienschmidt/httprouter" +) + +func init() { + router.GET("/api/qa_mywork.json", apiHandler(getQAWork)) + router.GET("/api/qa_work.json", apiHandler(getQATodo)) + router.POST("/api/qa_work.json", apiHandler(createQATodo)) +} + +func getQAWork(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) { + if team, err := fic.GetTeam(u.TeamId); err != nil { + return nil, err + } else { + return team.GetQAQueries() + } +} + +func getQATodo(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) { + if team, err := fic.GetTeam(u.TeamId); err != nil { + return nil, err + } else { + return team.GetQATodo() + } +} + +func createQATodo(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) { + if u.User != "nemunaire" { + return nil, errors.New("Restricted") + } + + var ut fic.QATodo + if err := json.Unmarshal(body, &ut); err != nil { + return nil, err + } + + if team, err := fic.GetTeam(ut.IdTeam); err != nil { + return nil, err + } else { + return team.NewQATodo(ut.IdExercice) + } +} diff --git a/qa/static/js/qa.js b/qa/static/js/qa.js index 6f0f378d..bfc98a70 100644 --- a/qa/static/js/qa.js +++ b/qa/static/js/qa.js @@ -90,6 +90,12 @@ angular.module("FICApp") .factory("Version", function($resource) { return $resource("/api/version") }) + .factory("Todo", function($resource) { + return $resource("/api/qa_work.json") + }) + .factory("TodoWorked", function($resource) { + return $resource("/api/qa_mywork.json") + }) .factory("Team", function($resource) { return $resource("/api/teams/:teamId", { teamId: '@id' }, { 'update': {method: 'PUT'}, @@ -216,6 +222,20 @@ angular.module("FICApp") $scope.v = Version.get(); }) + .controller("ToDoController", function($scope, Todo, TodoWorked, $location) { + $scope.todos = Todo.query(); + $scope.tododone = {} + $scope.work = TodoWorked.query(function(tw) { + tw.forEach(function(t) { + $scope.tododone[t.id_exercice] = t + }) + }); + + $scope.show = function(id) { + $location.url("/exercices/" + id); + }; + }) + .controller("ThemesListController", function($scope, Theme, $location, $rootScope, $http) { $scope.themes = Theme.query(); $scope.fields = ["name", "authors", "headline"]; @@ -286,6 +306,13 @@ angular.module("FICApp") }; }) + .controller("MyTodoExerciceController", function($scope, Exercice, ExerciceQA, Theme) { + $scope.mytheme = null + $scope.myexercice = Exercice.get({ exerciceId: $scope.todo.id_exercice }, function(e) { + $scope.mytheme = Theme.get({ themeId: e.id_theme }) + }); + }) + .controller("ExerciceController", function($scope, $rootScope, Exercice, ThemedExercice, $routeParams, $location, $http) { if ($routeParams.themeId && $routeParams.exerciceId == "new") { $scope.exercice = new ThemedExercice(); diff --git a/qa/static/views/home.html b/qa/static/views/home.html index 39ee5004..16c4420a 100644 --- a/qa/static/views/home.html +++ b/qa/static/views/home.html @@ -1,7 +1,23 @@

Interface QA du challenge

-
+
+ + + + + + + +
+ À tester + + Testé + + {{ mytheme.name }} + + {{ myexercice.title }} +
From 940b32debc25052521048bcf9f8bb04ca0b5858f Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 8 Sep 2020 19:06:36 +0200 Subject: [PATCH 0229/1637] qa: delete comment related to a query --- libfic/qa.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libfic/qa.go b/libfic/qa.go index c01e3717..8586dd5b 100644 --- a/libfic/qa.go +++ b/libfic/qa.go @@ -114,7 +114,9 @@ func (q QAQuery) Update() (int64, error) { // Delete the query from the database. func (q QAQuery) Delete() (int64, error) { - if res, err := DBExec("DELETE FROM exercices_qa WHERE id_qa = ?", q.Id); err != nil { + if _, err := DBExec("DELETE FROM qa_comments WHERE id_qa = ?", q.Id); err != nil { + return 0, err + } else if res, err := DBExec("DELETE FROM exercices_qa WHERE id_qa = ?", q.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err From 5aa21d267983c6e0d980b47d51c739ffef263179 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 9 Sep 2020 19:26:47 +0200 Subject: [PATCH 0230/1637] qa: Permit empty content if all is Ok --- qa/api/qa.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qa/api/qa.go b/qa/api/qa.go index d8814ac4..373b5c53 100644 --- a/qa/api/qa.go +++ b/qa/api/qa.go @@ -67,7 +67,11 @@ func createExerciceQA(u QAUser, exercice fic.Exercice, body []byte) (interface{} } if len(uq.Subject) == 0 { - return nil, errors.New("Subject not filled") + if uq.State == "ok" { + uq.Subject = "RAS" + } else { + return nil, errors.New("Subject not filled") + } } if qa, err := exercice.NewQAQuery(uq.Subject, u.TeamId, u.User, uq.State); err != nil { From 7c51ce7c4ff6af954297fdc893dedddc4fa75d72 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 9 Sep 2020 19:50:39 +0200 Subject: [PATCH 0231/1637] qa: Use relative addresses --- qa/static/js/qa.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/qa/static/js/qa.js b/qa/static/js/qa.js index bfc98a70..82432542 100644 --- a/qa/static/js/qa.js +++ b/qa/static/js/qa.js @@ -88,45 +88,45 @@ angular.module("FICApp") angular.module("FICApp") .factory("Version", function($resource) { - return $resource("/api/version") + return $resource("api/version") }) .factory("Todo", function($resource) { - return $resource("/api/qa_work.json") + return $resource("api/qa_work.json") }) .factory("TodoWorked", function($resource) { - return $resource("/api/qa_mywork.json") + return $resource("api/qa_mywork.json") }) .factory("Team", function($resource) { - return $resource("/api/teams/:teamId", { teamId: '@id' }, { + return $resource("api/teams/:teamId", { teamId: '@id' }, { 'update': {method: 'PUT'}, }) }) .factory("Teams", function($resource) { - return $resource("/api/teams.json") + return $resource("api/teams.json") }) .factory("Theme", function($resource) { - return $resource("/api/themes/:themeId", { themeId: '@id' }, { + return $resource("api/themes/:themeId", { themeId: '@id' }, { update: {method: 'PUT'} }); }) .factory("Themes", function($resource) { - return $resource("/api/themes.json", null, { + return $resource("api/themes.json", null, { 'get': {method: 'GET'}, }) }) .factory("ThemedExercice", function($resource) { - return $resource("/api/themes/:themeId/exercices/:exerciceId", { themeId: '@id', exerciceId: '@idExercice' }, { + 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' }, { + return $resource("api/exercices/:exerciceId", { exerciceId: '@id' }, { update: {method: 'PUT'}, patch: {method: 'PATCH'} }) }) .factory("ExerciceQA", function($resource) { - return $resource("/api/qa/:exerciceId/:qaId", { exerciceId: '@idExercice', qaId: '@id' }, { + return $resource("api/qa/:exerciceId/:qaId", { exerciceId: '@idExercice', qaId: '@id' }, { update: {method: 'PUT'}, patch: {method: 'PATCH'} }) @@ -267,7 +267,7 @@ angular.module("FICApp") .controller("AllExercicesListController", function($scope, Exercice, Theme, $routeParams, $location, $rootScope, $http, $filter) { $http({ - url: "/api/themes.json", + url: "api/themes.json", method: "GET" }).then(function(response) { $scope.themes = response.data @@ -320,7 +320,7 @@ angular.module("FICApp") $scope.exercice = Exercice.get({ exerciceId: $routeParams.exerciceId }); } $http({ - url: "/api/themes.json", + url: "api/themes.json", method: "GET" }).then(function(response) { $scope.themes = response.data @@ -370,7 +370,7 @@ angular.module("FICApp") } else { $scope.query_selected = qid $http({ - url: "/api/qa/" + $routeParams.exerciceId + "/" + $scope.queries[$scope.query_selected].id + "/comments" + url: "api/qa/" + $routeParams.exerciceId + "/" + $scope.queries[$scope.query_selected].id + "/comments" }).then(function(response) { $scope.queries_comments = response.data }) @@ -380,13 +380,13 @@ angular.module("FICApp") $scope.newComment = {content: ""} $scope.addComment = function() { $http({ - url: "/api/qa/" + $routeParams.exerciceId + "/" + $scope.queries[$scope.query_selected].id + "/comments", + url: "api/qa/" + $routeParams.exerciceId + "/" + $scope.queries[$scope.query_selected].id + "/comments", method: "POST", data: $scope.newComment, }).then(function(response) { $scope.newComment = {content: ""} $http({ - url: "/api/qa/" + $routeParams.exerciceId + "/" + $scope.queries[$scope.query_selected].id + "/comments" + url: "api/qa/" + $routeParams.exerciceId + "/" + $scope.queries[$scope.query_selected].id + "/comments" }).then(function(response) { $scope.queries_comments = response.data }) From 70afa61814c652b170a4adc5b14ae74ed2676a71 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 9 Sep 2020 21:15:58 +0200 Subject: [PATCH 0232/1637] qa: new button to speed up +1 --- qa/static/js/qa.js | 16 ++++++++++++++++ qa/static/views/exercice.html | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/qa/static/js/qa.js b/qa/static/js/qa.js index 82432542..d5e658fe 100644 --- a/qa/static/js/qa.js +++ b/qa/static/js/qa.js @@ -395,6 +395,22 @@ angular.module("FICApp") }) } + $scope.thumbUp = function(qid) { + $http({ + url: "api/qa/" + $routeParams.exerciceId + "/" + $scope.queries[qid].id + "/comments", + method: "POST", + data: { "content": "+1" }, + }).then(function(response) { + $http({ + url: "api/qa/" + $routeParams.exerciceId + "/" + $scope.queries[$scope.query_selected].id + "/comments" + }).then(function(response) { + $scope.queries_comments = response.data + }) + }, function(response) { + $scope.addToast('danger', 'An error occurs when trying to respond to QA entry:', response.data.errmsg); + }) + } + $scope.updateQA = function(qid) { $scope.newQuery = $scope.queries[$scope.query_selected] } diff --git a/qa/static/views/exercice.html b/qa/static/views/exercice.html index 7152b459..87e488af 100644 --- a/qa/static/views/exercice.html +++ b/qa/static/views/exercice.html @@ -38,11 +38,15 @@
{{ field }}
+ +
- From 4490eb7036aac07d634c29b599d8d3548eeef8ea Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 9 Sep 2020 21:20:00 +0200 Subject: [PATCH 0234/1637] qa: Add rapid access to corresponding challenge --- qa/static/views/exercice.html | 1 + 1 file changed, 1 insertion(+) diff --git a/qa/static/views/exercice.html b/qa/static/views/exercice.html index c52e13ba..668a0b77 100644 --- a/qa/static/views/exercice.html +++ b/qa/static/views/exercice.html @@ -5,6 +5,7 @@ + Site du challenge
From 95ca255d7596f739cc7b6da1a3cae6126e720878 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 9 Sep 2020 21:17:09 +0200 Subject: [PATCH 0235/1637] qa: Add multiple color on home page --- qa/api/todo.go | 27 +++++++++++++++++++++++++++ qa/static/js/qa.js | 6 +++++- qa/static/views/home.html | 9 ++++++--- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/qa/api/todo.go b/qa/api/todo.go index 94222e93..9aa7cbc8 100644 --- a/qa/api/todo.go +++ b/qa/api/todo.go @@ -10,11 +10,38 @@ import ( ) func init() { + router.GET("/api/qa_exercices.json", apiHandler(getExerciceTested)) router.GET("/api/qa_mywork.json", apiHandler(getQAWork)) router.GET("/api/qa_work.json", apiHandler(getQATodo)) router.POST("/api/qa_work.json", apiHandler(createQATodo)) } +type exerciceTested map[int64]string + +func getExerciceTested(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) { + if team, err := fic.GetTeam(u.TeamId); err != nil { + return nil, err + } else if exercices, err := fic.GetExercices(); err != nil { + return nil, err + } else { + ret := exerciceTested{} + + for _, exercice := range exercices { + if team.HasAccess(exercice) { + if ok, _ := team.HasSolved(exercice); ok { + ret[exercice.Id] = "solved" + } else if cnt, _ := team.CountTries(exercice); cnt > 0 { + ret[exercice.Id] = "tried" + } else { + ret[exercice.Id] = "access" + } + } + } + + return ret, nil + } +} + func getQAWork(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) { if team, err := fic.GetTeam(u.TeamId); err != nil { return nil, err diff --git a/qa/static/js/qa.js b/qa/static/js/qa.js index d5e658fe..b4894d19 100644 --- a/qa/static/js/qa.js +++ b/qa/static/js/qa.js @@ -96,6 +96,9 @@ angular.module("FICApp") .factory("TodoWorked", function($resource) { return $resource("api/qa_mywork.json") }) + .factory("ExercicesTested", function($resource) { + return $resource("api/qa_exercices.json") + }) .factory("Team", function($resource) { return $resource("api/teams/:teamId", { teamId: '@id' }, { 'update': {method: 'PUT'}, @@ -222,8 +225,9 @@ angular.module("FICApp") $scope.v = Version.get(); }) - .controller("ToDoController", function($scope, Todo, TodoWorked, $location) { + .controller("ToDoController", function($scope, Todo, TodoWorked, ExercicesTested, $location) { $scope.todos = Todo.query(); + $scope.exo_done = ExercicesTested.get(); $scope.tododone = {} $scope.work = TodoWorked.query(function(tw) { tw.forEach(function(t) { diff --git a/qa/static/views/home.html b/qa/static/views/home.html index 16c4420a..29733a15 100644 --- a/qa/static/views/home.html +++ b/qa/static/views/home.html @@ -3,12 +3,15 @@
+ Le {{ comment.date }}, {{ comment.user }} a écrit : {{ comment.content }}
- - + + + + + + {/if} + diff --git a/frontend/ui/src/components/RegistrationFormJoinTeam.svelte b/frontend/ui/src/components/RegistrationFormJoinTeam.svelte new file mode 100644 index 00000000..160c60bf --- /dev/null +++ b/frontend/ui/src/components/RegistrationFormJoinTeam.svelte @@ -0,0 +1,99 @@ + + +{#if Object.keys($teams).length} + + + +
+
+ + +
+ 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/ui/src/components/RegistrationRowMember.svelte b/frontend/ui/src/components/RegistrationRowMember.svelte new file mode 100644 index 00000000..7ac8b83a --- /dev/null +++ b/frontend/ui/src/components/RegistrationRowMember.svelte @@ -0,0 +1,36 @@ + + + +
+ +
+
+ +
+
+ +
+
+ +
+ {#if canDelete} +
+ +
+ {/if} +
diff --git a/frontend/ui/src/components/TeamChangeName.svelte b/frontend/ui/src/components/TeamChangeName.svelte new file mode 100644 index 00000000..bf7808e0 --- /dev/null +++ b/frontend/ui/src/components/TeamChangeName.svelte @@ -0,0 +1,109 @@ + + + + + + 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/ui/src/components/TeamMembers.svelte b/frontend/ui/src/components/TeamMembers.svelte new file mode 100644 index 00000000..7860cd75 --- /dev/null +++ b/frontend/ui/src/components/TeamMembers.svelte @@ -0,0 +1,37 @@ + + + + + + Membres de l'équipe + + {#if 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/ui/src/components/ThemeNav.svelte b/frontend/ui/src/components/ThemeNav.svelte new file mode 100644 index 00000000..54855b7c --- /dev/null +++ b/frontend/ui/src/components/ThemeNav.svelte @@ -0,0 +1,59 @@ + + + + + {#each Object.keys(theme.exercices) as k, index} + + {#if k == exercice.id} + + {theme.exercices[k].title} + {#if theme.exercices[k].curcoeff > 1.0} + + {:else if $my && $my.exercices[k]} + + {theme.exercices[k].title} + {#if theme.exercices[k].curcoeff > 1.0} + + {:else} + + {theme.exercices[k].title} + {#if theme.exercices[k].curcoeff > 1.0} + + {/if} + + {/each} + + + + diff --git a/frontend/ui/src/routes/[theme]/[exercice].svelte b/frontend/ui/src/routes/[theme]/[exercice].svelte new file mode 100644 index 00000000..739a2501 --- /dev/null +++ b/frontend/ui/src/routes/[theme]/[exercice].svelte @@ -0,0 +1,161 @@ + + + + + + {exercice.title} - {$settings.title} + + +{#if exercice} + +{/if} + +{#if !$my || !$my.exercices[exercice.id]} + + + Vous n'avez pas encore accès à ce défi. + +{/if} + +{#if exercice} + +

{exercice.title}

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

{@html exercice.headline}

+ {:else} +

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

+ {#if $my.exercices[exercice.id].issue} + + {@html $my.exercices[exercice.id].issue} + + {/if} + {/if} +
+ +
+
    +
  • + Gain : + {exercice.gain} {exercice.gain==1?"point":"points"} + {#if $settings.firstBlood && exercice.solved < 1} + +{$settings.firstBlood * 100}% (prem's) + {/if} + {#if exercice.curcoeff != 1.0 || $settings.exerciceCurrentCoefficient != 1.0} + {#if exercice.curcoeff * $settings.exerciceCurrentCoefficient > 1}+{Math.round((exercice.curcoeff * $settings.exerciceCurrentCoefficient - 1) * 100)}{:else}-{Math.round((1-(exercice.curcoeff * $settings.exerciceCurrentCoefficient)) * 100)}{/if}% (bonus) + {/if} +
  • +
  • + Tenté par : + {#if !exercice.tried} + aucune équipe + {:else} + {exercice.tried} {exercice.tried == 1?"équipe":"équipes"} + {#if $my && $my.exercices[exercice.id].total_tries} + (cumulant {$my.exercices[exercice.id].total_tries} {$my.exercices[exercice.id].total_tries == 1?"tentative":"tentatives"}) + {/if} + {/if} +
  • +
  • + Résolu par : + {#if !exercice.solved} + aucune équipe + {:else} + {exercice.solved} {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} + + + + {#if $my && $my.exercices[exercice.id]} + + {#if $my.exercices[exercice.id].files || $my.exercices[exercice.id].hints} + + {#if $my.exercices[exercice.id].files} + + {/if} + {#if $my.exercices[exercice.id].hints} + + {/if} + + {/if} + + {#if !$my.exercices[exercice.id].solved} + + {:else} + + {/if} + {#if $my.exercices[exercice.id].video_uri} + + {/if} + + + {/if} +{/if} diff --git a/frontend/ui/src/routes/[theme]/__layout.svelte b/frontend/ui/src/routes/[theme]/__layout.svelte new file mode 100644 index 00000000..9d5288e2 --- /dev/null +++ b/frontend/ui/src/routes/[theme]/__layout.svelte @@ -0,0 +1,94 @@ + + + + + + {theme.name} - {$settings.title} + + +{#if theme} + +{/if} + + + + + diff --git a/frontend/ui/src/routes/[theme]/index.svelte b/frontend/ui/src/routes/[theme]/index.svelte new file mode 100644 index 00000000..3a47e7b6 --- /dev/null +++ b/frontend/ui/src/routes/[theme]/index.svelte @@ -0,0 +1,103 @@ + + + + +{#if theme && theme.exercices} +
+ +
+

{@html theme.headline}

+

{@html theme.intro}

+
+ +
    + {#each Object.keys(theme.exercices) as k, index} +
  • +
    +
    +
    +
    +
    + + + + +
    + {#each theme.exercices[k].tags as tag, idx} + #{tag} + {/each} +
    + {#if $my && $my.exercices[k]} + {theme.exercices[k].title} + {:else} + + + {/if} + {#if theme.exercices[k].curcoeff > 1.0} +
    +

    {@html theme.exercices[k].headline}

    +
    +
    + {#if $my && $my.exercices[k]} + + + {:else} + + + {/if} +
    +
    +
  • + {/each} +
+ +
+{:else} + + + Ce scénario n'existe pas. + +{/if} + + diff --git a/frontend/ui/src/routes/__layout.svelte b/frontend/ui/src/routes/__layout.svelte new file mode 100644 index 00000000..bb5786be --- /dev/null +++ b/frontend/ui/src/routes/__layout.svelte @@ -0,0 +1,138 @@ + + + + + + {$settings.title} + + + + +
+ + + diff --git a/frontend/ui/src/routes/edit.svelte b/frontend/ui/src/routes/edit.svelte new file mode 100644 index 00000000..88bea7fc --- /dev/null +++ b/frontend/ui/src/routes/edit.svelte @@ -0,0 +1,57 @@ + + + + + +

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

+ + {#if $my} + +
+ + {#if !$settings.denyNameChange} + + {/if} + + + + + + {:else} + + Vous n'avez pas encore d'équipe ! + Rendez-vous sur la page d'inscription pour plus d'information. + + {/if} + diff --git a/frontend/ui/src/routes/index.svelte b/frontend/ui/src/routes/index.svelte index 5982b0ae..5827bbd1 100644 --- a/frontend/ui/src/routes/index.svelte +++ b/frontend/ui/src/routes/index.svelte @@ -1,2 +1,62 @@ -

Welcome to SvelteKit

-

Visit kit.svelte.dev to read the documentation

+ + + + {#if !$my} + {#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} + {:else 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} + + Félicitations {#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} ! vous êtes maintenant connecté à l'espace de votre équipe {$teams[$my.team_id].name}. Vous pouvez changer ce nom dès maintenant en vous rendant sur la page de votre équipe. + + + {#if ($my.team_id && !$my.members.length)} + + Les membres de votre équipe ne sont pas encore enregistrés. Passez voir l'équipe serveur pour corriger cela. + + {/if} + {/if} + + + {#each Object.keys($themes) as th, index} + + + + {/each} + + diff --git a/frontend/ui/src/routes/issues.svelte b/frontend/ui/src/routes/issues.svelte new file mode 100644 index 00000000..4b50bf2b --- /dev/null +++ b/frontend/ui/src/routes/issues.svelte @@ -0,0 +1,186 @@ + + + + + +{#if message || sberr} + + {#if !sberr} + Votre rapport a bien été envoyé ! + {:else} + {sberr} + {/if} + {message} + +{/if} + +{#if 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} + + +
+
À tester + À commenter + - Testé + Commenté mais pas testé {{ mytheme.name }} From 74c3599b5d625888a0077a67071d553485e2c383 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 24 Oct 2020 17:41:47 +0200 Subject: [PATCH 0236/1637] Update FIC logo --- frontend/static/favicon.ico | Bin 1150 -> 2238 bytes frontend/static/img/fic.png | Bin 222417 -> 11868 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/frontend/static/favicon.ico b/frontend/static/favicon.ico index beedd686dc92a2456391f97c85b803940f367818..3da960762392e9c97016697e56d1aabd11690e7d 100644 GIT binary patch literal 2238 zcmc)McTkj96b0~u3Llo(5Nsfw6_6sb^tRO9WrbZ!6uYsD1!M0hHn1S}1`2|pCLWDYc zB2?&wP$zHVgHUG?Lfiup;vq(ecX!AF`#>fRBmE$g^oLA30J7kLWDsPs!3Yi+3Tfyt z1j~m*su&4rk5Q2H91Ur&v5@u-hosL0Nc&ENq;CWyVG)pqO@gG~WQYe$g=F9~hzHGp zgop>vgm}m-1Pz;ypy81S9I*gFN(&JRf* z!<(A~?|Dh^nx71>$Q1Yx&jq{Sy)YGCQM=)}Xb(w;=i+pDEXjb!(oDE5%Y-{|i_U_3 zOg7wN4!~{sLAb8Sfg5pIc?hn=Wo0g0WAoq=n-AyJhvBli0M2m*5XK#W(;8kOoYoeR zVhGojK)9Y)3dapcAta6)kE7eB6L8pk65WWymQ%3bdKz7M@n>Kke-;kJKH)5E6VAbo zxBVP!wx5UXjtj6!ya?OGOR!G53>)6gE3n>q6#*Ea1}kEjdIO!) zZbFb&PFW7Y?px$G1bc47BK;1`((l5YSnQ?Tdk^OO?!#=~1DIqyfN{n{%7-xJGn0}S zXFj6&5e)Y~h7mE$dIDo&l=T#yvY){u`zZ`5^$$FU0qLal0v!*&fIcx$W(8#hI_6ZM zeNH9X9eRoOqyuS}`wDGyt0=3Wmsf?hL^rRRvKo5%)zHmb!i_h?%59!-iqP<}u&!WI7xZPJ)%mHYv1qFM404N5L&*{}9z1>?&UEue)YT zMlcpPc+4DS-GA*VtuE6psXs~dV{A%{7Ukg=p?Q_Ilq@)w-%Z$7xwc zF}8r4%%WRyXsN+lDLpn!9R>)%&p<^*hXy^-g$dvT$45>UmgtMHZxXsot@zv z@3+$}Q1**B(U-k1cUd{l;AXGj!d11K*4f$zb2z8f59ngo_~IZgQ`Hs@ORn9&2y4jc z_WY$-(~}M0@@iZ4ES+SoeRU!i#l~{0)O)r081rsk#f6zB-Z|fP~Me=_h{11n| IFMl-t21&Mmb^rhX literal 1150 zcmb7EOHWfl7`-ZLl!etqP{T_p(iSy7qQTg?_qL^^Jo})n#{dHb6b9$ zO(J6sprE&5Rh(2~UUyYwL2J=lMee;+#qnI;$kn)TnZ4Mzxt_9;(m8+3l?Z^pNrAGZC*2FQ) zGk@B*NDcQ{Lq8sh18SV{PGi!)PQG47KyfYWS5R=bD7@5{F^7@zPEbb&!=Y+On?X$W ztYXpV#*%TJ^A)b&M#hjFDW2b@@av);kfl8uBmz?XHM98ev=g&+`$+iRO^&N8xN`pM7AkNayE)?|^<%PTt zc~7-Ng~ vA$GRg{;Xs=f7IS@t)`l{@-3I$^}OeLYsGznes?mQ)%V-6z2~38e=Pn6<@@I% diff --git a/frontend/static/img/fic.png b/frontend/static/img/fic.png index d46580dfcc6fe80914ad0d53655227ddb0757595..7e8d3a983de113302f1f13de31909477fe55bb62 100644 GIT binary patch literal 11868 zcmV-iE~C+jP) z2Y6J~)`k}WLsgL8I|-o$LWj^ojFixkW=GIV(W}@iHmoR$1r-D-7LcNp&_aih00||O zgx-6Xj*$QPwwe3KaQ2zAPn&aQ_WL|M_y0|1GIM6u*?X_`t#9SHwTeV2NJOzj)S8Iu z5>X{0DkAg@5{WxGLqz+CXcG}FC!)DTbRj4*L(`kl8ff7}^b!&EAfnvziz1QGbt0NU zM4tpjW^DF$IlKj0aUvQ?L=VgBDv@xROhhjPMP{7j{X&q+gJXzjs|+%U#4CuAt+!X_ zh~fPrFVF@M(R?B*CaB4w^7zQkagOx`%FOXH;Fb-u-0aJAaNDP&)iK* zlPlBmjg@HqjJz<%+ScF0f>4s}8p%L&KR9DeSy-2*FRMitF6Hs*`zclA zFM6c?MtUGBm2%&8%Rer#3CKWmG3WluOW*&|h^8;E$&wpRqFABJG_+?Tb!)ket$2Zs z3^W&l^VX#K_2~OQ8_|_(cRBhE)Tnfl-gtBw)vj{F0k6h)$Ut)$DLYHk@NsQv|FL^r z^TKgvd#rs5TjB1?6`0#e8E7uy)~z5K{d-gTDK0`NE$<{k%AKaqpNXRiCC>-k=O+Wr z1)!+>+?O3_^X^ive=h~{-k=drEvBgYdmQj;>_Y~c!`Z&CI1T;06ALzf0m+eJeOA+B z9Z~}3bCiMRU=mWx(|^B+Wy%hJxT; zUZkRhu26xz|46EC-40^)IIN=3L*Bk^T_kjDmQEv{TueE0-nQ&-I0313bRbyCtO~Mf z<<3ykS_dh->M0 zi}5=B$hF8oa}Z}PDR^KcXSPeRXj-@n(m}_jdulXJB>v}8-%|E;}E8yEt+=2`=BIf=S zzrHHPtqq~1?d9$`D?uHxX;&%JSB@X&G@wgYf`uOk$NM!nF3fh# z8z`iTvLv~RDa!Sk%WDch28xOYW8|1|x8mwE8ou-^H&vwhYeQ+pri%YN^Wf~&v&~l3 z$3hka#e#^*4KjrId(jQ_x8WZ=QHZweDJlH8A>EUtPoj%)U7*3324^K~#GPqrue;=SeuK)y|^aOHtsCNzU0XMAu*u_ip+6| zA=JNPvg+?aizgarSUJ&?l|Ro7UGHPXM)A3iz1pVweFw6?T{EJR*?#w)_=G1R6|WumWNA!u&3? zfL?KEk3`jHL!gcNt+|jXDZDe_doLOQ+xCjL{@>SdxA0>m&ckPccJXp!kV+nzn@u;{VYAHE@-ES~G(9oSc?5hs6WP`ZWNG}+4hk6rMC8WdQr6CGd zzt;8kF&ADD=B*8-A>Gw8n|cYB+D5;@^gw&<`xsk-46QD;s~l&+71MYZ6)kwh@N=T3 zCK_m{|5+7i692)}K!X7&(%x2rAq@zwc1?CtnPTRiVNkQeYAE94p|ymNGpBfG_ALvi zxJc~9v_M0B>|33VPTqjEb-PS6&Z=e4SayM6K8mg9tQ9qc9;c;*P*AX=8(g@gE}jlV zb{#6hZnkd2lt4q_`JM4mrq0nSR_HR_-*^|bZ@SY~i)(BjMFS1n$LIU3RvoB^5`rk_ z{>o$U-S`KGPZp$kkm#)qWl5#QUrcb_`!XfarYxw-wopd7C;xSdZm^p=G)t$3VMk0e z!V#fy%|lf9o~vG4ak7HxFDdC|sDAY$+~2`!iF4%X3)<8C2Ti!9ZiVSz6GNDcE_KiU z>tFHtxDmsFhSrat;v#fE9&I2k>+YkjEq734odZ7ijz@4~oBGQlvC;%O4%7k-lr4Ti zDA3Xm6%~37ZpWj4MR2X>=wB9nw3hQV@9l6*?G|a0}VIoWhs@|ws7^93SMP!6#_NT^kf;)o&5FlbEtur6~I^2 zsd}8vb%uOQE13D5u(dq32HQyopoa5)#7q+T% zBDfKanbk=6m7!nI%^3RRU{x&wYE619*L(@|mZDOybvw$^85IRcr^qy6=V5sEPh3#X zX_rTs@pI}k?V<>{{;>`khOY-hfu`ro1qFqhfNG^-jl)8(7~*QRzBXtcqjZ!w#>{N& z>I@`?;*Xu(Q2251b1}Om%5Dq=8tOu#cV;bSIZi`QQe>W&?c+o3#78ipxckNr_qlHS zO_}0`m%qJVm&KsB$v|$vP@rJ~Wk|WxLXSClEpa|2|5ab1;x&(drGPv_#;A& z>Y9m-zP~T0HFMJC(ZqWbqG+cMk#vQU=h^jlJBCl!dYp94nw)7DP$M)GtbbwEh6-Jc zLEVycy@Lt!>I+*g_>%GS>g&#$4(PN&wa?Ej7!EW&aw?qDtjwUH=7jM`FMr!k)AUa* zbkuPe_qKgR@apNWLJ+_DU5w9c{6s*2eKEDUuE)Y%%wOih8UD7{taOsqn?&#^-OU7F zzIEHnvQF>TLkG~%&pR=Z@2r(#-O@Z-N9<)zSt3Arj|6}kFZ5l_T)afN`FBnlHrigd zF;Ose=dE$ky3gdeHvTdfF6Z$6^tRC_fjMZ3j&oOZL*um(bl*;zA3<-8yN~%uZr@-0 zU*3$6iFJ-lGb~0$gX-?XMQN9pz{w8&ZsVH#tYy(cSC}_Qs*Z8Hw{Hj0qV?4%f1aBZ zUd=wc2Q5JP@%H$(l%X?mgC^i1UIvu72~+lOda`oMQYxz|-PPE8v@rAW@9_Mi)bH)y zG-*LSmSo{E-^*D^D;;zw`n{dgC{t*yH+_v#!l%Eqpy$8pz;+702HW-#6I;`VlUwOt z$C6d8U%^%3Z`BFe$i_jjUU&p@c48XsqG5g3 zSn)D_ifMs{IbJ_6sw?!EBc}?|gZFR#m-Y3ftHErmhfLM`6Wh?rO_k{QS>tL57^9k@ z{?3>CCe0RXTfumzhud%Rnx;nAIhtvWI*MUs`F&X}X2npc)Hy12x46}v7O+jkAd}}t zccA$7Rq57kV;dyY%*MPho9@cxe}nOJ+*(DZ=` z3Zrqa&hqQQi>3w|&QDR#JYlM1v6=LX*-WoKyu$D^;tbSxcyFP{+@0?_t@vh~X5UJj z1qXcCO~{E$6s#iHT9z($k?y_w8heez&Rjw4d<+v))B=y4xyLenDuV2r=VwzRLE*OB zFg4J?z>m67qF_?QE{YVqLQB8;(G&+{loqsRJw1$mZmy7$1`f#mKj?0X3$zo-oBJkx zJ#3EO2N_w>VXdduWETi9(OxL-`_v)H64iKOTRmm?G^!Wke_JZFA5DC7dO!pjSrKTM z#v-b8w-RiJa6UpZB7XFE(+=CZ?C4S|GiP2AFdXB@S7);K;4g$#I7Pp_J&pZupkVqS zLZs0n!}l|fLtlXrxoE6}iBBEq_`lF#u`UvgFFbt07)L5-C{OkZii5V@$78 z=oOVpFF(A}Q8!~duw;fciqLik8nlQ|!(*jsQ-3ep|62{V$)XuJ%u>^{?F3z0Zl`)7 zN9nDx(Uh{YwB_&5Owd*S=Hts;@BN_{(~}{T*-hJW@ROF`d2!~72tCTa@bLDq+r<_& zq?qR(7s|-thOpwBv4&nN{@%2uwQ2P4&DaDMCxg$ZOUoVfQoohDW{dj^maJ&7y+(G; zsxiB0&FPRiUbpzYzMDh?Js#>&8+U4-5Cb&@nl>MZ(%+sBg-P$WsqE(R?TjX@aD3B( zBEM0fbb0B)RTNfHeHv;YV9AOGP}m$iva@N=hcl69b~U6rPT|-1I`qNB)@LI%|Axp1hvO<7o%{#Y5NYi6Ah{+a*1hx$Zs_RDgFQ7j8IVumbj z*jYk^0(oyR+W;gCU|{1027TIv$&*BQvEM3sr2R%q-W^u8_zhK9hjT(|c}ujTl(~hi zUFA62!oqUEu5JN0VrigZTF7G~dI&uRXRgIxj`zv@YQ4Z012%9X;GFr}yT4if+MprD zJOEfn!0JKNiaR?{*Wd<-3@=J2zUmvp`#OC-KTEtp?T_7$9hqjmSQ=>mDBb(BEsY4{ zUY$veU8ESmhu&)NK;e~R-pEO|tJSfkgj@99V}n(kN|TImc5htc5XCl6bKLgT(m(?ve~qMx zg2Uu{wBAN8Y;kT=hQf2Auciv80fXM1--8`j9`D=d* zH;_ehOU+OZ(7eJy4BgavfeG%7n>ndtvz^qm)2R45}$8$!`;-sV~VN5W4KtuU{{I$vB~sY zZ~J{CZ5xUV1wlQ*?GZk}MJ}5ViY;I%LA3<7nrpYYE6eHuXK6S`4y-%E>UmkNmJu@oS zuq_^TIp{oqrMalvwG!AF@FuDvA(G&O(vhu!20xKpf(D+@OZqZfqz66ajHM}6bPxTo z8>~DJzx#mcB^a#i|M|3wDbEX`1kb_(?#gy!YoI~hEo#e+W!`NmaHO2kKPSSaE5WRX z&*_}Ml!qR9ucu{&c5EkK{XWL;yqDXk-`K;bq7N()cLe_lZtrtw4{q-^hf%4Z@C6k%CuQ zVoa?@ikWIFHdSI)=EVwK(ftmCbKRx|Nq63)@jg%Ho=-`I@1bg$Gc@h}-*mf}K`-0p=_eGMkIn9K_lE5&8f?bW4;G_6M+yr)uJGMg znFobifFC0!;bcEw!k`9zCrB~B@z`=UY`(Jkn3Gy|;60g}(+>xsvDNq_>*xVR zS}5I*2@D*E9xicvxq!2qK7An{TV0TyLk$RXt}z1l*6kpcT!Q2X|EiQZ%T@*auNk9} zculM&3tLnB68a7MHW-v?ULox*{J9w>Yfy(tjf2x?YHr(@#GH9?tLR&afgK)}CIQzKc>b#1YO zomE6SDDvc1-fmenF6#VW$}3mF{Kh-6ArgSo@|g?y?)cg4Rn;vGG@l=KLdu<{%B9Z= zJqATgEcgCI()O~f^`y^ReVE0#ks!+s`n}L@^yb)THst>2Md6xTa!PZWSdAtpctGl< zL^%^yWJtewMp2u0monT}r~0=odu9RjOu+QDeVAFGX!_JAU1`9F-I?j9)!;n*(d1UF z$x=(Ujb||cF|df)k`|Bw+{Mv7nq_s+DN;H6NEY|0IU>+H>c~D~MZ?PGR5Ji1px1li!|tlh zj+E&?7~3qJY5!^(oQ8sq2TTD)qoub(KZ5wfrxx9DW=0JQXGpaD#5PZ-;`f?gLxV-1 zlDrO?Wz5Snd&nWq#Y$UtC|+Q3Xhsb(D~?&wFlM%4*b$a}QvzQCbj6|^fIF#L2d@)B zky%TS;l}abUJ0gcm2f73JMZA`iS)(P=BmPM+~MKZkvGpxR^0Ufb^i1bzgTuvH>8!} zYFN9p*lx)rQS_-Pw&h$2re(>MnUcgz9J8W<>C0--JL9AN_y2oFx8hEUFQC~opX~zY zNu=)8d=GIJLd!|kFSr$;u7>HPP?qq(93>h=7cz>e$O=94z*>qMIgx%IK8+rX+3eaN1MiNHQVlX} z4}X9ESIdKpGP@?QI;l-mg>ep&HFntFZP{H?G02DsfzvG;-HXH$?~I9&VIuPrwBV>f zgVrqWvxzk8!=G8_t6OA4YG7#lpDI01_`*+oW4f(35^Tl3d+86=^DR2sM+weK(8HKn zjTIk-bE?(d&%B0EoMq!oE?qcI1}|!-?PRduAMsNg?kmKZ6I&FXs@zJjbNF=Vd}gM@ zr=xGM%1ZI4H;+_fh;4E zJ4#?Rfg_d^rsN>dQLN_qb-)YX3_twrpzN?LMcE<&89&i+%zEjpBFQq-=0FCTMPQsO zZ{YnCvB|9X>=dv?dsbzX5$bSegVjuFgcnw`|8!et+m(T!omZK~UXmCk} zQUW?jwA$c&fdAj@5xyw!^6rK>gFe~SzAutidPk))XJ92TP4nn!g#!XG|M)Au)gTeutwF&d;YdX;4L&uo`|ImcmxaJ`i5PIiI zPhYNlHbYIQGw1H<1Yq=vB+U^8pAZPR%?LDDF)ZXvifI$E$0r84D0jd%Z@EH`BdqqY z<3SPM2!6M9+_I-6b${(arhd?yHNxQ^+Pjy(ZKpUOK@IemVdYe!XX|Z_9-oU9rFYvc zia$dZPQ=@V2S(m1iFxt22ibTw%MPzk4a$+loNcZMeidlr<}_eZC$rMBm^Rwu^?pJP zj!Jh$%KRdAZMof)*JUN`oA5P$Xdy8IR?4{?yTHgg`+aKn4pk0MKWj#xnqLGOwtjDo zi>5E9HRCo8>IEWTtZwT#V(0AeQsL+j<`qYs1h%)ljOZ4Q$D>v-u!7e?U}vUL3Fs<$ zLSlUONQ@Z4F9Pko32kWBiW-`KANy_Gy?8d!*})$D=~11y{Zd)k#?dso$+fFNn6j&{ z5tRt)inCTKe?~YTVIqyM;hmo`JX7n9mWS(;A#jZ16^1>)(+|yltt0kQ?aC*Z{~={+ z+IAl)%-SW8DTK#65A4-37=6ZwUs%tQ5>Uj$_d*GyePwo1oAaeVd{Tdke?nXYx31{x z)yzY^kYh|=3!4XkqB(*O>Jhjo8^P6qhI-6rzw(8zY&w6;CK}W&NijGeTn#OMn*Dpc zV+uR;emtcmw`UBsq>rYwV*Oo8C}rzBxM|!JuNMwBZMZp!Iut{OHeTBj!>byf zb_(AU*fOBwUc^o>JN@q4%-pb$95Hj1b#2dNdbO*F1?h#A)K z=7C{W7q9dA)Or3s9@A3X0#DeDv+#w>xrK+5jGxS415du=} zYQ{#ZHdVCTM_&)FT+vVzhV!lxt?TVG#qR+h)JqSpRQwsXP}o9dCzxYVZg$DRYHr@* zcUFshf$@dsO$&vEowrOZ0u(D0jDcdFeVmnVyt#*AWpm=dN>{FE3)k^0^)R+E3v?jE zR$;e-d+yTY$}Gu}l>&K`vyl)p^kxFD%_0Nmya#u6n*$g+a#I5Dn zjSR9cJ=W-0_0$H?`vO-6+WPIvi3(^Fc`mei!C;7y3YpFHgT+_?<V5Q5NnpRVOK(OqVKuhHeP#3w0{kEhI*J}K!%r+scob%R( zvK1&zr;k5YS=2|7*2e@=D~2PQf$IVdA0E$hKeU4R_GlGLoo5OXr3SzgXa9ZYEnDmY zQ=KUFx8chRH)c1mk)vtUnqb2FpESQ7=X#!_tJibUppjjeP(aW56`x1FkfUtn!Zj7y z&iV=$1{&X2zGy-7YgI_g7sz{E@$Q^Gpim&kXjmojEhe|T*l(5Tug`3tY~f9{39qKD zu>9-dI$VVe5?Sbq&;yhjum?vmIeW&}>G)MYnpM;FdBxV83N0^ns~%GfG_=m(OXl?L zf#Pb7EF-=Hs=oH>#nm zg#>+~mF)n)4}UfkwnwmoKUUV@c9QpKy;ZXX0|t_KQ_thPhi>!nug#=|qb9I93vMZW z=*5+RhP$THxL0i1j-58{YvEyGQZ=qhLT!tRDJ;BUcF10SFu9d!uh9(f9zXr1h2k@z zK~_Z3j-oNnbJ;nIS%?26IXGV2m9+*6;lsQYo`!BKO^#lqs(6WJ|Eylen(JN}J@PQuLQOrS8 zn_dd<3uOJ^AIWn$HUso`b$#U_uE{H9daZo<>|E{`Cy74f87&+5vZw64Q>%#8Y{BP0 z>CT)%=lIBbJ-K3A56GH!Yq_2I+?zGO86x4)zc=N2ta!j&=kH(oLv;m~=tBlt7I0R2 zd_)h$pW$rw`lHJYJwN)_kn-nE0nG{m4rinXqP9|#8i$0-7D(4)1)I90j;f5z1B%F_ zpPNgKREEe)^dkc;3;jRnu6Wmns5NfnBvV!xctbw@c`WTb1hqVm*cNsHu&myHybx{MSAvq$N>g%r8O^<3rHfvqZ=RoHdUv|y|B-=~3C?8? zzSB$bXQ-RK`S>!^{uWwEMobj5F}DiL{`z?ELRM;!Scl&aBGk&5S&bEc6I-?coi~{J zx1ipKB_6hhA5Cs)$~YuZknDJ__Zn4Qro=pCpb^p5y(QR~T_s|hrE{Hoje@~{@FaoI zSj{oYOmO3X;n3jjiLP=_b~|$G2#dNeI?~eQDtG*Md@0esQBEZ_Zx(2wL9k7YukAH6 zsRv{fp{;zheF{}7?cYjyClc_12KOb%q?F8e;H<w+dUN*AF*b|4NB}G5okTzf8os#koW@SridQau*7nzw6%@%M!w;~39#9`U zed)u4L?8pJH@I6S$w;grAOj7Z#kV+J&!Vdj zGL}i(%dy~!-%$0AqBADWEJ?1+8Yv}#OTYyh+>B?fRMr?q);U078do#;iC~7;+%=(0 z(5uFr)nPKw0-S&gvKGFN4c5qdE>_hSpSPbuFzVY%R}PV*=R)D$oh2K*N`v zH@F%akd5_<@!KpQ`|0e3RYfKLBb=%dRT=dJ}w zA<43pB<2zj>V4<~?|Zht z>HS`qY6JUgWE7>&P@+aatY~I7Rl+74eO+ifkuoibR{TEDkjR+4z}To)wEd%P1&Ixe zYYLPJC=&DV`#`&RB@bP?syr!04Ji1V)H=k13nJsZxuv0~L_dBXXs}YNSM8{xDXJ1x z%ba1eld(ONo*ELf2#7NpQtfJihNej*G?3Wv)npfme-jW{N1W4I4H?K(9v+~k$6RKl z*!dCL=OS6qQU7G!|@iat40x_N7%UwMdbo{Z#dx9Ndkd@alyhM`MYx6W@k2u z#BKuKZcZYx134c}BC!Dknj9e_kyy(a1lnGCktGsK*^5BiEU&RdVkw&uXyy(6l1L;< zEJN0@AUta@a711riNtiS5m8C3Xy~z@A+N7QVhJ;XA~P;{()*vtYb=pi!pD@Qj)wWF zljL=lNK9p7P-I3bWh+>}Ktw0xb(Tm>0kQO==ZUfg8u~*XA)?#zI!h#mKny(;6q)hA zo~!HR=(fS|~XAG1G@Co0A-wwGr% zi9|Dqm3B%&MqbqW*hbct*+_}RD~OTWL6I4YdA|r{h!*M)(Mv?s6D|Pqx=SR4LE_U< zgAR(!*y8PSXbm(EkZX0sU`!$kC!)&mwvpFd5&)o`Y#$MA%G3wXgQ_YhQbXS~5&a(n WiY~F7ZIik(N&BhJm4m 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�vt_-hc+SZ7EbwJPg;I78;+CA^e?rA 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

N!FjN&@qy0hoSdMY_y5WcISfl`&mTN& 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 i7o%brDi=5Rd Date: Fri, 13 Nov 2020 11:16:58 +0100 Subject: [PATCH 0237/1637] Build and deploy for amd64 --- .drone.yml | 108 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 91 insertions(+), 17 deletions(-) diff --git a/.drone.yml b/.drone.yml index 40fce85a..c6de1579 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,7 +1,66 @@ --- kind: pipeline type: docker -name: default +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 srs.epita.fr/fic-server/admin + - go get -v -d srs.epita.fr/fic-server/backend + - go get -v -d srs.epita.fr/fic-server/frontend + - go get -v -d srs.epita.fr/fic-server/dashboard + - mkdir deploy + + - name: build admin + image: golang:alpine + commands: + - go build -v -o deploy/admin-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/admin + + - name: build backend + image: golang:alpine + commands: + - go build -v -o deploy/backend-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/backend + + - name: build frontend + image: golang:alpine + commands: + - go build -v -o deploy/frontend-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/frontend + + - name: build dashboard + image: golang:alpine + commands: + - go build -v -o deploy/dashboard-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/dashboard + + - name: deploy + image: appleboy/drone-scp + settings: + tar_tmp_path: /tmp/ + host: srs.nemunai.re + target: /var/www/nemunai.re/srs/fic-binaries/${DRONE_BRANCH//\//-} + source: deploy/* + strip_components: 1 + username: + from_secret: ssh_username + key: + from_secret: deploy_key + port: + from_secret: ssh_port + +--- +kind: pipeline +type: docker +name: build-arm64 platform: os: linux @@ -24,7 +83,37 @@ steps: - name: build admin image: golang:alpine commands: - - go build -v -o admin/admin srs.epita.fr/fic-server/admin + - go build -v -o deploy/admin-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/admin + + - name: build backend + image: golang:alpine + commands: + - go build -v -o deploy/backend-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/backend + + - name: build frontend + image: golang:alpine + commands: + - go build -v -o deploy/frontend-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/frontend + + - name: build dashboard + image: golang:alpine + commands: + - go build -v -o deploy/dashboard-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/dashboard + + - name: deploy + image: appleboy/drone-scp + settings: + tar_tmp_path: /tmp/ + host: srs.nemunai.re + target: /var/www/nemunai.re/srs/fic-binaries/${DRONE_BRANCH//\//-} + source: deploy/* + strip_components: 1 + username: + from_secret: ssh_username + key: + from_secret: deploy_key + port: + from_secret: ssh_port - name: docker admin image: plugins/docker @@ -40,11 +129,6 @@ steps: branch: - master - - name: build backend - image: golang:alpine - commands: - - go build -v -o backend/backend srs.epita.fr/fic-server/backend - - name: docker backend image: plugins/docker settings: @@ -59,11 +143,6 @@ steps: branch: - master - - name: build frontend - image: golang:alpine - commands: - - go build -v -o frontend/frontend srs.epita.fr/fic-server/frontend - - name: docker frontend image: plugins/docker settings: @@ -78,11 +157,6 @@ steps: branch: - master - - name: build dashboard - image: golang:alpine - commands: - - go build -v -o dashboard/dashboard srs.epita.fr/fic-server/dashboard - - name: docker dashboard image: plugins/docker settings: From 1436d9ca815e54706b4d98d951b4d6add6a6e1d3 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 13 Nov 2020 11:25:29 +0100 Subject: [PATCH 0238/1637] admin: New route to reset settings to sane default values --- admin/api/settings.go | 28 ++++++++++++++++++++++++++++ admin/main.go | 21 +-------------------- admin/static/js/app.js | 1 + admin/static/views/settings.html | 15 +++++---------- 4 files changed, 35 insertions(+), 30 deletions(-) diff --git a/admin/api/settings.go b/admin/api/settings.go index fbd7867d..7cee373e 100644 --- a/admin/api/settings.go +++ b/admin/api/settings.go @@ -16,6 +16,9 @@ func init() { router.GET("/api/settings-ro.json", apiHandler(getROSettings)) router.GET("/api/settings.json", apiHandler(getSettings)) router.PUT("/api/settings.json", apiHandler(saveSettings)) + router.DELETE("/api/settings.json", apiHandler(func(_ httprouter.Params, _ []byte) (interface{}, error) { + return true, ResetSettings() + })) router.POST("/api/reset", apiHandler(reset)) } @@ -61,6 +64,29 @@ func ApplySettings(config settings.FICSettings) { fic.SubmissionUniqueness = config.SubmissionUniqueness } +func ResetSettings() error { + return settings.SaveSettings(path.Join(settings.SettingsDir, settings.SettingsFile), settings.FICSettings{ + Title: "Challenge FIC", + Authors: "Laboratoire SRS, ÉPITA", + FirstBlood: fic.FirstBlood, + SubmissionCostBase: fic.SubmissionCostBase, + ExerciceCurCoefficient: 1, + HintCurCoefficient: 1, + WChoiceCurCoefficient: 1, + AllowRegistration: false, + CanJoinTeam: false, + DenyTeamCreation: false, + DenyNameChange: false, + AcceptNewIssue: true, + EnableResolutionRoute: false, + PartialValidation: true, + UnlockedChallengeDepth: 0, + SubmissionUniqueness: false, + DisplayAllFlags: false, + EventKindness: false, + }) +} + func reset(_ httprouter.Params, body []byte) (interface{}, error) { var m map[string]string if err := json.Unmarshal(body, &m); err != nil { @@ -75,6 +101,8 @@ func reset(_ httprouter.Params, body []byte) (interface{}, error) { return true, fic.ResetExercices() } else if t == "game" { return true, fic.ResetGame() + } else if t == "settings" { + return true, ResetSettings() } else { return nil, errors.New("Unknown reset type") } diff --git a/admin/main.go b/admin/main.go index 31d18408..b818c7a5 100644 --- a/admin/main.go +++ b/admin/main.go @@ -168,26 +168,7 @@ func main() { // Initialize settings and load them if !settings.ExistsSettings(path.Join(settings.SettingsDir, settings.SettingsFile)) { - if err = settings.SaveSettings(path.Join(settings.SettingsDir, settings.SettingsFile), settings.FICSettings{ - Title: "Challenge FIC", - Authors: "Laboratoire SRS, ÉPITA", - FirstBlood: fic.FirstBlood, - SubmissionCostBase: fic.SubmissionCostBase, - ExerciceCurCoefficient: 1, - HintCurCoefficient: 1, - WChoiceCurCoefficient: 1, - AllowRegistration: false, - CanJoinTeam: false, - DenyTeamCreation: false, - DenyNameChange: false, - AcceptNewIssue: true, - EnableResolutionRoute: false, - PartialValidation: true, - UnlockedChallengeDepth: 0, - SubmissionUniqueness: false, - DisplayAllFlags: false, - EventKindness: false, - }); err != nil { + if err = api.ResetSettings(); err != nil { log.Fatal("Unable to initialize settings.json:", err) } } else { diff --git a/admin/static/js/app.js b/admin/static/js/app.js index e5ce31b4..d59fb03f 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -578,6 +578,7 @@ angular.module("FICApp") } $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.", "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.", diff --git a/admin/static/views/settings.html b/admin/static/views/settings.html index 44883487..c0117c5f 100644 --- a/admin/static/views/settings.html +++ b/admin/static/views/settings.html @@ -242,15 +242,10 @@ -

From 911bcb032eefff6635e67476253c35436a87dc64 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 13 Nov 2020 11:38:47 +0100 Subject: [PATCH 0239/1637] Nouvelle option pour avoir un lien vers le rapport QA de l'exercice --- admin/api/settings.go | 1 + admin/static/views/settings.html | 7 +++++++ frontend/static/views/defi.html | 1 + 3 files changed, 9 insertions(+) diff --git a/admin/api/settings.go b/admin/api/settings.go index 7cee373e..9196fdee 100644 --- a/admin/api/settings.go +++ b/admin/api/settings.go @@ -78,6 +78,7 @@ func ResetSettings() error { DenyTeamCreation: false, DenyNameChange: false, AcceptNewIssue: true, + QAenabled: false, EnableResolutionRoute: false, PartialValidation: true, UnlockedChallengeDepth: 0, diff --git a/admin/static/views/settings.html b/admin/static/views/settings.html index c0117c5f..6a57c8d8 100644 --- a/admin/static/views/settings.html +++ b/admin/static/views/settings.html @@ -150,6 +150,13 @@ +
+ +
+
Rapporter une anomalie sur cet exercice + Voir les éléments QA sur cet exercice
  • Gain : {{ 1 + settings.firstBlood | coeff }} prem's {{ themes[current_theme].exercices[current_exercice].curcoeff * settings.exerciceCurrentCoefficient | coeff }} bonus
  • Tenté par : (cumulant )
  • From ea334a8a2f30baa98397803561045947661350f5 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 13 Nov 2020 13:11:58 +0100 Subject: [PATCH 0240/1637] QA: add a list of team's exercices --- libfic/db.go | 11 ++++++ libfic/qa.go | 31 +++++++++++++++++ qa/api/todo.go | 27 +++++++++++++++ qa/static/js/qa.js | 35 +++++++++++++++++-- qa/static/views/home.html | 73 ++++++++++++++++++++++++++++----------- 5 files changed, 154 insertions(+), 23 deletions(-) diff --git a/libfic/db.go b/libfic/db.go index fc49decf..77005d9d 100644 --- a/libfic/db.go +++ b/libfic/db.go @@ -457,6 +457,17 @@ CREATE TABLE IF NOT EXISTS teams_qa_todo( FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice), FOREIGN KEY(id_team) REFERENCES teams(id_team) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; +`); err != nil { + return err + } + if _, err := db.Exec(` +CREATE TABLE IF NOT EXISTS teams_qa_view( + id_view INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, + id_team INTEGER NOT NULL, + id_exercice INTEGER NOT NULL, + FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice), + FOREIGN KEY(id_team) REFERENCES teams(id_team) +) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } diff --git a/libfic/qa.go b/libfic/qa.go index 8586dd5b..7e60f18e 100644 --- a/libfic/qa.go +++ b/libfic/qa.go @@ -238,3 +238,34 @@ func (t Team) NewQATodo(idExercice int64) (QATodo, error) { return QATodo{tid, t.Id, idExercice}, nil } } + +// QAView + +func (t Team) GetQAView() (res []QATodo, err error) { + var rows *sql.Rows + if rows, err = DBQuery("SELECT id_view, id_exercice FROM teams_qa_view WHERE id_team = ?", t.Id); err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + var t QATodo + if err = rows.Scan(&t.Id, &t.IdExercice); err != nil { + return + } + res = append(res, t) + } + err = rows.Err() + + return +} + +func (t Team) NewQAView(idExercice int64) (QATodo, error) { + if res, err := DBExec("INSERT INTO teams_qa_view (id_team, id_exercice) VALUES (?, ?)", t.Id, idExercice); err != nil { + return QATodo{}, err + } else if tid, err := res.LastInsertId(); err != nil { + return QATodo{}, err + } else { + return QATodo{tid, t.Id, idExercice}, nil + } +} diff --git a/qa/api/todo.go b/qa/api/todo.go index 9aa7cbc8..2d46feef 100644 --- a/qa/api/todo.go +++ b/qa/api/todo.go @@ -12,6 +12,8 @@ import ( func init() { router.GET("/api/qa_exercices.json", apiHandler(getExerciceTested)) router.GET("/api/qa_mywork.json", apiHandler(getQAWork)) + router.GET("/api/qa_myexercices.json", apiHandler(getQAView)) + router.POST("/api/qa_my_exercices.json", apiHandler(addQAView)) router.GET("/api/qa_work.json", apiHandler(getQATodo)) router.POST("/api/qa_work.json", apiHandler(createQATodo)) } @@ -42,6 +44,14 @@ func getExerciceTested(u QAUser, ps httprouter.Params, body []byte) (interface{} } } +func getQAView(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) { + if team, err := fic.GetTeam(u.TeamId); err != nil { + return nil, err + } else { + return team.GetQAView() + } +} + func getQAWork(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) { if team, err := fic.GetTeam(u.TeamId); err != nil { return nil, err @@ -74,3 +84,20 @@ func createQATodo(u QAUser, ps httprouter.Params, body []byte) (interface{}, err return team.NewQATodo(ut.IdExercice) } } + +func addQAView(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) { + if u.User != "nemunaire" { + return nil, errors.New("Restricted") + } + + var ut fic.QATodo + if err := json.Unmarshal(body, &ut); err != nil { + return nil, err + } + + if team, err := fic.GetTeam(ut.IdTeam); err != nil { + return nil, err + } else { + return team.NewQAView(ut.IdExercice) + } +} diff --git a/qa/static/js/qa.js b/qa/static/js/qa.js index b4894d19..efd177ac 100644 --- a/qa/static/js/qa.js +++ b/qa/static/js/qa.js @@ -96,6 +96,9 @@ angular.module("FICApp") .factory("TodoWorked", function($resource) { return $resource("api/qa_mywork.json") }) + .factory("MyExercices", function($resource) { + return $resource("api/qa_myexercices.json") + }) .factory("ExercicesTested", function($resource) { return $resource("api/qa_exercices.json") }) @@ -240,6 +243,14 @@ angular.module("FICApp") }; }) + .controller("MyExercicesController", function($scope, MyExercices, $location) { + $scope.my_exercices = MyExercices.query(); + + $scope.show = function(id) { + $location.url("/exercices/" + id); + }; + }) + .controller("ThemesListController", function($scope, Theme, $location, $rootScope, $http) { $scope.themes = Theme.query(); $scope.fields = ["name", "authors", "headline"]; @@ -322,7 +333,7 @@ angular.module("FICApp") $scope.exercice = new ThemedExercice(); } else { $scope.exercice = Exercice.get({ exerciceId: $routeParams.exerciceId }); - } + } $http({ url: "api/themes.json", method: "GET" @@ -342,7 +353,27 @@ angular.module("FICApp") }) .controller("ExerciceQAController", function($scope, $rootScope, ExerciceQA, $routeParams, $location, $http) { - $scope.queries = ExerciceQA.query({ exerciceId: $routeParams.exerciceId }); + if ($routeParams.exerciceId) { + $scope.queries = ExerciceQA.query({ exerciceId: $routeParams.exerciceId }); + } else { + $scope.queries = ExerciceQA.query({ exerciceId: $scope.todo.id_exercice }); + } + $scope.queriesNSolved = "N/A" + $scope.queriesNClosed = "N/A" + $scope.queries.$promise.then(function (queries) { + var nbResolved = 0; + var nbClosed = 0; + queries.forEach(function(q) { + if (q.solved) { + nbResolved++; + } + if (q.closed) { + nbClosed++; + } + }) + $scope.queriesNSolved = queries.length - nbResolved + $scope.queriesNClosed = queries.length - nbClosed + }) $scope.fields = ["state", "subject", "user", "creation"]; $scope.namedFields = { "state": "État", diff --git a/qa/static/views/home.html b/qa/static/views/home.html index 29733a15..6cf7271c 100644 --- a/qa/static/views/home.html +++ b/qa/static/views/home.html @@ -1,25 +1,56 @@ -
    -

    Interface QA du challenge

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

    Challenges à valider

    +
    - À tester - - À commenter - - Commenté mais pas testé - - {{ mytheme.name }} - - {{ myexercice.title }} -
    + + + + + + + + + + + + + + + + +
    AvancementScénarioDéfi
    + À tester + + À commenter + + Commenté mais pas testé + + {{ mytheme.name }} + + {{ myexercice.title }} +
    +
    +
    +

    Vos challenges

    + + + + + + + + + + + + +
    DéfiRequêtes
    + {{ mytheme.name }} – + {{ myexercice.title }} + + {{ queriesNSolved }} / {{ queriesNClosed }} +
    From 1a3e14040c90a9ff4ab599c4a04a0e2d76b96e9f Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 13 Nov 2020 14:29:23 +0100 Subject: [PATCH 0241/1637] Settings: avoid transmitting false variables --- settings/settings.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/settings/settings.go b/settings/settings.go index b0b30afb..31d2f494 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -50,27 +50,29 @@ type FICSettings struct { WChoiceCurCoefficient float64 `json:"wchoiceCurrentCoefficient"` // AllowRegistration permits unregistered Team to register themselves. - AllowRegistration bool `json:"allowRegistration"` + AllowRegistration bool `json:"allowRegistration,omitempty"` // CanJoinTeam permits unregistered account to join an already existing team. - CanJoinTeam bool `json:"canJoinTeam"` + CanJoinTeam bool `json:"canJoinTeam,omitempty"` // DenyTeamCreation forces unregistered account to join a team, it's not possible to create a new team. - DenyTeamCreation bool `json:"denyTeamCreation"` + DenyTeamCreation bool `json:"denyTeamCreation,omitempty"` // DenyNameChange disallow Team to change their name. - DenyNameChange bool `json:"denyNameChange"` + DenyNameChange bool `json:"denyNameChange,omitempty"` // AcceptNewIssue enables the reporting system. - AcceptNewIssue bool `json:"acceptNewIssue"` + AcceptNewIssue bool `json:"acceptNewIssue,omitempty"` + // QAenabled enables links to QA interface. + QAenabled bool `json:"QAenabled,omitempty"` // EnableResolutionRoute activates the route displaying resolution movies. - EnableResolutionRoute bool `json:"enableResolutionRoute"` + EnableResolutionRoute bool `json:"enableResolutionRoute,omitempty"` // PartialValidation validates each correct given answers, don't expect Team to give all correct answer in a try. - PartialValidation bool `json:"partialValidation"` + PartialValidation bool `json:"partialValidation,omitempty"` // UnlockedChallengeDepth don't show (or permit to solve) to team challenges they are not unlocked through dependancies. UnlockedChallengeDepth int `json:"unlockedChallengeDepth"` // SubmissionUniqueness don't count multiple times identical tries. - SubmissionUniqueness bool `json:"submissionUniqueness"` + SubmissionUniqueness bool `json:"submissionUniqueness,omitempty"` // DisplayAllFlags doesn't respect the predefined constraint existing between flags. - DisplayAllFlags bool `json:"displayAllFlags"` + DisplayAllFlags bool `json:"displayAllFlags,omitempty"` // EventKindness will ask browsers to delay notification interval. - EventKindness bool `json:"eventKindness"` + EventKindness bool `json:"eventKindness,omitempty"` } // ExistsSettings checks if the settings file can by found at the given path. From 1ad4382e971c6558589dc7c7163a107ef3cdde83 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 13 Nov 2020 14:31:51 +0100 Subject: [PATCH 0242/1637] CI: also build qa --- .drone.yml | 25 ++++++++++++++++++++++ Dockerfile-qa | 31 ++++++++++++++++++++++++++++ configs/nginx-frontend-htpasswd.conf | 6 ++++++ 3 files changed, 62 insertions(+) create mode 100644 Dockerfile-qa diff --git a/.drone.yml b/.drone.yml index c6de1579..dcf805c5 100644 --- a/.drone.yml +++ b/.drone.yml @@ -20,6 +20,7 @@ steps: - go get -v -d srs.epita.fr/fic-server/backend - go get -v -d srs.epita.fr/fic-server/frontend - go get -v -d srs.epita.fr/fic-server/dashboard + - go get -v -d srs.epita.fr/fic-server/qa - mkdir deploy - name: build admin @@ -42,6 +43,11 @@ steps: commands: - go build -v -o deploy/dashboard-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/dashboard + - name: build qa + image: golang:alpine + commands: + - go build -v -o deploy/qa-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/qa + - name: deploy image: appleboy/drone-scp settings: @@ -100,6 +106,11 @@ steps: commands: - go build -v -o deploy/dashboard-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/dashboard + - name: build qa + image: golang:alpine + commands: + - go build -v -o deploy/qa-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/qa + - name: deploy image: appleboy/drone-scp settings: @@ -170,3 +181,17 @@ steps: when: branch: - master + + - name: docker qa + image: plugins/docker + settings: + username: + from_secret: docker_username + password: + from_secret: docker_password + repo: nemunaire/fic-qa + tags: latest + dockerfile: Dockerfile-qa + when: + branch: + - master diff --git a/Dockerfile-qa b/Dockerfile-qa new file mode 100644 index 00000000..b3ebd44c --- /dev/null +++ b/Dockerfile-qa @@ -0,0 +1,31 @@ +FROM golang:alpine as gobuild + +RUN apk add --no-cache git + +WORKDIR /go/src/srs.epita.fr/fic-server/qa + +ADD qa/*.go ./ +ADD qa/api/*.go ./api/ +ADD libfic ../libfic/ +ADD settings ../settings/ + +RUN go get -d -v +RUN go build -v + + +FROM alpine + +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 +COPY qa/static/index.html /srv/htdocs-qa/ +COPY qa/static/css/bootstrap.min.css frontend/static/css/fic.css frontend/static/css/glyphicon.css /srv/htdocs-qa/css/ +COPY frontend/static/fonts /srv/htdocs-qa/fonts +COPY frontend/static/img/ /srv/htdocs-qa/img/ +COPY qa/static/js/qa.js frontend/static/js/angular.min.js qa/static/js/angular-resource.min.js frontend/static/js/angular-route.min.js frontend/static/js/angular-sanitize.min.js frontend/static/js/bootstrap.min.js frontend/static/js/common.js frontend/static/js/i18n frontend/static/js/jquery.min.js /srv/htdocs-qa/js/ diff --git a/configs/nginx-frontend-htpasswd.conf b/configs/nginx-frontend-htpasswd.conf index 74b5af77..c745f3fb 100644 --- a/configs/nginx-frontend-htpasswd.conf +++ b/configs/nginx-frontend-htpasswd.conf @@ -182,6 +182,12 @@ server { proxy_redirect off; } + location /qa/ { + proxy_pass http://fic-qa:8083; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_redirect off; + } + location /registration { #auth_basic "Secure Zone"; #auth_basic_user_file ficpasswd; From 3bc8f0bf952dd1a52eab67bc462ef59eb904da11 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 13 Nov 2020 15:15:08 +0100 Subject: [PATCH 0243/1637] CI: add Go vet --- .drone.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.drone.yml b/.drone.yml index dcf805c5..f5e5e29d 100644 --- a/.drone.yml +++ b/.drone.yml @@ -23,6 +23,16 @@ steps: - go get -v -d srs.epita.fr/fic-server/qa - mkdir deploy + - name: vet + image: golang:alpine + commands: + - apk --no-cache add build-base + - go vet -v srs.epita.fr/fic-server/admin + - go vet -v srs.epita.fr/fic-server/backend + - go vet -v srs.epita.fr/fic-server/frontend + - go vet -v srs.epita.fr/fic-server/dashboard + - go vet -v srs.epita.fr/fic-server/qa + - name: build admin image: golang:alpine commands: From 38c18ef1aae3ef35cb79eadd112834e9a9752c46 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 13 Nov 2020 15:15:37 +0100 Subject: [PATCH 0244/1637] CI: build repochecker and generate repochecker.version --- .drone.yml | 21 +++++++++++++++++++++ docker-compose.yml | 35 +++++++++++++++++++++++++++++++++++ repochecker/update.go | 4 ++-- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index f5e5e29d..b6b0e650 100644 --- a/.drone.yml +++ b/.drone.yml @@ -20,6 +20,7 @@ steps: - go get -v -d srs.epita.fr/fic-server/backend - go get -v -d srs.epita.fr/fic-server/frontend - go get -v -d srs.epita.fr/fic-server/dashboard + - go get -v -d srs.epita.fr/fic-server/repochecker - go get -v -d srs.epita.fr/fic-server/qa - mkdir deploy @@ -31,6 +32,7 @@ steps: - go vet -v srs.epita.fr/fic-server/backend - go vet -v srs.epita.fr/fic-server/frontend - go vet -v srs.epita.fr/fic-server/dashboard + - go vet -v srs.epita.fr/fic-server/repochecker - go vet -v srs.epita.fr/fic-server/qa - name: build admin @@ -53,6 +55,20 @@ steps: commands: - go build -v -o deploy/dashboard-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/dashboard + - name: build repochecker + image: golang:alpine + commands: + - go build -v -o deploy/repochecker-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker + - grep "const version" repochecker/update.go | sed -r 's/^.*=\s*(\S.*)$/\1/' > deploy/repochecker.version + + - name: build repochecker for macOS + image: golang:alpine + commands: + - go build -v -o deploy/repochecker-darwin-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker + environment: + GOOS: darwin + GOARCH: amd64 + - name: build qa image: golang:alpine commands: @@ -116,6 +132,11 @@ steps: commands: - go build -v -o deploy/dashboard-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/dashboard + - name: build repochecker + image: golang:alpine + commands: + - go build -v -o deploy/repochecker-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker + - name: build qa image: golang:alpine commands: diff --git a/docker-compose.yml b/docker-compose.yml index a7cea8b2..1b5d97a3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,6 +3,8 @@ version: '3' services: mysql: image: mariadb + networks: + - fic-net volumes: - mysql-data:/var/lib/mysql environment: @@ -20,12 +22,15 @@ services: - "8081:8081" links: - mysql + networks: + - fic-net volumes: - /mnt/fic:/mnt/fic:ro - dashboard:/srv/DASHBOARD - files:/srv/FILES - pki:/srv/PKI - settings:/srv/SETTINGS + - submissions:/srv/submissions - teams:/srv/TEAMS command: -baseurl /admin/ -localimport /mnt/fic -localimportsymlink depends_on: @@ -41,6 +46,8 @@ services: image: nemunaire/fic-backend:latest links: - mysql + networks: + - fic-net volumes: - files:/srv/FILES:ro - teams:/srv/TEAMS @@ -51,6 +58,25 @@ services: environment: - MYSQL_HOST=mysql + fic-qa: + build: + context: . + dockerfile: Dockerfile-qa + image: nemunaire/fic-qa:latest + ports: + - "8083:8083" + links: + - mysql + networks: + - fic-net + volumes: + - teams:/srv/TEAMS + command: -baseurl /qa/ + depends_on: + - mysql + environment: + - MYSQL_HOST=mysql + fic-frontend: build: context: . @@ -59,6 +85,8 @@ services: command: "-startedFile /srv/startingblock/started" ports: - "8080:8080" + networks: + - fic-net volumes: - htdocs:/srv/htdocs-frontend:ro - files:/srv/FILES:ro @@ -77,6 +105,8 @@ services: command: "-baseurl /dashboard/" ports: - "8082:8082" + networks: + - fic-net volumes: - dashboard:/srv/DASHBOARD - teams:/srv/TEAMS:ro @@ -88,6 +118,8 @@ services: image: nginx:latest ports: - "8042:80" + networks: + - fic-net volumes: - /mnt/fic:/mnt/fic:ro - ./configs/nginx-frontend-htpasswd.conf:/etc/nginx/conf.d/default.conf:ro @@ -114,3 +146,6 @@ volumes: device: tmpfs submissions: teams: + +networks: + fic-net: diff --git a/repochecker/update.go b/repochecker/update.go index 9e51dd32..bfd1335b 100644 --- a/repochecker/update.go +++ b/repochecker/update.go @@ -9,7 +9,7 @@ import ( "net/http" ) -const version = 0 +const version = 9 func init() { go checkUpdate() @@ -24,7 +24,7 @@ func checkUpdate() { var v int if err := dec.Decode(&v); err == io.EOF || err == nil { if v > version { - log.Println("Your repochecker version is outdated, please update it:\n wget https://srs.nemunai.re/repochecker") + log.Println("Your repochecker version is outdated, please update it:\n https://srs.nemunai.re/fic-binaries/") } } } From a93d6c8c4982eddcca7d0cbecf2263f1eb6fc91f Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 13 Nov 2020 15:25:16 +0100 Subject: [PATCH 0245/1637] backend: fix bad printf format, thanks to go vet --- backend/issue.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/issue.go b/backend/issue.go index 01d07e51..2df3a8e0 100644 --- a/backend/issue.go +++ b/backend/issue.go @@ -56,7 +56,7 @@ func treatIssue(pathname string, team fic.Team) { claim.State = "new" claim.Update() - log.Printf("%s [OOK] New comment added to issue id=%d: id_description=%s\n", id, claim.Id, desc.Id) + 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) } From c10becba91598e7fe30d7a6d56b42285c17a836f Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 13 Nov 2020 16:08:22 +0100 Subject: [PATCH 0246/1637] CI: also deploy tarballs for static files --- .drone.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.drone.yml b/.drone.yml index b6b0e650..3aaa50c3 100644 --- a/.drone.yml +++ b/.drone.yml @@ -39,6 +39,7 @@ steps: image: golang:alpine commands: - go build -v -o deploy/admin-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/admin + - tar chjf deploy/htdocs-admin.tar.bz2 htdocs-admin - name: build backend image: golang:alpine @@ -49,11 +50,13 @@ steps: image: golang:alpine commands: - go build -v -o deploy/frontend-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/frontend + - tar chjf deploy/htdocs-frontend.tar.bz2 htdocs-frontend - name: build dashboard image: golang:alpine commands: - go build -v -o deploy/dashboard-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/dashboard + - tar chjf deploy/htdocs-dashboard.tar.bz2 htdocs-dashboard - name: build repochecker image: golang:alpine @@ -73,6 +76,7 @@ steps: image: golang:alpine commands: - go build -v -o deploy/qa-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/qa + - tar chjf deploy/htdocs-qa.tar.bz2 htdocs-qa - name: deploy image: appleboy/drone-scp From f53a5dbcf9659e940cdaebb11f9e92dd02807384 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 11 Dec 2020 19:38:57 +0100 Subject: [PATCH 0247/1637] admin: also delete useless QA content with exercice --- libfic/exercice.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libfic/exercice.go b/libfic/exercice.go index 0a4b91fa..72adb784 100644 --- a/libfic/exercice.go +++ b/libfic/exercice.go @@ -257,6 +257,10 @@ func (e Exercice) DeleteCascade() (int64, error) { return 0, err } else if _, err := DBExec("DELETE FROM exercice_tags WHERE id_exercice = ?", e.Id); err != nil { return 0, err + } else if _, err := DBExec("DELETE FROM teams_qa_todo WHERE id_exercice = ?", e.Id); err != nil { + return 0, err + } else if _, err := DBExec("DELETE FROM teams_qa_view WHERE id_exercice = ?", e.Id); err != nil { + return 0, err } else { return e.Delete() } From 8e8fa7c61cb4720ca86e3ccf018c2888559395c7 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 11 Dec 2020 21:03:12 +0100 Subject: [PATCH 0248/1637] sync: use Separator attribute --- admin/sync/exercice_keys.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index 43be18de..10e8f366 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -33,9 +33,7 @@ func validatorRegexp(vre string) (validator_regexp *string) { return } -func getRawKey(input interface{}, validatorRe string, ordered bool, showLines bool) (raw string, prep string, errs []string) { - separator := "," - +func getRawKey(input interface{}, validatorRe string, ordered bool, showLines bool, separator string) (raw string, prep string, errs []string) { // Concatenate array if f, ok := input.([]interface{}); ok { if len(validatorRe) > 0 { @@ -108,7 +106,7 @@ func buildKeyFlag(exercice fic.Exercice, flag ExerciceFlag, flagline int, defaul errs = append(errs, fmt.Sprintf("%q: flag #%d: Label should not end with punct (%q). Reword your label as a description of the expected flag, `:` are automatically appended.", path.Base(exercice.Path), flagline, flag.Label[len(flag.Label)-1])) } - raw, prep, terrs := getRawKey(flag.Raw, flag.ValidatorRe, flag.Ordered, flag.ShowLines) + raw, prep, terrs := getRawKey(flag.Raw, flag.ValidatorRe, flag.Ordered, flag.ShowLines, flag.Separator) if len(terrs) > 0 { for _, err := range terrs { @@ -151,7 +149,7 @@ func buildKeyFlag(exercice fic.Exercice, flag ExerciceFlag, flagline int, defaul } for _, choice := range flag.Choice { - val, prep, terrs := getRawKey(choice.Value, "", false, false) + val, prep, terrs := getRawKey(choice.Value, "", false, false, "") if len(terrs) > 0 { for _, err := range terrs { errs = append(errs, fmt.Sprintf("%q: flag #%d: %s", path.Base(exercice.Path), flagline, err)) From f7c15925c6c5a92136b2d86cf6937ce9333d14df Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 11 Dec 2020 23:25:14 +0100 Subject: [PATCH 0249/1637] frontend: Fix random error when validating challenge --- frontend/static/js/challenge.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/static/js/challenge.js b/frontend/static/js/challenge.js index e6c5f497..2a012242 100644 --- a/frontend/static/js/challenge.js +++ b/frontend/static/js/challenge.js @@ -341,7 +341,7 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"]) this[cid].disabled = mcq.solved || mcq.part_solved || (this[cid].justification && this[cid].justification.solved); if (!this[cid].disabled) - this[cid].value = $scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].mcqs[mid] && $scope.my.exercices[eid].mcqs[mid].choices[cid] && $scope.my.exercices[eid].mcqs[mid].choices[cid].value + this[cid].value = $scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].mcqs && $scope.my.exercices[eid].mcqs[mid] && $scope.my.exercices[eid].mcqs[mid].choices[cid] && $scope.my.exercices[eid].mcqs[mid].choices[cid].value if (mcq.justify) { if (!this[cid].justification) From 1445917fecfe00c739d339769ab17c46ca0f3d3f Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 30 Jan 2021 05:13:56 +0100 Subject: [PATCH 0250/1637] Include all existing associations when generating htpasswd --- admin/api/certificate.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/admin/api/certificate.go b/admin/api/certificate.go index de46ccc5..9bc177cf 100644 --- a/admin/api/certificate.go +++ b/admin/api/certificate.go @@ -154,11 +154,16 @@ func genHtpasswd(ssha bool) (ret string, err error) { hash.Write([]byte(cert.Password)) hash.Write([]byte(salt)) - passwdline := fmt.Sprintf(":{SSHA}%s\n",base64.StdEncoding.EncodeToString(append(hash.Sum(nil), 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( @@ -274,7 +279,7 @@ type CertExported struct { Id string `json:"id"` Creation time.Time `json:"creation"` Password string `json:"password,omitempty"` - IdTeam *int64 `json:"id_team"` + IdTeam *int64 `json:"id_team"` Revoked *time.Time `json:"revoked"` } From 3434535f518e73d0a538f981a6def8b3ef643614 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 4 Feb 2021 19:04:12 +0100 Subject: [PATCH 0251/1637] CI: try generating static binaries --- .drone.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.drone.yml b/.drone.yml index 3aaa50c3..6669c5e3 100644 --- a/.drone.yml +++ b/.drone.yml @@ -40,35 +40,46 @@ steps: commands: - go build -v -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 - name: build backend image: golang:alpine commands: - go build -v -o deploy/backend-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/backend + environment: + CGO_ENABLED: 0 - name: build frontend image: golang:alpine commands: - go build -v -o deploy/frontend-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/frontend - tar chjf deploy/htdocs-frontend.tar.bz2 htdocs-frontend + environment: + CGO_ENABLED: 0 - name: build dashboard image: golang:alpine commands: - go build -v -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 - name: build repochecker image: golang:alpine commands: - go build -v -o deploy/repochecker-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker - grep "const version" repochecker/update.go | sed -r 's/^.*=\s*(\S.*)$/\1/' > deploy/repochecker.version + environment: + CGO_ENABLED: 0 - name: build repochecker for macOS image: golang:alpine commands: - go build -v -o deploy/repochecker-darwin-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker environment: + CGO_ENABLED: 0 GOOS: darwin GOARCH: amd64 @@ -77,6 +88,8 @@ steps: commands: - go build -v -o deploy/qa-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/qa - tar chjf deploy/htdocs-qa.tar.bz2 htdocs-qa + environment: + CGO_ENABLED: 0 - name: deploy image: appleboy/drone-scp From 2cf9723c6c435da3e4040b3963c6b3d43686be99 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 5 Feb 2021 12:05:23 +0100 Subject: [PATCH 0252/1637] qa: Add tested exercices to Todo list --- qa/api/todo.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/qa/api/todo.go b/qa/api/todo.go index 2d46feef..94a0d88f 100644 --- a/qa/api/todo.go +++ b/qa/api/todo.go @@ -64,7 +64,22 @@ func getQATodo(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) if team, err := fic.GetTeam(u.TeamId); err != nil { return nil, err } else { - return team.GetQATodo() + todo, err := team.GetQATodo() + if err != nil { + return nil, err + } + + if exercices, err := fic.GetExercices(); err != nil { + return todo, nil + } else { + for _, exercice := range exercices { + if cnt, _ := team.CountTries(exercice); cnt > 0 { + todo = append(todo, fic.QATodo{0, team.Id, exercice.Id}) + } + } + } + + return todo, nil } } From f4dcaa23a316a57e2ae958813582379453185afb Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 5 Feb 2021 16:56:27 +0100 Subject: [PATCH 0253/1637] QA: Add new script to migrate QA content from a DB to another --- admin/api/qa.go | 84 +++++++++++++++++++++++++++++++++++++++++++++++ libfic/db.go | 4 +-- libfic/qa.go | 8 ++--- qa-fill-todo.sh | 27 +++++++++++++++ qa-fill-view.sh | 16 +++++++++ qa-merge-db.sh | 70 +++++++++++++++++++++++++++++++++++++++ qa/api/handler.go | 2 ++ qa/api/qa.go | 6 ++-- 8 files changed, 208 insertions(+), 9 deletions(-) create mode 100644 admin/api/qa.go create mode 100755 qa-fill-todo.sh create mode 100755 qa-fill-view.sh create mode 100755 qa-merge-db.sh diff --git a/admin/api/qa.go b/admin/api/qa.go new file mode 100644 index 00000000..20166cc4 --- /dev/null +++ b/admin/api/qa.go @@ -0,0 +1,84 @@ +package api + +import ( + "encoding/json" + "errors" + "strconv" + + "srs.epita.fr/fic-server/libfic" + + "github.com/julienschmidt/httprouter" +) + +func init() { + router.POST("/api/qa/", apiHandler(importExerciceQA)) + router.POST("/api/qa/:qid/comments", apiHandler(qaHandler(importQAComment))) +} + +func qaHandler(f func(fic.QAQuery, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { + return func(ps httprouter.Params, body []byte) (interface{}, error) { + if qid, err := strconv.ParseInt(string(ps.ByName("qid")), 10, 64); err != nil { + return nil, err + } else if query, err := fic.GetQAQuery(qid); err != nil { + return nil, err + } else { + return f(query, body) + } + } +} + +func importExerciceQA(_ httprouter.Params, body []byte) (interface{}, error) { + // Create a new query + var uq fic.QAQuery + if err := json.Unmarshal(body, &uq); err != nil { + return nil, err + } + + var exercice fic.Exercice + var err error + if uq.IdExercice == 0 { + return nil, errors.New("id_exercice not filled") + } else if exercice, err = fic.GetExercice(uq.IdExercice); err != nil { + return nil, err + } + + if len(uq.State) == 0 { + return nil, errors.New("State not filled") + } + + if len(uq.Subject) == 0 { + return nil, errors.New("Subject not filled") + } + + if qa, err := exercice.NewQAQuery(uq.Subject, uq.IdTeam, uq.User, uq.State); err != nil { + return nil, err + } else { + qa.Creation = uq.Creation + qa.Solved = uq.Solved + qa.Closed = qa.Closed + + _, err = qa.Update() + return qa, err + } +} + +func importQAComment(query fic.QAQuery, body []byte) (interface{}, error) { + // Create a new query + var uc fic.QAComment + if err := json.Unmarshal(body, &uc); err != nil { + return nil, err + } + + if len(uc.Content) == 0 { + return nil, errors.New("Empty comment") + } + + if qac, err := query.AddComment(uc.Content, uc.IdTeam, uc.User); err != nil { + return nil, err + } else { + qac.Date = uc.Date + + _, err = qac.Update() + return qac, err + } +} diff --git a/libfic/db.go b/libfic/db.go index 77005d9d..41ad70af 100644 --- a/libfic/db.go +++ b/libfic/db.go @@ -422,7 +422,7 @@ CREATE TABLE IF NOT EXISTS claim_descriptions( CREATE TABLE IF NOT EXISTS exercices_qa( id_qa INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, id_exercice INTEGER NOT NULL, - id_team INTEGER NOT NULL, + id_team INTEGER NULL, authuser VARCHAR(255) NOT NULL, subject VARCHAR(255) NOT NULL, creation TIMESTAMP NOT NULL, @@ -439,7 +439,7 @@ CREATE TABLE IF NOT EXISTS exercices_qa( CREATE TABLE IF NOT EXISTS qa_comments( id_comment INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, id_qa INTEGER NOT NULL, - id_team INTEGER NOT NULL, + id_team INTEGER NULL, authuser VARCHAR(255) NOT NULL, date TIMESTAMP NOT NULL, content TEXT NOT NULL, diff --git a/libfic/qa.go b/libfic/qa.go index 7e60f18e..25980a77 100644 --- a/libfic/qa.go +++ b/libfic/qa.go @@ -9,7 +9,7 @@ import ( type QAQuery struct { Id int64 `json:"id"` IdExercice int64 `json:"id_exercice"` - IdTeam int64 `json:"id_team"` + IdTeam *int64 `json:"id_team"` User string `json:"user"` Creation time.Time `json:"creation"` State string `json:"state"` @@ -91,7 +91,7 @@ func (e Exercice) GetQAQuery(id int64) (q QAQuery, err error) { } // NewQAQuery creates and fills a new struct QAQuery and registers it into the database. -func (e Exercice) NewQAQuery(subject string, teamId int64, user string, state string) (QAQuery, error) { +func (e Exercice) NewQAQuery(subject string, teamId *int64, user string, state string) (QAQuery, error) { if res, err := DBExec("INSERT INTO exercices_qa (id_exercice, id_team, authuser, creation, state, subject) VALUES (?, ?, ?, ?, ?, ?)", e.Id, teamId, user, time.Now(), state, subject); err != nil { return QAQuery{}, err } else if qid, err := res.LastInsertId(); err != nil { @@ -139,7 +139,7 @@ func ClearQAQueries() (int64, error) { // QAComment represents some text describing a QAQuery. type QAComment struct { Id int64 `json:"id"` - IdTeam int64 `json:"id_team"` + IdTeam *int64 `json:"id_team"` User string `json:"user"` Date time.Time `json:"date"` Content string `json:"content"` @@ -172,7 +172,7 @@ func (q QAQuery) GetComment(id int64) (c QAComment, err error) { } // AddComment append in the database a new description; then returns the corresponding structure. -func (q QAQuery) AddComment(content string, teamId int64, user string) (QAComment, error) { +func (q QAQuery) AddComment(content string, teamId *int64, user string) (QAComment, error) { if res, err := DBExec("INSERT INTO qa_comments (id_qa, id_team, authuser, date, content) VALUES (?, ?, ?, ?, ?)", q.Id, teamId, user, time.Now(), content); err != nil { return QAComment{}, err } else if cid, err := res.LastInsertId(); err != nil { diff --git a/qa-fill-todo.sh b/qa-fill-todo.sh new file mode 100755 index 00000000..a4fd2935 --- /dev/null +++ b/qa-fill-todo.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +NB_THEMES_TODO=3 +MIN_LVL_TODO=1 +MAX_LVL_TODO=3 + + +MIN_EXO_TODO=$(($MIN_LVL_TODO - 1)) +MAX_EXO_TODO=$(($MAX_LVL_TODO - 1)) + +curl -s http://127.0.0.1:8081/api/teams/ | jq -r '.[] | [(.id | tostring), " ", .name] | add' | while read TEAMID TEAMSTR +do + if echo $TEAMSTR | grep "FIC Groupe" > /dev/null 2> /dev/null + then + GRPFIC=$(echo $TEAMSTR | sed -r 's/FIC Groupe //') + + THEMES_TO_TESTS=$(curl -s http://127.0.0.1:8081/api/themes | jq -r '.[] | [(.id | tostring), " ", (.path | split("-") | .[0])] | add' | grep -v " $GRPFIC" | shuf | head -n $NB_THEMES_TODO | cut -d " " -f 1 | xargs | sed 's/^/"/;s/ /","/g;s/$/"/') + + curl -s http://127.0.0.1:8081/api/themes.json | jq -r '.['$THEMES_TO_TESTS'] | .exercices | keys | .['$MIN_EXO_TODO:$MAX_EXO_TODO'] | .[]' | while read EXID + do + #curl -X POST -d @- -H "X-FIC-Team: nemunaire" http://127.0.0.1:8083/api/qa_work.json < /dev/null 2> /dev/null; then echo TEAM_$TEAM=$ID | sed -r 's/FIC Groupe //'; fi; done` + +# Add their themes and exercices +curl -s http://127.0.0.1:8081/api/themes | jq '.[].id' | while read tid +do + TEAM="TEAM_$(curl -s http://127.0.0.1:8081/api/themes/$tid | jq -r .path | sed -r 's/-.*$//')" + curl -s http://127.0.0.1:8081/api/themes/$tid/exercices | jq .[].id | while read ex + do + curl -X POST -d @- -H "X-FIC-Team: nemunaire" http://127.0.0.1:8083/api/qa_my_exercices.json <&2 echo "Please give DB to read from as argument"; exit 1; } + +MYSQL_USER="${MYSQL_USER:-fic}" +MYSQL_PASSWORD="${MYSQL_PASSWORD:-fic}" +MYSQL_DATABASE="$1" + +QA_ADMIN="${QA_ADMIN:-nemunaire@nemunai.re}" + +EXERCICES=$(curl -s -H "X-FIC-Team: ${QA_ADMIN}" http://127.0.0.1:8083/api/exercices/) +TEAMS=$(curl -s http://127.0.0.1:8081/api/teams/ | jq -r '.[].id' | while read IDTEAM; do curl -s http://127.0.0.1:8081/api/teams/$IDTEAM/associations | jq -r ".[] | {login: ., id_team: \"$IDTEAM\"}"; done) + +echo "select Q.*, T.name, E.title from exercices_qa Q INNER JOIN exercices E ON E.id_exercice = Q.id_exercice INNER JOIN themes T ON T.id_theme = E.id_theme;" | mysql --skip-column-names -u "${MYSQL_USER}" --password="${MYSQL_PASSWORD}" "${MYSQL_DATABASE}" | tr '\t' '|' | while IFS='|' read IDQ IDEXERCICE IDTEAM REMOTE_USER SUBJECT CREATION STATE SOLVED CLOSED THEME EXERCICE_TITLE +do + # Search exercice by title + IDEXERCICE=$(echo "${EXERCICES}" | jq -r ".[] | select(.title == \"${EXERCICE_TITLE}\") | .id") + [ -z "$IDEXERCICE" ] && { >&2 echo "QA query $IDQ: no exercice match"; continue; } + + # Search user in teams (if not found, set to null, this is allowed) + IDTEAM=$(echo "${TEAMS}" | jq -r "select(.login == \"${REMOTE_USER}\") | .id_team") + [ -z "$IDTEAM" ] && IDTEAM=null + + CREATION=$(echo ${CREATION}Z | sed 's/ /T/') + [ "$SOLVED" == "NULL" ] && SOLVED=null || SOLVED="\"$(echo ${SOLVED}Z | sed 's/ /T/')\"" + [ "$CLOSED" == "NULL" ] && CLOSED=null || CLOSED="\"$(echo ${CLOSED}Z | sed 's/ /T/')\"" + + SUBJECT=$(cat < /dev/null +{ + "id_team": $IDTEAM, + "user": "$REMOTE_USER", + "date": "$DATEC", + "content": $COMMENT +} +EOF + done +done diff --git a/qa/api/handler.go b/qa/api/handler.go index 1d76e57f..f823a853 100644 --- a/qa/api/handler.go +++ b/qa/api/handler.go @@ -42,9 +42,11 @@ func apiHandler(f DispatchFunction) func(http.ResponseWriter, *http.Request, htt } else if teamid, err = strconv.ParseInt(ficteam, 10, 64); err != nil { if lnk, err := os.Readlink(path.Join(TeamsDir, ficteam)); err != nil { log.Printf("[ERR] Unable to readlink %q: %s\n", path.Join(TeamsDir, ficteam), err) + http.Error(w, fmt.Sprintf("{errmsg:\"Unable to validate authentication.\"}"), http.StatusInternalServerError) return } else if teamid, err = strconv.ParseInt(lnk, 10, 64); err != nil { log.Printf("[ERR] Error during ParseInt team %q: %s\n", lnk, err) + http.Error(w, fmt.Sprintf("{errmsg:\"Unable to validate authentication.\"}"), http.StatusInternalServerError) return } } diff --git a/qa/api/qa.go b/qa/api/qa.go index 373b5c53..26b96703 100644 --- a/qa/api/qa.go +++ b/qa/api/qa.go @@ -74,7 +74,7 @@ func createExerciceQA(u QAUser, exercice fic.Exercice, body []byte) (interface{} } } - if qa, err := exercice.NewQAQuery(uq.Subject, u.TeamId, u.User, uq.State); err != nil { + if qa, err := exercice.NewQAQuery(uq.Subject, &u.TeamId, u.User, uq.State); err != nil { return nil, err } else { var uc fic.QAComment @@ -83,7 +83,7 @@ func createExerciceQA(u QAUser, exercice fic.Exercice, body []byte) (interface{} } if uc.Content != "" { - _, err = qa.AddComment(uc.Content, u.TeamId, u.User) + _, err = qa.AddComment(uc.Content, &u.TeamId, u.User) } return qa, err @@ -132,7 +132,7 @@ func createQAComment(u QAUser, query fic.QAQuery, exercice fic.Exercice, body [] return nil, errors.New("Empty comment") } - return query.AddComment(uc.Content, u.TeamId, u.User) + return query.AddComment(uc.Content, &u.TeamId, u.User) } func deleteQAComment(u QAUser, comment fic.QAComment, query fic.QAQuery, exercice fic.Exercice, body []byte) (interface{}, error) { From 0d792dcd8fbb651a639267ad24f39bfde2f4af23 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 5 Feb 2021 16:57:23 +0100 Subject: [PATCH 0254/1637] frontend: don't use path to give team's ID, use a dedicated header --- configs/nginx-demo.conf | 28 +++++++++++----------------- configs/nginx-prod.conf | 28 +++++++++++----------------- frontend/main.go | 13 ++++++------- frontend/register.go | 8 +++++--- frontend/submissions.go | 36 ++++++++++++++++++++---------------- 5 files changed, 53 insertions(+), 60 deletions(-) diff --git a/configs/nginx-demo.conf b/configs/nginx-demo.conf index d072a5ed..2087f737 100644 --- a/configs/nginx-demo.conf +++ b/configs/nginx-demo.conf @@ -180,55 +180,49 @@ server { location /submit/ { include fic-auth.conf; - rewrite ^/submit/(.*)$ /submission/$team/$1 break; - - proxy_pass http://frontend:8080/; + proxy_pass http://frontend:8080/submission; proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-FIC-Team $team; proxy_redirect off; } location /submit/issue { include fic-auth.conf; - rewrite ^/submit/.*$ /issue/$team break; - - proxy_pass http://frontend:8080/; + proxy_pass http://frontend:8080/issue; proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-FIC-Team $team; proxy_redirect off; } location /submit/name { include fic-auth.conf; - rewrite ^/submit/.*$ /chname/$team break; - - proxy_pass http://frontend:8080/; + proxy_pass http://frontend:8080/chname; proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-FIC-Team $team; proxy_redirect off; } location /registration { include fic-auth.conf; - rewrite ^/registration /registration/$team break; - proxy_pass http://frontend: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; - rewrite ^/openhint/(.*)$ /openhint/$team/$1 break; - - proxy_pass http://frontend:8080/; + proxy_pass http://frontend: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; - rewrite ^/wantchoices/(.*)$ /wantchoices/$team/$1 break; - - proxy_pass http://frontend:8080/; + proxy_pass http://frontend:8080; proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-FIC-Team $team; proxy_redirect off; } } diff --git a/configs/nginx-prod.conf b/configs/nginx-prod.conf index 1ced4fa3..8abc3636 100644 --- a/configs/nginx-prod.conf +++ b/configs/nginx-prod.conf @@ -172,55 +172,49 @@ server { location /submit/ { include fic-auth.conf; - rewrite ^/submit/(.*)$ /submission/$team/$1 break; - - proxy_pass http://frontend:8080/; + proxy_pass http://frontend:8080/submission; proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-FIC-Team $team; proxy_redirect off; } location /submit/issue { include fic-auth.conf; - rewrite ^/submit/.*$ /issue/$team break; - - proxy_pass http://frontend:8080/; + proxy_pass http://frontend:8080/issue; proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-FIC-Team $team; proxy_redirect off; } location /submit/name { include fic-auth.conf; - rewrite ^/submit/.*$ /chname/$team break; - - proxy_pass http://frontend:8080/; + proxy_pass http://frontend:8080/chname; proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-FIC-Team $team; proxy_redirect off; } location /registration { include fic-auth.conf; - rewrite ^/registration /registration/$team break; - proxy_pass http://frontend: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; - rewrite ^/openhint/(.*)$ /openhint/$team/$1 break; - - proxy_pass http://frontend:8080/; + proxy_pass http://frontend: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; - rewrite ^/wantchoices/(.*)$ /wantchoices/$team/$1 break; - - proxy_pass http://frontend:8080/; + proxy_pass http://frontend:8080; proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-FIC-Team $team; proxy_redirect off; } } diff --git a/frontend/main.go b/frontend/main.go index 9dd60c69..498cd3cc 100644 --- a/frontend/main.go +++ b/frontend/main.go @@ -54,13 +54,13 @@ func main() { settings.LoadAndWatchSettings(path.Join(settings.SettingsDir, settings.SettingsFile), reloadSettings) // Register handlers - http.Handle(fmt.Sprintf("%s/chname/", *prefix), http.StripPrefix(fmt.Sprintf("%s/chname/", *prefix), submissionTeamChecker{"name change", ChNameHandler, *teamsDir})) - http.Handle(fmt.Sprintf("%s/issue/", *prefix), http.StripPrefix(fmt.Sprintf("%s/issue/", *prefix), submissionTeamChecker{"issue", IssueHandler, *teamsDir})) - http.Handle(fmt.Sprintf("%s/openhint/", *prefix), http.StripPrefix(fmt.Sprintf("%s/openhint/", *prefix), submissionTeamChecker{"opening hint", HintHandler, *teamsDir})) - http.Handle(fmt.Sprintf("%s/wantchoices/", *prefix), http.StripPrefix(fmt.Sprintf("%s/wantchoices/", *prefix), submissionTeamChecker{"wantint choices", WantChoicesHandler, *teamsDir})) - http.Handle(fmt.Sprintf("%s/registration/", *prefix), http.StripPrefix(fmt.Sprintf("%s/registration/", *prefix), submissionChecker{"registration", RegistrationHandler})) + http.Handle(fmt.Sprintf("%s/chname/", *prefix), http.StripPrefix(fmt.Sprintf("%s/chname/", *prefix), submissionTeamChecker{"name change", ChNameHandler, *teamsDir, *simulator})) + http.Handle(fmt.Sprintf("%s/issue/", *prefix), http.StripPrefix(fmt.Sprintf("%s/issue/", *prefix), submissionTeamChecker{"issue", IssueHandler, *teamsDir, *simulator})) + http.Handle(fmt.Sprintf("%s/openhint/", *prefix), http.StripPrefix(fmt.Sprintf("%s/openhint/", *prefix), submissionTeamChecker{"opening hint", HintHandler, *teamsDir, *simulator})) + http.Handle(fmt.Sprintf("%s/wantchoices/", *prefix), http.StripPrefix(fmt.Sprintf("%s/wantchoices/", *prefix), submissionTeamChecker{"wantint choices", WantChoicesHandler, *teamsDir, *simulator})) + http.Handle(fmt.Sprintf("%s/registration", *prefix), http.StripPrefix(fmt.Sprintf("%s/registration", *prefix), submissionChecker{"registration", RegistrationHandler})) http.Handle(fmt.Sprintf("%s/resolution/", *prefix), http.StripPrefix(fmt.Sprintf("%s/resolution/", *prefix), ResolutionHandler{})) - http.Handle(fmt.Sprintf("%s/submission/", *prefix), http.StripPrefix(fmt.Sprintf("%s/submission/", *prefix), submissionTeamChecker{"submission", SubmissionHandler, *teamsDir})) + http.Handle(fmt.Sprintf("%s/submission/", *prefix), http.StripPrefix(fmt.Sprintf("%s/submission/", *prefix), submissionTeamChecker{"submission", SubmissionHandler, *teamsDir, *simulator})) if *simulator != "" { if _, err := os.Stat(path.Join(*teamsDir, *simulator)); os.IsNotExist(err) { @@ -120,7 +120,6 @@ loop: } } - log.Print("The service is shutting down...") srv.Shutdown(context.Background()) log.Println("done") diff --git a/frontend/register.go b/frontend/register.go index 60de99a1..e81b2ef2 100644 --- a/frontend/register.go +++ b/frontend/register.go @@ -15,11 +15,13 @@ func RegistrationHandler(w http.ResponseWriter, r *http.Request, sURL []string) return } - if len(sURL) < 1 || len(sURL[0]) == 0 { - http.Error(w, "{\"errmsg\":\"Arguments manquants.\"}", http.StatusBadRequest) + teamInitialName := "-" + if t := r.Header.Get("X-FIC-Team"); t != "" { + teamInitialName = t + } else { + http.Error(w, "{\"errmsg\":\"Votre jeton d'authentification semble invalide. Contactez l'équipe serveur.\"}", http.StatusInternalServerError) return } - teamInitialName := sURL[0] // Check request type and size if r.Method != "POST" { diff --git a/frontend/submissions.go b/frontend/submissions.go index 8d97f065..575587a8 100644 --- a/frontend/submissions.go +++ b/frontend/submissions.go @@ -10,24 +10,29 @@ import ( type submissionHandler func(w http.ResponseWriter, r *http.Request, sURL []string) -type submissionChecker struct{ - kind string - next submissionHandler +type submissionChecker struct { + kind string + next submissionHandler } type submissionTeamHandler func(w http.ResponseWriter, r *http.Request, team string, sURL []string) -type submissionTeamChecker struct{ - kind string - next submissionTeamHandler - teamsDir string +type submissionTeamChecker struct { + kind string + next submissionTeamHandler + teamsDir string + simulator string } func (c submissionChecker) ServeHTTP(w http.ResponseWriter, r *http.Request) { if addr := r.Header.Get("X-Forwarded-For"); addr != "" { r.RemoteAddr = addr } - log.Printf("%s \"%s %s\" => %s [%s]\n", r.RemoteAddr, r.Method, r.URL.Path, c.kind, r.UserAgent()) + team := "-" + if t := r.Header.Get("X-FIC-Team"); t != "" { + team = t + } + log.Printf("%s %s \"%s %s\" => %s [%s]\n", r.RemoteAddr, team, r.Method, r.URL.Path, c.kind, r.UserAgent()) w.Header().Set("Content-Type", "application/json") @@ -41,21 +46,20 @@ func (c submissionChecker) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // Extract URL arguments - var sURL = strings.Split(r.URL.Path, "/") + var sURL = strings.Split(strings.TrimPrefix(r.URL.Path, "/"), "/") c.next(w, r, sURL) } func (c submissionTeamChecker) ServeHTTP(w http.ResponseWriter, r *http.Request) { - submissionChecker{c.kind, func(w http.ResponseWriter, r *http.Request, sURL []string){ - if len(sURL) < 1 { - http.Error(w, "{\"errmsg\":\"Arguments manquants.\"}", http.StatusBadRequest) - return + submissionChecker{c.kind, func(w http.ResponseWriter, r *http.Request, sURL []string) { + team := c.simulator + if t := r.Header.Get("X-FIC-Team"); t != "" { + team = t } - team := sURL[0] // Check team validity and existance - if len(team) < 1 || team == "public" { + if len(team) < 1 || team == "-" || team == "public" { log.Println("INVALID TEAM:", team) http.Error(w, "{\"errmsg\":\"Équipe inexistante.\"}", http.StatusBadRequest) return @@ -65,6 +69,6 @@ func (c submissionTeamChecker) ServeHTTP(w http.ResponseWriter, r *http.Request) return } - c.next(w, r, team, sURL[1:]) + c.next(w, r, team, sURL) }}.ServeHTTP(w, r) } From 10c408eda6e0e7621dcaf430acc9d64af515c682 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 23 Feb 2021 08:50:59 +0100 Subject: [PATCH 0255/1637] CI: add repochecker built for Apple M1 --- .drone.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.drone.yml b/.drone.yml index 6669c5e3..237e3ac7 100644 --- a/.drone.yml +++ b/.drone.yml @@ -154,6 +154,16 @@ steps: commands: - go build -v -o deploy/repochecker-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker + - name: build repochecker for macOS + image: golang:alpine + commands: + - apk --no-cache add build-base + - go build -v -o deploy/repochecker-darwin-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker + environment: + CGO_ENABLED: 0 + GOOS: darwin + GOARCH: arm64 + - name: build qa image: golang:alpine commands: From 5d13cfe01e606b7c2224ee6ff40cc3433312780e Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 23 Feb 2021 09:10:25 +0100 Subject: [PATCH 0256/1637] CI: create Docker manifest image --- .drone-manifest-fic-admin.yml | 22 +++++ .drone-manifest-fic-backend.yml | 22 +++++ .drone-manifest-fic-dashboard.yml | 22 +++++ .drone-manifest-fic-frontend.yml | 22 +++++ .drone-manifest-fic-qa.yml | 22 +++++ .drone.yml | 159 +++++++++++++++++++++++++++++- 6 files changed, 264 insertions(+), 5 deletions(-) create mode 100644 .drone-manifest-fic-admin.yml create mode 100644 .drone-manifest-fic-backend.yml create mode 100644 .drone-manifest-fic-dashboard.yml create mode 100644 .drone-manifest-fic-frontend.yml create mode 100644 .drone-manifest-fic-qa.yml diff --git a/.drone-manifest-fic-admin.yml b/.drone-manifest-fic-admin.yml new file mode 100644 index 00000000..fc1154f7 --- /dev/null +++ b/.drone-manifest-fic-admin.yml @@ -0,0 +1,22 @@ +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-backend.yml b/.drone-manifest-fic-backend.yml new file mode 100644 index 00000000..fc1154f7 --- /dev/null +++ b/.drone-manifest-fic-backend.yml @@ -0,0 +1,22 @@ +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-dashboard.yml b/.drone-manifest-fic-dashboard.yml new file mode 100644 index 00000000..4f74d234 --- /dev/null +++ b/.drone-manifest-fic-dashboard.yml @@ -0,0 +1,22 @@ +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-frontend.yml b/.drone-manifest-fic-frontend.yml new file mode 100644 index 00000000..23a9aa80 --- /dev/null +++ b/.drone-manifest-fic-frontend.yml @@ -0,0 +1,22 @@ +image: nemunaire/fic-frontend:{{#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:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 + platform: + architecture: amd64 + os: linux + - image: nemunaire/fic-frontend:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 + platform: + architecture: arm64 + os: linux + variant: v8 + - image: nemunaire/fic-frontend:{{#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 new file mode 100644 index 00000000..5a158970 --- /dev/null +++ b/.drone-manifest-fic-qa.yml @@ -0,0 +1,22 @@ +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.yml b/.drone.yml index 237e3ac7..765b7254 100644 --- a/.drone.yml +++ b/.drone.yml @@ -106,6 +106,81 @@ steps: port: from_secret: ssh_port + - 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 backend + image: plugins/docker + settings: + username: + from_secret: docker_username + password: + from_secret: docker_password + repo: nemunaire/fic-backend + auto_tag: true + auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} + dockerfile: Dockerfile-backend + when: + branch: + - master + + - name: docker frontend + image: plugins/docker + settings: + username: + from_secret: docker_username + password: + from_secret: docker_password + repo: nemunaire/fic-frontend + auto_tag: true + auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} + dockerfile: Dockerfile-frontend + 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 + --- kind: pipeline type: docker @@ -192,7 +267,8 @@ steps: password: from_secret: docker_password repo: nemunaire/fic-admin - tags: latest + auto_tag: true + auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} dockerfile: Dockerfile-admin when: branch: @@ -206,7 +282,8 @@ steps: password: from_secret: docker_password repo: nemunaire/fic-backend - tags: latest + auto_tag: true + auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} dockerfile: Dockerfile-backend when: branch: @@ -220,7 +297,8 @@ steps: password: from_secret: docker_password repo: nemunaire/fic-frontend - tags: latest + auto_tag: true + auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} dockerfile: Dockerfile-frontend when: branch: @@ -234,7 +312,8 @@ steps: password: from_secret: docker_password repo: nemunaire/fic-dashboard - tags: latest + auto_tag: true + auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} dockerfile: Dockerfile-dashboard when: branch: @@ -248,8 +327,78 @@ steps: password: from_secret: docker_password repo: nemunaire/fic-qa - tags: latest + auto_tag: true + auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} dockerfile: Dockerfile-qa when: branch: - master + + +--- +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 backend + image: plugins/manifest + settings: + auto_tag: true + ignore_missing: true + spec: .drone-manifest-fic-backend.yml + username: + from_secret: docker_username + password: + from_secret: docker_password + + - name: publish frontend + image: plugins/manifest + settings: + auto_tag: true + ignore_missing: true + spec: .drone-manifest-fic-frontend.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 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 + +trigger: + event: + - push + - tag + +depends_on: +- build-amd64 +- build-arm64 From 99862b6daa566c914bbb971910a56defa422add2 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 28 Feb 2021 18:28:05 +0100 Subject: [PATCH 0257/1637] CI: disable new go1.16 module behaviour --- .drone.yml | 32 ++++++++++++++++++++++++++++++++ Dockerfile-admin | 2 ++ Dockerfile-backend | 2 ++ Dockerfile-dashboard | 2 ++ Dockerfile-frontend | 2 ++ Dockerfile-qa | 2 ++ 6 files changed, 42 insertions(+) diff --git a/.drone.yml b/.drone.yml index 765b7254..8e3e9821 100644 --- a/.drone.yml +++ b/.drone.yml @@ -23,6 +23,8 @@ steps: - go get -v -d srs.epita.fr/fic-server/repochecker - go get -v -d srs.epita.fr/fic-server/qa - mkdir deploy + environment: + GO111MODULE: off - name: vet image: golang:alpine @@ -34,6 +36,8 @@ steps: - go vet -v srs.epita.fr/fic-server/dashboard - go vet -v srs.epita.fr/fic-server/repochecker - go vet -v srs.epita.fr/fic-server/qa + environment: + GO111MODULE: off - name: build admin image: golang:alpine @@ -42,6 +46,7 @@ steps: - tar chjf deploy/htdocs-admin.tar.bz2 htdocs-admin environment: CGO_ENABLED: 0 + GO111MODULE: off - name: build backend image: golang:alpine @@ -49,6 +54,7 @@ steps: - go build -v -o deploy/backend-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/backend environment: CGO_ENABLED: 0 + GO111MODULE: off - name: build frontend image: golang:alpine @@ -57,6 +63,7 @@ steps: - tar chjf deploy/htdocs-frontend.tar.bz2 htdocs-frontend environment: CGO_ENABLED: 0 + GO111MODULE: off - name: build dashboard image: golang:alpine @@ -65,6 +72,7 @@ steps: - tar chjf deploy/htdocs-dashboard.tar.bz2 htdocs-dashboard environment: CGO_ENABLED: 0 + GO111MODULE: off - name: build repochecker image: golang:alpine @@ -73,6 +81,7 @@ steps: - grep "const version" repochecker/update.go | sed -r 's/^.*=\s*(\S.*)$/\1/' > deploy/repochecker.version environment: CGO_ENABLED: 0 + GO111MODULE: off - name: build repochecker for macOS image: golang:alpine @@ -82,6 +91,7 @@ steps: CGO_ENABLED: 0 GOOS: darwin GOARCH: amd64 + GO111MODULE: off - name: build qa image: golang:alpine @@ -90,6 +100,7 @@ steps: - tar chjf deploy/htdocs-qa.tar.bz2 htdocs-qa environment: CGO_ENABLED: 0 + GO111MODULE: off - name: deploy image: appleboy/drone-scp @@ -203,31 +214,48 @@ steps: - go get -v -d srs.epita.fr/fic-server/backend - go get -v -d srs.epita.fr/fic-server/frontend - go get -v -d srs.epita.fr/fic-server/dashboard + environment: + GO111MODULE: off - name: build admin image: golang:alpine commands: - go build -v -o deploy/admin-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/admin + environment: + CGO_ENABLED: 0 + GO111MODULE: off - name: build backend image: golang:alpine commands: - go build -v -o deploy/backend-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/backend + environment: + CGO_ENABLED: 0 + GO111MODULE: off - name: build frontend image: golang:alpine commands: - go build -v -o deploy/frontend-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/frontend + environment: + CGO_ENABLED: 0 + GO111MODULE: off - name: build dashboard image: golang:alpine commands: - go build -v -o deploy/dashboard-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/dashboard + environment: + CGO_ENABLED: 0 + GO111MODULE: off - name: build repochecker image: golang:alpine commands: - go build -v -o deploy/repochecker-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker + environment: + CGO_ENABLED: 0 + GO111MODULE: off - name: build repochecker for macOS image: golang:alpine @@ -238,11 +266,15 @@ steps: CGO_ENABLED: 0 GOOS: darwin GOARCH: arm64 + GO111MODULE: off - name: build qa image: golang:alpine commands: - go build -v -o deploy/qa-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/qa + environment: + CGO_ENABLED: 0 + GO111MODULE: off - name: deploy image: appleboy/drone-scp diff --git a/Dockerfile-admin b/Dockerfile-admin index 5f0b2ff7..33f00d6f 100644 --- a/Dockerfile-admin +++ b/Dockerfile-admin @@ -2,6 +2,8 @@ FROM golang:alpine as gobuild RUN apk add --no-cache git +ENV GO111MODULE=off + WORKDIR /go/src/srs.epita.fr/fic-server/admin ADD settings ../settings/ diff --git a/Dockerfile-backend b/Dockerfile-backend index f8727a2e..44d76f6b 100644 --- a/Dockerfile-backend +++ b/Dockerfile-backend @@ -2,6 +2,8 @@ FROM golang:alpine as gobuild RUN apk add --no-cache git +ENV GO111MODULE=off + WORKDIR /go/src/srs.epita.fr/fic-server/backend ADD backend/*.go ./ diff --git a/Dockerfile-dashboard b/Dockerfile-dashboard index 31967070..ae5de072 100644 --- a/Dockerfile-dashboard +++ b/Dockerfile-dashboard @@ -2,6 +2,8 @@ FROM golang:alpine as gobuild RUN apk add --no-cache git +ENV GO111MODULE=off + WORKDIR /go/src/srs.epita.fr/fic-server/dashboard ADD dashboard/*.go ./ diff --git a/Dockerfile-frontend b/Dockerfile-frontend index 61d94ddb..7eec0883 100644 --- a/Dockerfile-frontend +++ b/Dockerfile-frontend @@ -2,6 +2,8 @@ FROM golang:alpine as gobuild RUN apk add --no-cache git +ENV GO111MODULE=off + WORKDIR /go/src/srs.epita.fr/fic-server/frontend ADD frontend/*.go ./ diff --git a/Dockerfile-qa b/Dockerfile-qa index b3ebd44c..bf49e97d 100644 --- a/Dockerfile-qa +++ b/Dockerfile-qa @@ -2,6 +2,8 @@ FROM golang:alpine as gobuild RUN apk add --no-cache git +ENV GO111MODULE=off + WORKDIR /go/src/srs.epita.fr/fic-server/qa ADD qa/*.go ./ From 9dc1f401b729294b098255b2c87e1fc505aa14b5 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 13 May 2021 23:47:18 +0200 Subject: [PATCH 0258/1637] Use go modules --- .drone.yml | 20 -------------------- Dockerfile-admin | 18 +++++++----------- Dockerfile-backend | 15 +++++++-------- Dockerfile-dashboard | 16 +++++++--------- Dockerfile-frontend | 15 +++++++-------- Dockerfile-qa | 16 +++++++--------- admin/sync/exercices.go | 2 +- admin/sync/markdown.go | 6 +++--- admin/sync/themes.go | 3 ++- go.mod | 15 +++++++++++++++ go.sum | 26 ++++++++++++++++++++++++++ 11 files changed, 82 insertions(+), 70 deletions(-) create mode 100644 go.mod create mode 100644 go.sum diff --git a/.drone.yml b/.drone.yml index 8e3e9821..99d57e0d 100644 --- a/.drone.yml +++ b/.drone.yml @@ -23,8 +23,6 @@ steps: - go get -v -d srs.epita.fr/fic-server/repochecker - go get -v -d srs.epita.fr/fic-server/qa - mkdir deploy - environment: - GO111MODULE: off - name: vet image: golang:alpine @@ -36,8 +34,6 @@ steps: - go vet -v srs.epita.fr/fic-server/dashboard - go vet -v srs.epita.fr/fic-server/repochecker - go vet -v srs.epita.fr/fic-server/qa - environment: - GO111MODULE: off - name: build admin image: golang:alpine @@ -46,7 +42,6 @@ steps: - tar chjf deploy/htdocs-admin.tar.bz2 htdocs-admin environment: CGO_ENABLED: 0 - GO111MODULE: off - name: build backend image: golang:alpine @@ -54,7 +49,6 @@ steps: - go build -v -o deploy/backend-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/backend environment: CGO_ENABLED: 0 - GO111MODULE: off - name: build frontend image: golang:alpine @@ -63,7 +57,6 @@ steps: - tar chjf deploy/htdocs-frontend.tar.bz2 htdocs-frontend environment: CGO_ENABLED: 0 - GO111MODULE: off - name: build dashboard image: golang:alpine @@ -72,7 +65,6 @@ steps: - tar chjf deploy/htdocs-dashboard.tar.bz2 htdocs-dashboard environment: CGO_ENABLED: 0 - GO111MODULE: off - name: build repochecker image: golang:alpine @@ -81,7 +73,6 @@ steps: - grep "const version" repochecker/update.go | sed -r 's/^.*=\s*(\S.*)$/\1/' > deploy/repochecker.version environment: CGO_ENABLED: 0 - GO111MODULE: off - name: build repochecker for macOS image: golang:alpine @@ -91,7 +82,6 @@ steps: CGO_ENABLED: 0 GOOS: darwin GOARCH: amd64 - GO111MODULE: off - name: build qa image: golang:alpine @@ -100,7 +90,6 @@ steps: - tar chjf deploy/htdocs-qa.tar.bz2 htdocs-qa environment: CGO_ENABLED: 0 - GO111MODULE: off - name: deploy image: appleboy/drone-scp @@ -214,8 +203,6 @@ steps: - go get -v -d srs.epita.fr/fic-server/backend - go get -v -d srs.epita.fr/fic-server/frontend - go get -v -d srs.epita.fr/fic-server/dashboard - environment: - GO111MODULE: off - name: build admin image: golang:alpine @@ -223,7 +210,6 @@ steps: - go build -v -o deploy/admin-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/admin environment: CGO_ENABLED: 0 - GO111MODULE: off - name: build backend image: golang:alpine @@ -231,7 +217,6 @@ steps: - go build -v -o deploy/backend-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/backend environment: CGO_ENABLED: 0 - GO111MODULE: off - name: build frontend image: golang:alpine @@ -239,7 +224,6 @@ steps: - go build -v -o deploy/frontend-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/frontend environment: CGO_ENABLED: 0 - GO111MODULE: off - name: build dashboard image: golang:alpine @@ -247,7 +231,6 @@ steps: - go build -v -o deploy/dashboard-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/dashboard environment: CGO_ENABLED: 0 - GO111MODULE: off - name: build repochecker image: golang:alpine @@ -255,7 +238,6 @@ steps: - go build -v -o deploy/repochecker-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker environment: CGO_ENABLED: 0 - GO111MODULE: off - name: build repochecker for macOS image: golang:alpine @@ -266,7 +248,6 @@ steps: CGO_ENABLED: 0 GOOS: darwin GOARCH: arm64 - GO111MODULE: off - name: build qa image: golang:alpine @@ -274,7 +255,6 @@ steps: - go build -v -o deploy/qa-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/qa environment: CGO_ENABLED: 0 - GO111MODULE: off - name: deploy image: appleboy/drone-scp diff --git a/Dockerfile-admin b/Dockerfile-admin index 33f00d6f..95f71dbd 100644 --- a/Dockerfile-admin +++ b/Dockerfile-admin @@ -2,19 +2,15 @@ FROM golang:alpine as gobuild RUN apk add --no-cache git -ENV GO111MODULE=off +WORKDIR /go/src/srs.epita.fr/fic-server/ -WORKDIR /go/src/srs.epita.fr/fic-server/admin +ADD go.mod go.sum ./ +ADD settings settings/ +ADD libfic ./libfic/ +ADD admin ./admin/ -ADD settings ../settings/ -ADD libfic ../libfic/ -ADD admin/api ./api/ -ADD admin/pki ./pki/ -ADD admin/sync ./sync/ -ADD admin/*.go ./ - -RUN go get -d -v -RUN go build -v +RUN go get -d -v ./admin +RUN go build -v -o admin/admin ./admin FROM alpine diff --git a/Dockerfile-backend b/Dockerfile-backend index 44d76f6b..f6740abc 100644 --- a/Dockerfile-backend +++ b/Dockerfile-backend @@ -2,16 +2,15 @@ FROM golang:alpine as gobuild RUN apk add --no-cache git -ENV GO111MODULE=off +WORKDIR /go/src/srs.epita.fr/fic-server/ -WORKDIR /go/src/srs.epita.fr/fic-server/backend +ADD go.mod go.sum ./ +ADD settings settings/ +ADD libfic ./libfic/ +ADD backend ./backend/ -ADD backend/*.go ./ -ADD libfic ../libfic/ -ADD settings ../settings/ - -RUN go get -d -v -RUN go build -v +RUN go get -d -v ./backend +RUN go build -v -o backend/backend ./backend FROM alpine diff --git a/Dockerfile-dashboard b/Dockerfile-dashboard index ae5de072..5b2dc248 100644 --- a/Dockerfile-dashboard +++ b/Dockerfile-dashboard @@ -2,17 +2,15 @@ FROM golang:alpine as gobuild RUN apk add --no-cache git -ENV GO111MODULE=off +WORKDIR /go/src/srs.epita.fr/fic-server/ -WORKDIR /go/src/srs.epita.fr/fic-server/dashboard +ADD go.mod go.sum ./ +ADD settings settings/ +ADD libfic ./libfic/ +ADD dashboard ./dashboard/ -ADD dashboard/*.go ./ -ADD dashboard/api/*.go ./api/ -ADD libfic ../libfic/ -ADD settings ../settings/ - -RUN go get -d -v -RUN go build -v +RUN go get -d -v ./dashboard +RUN go build -v -o dashboard/dashboard ./dashboard FROM alpine diff --git a/Dockerfile-frontend b/Dockerfile-frontend index 7eec0883..72bed002 100644 --- a/Dockerfile-frontend +++ b/Dockerfile-frontend @@ -2,16 +2,15 @@ FROM golang:alpine as gobuild RUN apk add --no-cache git -ENV GO111MODULE=off +WORKDIR /go/src/srs.epita.fr/fic-server/ -WORKDIR /go/src/srs.epita.fr/fic-server/frontend +ADD go.mod go.sum ./ +ADD settings settings/ +ADD libfic ./libfic/ +ADD frontend ./frontend/ -ADD frontend/*.go ./ -ADD libfic ../libfic/ -ADD settings ../settings/ - -RUN go get -d -v -RUN go build -v +RUN go get -d -v ./frontend +RUN go build -v -o ./frontend/frontend ./frontend FROM alpine diff --git a/Dockerfile-qa b/Dockerfile-qa index bf49e97d..d3db773a 100644 --- a/Dockerfile-qa +++ b/Dockerfile-qa @@ -2,17 +2,15 @@ FROM golang:alpine as gobuild RUN apk add --no-cache git -ENV GO111MODULE=off +WORKDIR /go/src/srs.epita.fr/fic-server/ -WORKDIR /go/src/srs.epita.fr/fic-server/qa +ADD go.mod go.sum ./ +ADD settings settings/ +ADD libfic ./libfic/ +ADD qa ./qa/ -ADD qa/*.go ./ -ADD qa/api/*.go ./api/ -ADD libfic ../libfic/ -ADD settings ../settings/ - -RUN go get -d -v -RUN go build -v +RUN go get -d -v ./qa +RUN go build -v -o qa/qa ./qa FROM alpine diff --git a/admin/sync/exercices.go b/admin/sync/exercices.go index d0908549..e6261ae3 100644 --- a/admin/sync/exercices.go +++ b/admin/sync/exercices.go @@ -8,7 +8,7 @@ import ( "github.com/BurntSushi/toml" "github.com/julienschmidt/httprouter" - "gopkg.in/russross/blackfriday.v2" + "github.com/russross/blackfriday/v2" "srs.epita.fr/fic-server/libfic" ) diff --git a/admin/sync/markdown.go b/admin/sync/markdown.go index e102d4a1..3fb1881e 100644 --- a/admin/sync/markdown.go +++ b/admin/sync/markdown.go @@ -10,8 +10,8 @@ import ( "srs.epita.fr/fic-server/libfic" + "github.com/russross/blackfriday/v2" "golang.org/x/crypto/blake2b" - "gopkg.in/russross/blackfriday.v2" ) func ProcessMarkdown(i Importer, input string, rootDir string) (output string, err error) { @@ -25,7 +25,7 @@ func ProcessMarkdown(i Importer, input string, rootDir string) (output string, e blackfriday.WithRenderer(blackfriday.NewHTMLRenderer( blackfriday.HTMLRendererParameters{ AbsolutePrefix: absPath, - Flags: blackfriday.CommonHTMLFlags, + Flags: blackfriday.CommonHTMLFlags, }, )), )) @@ -52,7 +52,7 @@ func ProcessMarkdown(i Importer, input string, rootDir string) (output string, e } else { defer fdto.Close() writer := bufio.NewWriter(fdto) - if err = getFile(i, rootDir + iPath, writer); err != nil { + if err = getFile(i, rootDir+iPath, writer); err != nil { os.Remove(dPath) return } diff --git a/admin/sync/themes.go b/admin/sync/themes.go index 9e4f3704..33f99c8b 100644 --- a/admin/sync/themes.go +++ b/admin/sync/themes.go @@ -12,8 +12,9 @@ import ( "unicode" "github.com/julienschmidt/httprouter" + "github.com/russross/blackfriday/v2" "golang.org/x/image/draw" - "gopkg.in/russross/blackfriday.v2" + "srs.epita.fr/fic-server/libfic" ) diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..a865d440 --- /dev/null +++ b/go.mod @@ -0,0 +1,15 @@ +module srs.epita.fr/fic-server + +go 1.9 + +require ( + github.com/BurntSushi/toml v0.3.1 + github.com/fsnotify/fsnotify v1.4.9 // indirect + github.com/go-sql-driver/mysql v1.6.0 + github.com/julienschmidt/httprouter v1.3.0 + github.com/russross/blackfriday/v2 v2.1.0 + github.com/studio-b12/gowebdav v0.0.0-20210427212133-86f8378cf140 + golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a + golang.org/x/image v0.0.0-20210504121937-7319ad40d33e + gopkg.in/fsnotify.v1 v1.4.7 +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..4f8d41f9 --- /dev/null +++ b/go.sum @@ -0,0 +1,26 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +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/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/studio-b12/gowebdav v0.0.0-20210427212133-86f8378cf140 h1:JCSn/2k3AQ0aJGs5Yx2xv6qrW0CAULc1E+xtSxeeQ/E= +github.com/studio-b12/gowebdav v0.0.0-20210427212133-86f8378cf140/go.mod h1:gCcfDlA1Y7GqOaeEKw5l9dOGx1VLdc/HuQSlQAaZ30s= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/image v0.0.0-20210504121937-7319ad40d33e h1:PzJMNfFQx+QO9hrC1GwZ4BoPGeNGhfeQEgcQFArEjPk= +golang.org/x/image v0.0.0-20210504121937-7319ad40d33e/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= From 9fa89e079310d15a02f48a9b4a6fe4b10fae0564 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 14 May 2021 00:31:00 +0200 Subject: [PATCH 0259/1637] repochecker: fix file concatenation --- admin/sync/exercice_files.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/sync/exercice_files.go b/admin/sync/exercice_files.go index 5057b0d3..d9ea9295 100644 --- a/admin/sync/exercice_files.go +++ b/admin/sync/exercice_files.go @@ -103,7 +103,7 @@ func CheckExerciceFiles(i Importer, exercice fic.Exercice) (files []string, errs for _, fname := range flist { w, hash160, hash512 := fic.CreateHashBuffers() - if err := i.getFile(path.Join(exercice.Path, "files", fname), bufio.NewWriter(w)); err != nil { + if err := getFile(i, path.Join(exercice.Path, "files", fname), bufio.NewWriter(w)); err != nil { errs = append(errs, fmt.Sprintf("%q: unable to read file %q: %s", path.Base(exercice.Path), fname, err)) continue } else if _, err := fic.CheckBufferHash(hash160, hash512, digests[fname]); err != nil { From 5c990de2a06dc2891910c1ce9a97950e220139b3 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 14 May 2021 00:31:26 +0200 Subject: [PATCH 0260/1637] repochecker: version bump --- repochecker/update.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repochecker/update.go b/repochecker/update.go index bfd1335b..d99869b0 100644 --- a/repochecker/update.go +++ b/repochecker/update.go @@ -9,7 +9,7 @@ import ( "net/http" ) -const version = 9 +const version = 10 func init() { go checkUpdate() From d458ac963af59a633f48c12c7a86fe1f0ef6033f Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 14 May 2021 00:44:55 +0200 Subject: [PATCH 0261/1637] CI: add Dockerfile for repochecker (used for student's CI) --- .drone-manifest-fic-repochecker.yml | 22 ++++++++++++++++ .drone.yml | 41 +++++++++++++++++++++++++++++ Dockerfile-repochecker | 21 +++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 .drone-manifest-fic-repochecker.yml create mode 100644 Dockerfile-repochecker diff --git a/.drone-manifest-fic-repochecker.yml b/.drone-manifest-fic-repochecker.yml new file mode 100644 index 00000000..9b239931 --- /dev/null +++ b/.drone-manifest-fic-repochecker.yml @@ -0,0 +1,22 @@ +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.yml b/.drone.yml index 99d57e0d..2544ba98 100644 --- a/.drone.yml +++ b/.drone.yml @@ -181,6 +181,21 @@ steps: 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 + --- kind: pipeline type: docker @@ -346,6 +361,21 @@ steps: 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 + --- kind: pipeline @@ -395,6 +425,17 @@ steps: 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: diff --git a/Dockerfile-repochecker b/Dockerfile-repochecker new file mode 100644 index 00000000..90f02575 --- /dev/null +++ b/Dockerfile-repochecker @@ -0,0 +1,21 @@ +FROM golang:alpine as gobuild + +RUN apk add --no-cache git + +WORKDIR /go/src/srs.epita.fr/fic-server/ + +ADD go.mod go.sum ./ +ADD settings settings/ +ADD libfic ./libfic/ +ADD admin ./admin/ +ADD repochecker ./repochecker/ + +RUN go get -d -v ./repochecker +RUN go build -v -o repochecker/repochecker ./repochecker + + +FROM alpine + +ENTRYPOINT ["/usr/bin/repochecker"] + +COPY --from=gobuild /go/src/srs.epita.fr/fic-server/repochecker/repochecker /usr/bin/repochecker From 8b261011b6fb84c7df50e06deb57c6de994b43c5 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 14 May 2021 01:14:30 +0200 Subject: [PATCH 0262/1637] repochecker: new option avoiding failure if resolution.mp4 missing --- admin/sync/exercices.go | 10 +++++++++- repochecker/main.go | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/admin/sync/exercices.go b/admin/sync/exercices.go index e6261ae3..55686757 100644 --- a/admin/sync/exercices.go +++ b/admin/sync/exercices.go @@ -2,6 +2,7 @@ package sync import ( "fmt" + "log" "path" "strconv" "strings" @@ -13,6 +14,9 @@ import ( "srs.epita.fr/fic-server/libfic" ) +// LogMissingResolution logs the absence of resolution.mp4 instead of returning an error. +var LogMissingResolution = false + func fixnbsp(s string) string { return strings.Replace(strings.Replace(strings.Replace(s, " ?", " ?", -1), " !", " !", -1), " :", " :", -1) } @@ -189,7 +193,11 @@ func BuildExercice(i Importer, theme fic.Theme, epath string, dmap *map[int64]fi // Handle video e.VideoURI = path.Join(epath, "resolution.mp4") if !i.exists(e.VideoURI) { - errs = append(errs, fmt.Sprintf("%q: resolution.mp4: no video file found at %s", edir, e.VideoURI)) + if LogMissingResolution { + log.Printf("%q: resolution.mp4: no video file found at %s", edir, e.VideoURI) + } else { + errs = append(errs, fmt.Sprintf("%q: resolution.mp4: no video file found at %s", edir, e.VideoURI)) + } e.VideoURI = "" } else if size, err := getFileSize(i, e.VideoURI); err != nil { errs = append(errs, fmt.Sprintf("%q: resolution.mp4: ", edir, err)) diff --git a/repochecker/main.go b/repochecker/main.go index 81a8a38b..1964690a 100644 --- a/repochecker/main.go +++ b/repochecker/main.go @@ -76,6 +76,7 @@ func main() { 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(&skipFileChecks, "skipfiledigests", skipFileChecks, "Don't perform DIGESTS checks on file to speed up the checks") + flag.BoolVar(&sync.LogMissingResolution, "skipresolution", sync.LogMissingResolution, "Don't fail if resolution.mp4 is absent") flag.Parse() log.SetPrefix("[repochecker] ") From 57fe1a7517f598acbb02d5e4bcc2d7a050bb1e8e Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 14 May 2021 01:25:08 +0200 Subject: [PATCH 0263/1637] sync: Ignore exercice directories not containing at least - sep --- admin/sync/exercices.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/sync/exercices.go b/admin/sync/exercices.go index 55686757..afe43038 100644 --- a/admin/sync/exercices.go +++ b/admin/sync/exercices.go @@ -32,7 +32,7 @@ func GetExercices(i Importer, theme fic.Theme) ([]string, error) { } else { for _, dir := range dirs { if _, err := i.listDir(path.Join(theme.Path, dir)); err == nil { - if dir[0] != '.' { + if dir[0] != '.' && strings.Contains(dir, "-") { exercices = append(exercices, dir) } } From eb26879c74e26510b4d649f53b5ea9a745a9de05 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 14 May 2021 01:36:13 +0200 Subject: [PATCH 0264/1637] CI: add checkupdate tags for repochecker --- .drone.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.drone.yml b/.drone.yml index 2544ba98..ccb5a1a9 100644 --- a/.drone.yml +++ b/.drone.yml @@ -69,7 +69,7 @@ steps: - name: build repochecker image: golang:alpine commands: - - go build -v -o deploy/repochecker-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker + - go build --tags checkupdate -v -o deploy/repochecker-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker - grep "const version" repochecker/update.go | sed -r 's/^.*=\s*(\S.*)$/\1/' > deploy/repochecker.version environment: CGO_ENABLED: 0 @@ -77,7 +77,7 @@ steps: - name: build repochecker for macOS image: golang:alpine commands: - - go build -v -o deploy/repochecker-darwin-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker + - go build --tags checkupdate -v -o deploy/repochecker-darwin-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker environment: CGO_ENABLED: 0 GOOS: darwin @@ -250,7 +250,7 @@ steps: - name: build repochecker image: golang:alpine commands: - - go build -v -o deploy/repochecker-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker + - go build --tags checkupdate -v -o deploy/repochecker-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker environment: CGO_ENABLED: 0 @@ -258,7 +258,7 @@ steps: image: golang:alpine commands: - apk --no-cache add build-base - - go build -v -o deploy/repochecker-darwin-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker + - go build --tags checkupdate -v -o deploy/repochecker-darwin-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/repochecker environment: CGO_ENABLED: 0 GOOS: darwin From 7fc860edecaddde44894ae67aa0d5688dcfb42bb Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 9 Jun 2021 01:11:23 +0200 Subject: [PATCH 0265/1637] admin: Embed static assets into binary --- Dockerfile-admin | 6 - README.md | 2 +- admin/main.go | 30 +- admin/static.go | 66 +- admin/static/css/glyphicon.css | 806 +++++++++++++++++- admin/static/fonts | 1 - .../fonts/FantasqueSansMono-Regular.woff | Bin .../static/fonts/LinBiolinum_R.woff | Bin .../static/fonts/LinBiolinum_RB.woff | Bin .../static/fonts/LinBiolinum_RI.woff | Bin .../fonts/glyphicons-halflings-regular.eot | Bin .../fonts/glyphicons-halflings-regular.svg | 0 .../fonts/glyphicons-halflings-regular.ttf | Bin .../fonts/glyphicons-halflings-regular.woff | Bin .../fonts/glyphicons-halflings-regular.woff2 | Bin admin/static/index.html | 128 --- admin/static/js/angular-route.min.js | 18 +- admin/static/js/angular-sanitize.min.js | 19 +- admin/static/js/angular.min.js | 351 +++++++- admin/static/js/bootstrap.min.js | 8 +- admin/static/js/common.js | 288 ++++++- admin/static/js/d3.v3.min.js | 6 +- admin/static/js/i18n | 1 - .../static/js/i18n/angular-locale_fr-fr.js | 0 admin/static/js/jquery.min.js | 3 +- frontend/static/css/glyphicon.css | 806 +----------------- frontend/static/fonts | 1 + frontend/static/js/angular-route.min.js | 18 +- frontend/static/js/angular-sanitize.min.js | 19 +- frontend/static/js/angular.min.js | 351 +------- frontend/static/js/bootstrap.min.js | 8 +- frontend/static/js/common.js | 288 +------ frontend/static/js/d3.v3.min.js | 6 +- frontend/static/js/i18n | 1 + frontend/static/js/jquery.min.js | 3 +- go.mod | 2 +- htdocs-admin | 2 +- 37 files changed, 1569 insertions(+), 1669 deletions(-) mode change 120000 => 100644 admin/static/css/glyphicon.css delete mode 120000 admin/static/fonts rename {frontend => admin}/static/fonts/FantasqueSansMono-Regular.woff (100%) rename {frontend => admin}/static/fonts/LinBiolinum_R.woff (100%) rename {frontend => admin}/static/fonts/LinBiolinum_RB.woff (100%) rename {frontend => admin}/static/fonts/LinBiolinum_RI.woff (100%) rename {frontend => admin}/static/fonts/glyphicons-halflings-regular.eot (100%) rename {frontend => admin}/static/fonts/glyphicons-halflings-regular.svg (100%) rename {frontend => admin}/static/fonts/glyphicons-halflings-regular.ttf (100%) rename {frontend => admin}/static/fonts/glyphicons-halflings-regular.woff (100%) rename {frontend => admin}/static/fonts/glyphicons-halflings-regular.woff2 (100%) delete mode 100644 admin/static/index.html mode change 120000 => 100644 admin/static/js/angular-route.min.js mode change 120000 => 100644 admin/static/js/angular-sanitize.min.js mode change 120000 => 100644 admin/static/js/angular.min.js mode change 120000 => 100644 admin/static/js/bootstrap.min.js mode change 120000 => 100644 admin/static/js/common.js mode change 120000 => 100644 admin/static/js/d3.v3.min.js delete mode 120000 admin/static/js/i18n rename {frontend => admin}/static/js/i18n/angular-locale_fr-fr.js (100%) mode change 120000 => 100644 admin/static/js/jquery.min.js mode change 100644 => 120000 frontend/static/css/glyphicon.css create mode 120000 frontend/static/fonts mode change 100644 => 120000 frontend/static/js/angular-route.min.js mode change 100644 => 120000 frontend/static/js/angular-sanitize.min.js mode change 100644 => 120000 frontend/static/js/angular.min.js mode change 100644 => 120000 frontend/static/js/bootstrap.min.js mode change 100644 => 120000 frontend/static/js/common.js mode change 100644 => 120000 frontend/static/js/d3.v3.min.js create mode 120000 frontend/static/js/i18n mode change 100644 => 120000 frontend/static/js/jquery.min.js diff --git a/Dockerfile-admin b/Dockerfile-admin index 95f71dbd..c727f6e4 100644 --- a/Dockerfile-admin +++ b/Dockerfile-admin @@ -24,9 +24,3 @@ WORKDIR /srv ENTRYPOINT ["/srv/admin", "-bind=:8081", "-baseurl=/admin/"] COPY --from=gobuild /go/src/srs.epita.fr/fic-server/admin/admin /srv/admin -COPY admin/static/check_import.html /srv/htdocs-admin/ -COPY admin/static/css/bootstrap.min.css frontend/static/css/glyphicon.css /srv/htdocs-admin/css/ -COPY frontend/static/fonts /srv/htdocs-admin/fonts -COPY admin/static/img /srv/htdocs-admin/img -COPY admin/static/views /srv/htdocs-admin/views -COPY admin/static/js/app.js frontend/static/js/angular.min.js admin/static/js/angular-resource.min.js frontend/static/js/angular-route.min.js frontend/static/js/angular-sanitize.min.js frontend/static/js/bootstrap.min.js frontend/static/js/common.js frontend/static/js/d3.v3.min.js frontend/static/js/i18n frontend/static/js/jquery.min.js admin/static/js/popper.min.js /srv/htdocs-admin/js/ diff --git a/README.md b/README.md index bedb582c..cde41337 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ If your are trying to use the folder available with the Owncloud service, make t 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.9 at least and a `inotify`-aware system. +1.16 at least and a `inotify`-aware system. 1. First, you'll need to retrieve the dependencies: diff --git a/admin/main.go b/admin/main.go index b818c7a5..f60e51fe 100644 --- a/admin/main.go +++ b/admin/main.go @@ -4,6 +4,7 @@ import ( "context" "flag" "fmt" + "io/fs" "log" "net/http" "net/url" @@ -13,7 +14,6 @@ import ( "path/filepath" "strings" "syscall" - "text/template" "srs.epita.fr/fic-server/admin/api" "srs.epita.fr/fic-server/admin/pki" @@ -22,8 +22,6 @@ import ( "srs.epita.fr/fic-server/settings" ) -var StaticDir string - type ResponseWriterPrefix struct { real http.ResponseWriter prefix string @@ -94,7 +92,7 @@ func main() { var baseURL = flag.String("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") - flag.StringVar(&StaticDir, "static", "./htdocs-admin/", "Directory containing static files") + 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") @@ -129,10 +127,20 @@ func main() { // Sanitize options var err error log.Println("Checking paths...") - if StaticDir, err = filepath.Abs(StaticDir); err != nil { - log.Fatal(err) + if staticDir != nil && *staticDir != "" { + if sDir, err := filepath.Abs(*staticDir); err != nil { + log.Fatal(err) + } else { + 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) + } + staticFS = http.FS(sub) } - sync.DeepReportPath = path.Join(StaticDir, sync.DeepReportPath) if fic.FilesDir, err = filepath.Abs(fic.FilesDir); err != nil { log.Fatal(err) } @@ -194,13 +202,7 @@ func main() { // Update base URL on main page log.Println("Changing base URL to", *baseURL+"/", "...") - if file, err := os.OpenFile(path.Join(StaticDir, "index.html"), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0644)); err != nil { - log.Println("Unable to open index.html: ", err) - } else if indexTmpl, err := template.New("index").Parse(indextpl); err != nil { - log.Println("Cannot create template: ", err) - } else if err = indexTmpl.Execute(file, map[string]string{"urlbase": path.Clean(path.Join(*baseURL+"/", "nuke"))[:len(path.Clean(path.Join(*baseURL+"/", "nuke")))-4]}); err != nil { - log.Println("An error occurs during template execution: ", err) - } + genIndex(*baseURL) // Prepare graceful shutdown interrupt := make(chan os.Signal, 1) diff --git a/admin/static.go b/admin/static.go index 69052426..2b548fa5 100644 --- a/admin/static.go +++ b/admin/static.go @@ -1,9 +1,13 @@ package main import ( + "bytes" + "embed" + "log" "net/http" "path" "strings" + "text/template" "srs.epita.fr/fic-server/admin/api" "srs.epita.fr/fic-server/admin/sync" @@ -12,52 +16,80 @@ import ( "github.com/julienschmidt/httprouter" ) +//go:embed static + +var assets embed.FS + +var indexPage []byte + +func genIndex(baseURL string) { + 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, map[string]string{"urlbase": path.Clean(path.Join(baseURL+"/", "nuke"))[:len(path.Clean(path.Join(baseURL+"/", "nuke")))-4]}); err != nil { + log.Fatal("An error occurs during template execution:", err) + } else { + indexPage = b.Bytes() + } +} + +func serveIndex(w http.ResponseWriter, r *http.Request) { + w.Write(indexPage) +} + +var staticFS http.FileSystem + +func serveFile(w http.ResponseWriter, r *http.Request, url string) { + r.URL.Path = url + http.FileServer(staticFS).ServeHTTP(w, r) +} + func init() { api.Router().GET("/", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, "index.html")) + serveIndex(w, r) }) api.Router().GET("/claims/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, "index.html")) + serveIndex(w, r) }) api.Router().GET("/exercices/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, "index.html")) + serveIndex(w, r) }) api.Router().GET("/events/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, "index.html")) + serveIndex(w, r) }) api.Router().GET("/files", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, "index.html")) + serveIndex(w, r) }) api.Router().GET("/public/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, "index.html")) + serveIndex(w, r) }) api.Router().GET("/pki/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, "index.html")) + serveIndex(w, r) }) api.Router().GET("/settings/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, "index.html")) + serveIndex(w, r) }) api.Router().GET("/teams/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, "index.html")) + serveIndex(w, r) }) api.Router().GET("/themes/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, "index.html")) + serveIndex(w, r) }) api.Router().GET("/css/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, r.URL.Path)) + serveFile(w, r, r.URL.Path) }) api.Router().GET("/fonts/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, r.URL.Path)) + serveFile(w, r, r.URL.Path) }) api.Router().GET("/img/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, r.URL.Path)) + serveFile(w, r, r.URL.Path) }) api.Router().GET("/js/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, r.URL.Path)) + serveFile(w, r, r.URL.Path) }) api.Router().GET("/views/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, r.URL.Path)) + serveFile(w, r, r.URL.Path) }) api.Router().GET("/files/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { @@ -75,9 +107,9 @@ func init() { }) api.Router().GET("/check_import.html", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, "check_import.html")) + serveFile(w, r, "check_import.html") }) api.Router().GET("/full_import_report.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, "full_import_report.json")) + http.ServeFile(w, r, sync.DeepReportPath) }) } diff --git a/admin/static/css/glyphicon.css b/admin/static/css/glyphicon.css deleted file mode 120000 index 6d65cb92..00000000 --- a/admin/static/css/glyphicon.css +++ /dev/null @@ -1 +0,0 @@ -../../../frontend/static/css/glyphicon.css \ No newline at end of file diff --git a/admin/static/css/glyphicon.css b/admin/static/css/glyphicon.css new file mode 100644 index 00000000..1bf9e4de --- /dev/null +++ b/admin/static/css/glyphicon.css @@ -0,0 +1,805 @@ +@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 b/admin/static/fonts deleted file mode 120000 index 5431051e..00000000 --- a/admin/static/fonts +++ /dev/null @@ -1 +0,0 @@ -../../frontend/static/fonts/ \ No newline at end of file diff --git a/frontend/static/fonts/FantasqueSansMono-Regular.woff b/admin/static/fonts/FantasqueSansMono-Regular.woff similarity index 100% rename from frontend/static/fonts/FantasqueSansMono-Regular.woff rename to admin/static/fonts/FantasqueSansMono-Regular.woff diff --git a/frontend/static/fonts/LinBiolinum_R.woff b/admin/static/fonts/LinBiolinum_R.woff similarity index 100% rename from frontend/static/fonts/LinBiolinum_R.woff rename to admin/static/fonts/LinBiolinum_R.woff diff --git a/frontend/static/fonts/LinBiolinum_RB.woff b/admin/static/fonts/LinBiolinum_RB.woff similarity index 100% rename from frontend/static/fonts/LinBiolinum_RB.woff rename to admin/static/fonts/LinBiolinum_RB.woff diff --git a/frontend/static/fonts/LinBiolinum_RI.woff b/admin/static/fonts/LinBiolinum_RI.woff similarity index 100% rename from frontend/static/fonts/LinBiolinum_RI.woff rename to admin/static/fonts/LinBiolinum_RI.woff diff --git a/frontend/static/fonts/glyphicons-halflings-regular.eot b/admin/static/fonts/glyphicons-halflings-regular.eot similarity index 100% rename from frontend/static/fonts/glyphicons-halflings-regular.eot rename to admin/static/fonts/glyphicons-halflings-regular.eot diff --git a/frontend/static/fonts/glyphicons-halflings-regular.svg b/admin/static/fonts/glyphicons-halflings-regular.svg similarity index 100% rename from frontend/static/fonts/glyphicons-halflings-regular.svg rename to admin/static/fonts/glyphicons-halflings-regular.svg diff --git a/frontend/static/fonts/glyphicons-halflings-regular.ttf b/admin/static/fonts/glyphicons-halflings-regular.ttf similarity index 100% rename from frontend/static/fonts/glyphicons-halflings-regular.ttf rename to admin/static/fonts/glyphicons-halflings-regular.ttf diff --git a/frontend/static/fonts/glyphicons-halflings-regular.woff b/admin/static/fonts/glyphicons-halflings-regular.woff similarity index 100% rename from frontend/static/fonts/glyphicons-halflings-regular.woff rename to admin/static/fonts/glyphicons-halflings-regular.woff diff --git a/frontend/static/fonts/glyphicons-halflings-regular.woff2 b/admin/static/fonts/glyphicons-halflings-regular.woff2 similarity index 100% rename from frontend/static/fonts/glyphicons-halflings-regular.woff2 rename to admin/static/fonts/glyphicons-halflings-regular.woff2 diff --git a/admin/static/index.html b/admin/static/index.html deleted file mode 100644 index 43a89dcd..00000000 --- a/admin/static/index.html +++ /dev/null @@ -1,128 +0,0 @@ - - - - - Challenge Forensic - Administration - - - - - - - - - -
    -
    -
    - -
    - -
    - -
    - - - - - - - - - - - - diff --git a/admin/static/js/angular-route.min.js b/admin/static/js/angular-route.min.js deleted file mode 120000 index 407db24d..00000000 --- a/admin/static/js/angular-route.min.js +++ /dev/null @@ -1 +0,0 @@ -../../../frontend/static/js/angular-route.min.js \ No newline at end of file diff --git a/admin/static/js/angular-route.min.js b/admin/static/js/angular-route.min.js new file mode 100644 index 00000000..d2622160 --- /dev/null +++ b/admin/static/js/angular-route.min.js @@ -0,0 +1,17 @@ +/* + AngularJS v1.7.9 + (c) 2010-2018 Google, Inc. http://angularjs.org + License: MIT +*/ +(function(I,b){'use strict';function z(b,h){var d=[],c=b.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)(\*\?|[?*])?/g,function(b,c,h,k){b="?"===k||"*?"===k;k="*"===k||"*?"===k;d.push({name:h,optional:b});c=c||"";return(b?"(?:"+c:c+"(?:")+(k?"(.+?)":"([^/]+)")+(b?"?)?":")")}).replace(/([/$*])/g,"\\$1");h.ignoreTrailingSlashes&&(c=c.replace(/\/+$/,"")+"/*");return{keys:d,regexp:new RegExp("^"+c+"(?:[?#]|$)",h.caseInsensitiveMatch?"i":"")}}function A(b){p&&b.get("$route")}function v(u,h,d){return{restrict:"ECA", +terminal:!0,priority:400,transclude:"element",link:function(c,f,g,l,k){function q(){r&&(d.cancel(r),r=null);m&&(m.$destroy(),m=null);s&&(r=d.leave(s),r.done(function(b){!1!==b&&(r=null)}),s=null)}function C(){var g=u.current&&u.current.locals;if(b.isDefined(g&&g.$template)){var g=c.$new(),l=u.current;s=k(g,function(g){d.enter(g,null,s||f).done(function(d){!1===d||!b.isDefined(w)||w&&!c.$eval(w)||h()});q()});m=l.scope=g;m.$emit("$viewContentLoaded");m.$eval(p)}else q()}var m,s,r,w=g.autoscroll,p=g.onload|| +"";c.$on("$routeChangeSuccess",C);C()}}}function x(b,h,d){return{restrict:"ECA",priority:-400,link:function(c,f){var g=d.current,l=g.locals;f.html(l.$template);var k=b(f.contents());if(g.controller){l.$scope=c;var q=h(g.controller,l);g.controllerAs&&(c[g.controllerAs]=q);f.data("$ngControllerController",q);f.children().data("$ngControllerController",q)}c[g.resolveAs||"$resolve"]=l;k(c)}}}var D,E,F,G,y=b.module("ngRoute",[]).info({angularVersion:"1.7.9"}).provider("$route",function(){function u(d, +c){return b.extend(Object.create(d),c)}D=b.isArray;E=b.isObject;F=b.isDefined;G=b.noop;var h={};this.when=function(d,c){var f;f=void 0;if(D(c)){f=f||[];for(var g=0,l=c.length;g/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 120000 index 86b88964..00000000 --- a/admin/static/js/angular.min.js +++ /dev/null @@ -1 +0,0 @@ -../../../frontend/static/js/angular.min.js \ No newline at end of file diff --git a/admin/static/js/angular.min.js b/admin/static/js/angular.min.js new file mode 100644 index 00000000..f6bf3370 --- /dev/null +++ b/admin/static/js/angular.min.js @@ -0,0 +1,350 @@ +/* + 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/bootstrap.min.js b/admin/static/js/bootstrap.min.js deleted file mode 120000 index d074c9db..00000000 --- a/admin/static/js/bootstrap.min.js +++ /dev/null @@ -1 +0,0 @@ -../../../frontend/static/js/bootstrap.min.js \ No newline at end of file diff --git a/admin/static/js/bootstrap.min.js b/admin/static/js/bootstrap.min.js new file mode 100644 index 00000000..9df6b6c2 --- /dev/null +++ b/admin/static/js/bootstrap.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v4.2.1 (https://getbootstrap.com/) + * Copyright 2011-2018 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("popper.js"),require("jquery")):"function"==typeof define&&define.amd?define(["exports","popper.js","jquery"],e):e(t.bootstrap={},t.Popper,t.jQuery)}(this,function(t,u,g){"use strict";function i(t,e){for(var n=0;nthis._items.length-1||t<0))if(this._isSliding)g(this._element).one(Q.SLID,function(){return e.to(t)});else{if(n===t)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},t._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},t._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right
    ',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent"},De="show",we="out",Ae={HIDE:"hide"+Ee,HIDDEN:"hidden"+Ee,SHOW:"show"+Ee,SHOWN:"shown"+Ee,INSERTED:"inserted"+Ee,CLICK:"click"+Ee,FOCUSIN:"focusin"+Ee,FOCUSOUT:"focusout"+Ee,MOUSEENTER:"mouseenter"+Ee,MOUSELEAVE:"mouseleave"+Ee},Ne="fade",Oe="show",ke=".tooltip-inner",Pe=".arrow",Le="hover",je="focus",He="click",Re="manual",Ue=function(){function i(t,e){if("undefined"==typeof u)throw new TypeError("Bootstrap's tooltips require Popper.js (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 t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=g(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(g(this.getTipElement()).hasClass(Oe))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),g.removeData(this.element,this.constructor.DATA_KEY),g(this.element).off(this.constructor.EVENT_KEY),g(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&g(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},t.show=function(){var e=this;if("none"===g(this.element).css("display"))throw new Error("Please use show on visible elements");var t=g.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){g(this.element).trigger(t);var n=_.findShadowRoot(this.element),i=g.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!i)return;var o=this.getTipElement(),r=_.getUID(this.constructor.NAME);o.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&g(o).addClass(Ne);var s="function"==typeof this.config.placement?this.config.placement.call(this,o,this.element):this.config.placement,a=this._getAttachment(s);this.addAttachmentClass(a);var l=this._getContainer();g(o).data(this.constructor.DATA_KEY,this),g.contains(this.element.ownerDocument.documentElement,this.tip)||g(o).appendTo(l),g(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new u(this.element,o,{placement:a,modifiers:{offset:{offset:this.config.offset},flip:{behavior:this.config.fallbackPlacement},arrow:{element:Pe},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}}),g(o).addClass(Oe),"ontouchstart"in document.documentElement&&g(document.body).children().on("mouseover",null,g.noop);var c=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,g(e.element).trigger(e.constructor.Event.SHOWN),t===we&&e._leave(null,e)};if(g(this.tip).hasClass(Ne)){var h=_.getTransitionDurationFromElement(this.tip);g(this.tip).one(_.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},t.hide=function(t){var e=this,n=this.getTipElement(),i=g.Event(this.constructor.Event.HIDE),o=function(){e._hoverState!==De&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),g(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(g(this.element).trigger(i),!i.isDefaultPrevented()){if(g(n).removeClass(Oe),"ontouchstart"in document.documentElement&&g(document.body).children().off("mouseover",null,g.noop),this._activeTrigger[He]=!1,this._activeTrigger[je]=!1,this._activeTrigger[Le]=!1,g(this.tip).hasClass(Ne)){var r=_.getTransitionDurationFromElement(n);g(n).one(_.TRANSITION_END,o).emulateTransitionEnd(r)}else o();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){g(this.getTipElement()).addClass(Ce+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},t.setContent=function(){var t=this.getTipElement();this.setElementContent(g(t.querySelectorAll(ke)),this.getTitle()),g(t).removeClass(Ne+" "+Oe)},t.setElementContent=function(t,e){var n=this.config.html;"object"==typeof e&&(e.nodeType||e.jquery)?n?g(e).parent().is(t)||t.empty().append(e):t.text(g(e).text()):t[n?"html":"text"](e)},t.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},t._getContainer=function(){return!1===this.config.container?document.body:_.isElement(this.config.container)?g(this.config.container):g(document).find(this.config.container)},t._getAttachment=function(t){return be[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)g(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==Re){var e=t===Le?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===Le?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;g(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}}),g(this.element).closest(".modal").on("hide.bs.modal",function(){i.element&&i.hide()}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._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",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?je:Le]=!0),g(e.getTipElement()).hasClass(Oe)||e._hoverState===De?e._hoverState=De:(clearTimeout(e._timeout),e._hoverState=De,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===De&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?je:Le]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=we,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===we&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){return"number"==typeof(t=l({},this.constructor.Default,g(this.element).data(),"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()),_.typeCheckConfig(pe,t,this.constructor.DefaultType),t},t._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},t._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(Te);null!==e&&e.length&&t.removeClass(e.join(""))},t._handlePopperPlacementChange=function(t){var e=t.instance;this.tip=e.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},t._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(g(t).removeClass(Ne),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},i._jQueryInterface=function(n){return this.each(function(){var t=g(this).data(ve),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),g(this).data(ve,t)),"string"==typeof n)){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.2.1"}},{key:"Default",get:function(){return Ie}},{key:"NAME",get:function(){return pe}},{key:"DATA_KEY",get:function(){return ve}},{key:"Event",get:function(){return Ae}},{key:"EVENT_KEY",get:function(){return Ee}},{key:"DefaultType",get:function(){return Se}}]),i}();g.fn[pe]=Ue._jQueryInterface,g.fn[pe].Constructor=Ue,g.fn[pe].noConflict=function(){return g.fn[pe]=ye,Ue._jQueryInterface};var We="popover",xe="bs.popover",Fe="."+xe,qe=g.fn[We],Me="bs-popover",Ke=new RegExp("(^|\\s)"+Me+"\\S+","g"),Qe=l({},Ue.Default,{placement:"right",trigger:"click",content:"",template:''}),Be=l({},Ue.DefaultType,{content:"(string|element|function)"}),Ve="fade",Ye="show",Xe=".popover-header",ze=".popover-body",Ge={HIDE:"hide"+Fe,HIDDEN:"hidden"+Fe,SHOW:"show"+Fe,SHOWN:"shown"+Fe,INSERTED:"inserted"+Fe,CLICK:"click"+Fe,FOCUSIN:"focusin"+Fe,FOCUSOUT:"focusout"+Fe,MOUSEENTER:"mouseenter"+Fe,MOUSELEAVE:"mouseleave"+Fe},Je=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).__proto__=n;var o=i.prototype;return o.isWithContent=function(){return this.getTitle()||this._getContent()},o.addAttachmentClass=function(t){g(this.getTipElement()).addClass(Me+"-"+t)},o.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},o.setContent=function(){var t=g(this.getTipElement());this.setElementContent(t.find(Xe),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(ze),e),t.removeClass(Ve+" "+Ye)},o._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},o._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(Ke);null!==e&&0=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t= 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.validator_regexp) { + var re = new RegExp(flag.validator_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(); + }); + } + } + }]); + +angular.module("FICApp") + .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("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 ""; + } + }); + +angular.module("FICApp") + .component('flagKey', { + bindings: { + kid: '=', + key: '=', + settings: '=', + wantchoices: '=', + }, + controller: function() { + this.additem = function(key) { + this.key.values.push(""); + }; + }, + template: ` +
    + + +
    + + + +
    + +
    +
    + +
    +
    + + +
    + ` + }); + +angular.module("FICApp") + .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; + } + $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; + } + + function updTime() { + if (time && $rootScope.settings) { + 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); + + 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 120000 index d2407e47..00000000 --- a/admin/static/js/d3.v3.min.js +++ /dev/null @@ -1 +0,0 @@ -../../../frontend/static/js/d3.v3.min.js \ No newline at end of file diff --git a/admin/static/js/d3.v3.min.js b/admin/static/js/d3.v3.min.js new file mode 100644 index 00000000..16648730 --- /dev/null +++ b/admin/static/js/d3.v3.min.js @@ -0,0 +1,5 @@ +!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 b/admin/static/js/i18n deleted file mode 120000 index dab94408..00000000 --- a/admin/static/js/i18n +++ /dev/null @@ -1 +0,0 @@ -../../../frontend/static/js/i18n/ \ No newline at end of file diff --git a/frontend/static/js/i18n/angular-locale_fr-fr.js b/admin/static/js/i18n/angular-locale_fr-fr.js similarity index 100% rename from frontend/static/js/i18n/angular-locale_fr-fr.js rename to admin/static/js/i18n/angular-locale_fr-fr.js diff --git a/admin/static/js/jquery.min.js b/admin/static/js/jquery.min.js deleted file mode 120000 index 022463c1..00000000 --- a/admin/static/js/jquery.min.js +++ /dev/null @@ -1 +0,0 @@ -../../../frontend/static/js/jquery.min.js \ No newline at end of file diff --git a/admin/static/js/jquery.min.js b/admin/static/js/jquery.min.js new file mode 100644 index 00000000..a1c07fd8 --- /dev/null +++ b/admin/static/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.4.1 | (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(C,e){"use strict";var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).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 w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|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(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.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,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){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 H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.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--)b.attrHandle[n[r]]=t}function pe(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 de(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 d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.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}},b.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[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.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},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),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(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.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)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(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===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(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?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(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 G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.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=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(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 j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.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 k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?: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}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;nx",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="
    ",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.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),m(t)&&(t=t.call(e,n,k.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):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.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"===k.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"===k.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===k.css(e,"position"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.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)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.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?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.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){k.fn[n]=function(e,t){return 0/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/frontend/static/js/angular-sanitize.min.js b/frontend/static/js/angular-sanitize.min.js new file mode 120000 index 00000000..d0410452 --- /dev/null +++ b/frontend/static/js/angular-sanitize.min.js @@ -0,0 +1 @@ +../../../admin/static/js/angular-sanitize.min.js \ No newline at end of file diff --git a/frontend/static/js/angular.min.js b/frontend/static/js/angular.min.js deleted file mode 100644 index f6bf3370..00000000 --- a/frontend/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/frontend/static/js/angular.min.js b/frontend/static/js/angular.min.js new file mode 120000 index 00000000..5eba27d2 --- /dev/null +++ b/frontend/static/js/angular.min.js @@ -0,0 +1 @@ +../../../admin/static/js/angular.min.js \ No newline at end of file diff --git a/frontend/static/js/bootstrap.min.js b/frontend/static/js/bootstrap.min.js deleted file mode 100644 index 9df6b6c2..00000000 --- a/frontend/static/js/bootstrap.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v4.2.1 (https://getbootstrap.com/) - * Copyright 2011-2018 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("popper.js"),require("jquery")):"function"==typeof define&&define.amd?define(["exports","popper.js","jquery"],e):e(t.bootstrap={},t.Popper,t.jQuery)}(this,function(t,u,g){"use strict";function i(t,e){for(var n=0;nthis._items.length-1||t<0))if(this._isSliding)g(this._element).one(Q.SLID,function(){return e.to(t)});else{if(n===t)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},t._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},t._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent"},De="show",we="out",Ae={HIDE:"hide"+Ee,HIDDEN:"hidden"+Ee,SHOW:"show"+Ee,SHOWN:"shown"+Ee,INSERTED:"inserted"+Ee,CLICK:"click"+Ee,FOCUSIN:"focusin"+Ee,FOCUSOUT:"focusout"+Ee,MOUSEENTER:"mouseenter"+Ee,MOUSELEAVE:"mouseleave"+Ee},Ne="fade",Oe="show",ke=".tooltip-inner",Pe=".arrow",Le="hover",je="focus",He="click",Re="manual",Ue=function(){function i(t,e){if("undefined"==typeof u)throw new TypeError("Bootstrap's tooltips require Popper.js (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 t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=g(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(g(this.getTipElement()).hasClass(Oe))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),g.removeData(this.element,this.constructor.DATA_KEY),g(this.element).off(this.constructor.EVENT_KEY),g(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&g(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},t.show=function(){var e=this;if("none"===g(this.element).css("display"))throw new Error("Please use show on visible elements");var t=g.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){g(this.element).trigger(t);var n=_.findShadowRoot(this.element),i=g.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!i)return;var o=this.getTipElement(),r=_.getUID(this.constructor.NAME);o.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&g(o).addClass(Ne);var s="function"==typeof this.config.placement?this.config.placement.call(this,o,this.element):this.config.placement,a=this._getAttachment(s);this.addAttachmentClass(a);var l=this._getContainer();g(o).data(this.constructor.DATA_KEY,this),g.contains(this.element.ownerDocument.documentElement,this.tip)||g(o).appendTo(l),g(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new u(this.element,o,{placement:a,modifiers:{offset:{offset:this.config.offset},flip:{behavior:this.config.fallbackPlacement},arrow:{element:Pe},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}}),g(o).addClass(Oe),"ontouchstart"in document.documentElement&&g(document.body).children().on("mouseover",null,g.noop);var c=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,g(e.element).trigger(e.constructor.Event.SHOWN),t===we&&e._leave(null,e)};if(g(this.tip).hasClass(Ne)){var h=_.getTransitionDurationFromElement(this.tip);g(this.tip).one(_.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},t.hide=function(t){var e=this,n=this.getTipElement(),i=g.Event(this.constructor.Event.HIDE),o=function(){e._hoverState!==De&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),g(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(g(this.element).trigger(i),!i.isDefaultPrevented()){if(g(n).removeClass(Oe),"ontouchstart"in document.documentElement&&g(document.body).children().off("mouseover",null,g.noop),this._activeTrigger[He]=!1,this._activeTrigger[je]=!1,this._activeTrigger[Le]=!1,g(this.tip).hasClass(Ne)){var r=_.getTransitionDurationFromElement(n);g(n).one(_.TRANSITION_END,o).emulateTransitionEnd(r)}else o();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){g(this.getTipElement()).addClass(Ce+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},t.setContent=function(){var t=this.getTipElement();this.setElementContent(g(t.querySelectorAll(ke)),this.getTitle()),g(t).removeClass(Ne+" "+Oe)},t.setElementContent=function(t,e){var n=this.config.html;"object"==typeof e&&(e.nodeType||e.jquery)?n?g(e).parent().is(t)||t.empty().append(e):t.text(g(e).text()):t[n?"html":"text"](e)},t.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},t._getContainer=function(){return!1===this.config.container?document.body:_.isElement(this.config.container)?g(this.config.container):g(document).find(this.config.container)},t._getAttachment=function(t){return be[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)g(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==Re){var e=t===Le?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===Le?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;g(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}}),g(this.element).closest(".modal").on("hide.bs.modal",function(){i.element&&i.hide()}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._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",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?je:Le]=!0),g(e.getTipElement()).hasClass(Oe)||e._hoverState===De?e._hoverState=De:(clearTimeout(e._timeout),e._hoverState=De,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===De&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?je:Le]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=we,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===we&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){return"number"==typeof(t=l({},this.constructor.Default,g(this.element).data(),"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()),_.typeCheckConfig(pe,t,this.constructor.DefaultType),t},t._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},t._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(Te);null!==e&&e.length&&t.removeClass(e.join(""))},t._handlePopperPlacementChange=function(t){var e=t.instance;this.tip=e.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},t._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(g(t).removeClass(Ne),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},i._jQueryInterface=function(n){return this.each(function(){var t=g(this).data(ve),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),g(this).data(ve,t)),"string"==typeof n)){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.2.1"}},{key:"Default",get:function(){return Ie}},{key:"NAME",get:function(){return pe}},{key:"DATA_KEY",get:function(){return ve}},{key:"Event",get:function(){return Ae}},{key:"EVENT_KEY",get:function(){return Ee}},{key:"DefaultType",get:function(){return Se}}]),i}();g.fn[pe]=Ue._jQueryInterface,g.fn[pe].Constructor=Ue,g.fn[pe].noConflict=function(){return g.fn[pe]=ye,Ue._jQueryInterface};var We="popover",xe="bs.popover",Fe="."+xe,qe=g.fn[We],Me="bs-popover",Ke=new RegExp("(^|\\s)"+Me+"\\S+","g"),Qe=l({},Ue.Default,{placement:"right",trigger:"click",content:"",template:''}),Be=l({},Ue.DefaultType,{content:"(string|element|function)"}),Ve="fade",Ye="show",Xe=".popover-header",ze=".popover-body",Ge={HIDE:"hide"+Fe,HIDDEN:"hidden"+Fe,SHOW:"show"+Fe,SHOWN:"shown"+Fe,INSERTED:"inserted"+Fe,CLICK:"click"+Fe,FOCUSIN:"focusin"+Fe,FOCUSOUT:"focusout"+Fe,MOUSEENTER:"mouseenter"+Fe,MOUSELEAVE:"mouseleave"+Fe},Je=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).__proto__=n;var o=i.prototype;return o.isWithContent=function(){return this.getTitle()||this._getContent()},o.addAttachmentClass=function(t){g(this.getTipElement()).addClass(Me+"-"+t)},o.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},o.setContent=function(){var t=g(this.getTipElement());this.setElementContent(t.find(Xe),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(ze),e),t.removeClass(Ve+" "+Ye)},o._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},o._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(Ke);null!==e&&0=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t= 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.validator_regexp) { - var re = new RegExp(flag.validator_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(); - }); - } - } - }]); - -angular.module("FICApp") - .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("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 ""; - } - }); - -angular.module("FICApp") - .component('flagKey', { - bindings: { - kid: '=', - key: '=', - settings: '=', - wantchoices: '=', - }, - controller: function() { - this.additem = function(key) { - this.key.values.push(""); - }; - }, - template: ` -
- - -
- - - -
- -
-
- -
-
- - -
- ` - }); - -angular.module("FICApp") - .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; - } - $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; - } - - function updTime() { - if (time && $rootScope.settings) { - 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); - - 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/frontend/static/js/common.js b/frontend/static/js/common.js new file mode 120000 index 00000000..c978b8f5 --- /dev/null +++ b/frontend/static/js/common.js @@ -0,0 +1 @@ +../../../admin/static/js/common.js \ No newline at end of file diff --git a/frontend/static/js/d3.v3.min.js b/frontend/static/js/d3.v3.min.js deleted file mode 100644 index 16648730..00000000 --- a/frontend/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/frontend/static/js/d3.v3.min.js b/frontend/static/js/d3.v3.min.js new file mode 120000 index 00000000..58337036 --- /dev/null +++ b/frontend/static/js/d3.v3.min.js @@ -0,0 +1 @@ +../../../admin/static/js/d3.v3.min.js \ No newline at end of file diff --git a/frontend/static/js/i18n b/frontend/static/js/i18n new file mode 120000 index 00000000..0bb28e47 --- /dev/null +++ b/frontend/static/js/i18n @@ -0,0 +1 @@ +../../../admin/static/js/i18n/ \ No newline at end of file diff --git a/frontend/static/js/jquery.min.js b/frontend/static/js/jquery.min.js deleted file mode 100644 index a1c07fd8..00000000 --- a/frontend/static/js/jquery.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v3.4.1 | (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(C,e){"use strict";var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).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 w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|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(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.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,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){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 H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.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--)b.attrHandle[n[r]]=t}function pe(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 de(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 d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.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}},b.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[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.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},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),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(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.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)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(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===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(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?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(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 G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.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=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(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 j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.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 k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?: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}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;nx",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.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),m(t)&&(t=t.call(e,n,k.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):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.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"===k.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"===k.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===k.css(e,"position"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.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)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.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?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.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){k.fn[n]=function(e,t){return 0 Date: Wed, 9 Jun 2021 01:46:09 +0200 Subject: [PATCH 0266/1637] frontend: Include chbase.sh in entrypoint --- Dockerfile-frontend | 5 ++++- entrypoint-frontend.sh | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100755 entrypoint-frontend.sh diff --git a/Dockerfile-frontend b/Dockerfile-frontend index 72bed002..749ce9ed 100644 --- a/Dockerfile-frontend +++ b/Dockerfile-frontend @@ -19,7 +19,10 @@ EXPOSE 8080 WORKDIR /srv -ENTRYPOINT ["/srv/frontend", "--bind=:8080"] +ENTRYPOINT ["/usr/sbin/entrypoint.sh"] +CMD ["--bind=:8080"] + +COPY entrypoint-frontend.sh /usr/sbin/entrypoint.sh VOLUME /srv/htdocs-frontend/ diff --git a/entrypoint-frontend.sh b/entrypoint-frontend.sh new file mode 100755 index 00000000..dee4699e --- /dev/null +++ b/entrypoint-frontend.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +CURRENT_BASE="/" +[ -n "${BASEURL}" ] || BASEURL="/" + +run() { + local NEWBASE=$1 + local FILE=$2 + + if [ -d "${FILE}" ] + then + for f in "${FILE}/"*.html "${FILE}/"*.js + do + run "${NEWBASE}" "${f}" + done + [ -d "${FILE}/js/" ] && run "${NEWBASE}" "${FILE}/js" + [ -d "${FILE}/views/" ] && run "${NEWBASE}" "${FILE}/views" + elif [ -f "${FILE}" ] + then + sed -ri "s@(href|src)=\"${CURRENT_BASE}@\1=\"${NEWBASE}@g;s@\\\$http.get\(\"${CURRENT_BASE}@\$http.get\(\"${NEWBASE}@g;s@\\\$http\((.*)\"${CURRENT_BASE}@\$http(\1\"${NEWBASE}@g" ${FILE} + fi +} + +[ "${CURRENT_BASE}" != "${BASEURL}"] && run "${BASEURL}" /srv/htdocs-frontend + +exec /srv/frontend $@ From 8e95cec1048553eb02ab7a95904f1ff5c3001a88 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 20 Jul 2021 20:27:39 +0200 Subject: [PATCH 0267/1637] Introduce fic-nginx --- .drone-manifest-fic-nginx.yml | 22 ++++ .drone.yml | 41 +++++++ Dockerfile-nginx | 11 ++ configs/fic-auth-docker.conf | 1 + configs/nginx-docker.conf | 225 ++++++++++++++++++++++++++++++++++ 5 files changed, 300 insertions(+) create mode 100644 .drone-manifest-fic-nginx.yml create mode 100644 Dockerfile-nginx create mode 100644 configs/fic-auth-docker.conf create mode 100644 configs/nginx-docker.conf diff --git a/.drone-manifest-fic-nginx.yml b/.drone-manifest-fic-nginx.yml new file mode 100644 index 00000000..cbd2dcce --- /dev/null +++ b/.drone-manifest-fic-nginx.yml @@ -0,0 +1,22 @@ +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.yml b/.drone.yml index ccb5a1a9..1fef3ccd 100644 --- a/.drone.yml +++ b/.drone.yml @@ -151,6 +151,21 @@ steps: 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: @@ -331,6 +346,21 @@ steps: 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: @@ -414,6 +444,17 @@ steps: 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 dashboard image: plugins/manifest settings: diff --git a/Dockerfile-nginx b/Dockerfile-nginx new file mode 100644 index 00000000..eadeacb3 --- /dev/null +++ b/Dockerfile-nginx @@ -0,0 +1,11 @@ +FROM nginx:stable-alpine + +ENV HOST_FRONTEND=frontend:8080 HOST_ADMIN=admin:8081 HOST_DASHBOARD=dashboard:8082 HOST_QA=qa:8083 \ + PATH_FILES=/srv/FILES PATH_STATIC=/srv/htdocs-frontend PATH_SETTINGS=/srv/SETTINGS PATH_TEAMS=/srv/TEAMS + +EXPOSE 80 + +COPY configs/fic-auth-docker.conf /etc/nginx/auth.conf +COPY configs/nginx-docker.conf /etc/nginx/templates/default.conf.template + +COPY frontend/static /srv/htdocs-frontend diff --git a/configs/fic-auth-docker.conf b/configs/fic-auth-docker.conf new file mode 100644 index 00000000..6470cb3c --- /dev/null +++ b/configs/fic-auth-docker.conf @@ -0,0 +1 @@ +set $team "$http_x_fic_user"; diff --git a/configs/nginx-docker.conf b/configs/nginx-docker.conf new file mode 100644 index 00000000..0a36cbbd --- /dev/null +++ b/configs/nginx-docker.conf @@ -0,0 +1,225 @@ +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; + + 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; + + location = / { + include /etc/nginx/auth.conf; + } + location = /index.html { + include /etc/nginx/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 /etc/nginx/auth.conf; + + rewrite ^/.*$ /index.html; + } + location /edit { + include /etc/nginx/auth.conf; + + rewrite ^/.*$ /index.html; + } + location /issue { + include /etc/nginx/auth.conf; + + rewrite ^/.*$ /index.html; + } + location /issues { + include /etc/nginx/auth.conf; + + rewrite ^/.*$ /index.html; + } + location /rank { + include /etc/nginx/auth.conf; + + rewrite ^/.*$ /index.html; + } + location /rules { + include /etc/nginx/auth.conf; + + rewrite ^/.*$ /index.html; + } + location /tags/ { + include /etc/nginx/auth.conf; + + rewrite ^/.*$ /index.html; + } + location /register { + include /etc/nginx/auth.conf; + + rewrite ^/.*$ /index.html; + } + + location /files/ { + alias ${PATH_FILES}/; + sendfile on; + tcp_nodelay on; + } + + location /wait.json { + include /etc/nginx/auth.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 /etc/nginx/auth.conf; + + root ${PATH_TEAMS}/$team/; + expires epoch; + add_header Cache-Control no-cache; + + if (!-f $document_root/../../startingblock/started) { + rewrite ^/ /wait.json; + } + } + location /issues.json { + include /etc/nginx/auth.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 { + root ${PATH_TEAMS}; + expires epoch; + 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 /etc/nginx/auth.conf; + + rewrite ^/submit/(.*)$ /submission/$team/$1 break; + + proxy_pass http://${HOST_FRONTEND}/; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_redirect off; + } + + location /submit/name { + include /etc/nginx/auth.conf; + + rewrite ^/submit/.*$ /chname/$team break; + + proxy_pass http://${HOST_FRONTEND}/; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_redirect off; + } + + location /registration { + include /etc/nginx/auth.conf; + + rewrite ^/registration /registration/$team break; + + proxy_pass http://${HOST_FRONTEND}; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_redirect off; + } + + location /openhint/ { + include /etc/nginx/auth.conf; + + rewrite ^/openhint/(.*)$ /openhint/$team/$1 break; + + proxy_pass http://${HOST_FRONTEND}/; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_redirect off; + } + + location /wantchoices/ { + include /etc/nginx/auth.conf; + + rewrite ^/wantchoices/(.*)$ /wantchoices/$team/$1 break; + + proxy_pass http://${HOST_FRONTEND}/; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_redirect off; + } + + location /dashboard/ { + proxy_pass http://${HOST_DASHBOARD}; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_redirect off; + } + + location /api/ { + proxy_pass http://${HOST_ADMIN}/admin/api/; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_redirect off; + } + + location /admin/ { + proxy_pass http://${HOST_ADMIN}; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_redirect off; + } + + location /qa/ { + proxy_pass http://${HOST_QA}; + proxy_set_header X-Forwarded-For $remote_addr; + 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; + } +} From c2fe14c0d774f89a12ab593a1acebfd8803ce6e5 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 21 Jul 2021 10:42:33 +0200 Subject: [PATCH 0268/1637] db: Handle connection through unix socket --- libfic/db.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/libfic/db.go b/libfic/db.go index 41ad70af..eb049ff6 100644 --- a/libfic/db.go +++ b/libfic/db.go @@ -5,6 +5,7 @@ import ( _ "github.com/go-sql-driver/mysql" "log" "os" + "strings" "time" ) @@ -19,11 +20,15 @@ func DSNGenerator() string { db_db := "fic" if v, exists := os.LookupEnv("MYSQL_HOST"); exists { - db_host = "tcp(" + v + ":" - if p, exists := os.LookupEnv("MYSQL_PORT"); exists { - db_host += p + ")" + if strings.HasPrefix(v, "/") { + db_host = "unix(" + v + ")" } else { - db_host += "3306)" + db_host = "tcp(" + v + ":" + if p, exists := os.LookupEnv("MYSQL_PORT"); exists { + db_host += p + ")" + } else { + db_host += "3306)" + } } } if v, exists := os.LookupEnv("MYSQL_PASSWORD"); exists { From 74ae52ef41795881772407c241716f17f95fca62 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 21 Jul 2021 11:23:06 +0200 Subject: [PATCH 0269/1637] CD: Fix deployment of fic-backend image --- .drone-manifest-fic-backend.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.drone-manifest-fic-backend.yml b/.drone-manifest-fic-backend.yml index fc1154f7..6d2fda72 100644 --- a/.drone-manifest-fic-backend.yml +++ b/.drone-manifest-fic-backend.yml @@ -1,4 +1,4 @@ -image: nemunaire/fic-admin:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} +image: nemunaire/fic-backend:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} {{#if build.tags}} tags: {{#each build.tags}} @@ -6,16 +6,16 @@ tags: {{/each}} {{/if}} manifests: - - image: nemunaire/fic-admin:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 + - image: nemunaire/fic-backend:{{#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 + - image: nemunaire/fic-backend:{{#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 + - image: nemunaire/fic-backend:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm platform: architecture: arm os: linux From 8f1b44e3dd9c73e4800d6fd479b0d3c18c2a2a6a Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 21 Jul 2021 12:30:51 +0200 Subject: [PATCH 0270/1637] New env variable FIC_BASEURL to change the base URL --- Dockerfile-dashboard | 3 +- Dockerfile-frontend | 5 ++ Dockerfile-nginx | 15 +++- Dockerfile-qa | 1 + admin/index.go | 10 +-- admin/main.go | 21 +++--- backend/main.go | 8 +- configs/fic-auth-docker.conf | 2 +- configs/nginx-chbase.sh | 27 +++++++ configs/nginx-docker.conf | 140 +++++++++++++++++++++-------------- dashboard/main.go | 5 ++ qa/main.go | 5 ++ 12 files changed, 165 insertions(+), 77 deletions(-) create mode 100755 configs/nginx-chbase.sh diff --git a/Dockerfile-dashboard b/Dockerfile-dashboard index 5b2dc248..262351db 100644 --- a/Dockerfile-dashboard +++ b/Dockerfile-dashboard @@ -28,4 +28,5 @@ COPY dashboard/static/index.html /srv/htdocs-dashboard/ COPY dashboard/static/css/bootstrap.min.css frontend/static/css/fic.css frontend/static/css/glyphicon.css /srv/htdocs-dashboard/css/ COPY frontend/static/fonts /srv/htdocs-dashboard/fonts COPY frontend/static/img/ dashboard/static/img/logo-epita-bw.png /srv/htdocs-dashboard/img/ -COPY dashboard/static/js/dashboard.js frontend/static/js/angular.min.js dashboard/static/js/angular-animate.min.js frontend/static/js/angular-route.min.js frontend/static/js/angular-sanitize.min.js frontend/static/js/bootstrap.min.js frontend/static/js/common.js frontend/static/js/d3.v3.min.js frontend/static/js/i18n frontend/static/js/jquery.min.js /srv/htdocs-dashboard/js/ +COPY dashboard/static/js/dashboard.js frontend/static/js/angular.min.js dashboard/static/js/angular-animate.min.js frontend/static/js/angular-route.min.js frontend/static/js/angular-sanitize.min.js frontend/static/js/bootstrap.min.js frontend/static/js/common.js frontend/static/js/d3.v3.min.js frontend/static/js/jquery.min.js /srv/htdocs-dashboard/js/ +COPY admin/static/js/i18n/* /srv/htdocs-dashboard/js/i18n/ diff --git a/Dockerfile-frontend b/Dockerfile-frontend index 749ce9ed..6c48e53e 100644 --- a/Dockerfile-frontend +++ b/Dockerfile-frontend @@ -28,3 +28,8 @@ VOLUME /srv/htdocs-frontend/ COPY --from=gobuild /go/src/srs.epita.fr/fic-server/frontend/frontend /srv/frontend COPY frontend/static /srv/htdocs-frontend + +COPY frontend/static/css/glyphicon.css /srv/htdocs-frontend/css/ +COPY admin/static/fonts/* /srv/htdocs-frontend/fonts/ +COPY frontend/static/js/angular.min.js frontend/static/js/angular-route.min.js frontend/static/js/angular-sanitize.min.js frontend/static/js/bootstrap.min.js frontend/static/js/common.js frontend/static/js/d3.v3.min.js frontend/static/js/jquery.min.js /srv/htdocs-frontend/js/ +COPY admin/static/js/i18n/* /srv/htdocs-frontend/js/i18n/ diff --git a/Dockerfile-nginx b/Dockerfile-nginx index eadeacb3..b5a1cd8c 100644 --- a/Dockerfile-nginx +++ b/Dockerfile-nginx @@ -1,11 +1,20 @@ FROM nginx:stable-alpine -ENV HOST_FRONTEND=frontend:8080 HOST_ADMIN=admin:8081 HOST_DASHBOARD=dashboard:8082 HOST_QA=qa:8083 \ - PATH_FILES=/srv/FILES PATH_STATIC=/srv/htdocs-frontend PATH_SETTINGS=/srv/SETTINGS PATH_TEAMS=/srv/TEAMS +ENV FIC_BASEURL=/ \ + HOST_FRONTEND=frontend:8080 HOST_ADMIN=admin:8081 HOST_DASHBOARD=dashboard:8082 HOST_QA=qa:8083 \ + PATH_FILES=/srv/FILES PATH_STARTINGBLOCK=/srv/STARTINGBLOCK PATH_STATIC=/srv/htdocs-frontend PATH_SETTINGS=/srv/SETTINGS PATH_SYNC=/srv/SYNC PATH_TEAMS=/srv/TEAMS EXPOSE 80 -COPY configs/fic-auth-docker.conf /etc/nginx/auth.conf +COPY configs/nginx-chbase.sh /docker-entrypoint.d/40-update-baseurl.sh + +COPY configs/fic-auth-docker.conf /etc/nginx/fic-auth.conf COPY configs/nginx-docker.conf /etc/nginx/templates/default.conf.template COPY frontend/static /srv/htdocs-frontend + +# Dereference symlink +COPY frontend/static/css/glyphicon.css /srv/htdocs-frontend/css/ +COPY admin/static/fonts/* /srv/htdocs-frontend/fonts/ +COPY frontend/static/js/angular.min.js frontend/static/js/angular-route.min.js frontend/static/js/angular-sanitize.min.js frontend/static/js/bootstrap.min.js frontend/static/js/common.js frontend/static/js/d3.v3.min.js frontend/static/js/jquery.min.js /srv/htdocs-frontend/js/ +COPY admin/static/js/i18n/* /srv/htdocs-frontend/js/i18n/ diff --git a/Dockerfile-qa b/Dockerfile-qa index d3db773a..ec451bb2 100644 --- a/Dockerfile-qa +++ b/Dockerfile-qa @@ -29,3 +29,4 @@ COPY qa/static/css/bootstrap.min.css frontend/static/css/fic.css frontend/static COPY frontend/static/fonts /srv/htdocs-qa/fonts COPY frontend/static/img/ /srv/htdocs-qa/img/ COPY qa/static/js/qa.js frontend/static/js/angular.min.js qa/static/js/angular-resource.min.js frontend/static/js/angular-route.min.js frontend/static/js/angular-sanitize.min.js frontend/static/js/bootstrap.min.js frontend/static/js/common.js frontend/static/js/i18n frontend/static/js/jquery.min.js /srv/htdocs-qa/js/ +COPY qa/static/views/ /srv/htdocs-qa/views/ diff --git a/admin/index.go b/admin/index.go index 3854e250..6f3b444b 100644 --- a/admin/index.go +++ b/admin/index.go @@ -117,13 +117,13 @@ const indextpl = ` - + - - + + - - + + diff --git a/admin/main.go b/admin/main.go index f60e51fe..99191fa9 100644 --- a/admin/main.go +++ b/admin/main.go @@ -68,6 +68,7 @@ func main() { cloudPassword := "" localImporterDirectory := "" localImporterSymlink := false + baseURL := "/" // Read paremeters from environment if v, exists := os.LookupEnv("FICCA_PASS"); exists { @@ -85,11 +86,14 @@ func main() { if v, exists := os.LookupEnv("FICCLOUD_PASS"); exists { cloudPassword = v } + if v, exists := os.LookupEnv("FIC_BASEURL"); exists { + baseURL = v + } // Read parameters from command line var bind = flag.String("bind", "127.0.0.1:8081", "Bind port/socket") var dsn = flag.String("dsn", fic.DSNGenerator(), "DSN to connect to the MySQL server") - var baseURL = flag.String("baseurl", "/", "URL prepended to each URL") + 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)") @@ -140,6 +144,7 @@ func main() { log.Fatal("Unable to cd to static/ directory:", err) } staticFS = http.FS(sub) + sync.DeepReportPath = path.Join("SYNC", sync.DeepReportPath) } if fic.FilesDir, err = filepath.Abs(fic.FilesDir); err != nil { log.Fatal(err) @@ -159,12 +164,10 @@ func main() { if settings.SettingsDir, err = filepath.Abs(settings.SettingsDir); err != nil { log.Fatal(err) } - if *baseURL != "/" { - tmp := path.Clean(*baseURL) - baseURL = &tmp + if baseURL != "/" { + baseURL = path.Clean(baseURL) } else { - tmp := "" - baseURL = &tmp + baseURL = "" } // Creating minimal directories structure @@ -201,8 +204,8 @@ func main() { } // Update base URL on main page - log.Println("Changing base URL to", *baseURL+"/", "...") - genIndex(*baseURL) + log.Println("Changing base URL to", baseURL+"/", "...") + genIndex(baseURL) // Prepare graceful shutdown interrupt := make(chan os.Signal, 1) @@ -210,7 +213,7 @@ func main() { srv := &http.Server{ Addr: *bind, - Handler: StripPrefix(*baseURL, api.Router()), + Handler: StripPrefix(baseURL, api.Router()), } // Serve content diff --git a/backend/main.go b/backend/main.go index 630882a3..e9e03721 100644 --- a/backend/main.go +++ b/backend/main.go @@ -101,11 +101,17 @@ func reloadSettings(config settings.FICSettings) { } func main() { + if v, exists := os.LookupEnv("FIC_BASEURL"); exists { + fic.FilesDir = v + "files" + } else { + fic.FilesDir = "/files" + } + var dsn = flag.String("dsn", fic.DSNGenerator(), "DSN to connect to the MySQL server") flag.StringVar(&settings.SettingsDir, "settings", settings.SettingsDir, "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") - flag.StringVar(&fic.FilesDir, "files", "/files", "Request path prefix to reach files") + flag.StringVar(&fic.FilesDir, "files", fic.FilesDir, "Request path prefix to reach files") var debugINotify = flag.Bool("debuginotify", false, "Show skipped inotofy events") flag.BoolVar(&skipInitialGeneration, "skipfullgeneration", skipInitialGeneration, "Skip the initial regeneration") flag.IntVar(¶llelJobs, "jobs", parallelJobs, "Number of generation workers") diff --git a/configs/fic-auth-docker.conf b/configs/fic-auth-docker.conf index 6470cb3c..370ae10f 100644 --- a/configs/fic-auth-docker.conf +++ b/configs/fic-auth-docker.conf @@ -1 +1 @@ -set $team "$http_x_fic_user"; +set $team "$http_x_fic_team"; diff --git a/configs/nginx-chbase.sh b/configs/nginx-chbase.sh new file mode 100755 index 00000000..b7dd14e3 --- /dev/null +++ b/configs/nginx-chbase.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +[ -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}/"*.html "${FILE}/"*.js + do + run "${NEWBASE}" "${f}" + done + [ -d "${FILE}/js/" ] && run "${NEWBASE}" "${FILE}/js" + [ -d "${FILE}/views/" ] && run "${NEWBASE}" "${FILE}/views" + elif [ -f "${FILE}" ] + then + sed -ri "s@(href|src)=\"${CURRENT_BASE}@\1=\"${NEWBASE}@g;s@\\\$http.get\(\"${CURRENT_BASE}@\$http.get\(\"${NEWBASE}@g;s@\\\$http\((.*)\"${CURRENT_BASE}@\$http(\1\"${NEWBASE}@g" ${FILE} + fi +} + +run "${FIC_BASEURL}" "${PATH_STATIC}" + +exit 0 diff --git a/configs/nginx-docker.conf b/configs/nginx-docker.conf index 0a36cbbd..b0e991b9 100644 --- a/configs/nginx-docker.conf +++ b/configs/nginx-docker.conf @@ -13,11 +13,24 @@ server { 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 /etc/nginx/auth.conf; + include fic-auth.conf; } location = /index.html { - include /etc/nginx/auth.conf; + include fic-auth.conf; } location = /welcome.html { internal; @@ -44,43 +57,48 @@ server { } } + location ${FIC_BASEURL} { + rewrite ^${FIC_BASEURL}(.*)$ /$1; + } + location ~ ^/[A-Z] { - include /etc/nginx/auth.conf; + include fic-auth.conf; rewrite ^/.*$ /index.html; } + location /edit { - include /etc/nginx/auth.conf; + include fic-auth.conf; rewrite ^/.*$ /index.html; } location /issue { - include /etc/nginx/auth.conf; + include fic-auth.conf; rewrite ^/.*$ /index.html; } location /issues { - include /etc/nginx/auth.conf; + include fic-auth.conf; rewrite ^/.*$ /index.html; } location /rank { - include /etc/nginx/auth.conf; - - rewrite ^/.*$ /index.html; - } - location /rules { - include /etc/nginx/auth.conf; + include fic-auth.conf; rewrite ^/.*$ /index.html; } location /tags/ { - include /etc/nginx/auth.conf; + include fic-auth.conf; rewrite ^/.*$ /index.html; } location /register { - include /etc/nginx/auth.conf; + include fic-auth.conf; + + rewrite ^/.*$ /index.html; + } + location /rules { + include fic-auth.conf; rewrite ^/.*$ /index.html; } @@ -92,7 +110,7 @@ server { } location /wait.json { - include /etc/nginx/auth.conf; + include fic-auth.conf; root ${PATH_TEAMS}/$team/; expires epoch; @@ -104,18 +122,18 @@ server { add_header Cache-Control no-cache; } location /my.json { - include /etc/nginx/auth.conf; + include fic-auth.conf; root ${PATH_TEAMS}/$team/; expires epoch; add_header Cache-Control no-cache; - if (!-f $document_root/../../startingblock/started) { + if (!-f ${PATH_STARTINGBLOCK}/started) { rewrite ^/ /wait.json; } } location /issues.json { - include /etc/nginx/auth.conf; + include fic-auth.conf; root ${PATH_TEAMS}/$team/; expires epoch; @@ -139,76 +157,84 @@ server { } location /submit/ { - include /etc/nginx/auth.conf; + include fic-auth.conf; - rewrite ^/submit/(.*)$ /submission/$team/$1 break; - - proxy_pass http://${HOST_FRONTEND}/; + proxy_pass http://${HOST_FRONTEND}/submission; proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-FIC-Team $team; proxy_redirect off; } + location /submit/issue { + include fic-auth.conf; + proxy_pass http://${HOST_FRONTEND}/issue; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-FIC-Team $team; + proxy_redirect off; + } location /submit/name { - include /etc/nginx/auth.conf; + include fic-auth.conf; - rewrite ^/submit/.*$ /chname/$team break; - - proxy_pass http://${HOST_FRONTEND}/; + proxy_pass http://${HOST_FRONTEND}/chname; proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-FIC-Team $team; proxy_redirect off; } - location /registration { - include /etc/nginx/auth.conf; - - rewrite ^/registration /registration/$team break; + include fic-auth.conf; proxy_pass http://${HOST_FRONTEND}; proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-FIC-Team $team; proxy_redirect off; } + location /openhint/ { + include fic-auth.conf; - location /openhint/ { - include /etc/nginx/auth.conf; + proxy_pass http://${HOST_FRONTEND}; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-FIC-Team $team; + proxy_redirect off; + } + location /wantchoices/ { + include fic-auth.conf; - rewrite ^/openhint/(.*)$ /openhint/$team/$1 break; - - proxy_pass http://${HOST_FRONTEND}/; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_redirect off; - } - - location /wantchoices/ { - include /etc/nginx/auth.conf; - - rewrite ^/wantchoices/(.*)$ /wantchoices/$team/$1 break; - - proxy_pass http://${HOST_FRONTEND}/; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_redirect off; - } - - location /dashboard/ { - proxy_pass http://${HOST_DASHBOARD}; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_redirect off; - } + proxy_pass http://${HOST_FRONTEND}; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-FIC-Team $team; + proxy_redirect off; + } location /api/ { - proxy_pass http://${HOST_ADMIN}/admin/api/; + include fic-auth.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 /admin/ { + location ${FIC_BASEURL}admin/ { proxy_pass http://${HOST_ADMIN}; proxy_set_header X-Forwarded-For $remote_addr; proxy_redirect off; } - location /qa/ { + location ${FIC_BASEURL}qa/ { + include fic-auth.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-auth.conf; + + proxy_pass http://${HOST_DASHBOARD}; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-FIC-Team $team; proxy_redirect off; } diff --git a/dashboard/main.go b/dashboard/main.go index d5472054..d4fca6ca 100644 --- a/dashboard/main.go +++ b/dashboard/main.go @@ -64,6 +64,11 @@ func StripPrefix(prefix string, h http.Handler) http.Handler { } func main() { + // 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") flag.StringVar(&BaseURL, "baseurl", BaseURL, "URL prepended to each URL") diff --git a/qa/main.go b/qa/main.go index 5cc80a76..2d204306 100644 --- a/qa/main.go +++ b/qa/main.go @@ -61,6 +61,11 @@ func StripPrefix(prefix string, h http.Handler) http.Handler { } func main() { + // 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:8083", "Bind port/socket") var dsn = flag.String("dsn", fic.DSNGenerator(), "DSN to connect to the MySQL server") From 29607981e451cd1bacb5d4e575fd8adc87304bd6 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 21 Jul 2021 23:17:47 +0200 Subject: [PATCH 0271/1637] admin: Use relative path to call API --- admin/index.go | 2 +- admin/static/js/app.js | 146 +++++++++++++++---------------- dashboard/static/fonts | 2 +- dashboard/static/index.html | 8 +- dashboard/static/js/dashboard.js | 16 ++-- 5 files changed, 87 insertions(+), 87 deletions(-) diff --git a/admin/index.go b/admin/index.go index 6f3b444b..8ca67550 100644 --- a/admin/index.go +++ b/admin/index.go @@ -63,7 +63,7 @@ const indextpl = ` } - +
+ + + + + + + + + + + {#each $issues as issue (issue.id)} + + + + + + + + {:else} + + + + {/each} + +
ObjetÉtat / PrioritéGéré parMessages + {#if !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} + à {text.date} : + {text.cnt} +

+ {/each} +
+ +
+ Aucune anomalie remontée pour l'instant.
+ Vous souhaitez nous faire remonter un problème ? +
+ + diff --git a/frontend/ui/src/routes/rank.svelte b/frontend/ui/src/routes/rank.svelte new file mode 100644 index 00000000..f542217d --- /dev/null +++ b/frontend/ui/src/routes/rank.svelte @@ -0,0 +1,52 @@ + + + +

+ {$settings.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}{team.score}
+
+
diff --git a/frontend/ui/src/routes/register.svelte b/frontend/ui/src/routes/register.svelte new file mode 100644 index 00000000..bc4930b6 --- /dev/null +++ b/frontend/ui/src/routes/register.svelte @@ -0,0 +1,141 @@ + + + + + + + + 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 !$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/ui/src/routes/rules.svelte b/frontend/ui/src/routes/rules.svelte new file mode 100644 index 00000000..b16c1e27 --- /dev/null +++ b/frontend/ui/src/routes/rules.svelte @@ -0,0 +1,146 @@ + + + +

+ {$settings.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. +

+
+ +

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. +

+ +

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.
  • +
+

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

+
+ +

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. +

+ +

Prem's

+

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

+ +

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/ui/src/routes/tags/[tag].svelte b/frontend/ui/src/routes/tags/[tag].svelte new file mode 100644 index 00000000..e4ddac8c --- /dev/null +++ b/frontend/ui/src/routes/tags/[tag].svelte @@ -0,0 +1,69 @@ + + + + + +

+ Challenges {tag} +

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

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

+{/if} +
diff --git a/frontend/ui/src/stores/issues.js b/frontend/ui/src/stores/issues.js new file mode 100644 index 00000000..dc32e363 --- /dev/null +++ b/frontend/ui/src/stores/issues.js @@ -0,0 +1,55 @@ +import { derived, writable } from 'svelte/store'; + +function createIssuesStore() { + const { subscribe, set, update } = writable({issues: [], issues_idx: {}, issues_nb_responses: 0, issues_need_info: 0}); + + return { + subscribe, + update: (res_issues, cb=null) => { + if (res_issues.status === 200) { + res_issues.json().then((issues) => { + const issues_idx = {}; + let issues_nb_responses = 0; + let issues_need_info = 0; + issues.forEach(function(issue, k) { + issues_idx[issue.id] = issue; + issues_nb_responses += issue.texts.length; + if (issue.state == 'need-info') issues_need_info++; + issues[k].texts.reverse(); + }) + update((i) => (Object.assign(i, {issues, issues_idx, issues_nb_responses, issues_need_info}))); + + if (cb) { + cb(issues, issues_idx, issues_nb_responses, issues_need_info); + } + }); + } else if (res_issues.status === 404) { + update((i) => ({issues: [], issues_idx: {}, issues_nb_responses: 0, issues_need_info: 0})); + } + }, + }; +} + +export const issuesStore = createIssuesStore(); + +export const issues = derived( + issuesStore, + ($issuesStore) => ($issuesStore.issues), +); + +export const issues_idx = derived( + issuesStore, + ($issuesStore) => ($issuesStore.issues_idx), +); + +export const issues_nb_responses = derived( + issuesStore, + ($issuesStore) => ($issuesStore.issues_nb_responses), +); + +export const issues_need_info = derived( + issuesStore, + ($issuesStore) => ($issuesStore.issues_need_info), +); + +export const issues_known_responses = writable(0); diff --git a/frontend/ui/src/stores/my.js b/frontend/ui/src/stores/my.js new file mode 100644 index 00000000..72f4be00 --- /dev/null +++ b/frontend/ui/src/stores/my.js @@ -0,0 +1,28 @@ +import { writable } from 'svelte/store'; + +function createMyStore() { + const { subscribe, set, update } = writable(null); + + return { + subscribe, + update: (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; + } + + update((m) => (Object.assign(m?m:{}, my))); + + if (cb) { + cb(my); + } + }); + } else if (res_my.status === 404) { + update((m) => (null)); + } + }, + }; +} + +export const my = createMyStore(); diff --git a/frontend/ui/src/stores/mythemes.js b/frontend/ui/src/stores/mythemes.js new file mode 100644 index 00000000..726b71b5 --- /dev/null +++ b/frontend/ui/src/stores/mythemes.js @@ -0,0 +1,42 @@ +import { derived } from 'svelte/store'; + +import { my } from './my.js'; +import { themesStore } from './themes.js'; + +export const myThemes = derived([my, themesStore], ([$my, $themesStore]) => { + const themes = {}; + + for (let key in $themesStore.themes) { + themes[key] = {exercice_solved: 0}; + + if ($my && $my.exercices) { + for (let k in $themesStore.themes[key].exercices) { + if ($my.exercices[k] && $my.exercices[k].solved) { + themes[key].exercice_solved++; + } + } + } + } + + return themes; +}); + +export const tags = derived([my, themesStore], ([$my, $themesStore]) => { + const tags = {}; + + for (let key in $themesStore.themes) { + for (let k in $themesStore.themes[key].exercices) { + $themesStore.themes[key].exercices[k].tags.forEach((tag) => { + if (!tags[tag]) + tags[tag] = {count: 1, solved: 0}; + else + tags[tag].count += 1; + + if ($my && $my.exercices && $my.exercices[k] && $my.exercices[k].solved) + tags[tag].solved += 1; + }); + } + } + + return tags; +}); diff --git a/frontend/ui/src/stores/settings.js b/frontend/ui/src/stores/settings.js new file mode 100644 index 00000000..d0d90b47 --- /dev/null +++ b/frontend/ui/src/stores/settings.js @@ -0,0 +1,108 @@ +import { readable, writable } from 'svelte/store'; + +function createSettingsStore() { + const { subscribe, set, update } = writable({}); + + return { + subscribe, + update: (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.activateTime) + settings.activateTime = new Date(settings.activateTime); + + settings.recvTime = recvTime; + const x_fic_time = res_settings.headers["x-fic-time"]; + if (x_fic_time) { + settings.currentTime = Math.floor(x_fic_time * 1000); + } else { + settings.currentTime = settings.recvTime; + } + + update((s) => (Object.assign(s, settings))); + + if (cb) { + cb(settings); + } + }); + } + }, + } +} + +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; + }); + + const interval = setInterval(() => { + set(updateTime(_settings)); + }, 1000); + + return function stop() { + clearInterval(interval); + unsubscribe(); + } +}); diff --git a/frontend/ui/src/stores/teams.js b/frontend/ui/src/stores/teams.js new file mode 100644 index 00000000..ba42ad92 --- /dev/null +++ b/frontend/ui/src/stores/teams.js @@ -0,0 +1,45 @@ +import { derived, writable } from 'svelte/store'; + +function createTeamsStore() { + const { subscribe, set, update } = writable({teams:{}, teams_count: 0, rank: []}); + + return { + subscribe, + update: (res_teams, cb=null) => { + if (res_teams.status === 200) { + res_teams.json().then((teams) => { + const teams_count = Object.keys(teams).length + + const rank = []; + for (const tid in teams) { + teams[tid].id = Number(tid); + rank.push(teams[tid]); + } + + update((t) => (Object.assign(t, {teams, teams_count, rank}))); + + if (cb) { + cb(teams, teams_count, rank); + } + }); + } + }, + }; +} + +export const teamsStore = createTeamsStore(); + +export const teams = derived( + teamsStore, + ($teamsStore) => ($teamsStore.teams) +); + +export const teams_count = derived( + teamsStore, + ($teamsStore) => ($teamsStore.teams_count) +); + +export const rank = derived( + teamsStore, + ($teamsStore) => ($teamsStore.rank) +); diff --git a/frontend/ui/src/stores/themes.js b/frontend/ui/src/stores/themes.js new file mode 100644 index 00000000..df71f704 --- /dev/null +++ b/frontend/ui/src/stores/themes.js @@ -0,0 +1,68 @@ +import { derived, writable } from 'svelte/store'; + +function createThemesStore() { + const { subscribe, set, update } = writable({themes: {}, exercices_idx: {}, max_solved: 0}); + + return { + subscribe, + update: (res_themes, cb=null) => { + if (res_themes.status === 200) { + res_themes.json().then((themes) => { + let max_solved = 0; + const exercices_idx = {}; + + for (let key in themes) { + const theme = themes[key]; + + if (theme.solved > max_solved) { + max_solved = theme.solved; + } + + themes[key].exercice_count = Object.keys(theme.exercices).length; + themes[key].exercice_coeff_max = 0; + themes[key].max_gain = 0; + let last_exercice = null; + for (let 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 (last_exercice != null) + themes[key].exercices[last_exercice].next = k; + last_exercice = k; + + exercice.id = k; + exercices_idx[k] = exercice; + } + } + + update((t) => (Object.assign(t, {themes, exercices_idx, max_solved}))); + + if (cb) { + cb(themes, exercices_idx, max_solved); + } + }); + } + }, + }; +} + +export const themesStore = createThemesStore(); + +export const themes = derived( + themesStore, + ($themesStore) => ($themesStore.themes), +); + +export const exercices_idx = derived( + themesStore, + ($themesStore) => ($themesStore.exercices_idx), +); + +export const max_solved = derived( + themesStore, + ($themesStore) => ($themesStore.max_solved), +); diff --git a/frontend/ui/static/favicon.ico b/frontend/ui/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..3da960762392e9c97016697e56d1aabd11690e7d GIT binary patch literal 2238 zcmc)McTkj96b0~u3Llo(5Nsfw6_6sb^tRO9WrbZ!6uYsD1!M0hHn1S}1`2|pCLWDYc zB2?&wP$zHVgHUG?Lfiup;vq(ecX!AF`#>fRBmE$g^oLA30J7kLWDsPs!3Yi+3Tfyt z1j~m*su&4rk5Q2H91Ur&v5@u-hosL0Nc&ENq;CWyVG)pqO@gG~WQYe$g=F9~hzHGp zgop>vgm}m-1Pz;ypy81S9I*gFN(&JRf* z!<(A~?|Dh^nx71>$Q1Yx&jq{Sy)YGCQM=)}Xb(w;=i+pDEXjb!(oDE5%Y-{|i_U_3 zOg7wN4!~{sLAb8Sfg5pIc?hn=Wo0g0WAoq=n-AyJhvBli0M2m*5XK#W(;8kOoYoeR zVhGojK)9Y)3dapcAta6)kE7eB6L8pk65WWymQ%3bdKz7M@n>Kke-;kJKH)5E6VAbo zxBVP!wx5UXjtj6!ya?OGOR!G53>)6gE3n>q6#*Ea1}kEjdIO!) zZbFb&PFW7Y?px$G1bc47BK;1`((l5YSnQ?Tdk^OO?!#=~1DIqyfN{n{%7-xJGn0}S zXFj6&5e)Y~h7mE$dIDo&l=T#yvY){u`zZ`5^$$FU0qLal0v!*&fIcx$W(8#hI_6ZM zeNH9X9eRoOqyuS}`wDGyt0=3Wmsf?hL^rRRvKo5%)zHmb!i_h?%59!-iqP<}u&!WI7xZPJ)%mHYv1qFM404N5L&*{}9z1>?&UEue)YT zMlcpPc+4DS-GA*VtuE6psXs~dV{A%{7Ukg=p?Q_Ilq@)w-%Z$7xwc zF}8r4%%WRyXsN+lDLpn!9R>)%&p<^*hXy^-g$dvT$45>UmgtMHZxXsot@zv z@3+$}Q1**B(U-k1cUd{l;AXGj!d11K*4f$zb2z8f59ngo_~IZgQ`Hs@ORn9&2y4jc z_WY$-(~}M0@@iZ4ES+SoeRU!i#l~{0)O)r081rsk#f6zB-Z|fP~Me=_h{11n| IFMl-t21&Mmb^rhX literal 0 HcmV?d00001 diff --git a/frontend/ui/static/favicon.png b/frontend/ui/static/favicon.png deleted file mode 100644 index 825b9e65af7c104cfb07089bb28659393b4f2097..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1571 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 literal 0 HcmV?d00001 diff --git a/frontend/ui/static/img/fic.png b/frontend/ui/static/img/fic.png new file mode 100644 index 0000000000000000000000000000000000000000..7e8d3a983de113302f1f13de31909477fe55bb62 GIT binary patch literal 11868 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 WiY~F7ZIilUAc7I zd%v~i-Msdh_t6KRdt0A;;eCDYp7-s8p7(42q4)cvUGK@Wr-{2G{@64{7h|IVtO%!r zD&c{zFndOxsuq5N{ttpy(f(40;AR-s$s?L_`=UK zi?zE2ge&MgLO;MN6yp2D52a?b<2PcAy%3)XI0S`QCq@b$WX5mz$Xgz3ibdv*=NLUtBq}l_(RQ1htn+Y%!Nkhe}IMtEmztqzTPmR!e z!Ua8c!+4R+tlDLBFg4QyG@YJbNr@>7&0%FB`cpfl4LVs2ij$!({`;9j8|_7)pb>qX*g?#SuE5n3teL- zj8Yq}06kp+eU-U?2 zm|WzY++rbEE`3Fh#5bd#*jz}f5<8$;5nq&u<#n}x^=cI5-V??2A3Xq%*EtS){(W4W zEIk&|FC&<{bLWOI*5?eYtuEN2M_2FmqC)Y2N2^%9eE!Osi*S1gZ+3>(gerTloQE1z zJ{_7Lqk4ZyBlQ=8bU{OEN%@j(>{&cOJ4PTLd%7EnPCmIY8gnL#l&vZ~wnQINk<#}D z;xL=3K2GOIk79B%Rq56mqUFJ|NlraHO8R*Ug<{?0wteD*I@cgliqCLA6j*$7`uS_Z znWS3L<#oDq0T7UWg0h@3{R|(gM zfKHJ1h?^H=En0|fNd`94;?YpiG99J6pKj~{&qoln#lrqTnAYxXE$`?!G}i-4%tHuv zZK&`?AtXp7Yk(bI=d&;YCb{c6)gC$gT(mIn5jgJEB?nKB823pu%Z@@41C2un!`~HVewvuOJ|_T7&nWlqO;Ta z&7u!^I}3D1=Lq;eGGWHmyBRMW<0&N*dk&!-3Mb3llz6=2O z+vUsmi7s5!wrv^D!(>yIj56yI*@}%;`v1O^m>n#_q0Xp#u-$>0yP1P=;J$x777zWB zm)C(o9mVWj&fptIHtGB)GH|5YET*dd{kSr|k^}zzy+Q`$@AvdEp-BcN8JJ{Xl7UGE KCK>oYW#AuVF%ZH4 literal 0 HcmV?d00001 diff --git a/frontend/ui/static/img/icon-dark.ico b/frontend/ui/static/img/icon-dark.ico new file mode 100644 index 0000000000000000000000000000000000000000..2a3982232accf21851d0cbcb1a7d3c1fa9060f53 GIT binary patch literal 10366 zcmeHM&59jW5bitZR&Mur1s!On2?19ZCX(%di)@2EGHwFxR(u8*S^ETTeSydr1wp+F zbrFdOii#*`X5tOvKtF@uS3l>c`~FNOD{)Hgb52!#RdwprsqX6(#RUJ{y*tJ8x#FEy zi{hoCC@>Uv@gJDN=X51Oe(>6j;=Om@E>1ppzj$)*cJbv$_lqw+`?~n*n{SH;4<8f{ zzyGfI;in&qpMU+O`0bD1i$5PfDwcmgF>{l09aTFUh@dG=a8j)_GNouE34FTW3^d*C zMyv?ceOc}+HQN!nMll(<&c$0-K*MKhgCSH~aft%JL;ciVq8X+;nF6*RBsOVQo=4Q0 zY)RB~D4$w_J+!3E=F!IhG;y~plCs~;=fv$NNa za*B1|4>T;SYDer3gxQh`!1wKlOU63*BwRj;xV!afzibidj;;H6v9bXJ1X7qv|6880%rYkIVz?NaO*-laeNCL0>EvVO@XMnLgg$1e)^Lpu=UcyN#1k&s-;{D^` z6PXbIxuCd2IFMUGit6YrCIUm+Ig$Z1oACa|z(oC5t8|sU- z1$9af428`HWduT7z?r_-CGn9xqPSg|O+2Ln#7b`^EN53S7N}i&d;UZr_T5B6IN_^&DI(rE230Y&KCGjNvpzF_zcu59(%(b zoYHI|U9=agOax<4n#HLyc&?oJCktM~eZSzQUtY?)0r*$ARN^hk2DSGRlsei@Xq#PC}=*Te`!;hYzodiH0^iNf$8KVJtwJH<8S}fiP7z`G4 zVgL-gSSp^eIk9^6r?7#s=C%xrt*nLOcy&=-=c-iOcbaAie0eXWka(&;x{n)wSFT z*-mT5;;_(d^H*J`x5q33-XBd=vEjf7M;l=JojGMWh3bVMTnnkYdCCz@JSti~OVs?oC477Vo6+odo_1j<;T@RR=+EW?&A`MPcBj2^&Up zcN1P#;4mUQTURRaq9%d2M4kg16Nafm=fW`p=N*>PWBK`qCNG4m*~mR*EIW`9A4=1? z2OAJfQ;uhlIM5NFP0K4Y!1c!+)FbK3xB`BVer}`d``4o?X83W(0Gh}&$A^6%Ubj(* z2r_5*!hp#%(~?jCz#eFtmE6C#KmAIj3as%!7VGe4CJyuuGDd?b9PPX)xU0dc{*p?8}rkiCC+ zq$DVl7yzam$U0+B0og~U1+a1>Hw90J!~x^S2#)-+!PBgTPec*<1I9me z8gC1b6)yXJYJ5548yYYzd*a&*InI;_)9!}JSth#3LO%Rd5qOPch(j-KhP<#ogjYQ% z%=-M$gVLtS%y-S5NG4T#z00As17S402v2u{nW2d^mLz~ox_wbe1!V!?21sgR`q1;& z{i~fZIRgAIO@4_+@Rk@VUWMedkBfE-CKvMrPy`jb>ouL{3foo^04y9I3);U=w!wm& zmno&y^%&I06Wu{DF;>{dxfigQwA-{-$Snmla~XJ99(n|qgCisfL4FUfi0_K^AI^Yu zlgr0HEiR8G(*OQ^Su6JU^Jjn;&yWH6`#t}Le??arxXQp)2Cg!2m4T}a{GT%LFFhK* A*8l(j literal 0 HcmV?d00001 diff --git a/frontend/ui/static/img/icon-info.ico b/frontend/ui/static/img/icon-info.ico new file mode 100644 index 0000000000000000000000000000000000000000..8d15559341cc8b2e7b2243a8d69e8c2c023660f4 GIT binary patch literal 10366 zcmeHMUuzsy6rX?}Aa`t&P1YS)Q7DuK>IW$JTqyKe@L7Bi@*w(9mgKDu8p#K6A@~+# zaeOMY4*2h(D8bQM#hB5=G_9_4KZCz>?*Ey|Zg%IT;NH@?XU;vpbI<+VbI+Y5QM81A zTU*O`z7V~2C5l#}C_)!);XjnDC$X_%{A_e9`g8y7=<)uY=!gA}qHp&nyhJ;2d6XZ9d(6Qmm&%gFALrYBSTo)nfQGq$D5su z51m>I5nxw^`pQ=BK{vm9gG9L`#}GR7;0)3Sj9)`zAqKVEVm%x0k*Q85%x~`&Xh%H8 zuK0n;%+2GJEUl=85gto0&6i0Byfqy6$RN$we7b?4IuyFz^xuBl95 zpehU2M-3nUV#l1|C5BC20y~-qK26ZDymVO;y z*8sY1k9{TDtVaI^<~NhMzR(x>$6@#l^w&L-m#+GrxmJGZe3FF8f(12c7%`CJyLVMt za2{eJD|PRX8X{8TLzvdC@;X_K^s|S~{rkJhm3`LfkA06^82i?`K`3e=0dMLd;Hx*^ zeZ2u@RJ7QqLHX@Fk9~2ImOSa};}Q*zh3^3hwIQ)8q-1eUeWTjIEj96o_%al^dPO9r zQ5|V&SS&X;P9C?(G|u!Gcex*;^E9wFyIM&eSv0P-MXJYxE-oxy{;EcHH2qh2#EqAY z0gzG2mSB+^Uho-ixGVRBctefnYCL#y@z5M9(s3S2l4wNAN6LE0FAV|E8iiQlVTISX zK|dp1ZYJBIb=Wtfb+vm3!zrsbAd!I1-*v*9tYka&7?|R*R_KzEFrE*=k!MJUs$SG$ zDD|?%NLde+N6c5`9$t(*Q>TYAu+oP=rtrgXDFD4g4o*KDr0$g@Xb=%?i3v=PXHbQv zA||syr^`{jDk!Scz}V66)QUd3wW`6=NzI)hNyKt!ri)Kw353sQA)c=8!%&rRIifS1 zh65aINNJM_Kuup2&YV`1#9xrrIw263y{;^zTU54G9g^G{K%iaTYv6@#4y5`4-2=cp z*g#B7?{A{0dR9h!sYVLu@~bP+503ip)UrppxRe@Kx~KW1bl)3=lyuF)Z6*6EIQzHUZ?#44uZlk5oZUx22V_AS9b71 z!ltsKgV228U&ssp*fGxzSnsqIGp2yWl7Qc3`!#G;{uT^Rf60Ky2SA@K=`*R^o(}RX z_`=s;+pkhEwJ&eaxJl#ya6i5G{uhL9xR9DqPNzPg6qKITyU$skBT)|7R?e{*Ba4QR zo-@lNwdY_SxDa7~sF(laEDP UFtA`?!N7un1p^BP{!a}22L>?wp8x;= literal 0 HcmV?d00001 diff --git a/frontend/ui/static/img/icon-light.ico b/frontend/ui/static/img/icon-light.ico new file mode 100644 index 0000000000000000000000000000000000000000..812ccc6184a196d8e17bb266acaad59a6dbbb799 GIT binary patch literal 10366 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 literal 0 HcmV?d00001 diff --git a/frontend/ui/static/img/icon-secondary.ico b/frontend/ui/static/img/icon-secondary.ico new file mode 100644 index 0000000000000000000000000000000000000000..313f7b40e5d01faa535544222d71d891eca5c5cb GIT binary patch literal 10366 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 pRUjVT-D?^RZz<;{}e*-W9hd=-T literal 0 HcmV?d00001 diff --git a/frontend/ui/static/img/icon-warning.ico b/frontend/ui/static/img/icon-warning.ico new file mode 100644 index 0000000000000000000000000000000000000000..a2e4a3560dcdb0ed269382f37ca9ccb9b37a52b3 GIT binary patch literal 10366 zcmeHN&5j#I5FRABGjY7uuDx^Sx`*75p8z3QC>IVZapMBgynwZ@un&MG9|@9?IY1PF zg{4G@MGTnmCqxETQ37Ol`x*GEs=KGBd+eE=y}@*p>FTQbs%yUPu4zY!qL=XJP{rSh1CmsLNJ314@)q zOEYNGt5apDs&#-vPX3$L__+PlGA9VQQA^Hm1dk65et@me zVl(wL0VBZCj;@`J;QKuHUW*KlH#Fr;!t@l6NYk`20RuG`|K!^1*{9u`yfD+LwUO$3 zaitk^i<0&Hs~#N3`xoprTgJ{V98kDXBOdkEK{s$tAmM2*AZkr3@LFR3MT$?;^{=ki)H+tQWHo!+P-9+rDgm%AQW)Fwk*h6<5`-Y@Dh5pV{$T0c`B?zfOi&T zM~o3j9w-C}@gSfWNBsfnm$%#$k9Rk`U16{BGx3X`F(-Aizk{uQV{HmDch4tidzW z6HINUgh=ce$d^83T~ZuM;Yz4{Y}k^`L5qkO+cM2IQg}yvsxBt|1s^)el?W)Z?iZp& zpfA6P#r+HlMK9f#%>cY#7F_=^*3}(@tksK@GzS-~2cA+eZI&8IdQ9e6U0YAKo`-j1 zk@t4Ie(Q00eh=RRe&f#S$g=1vE#>dw1Nt0%LU)Q0*mW_k4D&AhfW8LRWx>vZ)iNj4 z>Z|}GFRnYMR}gm4hcGLpPSFiyB{^=Ea%UbP{wHH{dS{0@A{_d_&mBjI{&pg3acXQm z5>QAV)u~jB#WQ(oWS0u)LVT!!8-GkWgqJM{`p1Npz7ij2`fhyeEmlb1LJnU!Xgrin zdg$LOLVT#suutS-UgDj)=Ba>#OXf3Mo3^xl|S&P?&Oty&=t;qHsPG`?AnbIorrE z6C*vh1)!IuR{Zdyh!e&y$Y7cdDS?aU*x0X9t+e!+(Ar~JfE+0|c%>eR8WFEZqI2yw zZ`sIT=RX}k6u62uTWC7H8w!H^T^!pv|uzSh`0B?_zg;WFJmo}yr;Owp_X`w2! ztQREYMVvjdD&Aib*KeUG)*Wr3vIZYDj}tmIr<|AfO(Wncz*D-Vn0jJ#RRwDl>|lg6 z)rwQo_;CgOY@hS9c#ywRkF6q;+MRF1TSp@>zE}hdY2A+-<0S!e)_RzD1N^En9zTwq z6sVF>X4k+MWfr(ZKeuMD@$TSXlc;}*WW~1E85rOc! zv+rRXYWpb{NBeITn7_0wYw>cuvoKwS>S1LMi?=UQw%3jD7Mix)N_805KC{aOwxjC{^a3|YfR9yC!}`UQuwgD(3%eTl^@40B z4Uys|_ZPX=QCSIZ-ZkMCgi-43&YdqrI;{bCW;#r6D{h#c`-MC}m|IR?w|0(En9RnQ$9RnQ$9RnQ$|7`~T E1vvrgF8}}l literal 0 HcmV?d00001 diff --git a/frontend/ui/static/img/srs.png b/frontend/ui/static/img/srs.png new file mode 100644 index 0000000000000000000000000000000000000000..c25b6af4baabe95f6a119456dda46d7a4c0ee4fb GIT binary patch literal 160574 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 element in src/app.html target: '#svelte' } From 867e9bb345b46a2966c95aa6faa1a8d3d3f5e6db Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 30 Aug 2021 18:31:32 +0200 Subject: [PATCH 0287/1637] sync: Fix a div by 0 when no exercice detected in theme --- admin/sync/full.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/admin/sync/full.go b/admin/sync/full.go index 1fa71113..22622e5e 100644 --- a/admin/sync/full.go +++ b/admin/sync/full.go @@ -5,8 +5,8 @@ import ( "fmt" "log" "os" - "time" "sync" + "time" "srs.epita.fr/fic-server/libfic" "srs.epita.fr/fic-server/settings" @@ -25,7 +25,7 @@ var DeepSyncProgress uint8 // SpeedySyncDeep performs a recursive synchronisation without importing files. func SpeedySyncDeep(i Importer) (errs map[string][]string) { oneDeepSync.Lock() - defer func(){ + defer func() { oneDeepSync.Unlock() if DeepSyncProgress != 255 { log.Printf("Speedy synchronization terminated at step %d/255", DeepSyncProgress) @@ -44,16 +44,18 @@ func SpeedySyncDeep(i Importer) (errs map[string][]string) { var themeStep uint8 = uint8(250) / uint8(len(themes)) for tid, theme := range themes { - DeepSyncProgress = 3 + uint8(tid) * themeStep + DeepSyncProgress = 3 + uint8(tid)*themeStep errs[theme.Name] = SyncExercices(i, theme) 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("Deep synchronization in progress: %d/255 - doing Theme %q, Exercice %q: %q\n", DeepSyncProgress, theme.Name, exercice.Title, exercice.Path) + 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 - errs[theme.Name] = append(errs[theme.Name], SyncExerciceFiles(i, exercice)...) + DeepSyncProgress = 3 + uint8(tid)*themeStep + uint8(eid)*exerciceStep DeepSyncProgress += exerciceStep / 2 _, ferrs := SyncExerciceFlags(i, exercice) @@ -74,7 +76,7 @@ func SpeedySyncDeep(i Importer) (errs map[string][]string) { // SyncDeep performs a recursive synchronisation: from themes to challenge items. func SyncDeep(i Importer) (errs map[string][]string) { oneDeepSync.Lock() - defer func(){ + defer func() { oneDeepSync.Unlock() if DeepSyncProgress != 255 { log.Printf("Full synchronization terminated at step %d/255", DeepSyncProgress) @@ -167,7 +169,7 @@ func SyncThemeDeep(i Importer, theme fic.Theme, tid int, themeStep uint8) (errs oneThemeDeepSync.Lock() defer oneThemeDeepSync.Unlock() - DeepSyncProgress = 3 + uint8(tid) * themeStep + DeepSyncProgress = 3 + uint8(tid)*themeStep errs = SyncExercices(i, theme) if exercices, err := theme.GetExercices(); err == nil && len(exercices) > 0 { @@ -175,7 +177,7 @@ func SyncThemeDeep(i Importer, theme fic.Theme, tid int, themeStep uint8) (errs 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 + DeepSyncProgress = 3 + uint8(tid)*themeStep + uint8(eid)*exerciceStep errs = append(errs, SyncExerciceFiles(i, exercice)...) DeepSyncProgress += exerciceStep / 3 From 74e8c3801a0f0aeccbe67f851691bc282e95a5dc Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 30 Aug 2021 18:33:14 +0200 Subject: [PATCH 0288/1637] fic: Add Order, Help and Type values in struct --- admin/api/file.go | 2 +- admin/api/handlers.go | 14 ++--- admin/sync/exercice_defines.go | 1 + admin/sync/exercice_keys.go | 3 ++ backend/choices.go | 2 +- backend/submission.go | 8 +-- libfic/db.go | 4 ++ libfic/exercice.go | 2 +- libfic/file.go | 8 +-- libfic/flag.go | 3 +- libfic/flag_choice.go | 9 ++-- libfic/flag_key.go | 36 ++++++++----- libfic/hint.go | 4 +- libfic/mcq.go | 40 ++++++++------ libfic/mcq_justification.go | 10 ++-- libfic/team_my.go | 98 ++++++++++++++++------------------ 16 files changed, 134 insertions(+), 110 deletions(-) diff --git a/admin/api/file.go b/admin/api/file.go index b35e2c74..08822a95 100644 --- a/admin/api/file.go +++ b/admin/api/file.go @@ -143,7 +143,7 @@ func deleteFile(file fic.EFile, _ []byte) (interface{}, error) { return file.Delete() } -func deleteFileDep(file fic.EFile, depid int64, _ []byte) (interface{}, error) { +func deleteFileDep(file fic.EFile, depid int, _ []byte) (interface{}, error) { return true, file.DeleteDepend(fic.FlagKey{Id: depid}) } diff --git a/admin/api/handlers.go b/admin/api/handlers.go index fcd37344..4473750b 100644 --- a/admin/api/handlers.go +++ b/admin/api/handlers.go @@ -115,7 +115,7 @@ func teamAssocHandler(f func(fic.Team, string, []byte) (interface{}, error)) fun return func(ps httprouter.Params, body []byte) (interface{}, error) { var team fic.Team - teamHandler(func (tm fic.Team, _ []byte) (interface{}, error) { + teamHandler(func(tm fic.Team, _ []byte) (interface{}, error) { team = tm return nil, nil })(ps, body) @@ -193,7 +193,7 @@ func flagKeyHandler(f func(fic.FlagKey, fic.Exercice, []byte) (interface{}, erro return nil, err } else { for _, flag := range flags { - if flag.Id == kid { + if flag.Id == int(kid) { return f(flag, exercice, body) } } @@ -212,9 +212,9 @@ func choiceHandler(f func(fic.FlagChoice, fic.Exercice, []byte) (interface{}, er return nil, nil })(ps, body) - if cid, err := strconv.ParseInt(string(ps.ByName("cid")), 10, 64); err != nil { + if cid, err := strconv.ParseInt(string(ps.ByName("cid")), 10, 32); err != nil { return nil, err - } else if choice, err := flag.GetChoice(cid); err != nil { + } else if choice, err := flag.GetChoice(int(cid)); err != nil { return nil, err } else { return f(choice, exercice, body) @@ -236,7 +236,7 @@ func quizHandler(f func(fic.MCQ, fic.Exercice, []byte) (interface{}, error)) fun return nil, err } else { for _, mcq := range mcqs { - if mcq.Id == int64(qid) { + if mcq.Id == int(qid) { return f(mcq, exercice, body) } } @@ -316,13 +316,13 @@ func fileHandler(f func(fic.EFile, []byte) (interface{}, error)) func(httprouter } } -func fileDependancyHandler(f func(fic.EFile, int64, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { +func fileDependancyHandler(f func(fic.EFile, int, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { return func(ps httprouter.Params, body []byte) (interface{}, error) { if depid, err := strconv.ParseInt(string(ps.ByName("depid")), 10, 64); err != nil { return nil, err } else { return fileHandler(func(file fic.EFile, b []byte) (interface{}, error) { - return f(file, depid, b) + return f(file, int(depid), b) })(ps, body) } } diff --git a/admin/sync/exercice_defines.go b/admin/sync/exercice_defines.go index f1336f16..814fe134 100644 --- a/admin/sync/exercice_defines.go +++ b/admin/sync/exercice_defines.go @@ -40,6 +40,7 @@ type ExerciceFlag struct { CaseSensitive bool `toml:",omitempty"` ValidatorRe string `toml:"validator_regexp,omitempty"` Placeholder string `toml:",omitempty"` + Help string `toml:",omitempty"` ChoicesCost int64 `toml:"choices_cost,omitempty"` Choice []ExerciceFlagChoice LockedFile []ExerciceUnlockFile `toml:"unlock_file,omitempty"` diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index 10e8f366..77812442 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -128,8 +128,10 @@ func buildKeyFlag(exercice fic.Exercice, flag ExerciceFlag, flagline int, defaul } fl := fic.Flag(fic.FlagKey{ IdExercice: exercice.Id, + Order: int8(flagline), Label: flag.Label, Placeholder: flag.Placeholder, + Help: flag.Help, IgnoreCase: !flag.CaseSensitive, Multiline: flag.Type == "text", ValidatorRegexp: validatorRegexp(flag.ValidatorRe), @@ -221,6 +223,7 @@ func buildExerciceFlag(i Importer, exercice fic.Exercice, flag ExerciceFlag, nli } else if flag.Type == "mcq" { addedFlag := fic.MCQ{ IdExercice: exercice.Id, + Order: int8(nline + 1), Title: flag.Label, Entries: []fic.MCQ_entry{}, } diff --git a/backend/choices.go b/backend/choices.go index c2dc52aa..8c09af7d 100644 --- a/backend/choices.go +++ b/backend/choices.go @@ -13,7 +13,7 @@ import ( ) type wantChoices struct { - FlagId int64 `json:"id"` + FlagId int `json:"id"` } func treatWantChoices(pathname string, team fic.Team) { diff --git a/backend/submission.go b/backend/submission.go index b3e24fcf..1bd052df 100644 --- a/backend/submission.go +++ b/backend/submission.go @@ -18,9 +18,9 @@ import ( ) type ResponsesUpload struct { - Keys map[int64]string `json:"flags"` - MCQs map[int64]bool `json:"mcqs"` - MCQJ map[int64]string `json:"justifications"` + 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) { @@ -100,7 +100,7 @@ func treatSubmission(pathname string, team fic.Team, exercice_id string) { continue } else { if responses.Keys == nil { - responses.Keys = map[int64]string{} + responses.Keys = map[int]string{} } responses.Keys[key.Id] = j } diff --git a/libfic/db.go b/libfic/db.go index eb049ff6..e63336f4 100644 --- a/libfic/db.go +++ b/libfic/db.go @@ -176,7 +176,10 @@ CREATE TABLE IF NOT EXISTS exercice_hints( CREATE TABLE IF NOT EXISTS exercice_flags( id_flag INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, id_exercice INTEGER NOT NULL, + ordre TINYINT NOT NULL, + label VARCHAR(255) NOT NULL, type VARCHAR(255) NOT NULL, + placeholder VARCHAR(255) NOT NULL, help VARCHAR(255) NOT NULL, ignorecase BOOLEAN NOT NULL DEFAULT 0, multiline BOOLEAN NOT NULL DEFAULT 0, @@ -233,6 +236,7 @@ CREATE TABLE IF NOT EXISTS exercice_files_okey_deps( CREATE TABLE IF NOT EXISTS exercice_mcq( id_mcq INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, id_exercice INTEGER NOT NULL, + ordre TINYINT NOT NULL, title VARCHAR(255) NOT NULL, FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; diff --git a/libfic/exercice.go b/libfic/exercice.go index 72adb784..ce44e1c7 100644 --- a/libfic/exercice.go +++ b/libfic/exercice.go @@ -408,7 +408,7 @@ func (e Exercice) MCQSolved() (res []int64) { // CheckResponse, given both flags and MCQ responses, figures out if thoses are correct (or if they are previously solved). // In the meanwhile, CheckResponse registers good answers given (but it does not mark the challenge as solved at the end). -func (e Exercice) CheckResponse(cksum []byte, respflags map[int64]string, respmcq map[int64]bool, t Team) (bool, error) { +func (e Exercice) CheckResponse(cksum []byte, respflags map[int]string, respmcq map[int]bool, t Team) (bool, error) { if err := e.NewTry(t, cksum); err != nil { return false, err } else if flags, err := e.GetFlagKeys(); err != nil { diff --git a/libfic/file.go b/libfic/file.go index 9de693a3..750ee52b 100644 --- a/libfic/file.go +++ b/libfic/file.go @@ -326,11 +326,11 @@ func (f EFile) GetDepends() ([]Flag, error) { defer rows.Close() for rows.Next() { - var d int64 + var d int if err := rows.Scan(&d); err != nil { return nil, err } - deps = append(deps, FlagKey{d, f.IdExercice, "", "", false, false, nil, []byte{}, 0}) + deps = append(deps, FlagKey{d, f.IdExercice, 0, "", "", "", "", false, false, nil, []byte{}, 0}) } if err := rows.Err(); err != nil { return nil, err @@ -343,11 +343,11 @@ func (f EFile) GetDepends() ([]Flag, error) { defer rows.Close() for rows.Next() { - var d int64 + var d int if err := rows.Scan(&d); err != nil { return nil, err } - deps = append(deps, MCQ{d, f.IdExercice, "", []MCQ_entry{}}) + deps = append(deps, MCQ{d, f.IdExercice, 0, "", []MCQ_entry{}}) } if err := rows.Err(); err != nil { return nil, err diff --git a/libfic/flag.go b/libfic/flag.go index f4f97a61..c0065de7 100644 --- a/libfic/flag.go +++ b/libfic/flag.go @@ -3,13 +3,14 @@ package fic import () type Flag interface { - GetId() int64 + GetId() int RecoverId() (Flag, error) Create(e Exercice) (Flag, error) Update() (int64, error) Delete() (int64, error) AddDepend(d Flag) error GetDepends() ([]Flag, error) + GetOrder() int8 Check(val interface{}) int FoundBy(t Team) } diff --git a/libfic/flag_choice.go b/libfic/flag_choice.go index 1c5b9e59..2aee3f07 100644 --- a/libfic/flag_choice.go +++ b/libfic/flag_choice.go @@ -4,9 +4,9 @@ import () // FlagChoice represents a choice a respond to a classic flag type FlagChoice struct { - Id int64 `json:"id"` + Id int `json:"id"` // IdFlag is the identifier of the underlying flag - IdFlag int64 `json:"idFlag"` + IdFlag int `json:"idFlag"` // Label is the title of the choice as displayed to players Label string `json:"label"` // Value is the raw content that'll be written as response if this choice is selected @@ -40,7 +40,7 @@ func (f FlagKey) GetChoices() ([]FlagChoice, error) { } // GetChoice returns a choice for the given Flag. -func (f FlagKey) GetChoice(id int64) (c FlagChoice, err error) { +func (f FlagKey) GetChoice(id int) (c FlagChoice, err error) { if errr := DBQueryRow("SELECT id_choice, id_flag, label, response FROM flag_choices WHERE id_choice = ?", id).Scan(&c.Id, &c.IdFlag, &c.Label, &c.Value); errr != nil { return c, errr } @@ -52,7 +52,8 @@ func (f FlagKey) AddChoice(c FlagChoice) (FlagChoice, error) { if res, err := DBExec("INSERT INTO flag_choices (id_flag, label, response) VALUES (?, ?, ?)", f.Id, c.Label, c.Value); err != nil { return c, err } else { - c.Id, err = res.LastInsertId() + cid, err := res.LastInsertId() + c.Id = int(cid) return c, err } } diff --git a/libfic/flag_key.go b/libfic/flag_key.go index be27441b..d52ec382 100644 --- a/libfic/flag_key.go +++ b/libfic/flag_key.go @@ -12,13 +12,19 @@ import ( // FlagKey represents a flag's challenge, stored as hash. type FlagKey struct { - Id int64 `json:"id"` + Id int `json:"id"` // IdExercice is the identifier of the underlying challenge IdExercice int64 `json:"idExercice"` + // Order is used to sort the flag between them + Order int8 `json:"order"` // Label is the title of the flag as displayed to players Label string `json:"label"` + // Type is the kind of flag + Type string `json:"type,omitempty"` // Placeholder is a small piece of text that aims to add useful information like flag format, ... Placeholder string `json:"placeholder"` + // Help is a description of the flag + Help string `json:"help"` // IgnoreCase indicates if the case is sensitive to case or not IgnoreCase bool `json:"ignorecase"` // Multiline indicates if the flag is stored on multiple lines @@ -33,7 +39,7 @@ type FlagKey struct { // GetFlagKeys returns a list of key's flags comming with the challenge. func (e Exercice) GetFlagKeys() ([]FlagKey, error) { - if rows, err := DBQuery("SELECT id_flag, id_exercice, type, help, ignorecase, multiline, validator_regexp, cksum, choices_cost FROM exercice_flags WHERE id_exercice = ?", e.Id); err != nil { + if rows, err := DBQuery("SELECT id_flag, id_exercice, ordre, label, type, placeholder, help, ignorecase, multiline, validator_regexp, cksum, choices_cost FROM exercice_flags WHERE id_exercice = ?", e.Id); err != nil { return nil, err } else { defer rows.Close() @@ -43,7 +49,7 @@ func (e Exercice) GetFlagKeys() ([]FlagKey, error) { var k FlagKey k.IdExercice = e.Id - if err := rows.Scan(&k.Id, &k.IdExercice, &k.Label, &k.Placeholder, &k.IgnoreCase, &k.Multiline, &k.ValidatorRegexp, &k.Checksum, &k.ChoicesCost); err != nil { + if err := rows.Scan(&k.Id, &k.IdExercice, &k.Order, &k.Label, &k.Type, &k.Placeholder, &k.Help, &k.IgnoreCase, &k.Multiline, &k.ValidatorRegexp, &k.Checksum, &k.ChoicesCost); err != nil { return nil, err } @@ -58,14 +64,14 @@ func (e Exercice) GetFlagKeys() ([]FlagKey, error) { } // GetFlagKey returns a list of flags comming with the challenge. -func GetFlagKey(id int64) (k FlagKey, err error) { - err = DBQueryRow("SELECT id_flag, id_exercice, type, help, ignorecase, multiline, validator_regexp, cksum, choices_cost FROM exercice_flags WHERE id_flag = ?", id).Scan(&k.Id, &k.IdExercice, &k.Label, &k.Placeholder, &k.IgnoreCase, &k.Multiline, &k.ValidatorRegexp, &k.Checksum, &k.ChoicesCost) +func GetFlagKey(id int) (k FlagKey, err error) { + err = DBQueryRow("SELECT id_flag, id_exercice, ordre, label, type, placeholder, help, ignorecase, multiline, validator_regexp, cksum, choices_cost FROM exercice_flags WHERE id_flag = ?", id).Scan(&k.Id, &k.IdExercice, &k.Order, &k.Label, &k.Type, &k.Placeholder, &k.Help, &k.IgnoreCase, &k.Multiline, &k.ValidatorRegexp, &k.Checksum, &k.ChoicesCost) return } // GetFlagKeyByLabel returns a flag matching the given label. func (e Exercice) GetFlagKeyByLabel(label string) (k FlagKey, err error) { - err = DBQueryRow("SELECT id_flag, id_exercice, type, help, ignorecase, multiline, validator_regexp, cksum, choices_cost FROM exercice_flags WHERE type LIKE ? AND id_exercice = ?", label, e.Id).Scan(&k.Id, &k.IdExercice, &k.Label, &k.Placeholder, &k.IgnoreCase, &k.Multiline, &k.ValidatorRegexp, &k.Checksum, &k.ChoicesCost) + err = DBQueryRow("SELECT id_flag, id_exercice, ordre, label, type, placeholder, help, ignorecase, multiline, validator_regexp, cksum, choices_cost FROM exercice_flags WHERE type LIKE ? AND id_exercice = ?", label, e.Id).Scan(&k.Id, &k.IdExercice, &k.Order, &k.Label, &k.Type, &k.Placeholder, &k.Help, &k.IgnoreCase, &k.Multiline, &k.ValidatorRegexp, &k.Checksum, &k.ChoicesCost) return } @@ -126,13 +132,13 @@ func (e Exercice) AddRawFlagKey(name string, placeholder string, ignorecase bool } // GetId returns the Flag identifier. -func (k FlagKey) GetId() int64 { +func (k FlagKey) GetId() int { return k.Id } // RecoverId returns the Flag identifier as register in DB. func (k FlagKey) RecoverId() (Flag, error) { - if err := DBQueryRow("SELECT id_flag FROM exercice_flags WHERE type LIKE ? AND id_exercice = ?", k.Label, k.IdExercice).Scan(&k.Id); err != nil { + if err := DBQueryRow("SELECT id_flag FROM exercice_flags WHERE label LIKE ? AND id_exercice = ?", k.Label, k.IdExercice).Scan(&k.Id); err != nil { return FlagKey{}, err } else { return k, err @@ -148,12 +154,12 @@ func (k FlagKey) Create(e Exercice) (Flag, error) { } } - if res, err := DBExec("INSERT INTO exercice_flags (id_exercice, type, help, ignorecase, multiline, validator_regexp, cksum, choices_cost) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", e.Id, k.Label, k.Placeholder, k.IgnoreCase, k.Multiline, k.ValidatorRegexp, k.Checksum, k.ChoicesCost); err != nil { + if res, err := DBExec("INSERT INTO exercice_flags (id_exercice, ordre, label, type, placeholder, help, ignorecase, multiline, validator_regexp, cksum, choices_cost) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", e.Id, k.Order, k.Label, k.Type, k.Placeholder, k.Help, k.IgnoreCase, k.Multiline, k.ValidatorRegexp, k.Checksum, k.ChoicesCost); err != nil { return k, err } else if kid, err := res.LastInsertId(); err != nil { return k, err } else { - k.Id = kid + k.Id = int(kid) k.IdExercice = e.Id return k, nil } @@ -173,7 +179,7 @@ func (k FlagKey) Update() (int64, error) { } } - if res, err := DBExec("UPDATE exercice_flags SET id_exercice = ?, type = ?, help = ?, ignorecase = ?, multiline = ?, validator_regexp = ?, cksum = ?, choices_cost = ? WHERE id_flag = ?", k.IdExercice, k.Label, k.Placeholder, k.IgnoreCase, k.Multiline, k.ValidatorRegexp, k.Checksum, k.ChoicesCost, k.Id); err != nil { + if res, err := DBExec("UPDATE exercice_flags SET id_exercice = ?, ordre = ?, label = ?, type = ?, placeholder = ?, help = ?, ignorecase = ?, multiline = ?, validator_regexp = ?, cksum = ?, choices_cost = ? WHERE id_flag = ?", k.IdExercice, k.Order, k.Label, k.Type, k.Placeholder, k.Help, k.IgnoreCase, k.Multiline, k.ValidatorRegexp, k.Checksum, k.ChoicesCost, k.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err @@ -203,6 +209,10 @@ func (k FlagKey) Delete() (int64, error) { } } +func (k FlagKey) GetOrder() int8 { + return k.Order +} + // AddDepend insert a new dependency to a given flag. func (k FlagKey) AddDepend(j Flag) (err error) { if d, ok := j.(FlagKey); ok { @@ -225,7 +235,7 @@ func (k FlagKey) GetDepends() ([]Flag, error) { defer rows.Close() for rows.Next() { - var d int64 + var d int if err := rows.Scan(&d); err != nil { return nil, err } @@ -242,7 +252,7 @@ func (k FlagKey) GetDepends() ([]Flag, error) { defer rows.Close() for rows.Next() { - var d int64 + var d int if err := rows.Scan(&d); err != nil { return nil, err } diff --git a/libfic/hint.go b/libfic/hint.go index 42b53954..ad942cb6 100644 --- a/libfic/hint.go +++ b/libfic/hint.go @@ -152,7 +152,7 @@ func (h EHint) GetDepends() ([]Flag, error) { defer rows.Close() for rows.Next() { - var d int64 + var d int if err := rows.Scan(&d); err != nil { return nil, err } @@ -169,7 +169,7 @@ func (h EHint) GetDepends() ([]Flag, error) { defer rows.Close() for rows.Next() { - var d int64 + var d int if err := rows.Scan(&d); err != nil { return nil, err } diff --git a/libfic/mcq.go b/libfic/mcq.go index 70fc4532..c47ab504 100644 --- a/libfic/mcq.go +++ b/libfic/mcq.go @@ -8,9 +8,11 @@ import ( // MCQ represents a flag's challenge, in the form of checkbox. type MCQ struct { - Id int64 `json:"id"` + Id int `json:"id"` // IdExercice is the identifier of the underlying challenge IdExercice int64 `json:"idExercice"` + // Order is used to sort the flag between them + Order int8 `json:"order"` // Title is the label of the question Title string `json:"title"` // Entries stores the set of proposed answers @@ -19,7 +21,7 @@ type MCQ struct { // MCQ_entry represents a proposed response for a given MCQ. type MCQ_entry struct { - Id int64 `json:"id"` + Id int `json:"id"` // Label is the text displayed to players as proposed answer Label string `json:"label"` // Response stores if expected checked state. @@ -27,8 +29,8 @@ type MCQ_entry struct { } // GetMCQ returns a list of flags comming with the challenge. -func GetMCQ(id int64) (m MCQ, err error) { - err = DBQueryRow("SELECT id_mcq, id_exercice, title FROM exercice_mcq WHERE id_mcq = ?", id).Scan(&m.Id, &m.IdExercice, &m.Title) +func GetMCQ(id int) (m MCQ, err error) { + err = DBQueryRow("SELECT id_mcq, id_exercice, order, title FROM exercice_mcq WHERE id_mcq = ?", id).Scan(&m.Id, &m.IdExercice, &m.Order, &m.Title) m.fillEntries() return } @@ -55,7 +57,7 @@ func (m *MCQ) fillEntries() ([]MCQ_entry, error) { // GetMCQ returns the MCQs coming with the challenge. func (e Exercice) GetMCQ() ([]MCQ, error) { - if rows, err := DBQuery("SELECT id_mcq, id_exercice, title FROM exercice_mcq WHERE id_exercice = ?", e.Id); err != nil { + if rows, err := DBQuery("SELECT id_mcq, id_exercice, ordre, title FROM exercice_mcq WHERE id_exercice = ?", e.Id); err != nil { return nil, err } else { defer rows.Close() @@ -65,7 +67,7 @@ func (e Exercice) GetMCQ() ([]MCQ, error) { var m MCQ m.IdExercice = e.Id - if err := rows.Scan(&m.Id, &m.IdExercice, &m.Title); err != nil { + if err := rows.Scan(&m.Id, &m.IdExercice, &m.Order, &m.Title); err != nil { return nil, err } @@ -82,8 +84,8 @@ func (e Exercice) GetMCQ() ([]MCQ, error) { } // GetMCQbyChoice returns the MCQ corresponding to a choice ID. -func GetMCQbyChoice(cid int64) (m MCQ, c MCQ_entry, err error) { - if errr := DBQueryRow("SELECT id_mcq, id_exercice, title FROM exercice_mcq WHERE id_mcq = (SELECT id_mcq FROM mcq_entries WHERE id_mcq_entry = ?)", cid).Scan(&m.Id, &m.IdExercice, &m.Title); errr != nil { +func GetMCQbyChoice(cid int) (m MCQ, c MCQ_entry, err error) { + if errr := DBQueryRow("SELECT id_mcq, id_exercice, ordre, title FROM exercice_mcq WHERE id_mcq = (SELECT id_mcq FROM mcq_entries WHERE id_mcq_entry = ?)", cid).Scan(&m.Id, &m.IdExercice, &m.Order, &m.Title); errr != nil { return MCQ{}, MCQ_entry{}, errr } @@ -111,7 +113,7 @@ func GetMCQbyChoice(cid int64) (m MCQ, c MCQ_entry, err error) { } // GetId returns the MCQ identifier. -func (m MCQ) GetId() int64 { +func (m MCQ) GetId() int { return m.Id } @@ -126,12 +128,12 @@ func (m MCQ) RecoverId() (Flag, error) { // Create registers a MCQ into the database and recursively add its entries. func (m MCQ) Create(e Exercice) (Flag, error) { - if res, err := DBExec("INSERT INTO exercice_mcq (id_exercice, title) VALUES (?, ?)", e.Id, m.Title); err != nil { + if res, err := DBExec("INSERT INTO exercice_mcq (id_exercice, ordre, title) VALUES (?, ?, ?)", e.Id, m.Order, m.Title); err != nil { return m, err } else if qid, err := res.LastInsertId(); err != nil { return m, err } else { - m.Id = qid + m.Id = int(qid) m.IdExercice = e.Id // Add entries @@ -149,7 +151,7 @@ func (m MCQ) Create(e Exercice) (Flag, error) { // Update applies modifications back to the database. func (m MCQ) Update() (int64, error) { - if res, err := DBExec("UPDATE exercice_mcq SET id_exercice = ?, title = ? WHERE id_mcq = ?", m.IdExercice, m.Title, m.Id); err != nil { + if res, err := DBExec("UPDATE exercice_mcq SET id_exercice = ?, ordre = ?, title = ? WHERE id_mcq = ?", m.IdExercice, m.Order, m.Title, m.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err @@ -186,7 +188,7 @@ func (m MCQ) AddEntry(e MCQ_entry) (MCQ_entry, error) { } else if nid, err := res.LastInsertId(); err != nil { return e, err } else { - e.Id = nid + e.Id = int(nid) return e, nil } } @@ -236,6 +238,10 @@ func (e Exercice) WipeMCQs() (int64, error) { } } +func (m MCQ) GetOrder() int8 { + return m.Order +} + // AddDepend insert a new dependency to a given flag. func (m MCQ) AddDepend(j Flag) (err error) { if d, ok := j.(FlagKey); ok { @@ -258,7 +264,7 @@ func (m MCQ) GetDepends() ([]Flag, error) { defer rows.Close() for rows.Next() { - var d int64 + var d int if err := rows.Scan(&d); err != nil { return nil, err } @@ -276,7 +282,7 @@ func (m MCQ) GetDepends() ([]Flag, error) { defer rows.Close() for rows.Next() { - var d int64 + var d int if err := rows.Scan(&d); err != nil { return nil, err } @@ -298,8 +304,8 @@ func (c MCQ_entry) GetJustifiedFlag(e Exercice) (FlagKey, error) { // Check if the given vals are the expected ones to validate this flag. func (m MCQ) Check(v interface{}) int { - var vals map[int64]bool - if va, ok := v.(map[int64]bool); !ok { + var vals map[int]bool + if va, ok := v.(map[int]bool); !ok { return -1 } else { vals = va diff --git a/libfic/mcq_justification.go b/libfic/mcq_justification.go index 837929db..41e3c499 100644 --- a/libfic/mcq_justification.go +++ b/libfic/mcq_justification.go @@ -2,17 +2,17 @@ package fic import ( "errors" - "strings" "strconv" + "strings" ) type FlagLabel struct { Label string - IdChoice int64 + IdChoice int } // IsMCQJustification tells you if this key represent a justification from a MCQ. -func (k FlagKey) IsMCQJustification() (bool) { +func (k FlagKey) IsMCQJustification() bool { return len(k.Label) > 0 && k.Label[0] == '%' } @@ -20,7 +20,9 @@ func (k FlagKey) IsMCQJustification() (bool) { func (k FlagKey) GetMCQJustification() (fl FlagLabel, err error) { spl := strings.Split(k.Label, "%") if len(spl) >= 3 && len(spl[0]) == 0 { - fl.IdChoice, err = strconv.ParseInt(spl[1], 10, 64) + var idChoice int64 + idChoice, err = strconv.ParseInt(spl[1], 10, 32) + fl.IdChoice = int(idChoice) fl.Label = strings.Join(spl[2:], "%") } else { err = errors.New("This is not a MCQ justification") diff --git a/libfic/team_my.go b/libfic/team_my.go index e6582b50..ab2f5bf9 100644 --- a/libfic/team_my.go +++ b/libfic/team_my.go @@ -27,50 +27,46 @@ type myTeamHint struct { Cost int64 `json:"cost"` } type myTeamFlag struct { - Label string `json:"label"` - Placeholder string `json:"placeholder,omitempty"` - Separator string `json:"separator,omitempty"` - NbLines uint64 `json:"nb_lines,omitempty"` - IgnoreOrder bool `json:"ignore_order,omitempty"` - IgnoreCase bool `json:"ignore_case,omitempty"` - Multiline bool `json:"multiline,omitempty"` - ValidatorRe *string `json:"validator_regexp,omitempty"` - Solved *time.Time `json:"found,omitempty"` - Soluce string `json:"soluce,omitempty"` - Choices map[string]string `json:"choices,omitempty"` - ChoicesCost int64 `json:"choices_cost,omitempty"` + Order int8 `json:"order"` + Label string `json:"label"` + Type string `json:"type,omitempty"` + Placeholder string `json:"placeholder,omitempty"` + Help string `json:"help,omitempty"` + Separator string `json:"separator,omitempty"` + NbLines uint64 `json:"nb_lines,omitempty"` + IgnoreOrder bool `json:"ignore_order,omitempty"` + IgnoreCase bool `json:"ignore_case,omitempty"` + Multiline bool `json:"multiline,omitempty"` + ValidatorRe *string `json:"validator_regexp,omitempty"` + Solved *time.Time `json:"found,omitempty"` + PSolved *time.Time `json:"part_solved,omitempty"` + Soluce string `json:"soluce,omitempty"` + Justify bool `json:"justify,omitempty"` + Choices map[string]interface{} `json:"choices,omitempty"` + ChoicesCost int64 `json:"choices_cost,omitempty"` } type myTeamMCQJustifiedChoice struct { Label string `json:"label"` Value bool `json:"value,omitempty"` Justification myTeamFlag `json:"justification,omitempty"` } -type myTeamMCQ struct { - Title string `json:"title"` - Justify bool `json:"justify,omitempty"` - Choices map[int64]interface{} `json:"choices,omitempty"` - Solved *time.Time `json:"solved,omitempty"` - PSolved *time.Time `json:"part_solved,omitempty"` - Soluce string `json:"soluce,omitempty"` -} type myTeamExercice struct { - ThemeId int64 `json:"theme_id"` - Statement string `json:"statement"` - Overview string `json:"overview,omitempty"` - Finished string `json:"finished,omitempty"` - Hints []myTeamHint `json:"hints,omitempty"` - Gain int `json:"gain"` - Files []myTeamFile `json:"files,omitempty"` - Flags map[int64]myTeamFlag `json:"flags,omitempty"` - MCQs map[int64]myTeamMCQ `json:"mcqs,omitempty"` - SolveDist int64 `json:"solve_dist,omitempty"` - SolvedTime *time.Time `json:"solved_time,omitempty"` - SolvedRank int64 `json:"solved_rank,omitempty"` - Tries int64 `json:"tries,omitempty"` - TotalTries int64 `json:"total_tries,omitempty"` - VideoURI string `json:"video_uri,omitempty"` - Issue string `json:"issue,omitempty"` - IssueKind string `json:"issuekind,omitempty"` + ThemeId int64 `json:"theme_id"` + Statement string `json:"statement"` + Overview string `json:"overview,omitempty"` + Finished string `json:"finished,omitempty"` + Hints []myTeamHint `json:"hints,omitempty"` + Gain int `json:"gain"` + Files []myTeamFile `json:"files,omitempty"` + Flags map[int]myTeamFlag `json:"flags,omitempty"` + SolveDist int64 `json:"solve_dist,omitempty"` + SolvedTime *time.Time `json:"solved_time,omitempty"` + SolvedRank int64 `json:"solved_rank,omitempty"` + Tries int64 `json:"tries,omitempty"` + TotalTries int64 `json:"total_tries,omitempty"` + VideoURI string `json:"video_uri,omitempty"` + Issue string `json:"issue,omitempty"` + IssueKind string `json:"issuekind,omitempty"` } type myTeam struct { Id int64 `json:"team_id"` @@ -179,8 +175,8 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { // Expose exercice flags - justifiedMCQ := map[int64]myTeamFlag{} - exercice.Flags = map[int64]myTeamFlag{} + justifiedMCQ := map[int]myTeamFlag{} + exercice.Flags = map[int]myTeamFlag{} if flags, err := e.GetFlagKeys(); err != nil { return nil, err @@ -188,6 +184,8 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { for _, k := range flags { var flag myTeamFlag + flag.Order = k.Order + if !DisplayAllFlags && t != nil && !t.CanSeeFlag(k) { // Dependancy missing, skip the flag for now continue @@ -225,7 +223,7 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { if choices, err := k.GetChoices(); err != nil { return nil, err } else if t == nil || WChoiceCoefficient < 0 || k.ChoicesCost == 0 || t.SeeChoices(k) { - flag.Choices = map[string]string{} + flag.Choices = map[string]interface{}{} for _, c := range choices { flag.Choices[c.Value] = c.Label } @@ -243,10 +241,6 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { } } - // Expose exercice MCQs - - exercice.MCQs = map[int64]myTeamMCQ{} - if mcqs, err := e.GetMCQ(); err != nil { return nil, err } else { @@ -256,9 +250,11 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { continue } - m := myTeamMCQ{ - Title: mcq.Title, - Choices: map[int64]interface{}{}, + m := myTeamFlag{ + Type: "mcq", + Label: mcq.Title, + Order: mcq.Order, + Choices: map[string]interface{}{}, } soluce := "" @@ -292,12 +288,12 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { jc.Value = e.Response } - m.Choices[e.Id] = jc + m.Choices[strconv.Itoa(e.Id)] = jc } else { - m.Choices[e.Id] = e.Label + m.Choices[strconv.Itoa(e.Id)] = e.Label } } else { - m.Choices[e.Id] = e.Label + m.Choices[strconv.Itoa(e.Id)] = e.Label } } @@ -311,7 +307,7 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { m.PSolved = nil } - exercice.MCQs[mcq.Id] = m + exercice.Flags[mcq.Id] = m } } From 102a0878ac316eeb7ee5fb7c506a304857d9e3a2 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 30 Aug 2021 19:43:35 +0200 Subject: [PATCH 0289/1637] configs: Update scripts and config for new ui --- Dockerfile-frontend | 6 ------ Dockerfile-nginx | 20 +++++++++++++------- configs/nginx-demo.conf | 13 ++++--------- configs/nginx-docker.conf | 17 ++++++----------- configs/nginx-prod.conf | 13 ++++--------- docker-compose.yml | 29 ++++++++++++++++------------- entrypoint-frontend.sh | 1 + 7 files changed, 44 insertions(+), 55 deletions(-) diff --git a/Dockerfile-frontend b/Dockerfile-frontend index 6c48e53e..7eb9f23d 100644 --- a/Dockerfile-frontend +++ b/Dockerfile-frontend @@ -27,9 +27,3 @@ COPY entrypoint-frontend.sh /usr/sbin/entrypoint.sh VOLUME /srv/htdocs-frontend/ COPY --from=gobuild /go/src/srs.epita.fr/fic-server/frontend/frontend /srv/frontend -COPY frontend/static /srv/htdocs-frontend - -COPY frontend/static/css/glyphicon.css /srv/htdocs-frontend/css/ -COPY admin/static/fonts/* /srv/htdocs-frontend/fonts/ -COPY frontend/static/js/angular.min.js frontend/static/js/angular-route.min.js frontend/static/js/angular-sanitize.min.js frontend/static/js/bootstrap.min.js frontend/static/js/common.js frontend/static/js/d3.v3.min.js frontend/static/js/jquery.min.js /srv/htdocs-frontend/js/ -COPY admin/static/js/i18n/* /srv/htdocs-frontend/js/i18n/ diff --git a/Dockerfile-nginx b/Dockerfile-nginx index b5a1cd8c..6e7631a2 100644 --- a/Dockerfile-nginx +++ b/Dockerfile-nginx @@ -1,3 +1,15 @@ +FROM node:lts-alpine as nodebuild + +WORKDIR /ui + +RUN apk --no-cache add python2 build-base + +COPY frontend/ui/ . + +RUN npm install --network-timeout100000 +RUN npm run build + + FROM nginx:stable-alpine ENV FIC_BASEURL=/ \ @@ -11,10 +23,4 @@ COPY configs/nginx-chbase.sh /docker-entrypoint.d/40-update-baseurl.sh COPY configs/fic-auth-docker.conf /etc/nginx/fic-auth.conf COPY configs/nginx-docker.conf /etc/nginx/templates/default.conf.template -COPY frontend/static /srv/htdocs-frontend - -# Dereference symlink -COPY frontend/static/css/glyphicon.css /srv/htdocs-frontend/css/ -COPY admin/static/fonts/* /srv/htdocs-frontend/fonts/ -COPY frontend/static/js/angular.min.js frontend/static/js/angular-route.min.js frontend/static/js/angular-sanitize.min.js frontend/static/js/bootstrap.min.js frontend/static/js/common.js frontend/static/js/d3.v3.min.js frontend/static/js/jquery.min.js /srv/htdocs-frontend/js/ -COPY admin/static/js/i18n/* /srv/htdocs-frontend/js/i18n/ +COPY --from=nodebuild /ui/build/ /srv/htdocs-frontend diff --git a/configs/nginx-demo.conf b/configs/nginx-demo.conf index 2087f737..4b6c6864 100644 --- a/configs/nginx-demo.conf +++ b/configs/nginx-demo.conf @@ -88,11 +88,6 @@ server { rewrite ^/.*$ /index.html; } - location /issue { - include fic-auth.conf; - - rewrite ^/.*$ /index.html; - } location /issues { include fic-auth.conf; @@ -185,18 +180,18 @@ server { proxy_set_header X-FIC-Team $team; proxy_redirect off; } - location /submit/issue { + location /issue { include fic-auth.conf; - proxy_pass http://frontend:8080/issue; + proxy_pass http://frontend:8080; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-FIC-Team $team; proxy_redirect off; } - location /submit/name { + location /chname { include fic-auth.conf; - proxy_pass http://frontend:8080/chname; + proxy_pass http://frontend:8080; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-FIC-Team $team; proxy_redirect off; diff --git a/configs/nginx-docker.conf b/configs/nginx-docker.conf index bf4ee7eb..aee1c2cb 100644 --- a/configs/nginx-docker.conf +++ b/configs/nginx-docker.conf @@ -57,8 +57,8 @@ server { } } - location ${FIC_BASEURL} { - rewrite ^${FIC_BASEURL}(.*)$ /$1; + location ${FIC_BASEURL2} { + rewrite ^${FIC_BASEURL2}(.*)$ /$1; } location ~ ^/[A-Z] { @@ -72,11 +72,6 @@ server { rewrite ^/.*$ /index.html; } - location /issue { - include fic-auth.conf; - - rewrite ^/.*$ /index.html; - } location /issues { include fic-auth.conf; @@ -164,18 +159,18 @@ server { proxy_set_header X-FIC-Team $team; proxy_redirect off; } - location /submit/issue { + location /issue { include fic-auth.conf; - proxy_pass http://${HOST_FRONTEND}/issue; + proxy_pass http://${HOST_FRONTEND}; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-FIC-Team $team; proxy_redirect off; } - location /submit/name { + location /chname { include fic-auth.conf; - proxy_pass http://${HOST_FRONTEND}/chname; + proxy_pass http://${HOST_FRONTEND}; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-FIC-Team $team; proxy_redirect off; diff --git a/configs/nginx-prod.conf b/configs/nginx-prod.conf index 3ee63f55..d5819b2a 100644 --- a/configs/nginx-prod.conf +++ b/configs/nginx-prod.conf @@ -80,11 +80,6 @@ server { rewrite ^/.*$ /index.html; } - location /issue { - include fic-auth.conf; - - rewrite ^/.*$ /index.html; - } location /issues { include fic-auth.conf; @@ -177,18 +172,18 @@ server { proxy_set_header X-FIC-Team $team; proxy_redirect off; } - location /submit/issue { + location /issue { include fic-auth.conf; - proxy_pass http://frontend:8080/issue; + proxy_pass http://frontend:8080; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-FIC-Team $team; proxy_redirect off; } - location /submit/name { + location /chname { include fic-auth.conf; - proxy_pass http://frontend:8080/chname; + proxy_pass http://frontend:8080; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-FIC-Team $team; proxy_redirect off; diff --git a/docker-compose.yml b/docker-compose.yml index 1b5d97a3..a80e0a79 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,7 +13,7 @@ services: - MYSQL_USER=fic - MYSQL_PASSWORD=fic - fic-admin: + admin: build: context: . dockerfile: Dockerfile-admin @@ -39,7 +39,7 @@ services: - MYSQL_HOST=mysql - FICCA_PASS - fic-backend: + backend: build: context: . dockerfile: Dockerfile-backend @@ -58,7 +58,7 @@ services: environment: - MYSQL_HOST=mysql - fic-qa: + qa: build: context: . dockerfile: Dockerfile-qa @@ -77,7 +77,7 @@ services: environment: - MYSQL_HOST=mysql - fic-frontend: + frontend: build: context: . dockerfile: Dockerfile-frontend @@ -95,9 +95,9 @@ services: - submissions:/srv/submissions - startingblock:/srv/startingblock depends_on: - - fic-backend + - backend - fic-dashboard: + dashboard: build: context: . dockerfile: Dockerfile-dashboard @@ -112,26 +112,29 @@ services: - teams:/srv/TEAMS:ro - settings:/srv/SETTINGS:ro depends_on: - - fic-backend + - backend front: - image: nginx:latest + build: + context: . + dockerfile: Dockerfile-nginx + image: nemunaire/fic-nginx:latest ports: - "8042:80" networks: - fic-net volumes: - /mnt/fic:/mnt/fic:ro - - ./configs/nginx-frontend-htpasswd.conf:/etc/nginx/conf.d/default.conf:ro - - ./configs/nginx-fic.conf:/etc/nginx/auth.conf:ro - - htdocs:/srv/htdocs-frontend:ro - files:/srv/FILES:ro - settings:/srv/SETTINGS:ro - teams:/srv/TEAMS:ro - startingblock:/srv/startingblock:ro depends_on: - - fic-frontend - - fic-admin + - qa + - frontend + - dashboard + - backend + - admin volumes: mysql-data: diff --git a/entrypoint-frontend.sh b/entrypoint-frontend.sh index f660e9b0..37aaba48 100755 --- a/entrypoint-frontend.sh +++ b/entrypoint-frontend.sh @@ -2,6 +2,7 @@ [ -s /chbase-done ] && CURRENT_BASE=$(cat /chbase-done) || CURRENT_BASE="/" [ -n "${BASEURL}" ] || BASEURL="/" +[ "${BASEURL}" == "/" ] && BASEURL2="@baseurl" || BASEURL2="${BASEURL}" run() { local NEWBASE=$1 From ef899ee99b5021bb43c9a8521abd8c4efa33b8da Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 30 Aug 2021 20:03:17 +0200 Subject: [PATCH 0290/1637] fic: Sort mixed flags by order before packing them --- libfic/team_my.go | 62 +++++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/libfic/team_my.go b/libfic/team_my.go index ab2f5bf9..943d6699 100644 --- a/libfic/team_my.go +++ b/libfic/team_my.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "path" + "sort" "strconv" "strings" "time" @@ -27,7 +28,8 @@ type myTeamHint struct { Cost int64 `json:"cost"` } type myTeamFlag struct { - Order int8 `json:"order"` + Id int `json:"id"` + order int8 `json:"order"` Label string `json:"label"` Type string `json:"type,omitempty"` Placeholder string `json:"placeholder,omitempty"` @@ -51,22 +53,22 @@ type myTeamMCQJustifiedChoice struct { Justification myTeamFlag `json:"justification,omitempty"` } type myTeamExercice struct { - ThemeId int64 `json:"theme_id"` - Statement string `json:"statement"` - Overview string `json:"overview,omitempty"` - Finished string `json:"finished,omitempty"` - Hints []myTeamHint `json:"hints,omitempty"` - Gain int `json:"gain"` - Files []myTeamFile `json:"files,omitempty"` - Flags map[int]myTeamFlag `json:"flags,omitempty"` - SolveDist int64 `json:"solve_dist,omitempty"` - SolvedTime *time.Time `json:"solved_time,omitempty"` - SolvedRank int64 `json:"solved_rank,omitempty"` - Tries int64 `json:"tries,omitempty"` - TotalTries int64 `json:"total_tries,omitempty"` - VideoURI string `json:"video_uri,omitempty"` - Issue string `json:"issue,omitempty"` - IssueKind string `json:"issuekind,omitempty"` + ThemeId int64 `json:"theme_id"` + Statement string `json:"statement"` + Overview string `json:"overview,omitempty"` + Finished string `json:"finished,omitempty"` + Hints []myTeamHint `json:"hints,omitempty"` + Gain int `json:"gain"` + Files []myTeamFile `json:"files,omitempty"` + Flags []myTeamFlag `json:"flags,omitempty"` + SolveDist int64 `json:"solve_dist,omitempty"` + SolvedTime *time.Time `json:"solved_time,omitempty"` + SolvedRank int64 `json:"solved_rank,omitempty"` + Tries int64 `json:"tries,omitempty"` + TotalTries int64 `json:"total_tries,omitempty"` + VideoURI string `json:"video_uri,omitempty"` + Issue string `json:"issue,omitempty"` + IssueKind string `json:"issuekind,omitempty"` } type myTeam struct { Id int64 `json:"team_id"` @@ -76,6 +78,12 @@ type myTeam struct { Exercices map[string]myTeamExercice `json:"exercices"` } +type ByOrder []myTeamFlag + +func (a ByOrder) Len() int { return len(a) } +func (a ByOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a ByOrder) Less(i, j int) bool { return a[i].order < a[j].order } + func MyJSONTeam(t *Team, started bool) (interface{}, error) { ret := myTeam{} @@ -176,15 +184,17 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { // Expose exercice flags justifiedMCQ := map[int]myTeamFlag{} - exercice.Flags = map[int]myTeamFlag{} if flags, err := e.GetFlagKeys(); err != nil { return nil, err } else { for _, k := range flags { - var flag myTeamFlag - - flag.Order = k.Order + flag := myTeamFlag{ + Id: k.Id, + Type: k.Type, + order: k.Order, + Help: k.Help, + } if !DisplayAllFlags && t != nil && !t.CanSeeFlag(k) { // Dependancy missing, skip the flag for now @@ -236,7 +246,7 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { if fl.IdChoice != 0 { justifiedMCQ[fl.IdChoice] = flag } else { - exercice.Flags[k.Id] = flag + exercice.Flags = append(exercice.Flags, flag) } } } @@ -251,9 +261,10 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { } m := myTeamFlag{ + Id: mcq.Id, Type: "mcq", + order: mcq.Order, Label: mcq.Title, - Order: mcq.Order, Choices: map[string]interface{}{}, } @@ -307,10 +318,13 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { m.PSolved = nil } - exercice.Flags[mcq.Id] = m + exercice.Flags = append(exercice.Flags, m) } } + // Sort flags by order + sort.Sort(ByOrder(exercice.Flags)) + // Hash table ordered by exercice Id ret.Exercices[fmt.Sprintf("%d", e.Id)] = exercice } From 1def2c97c12e6999132fb52e5a732e51e5fcce45 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 31 Aug 2021 02:54:59 +0200 Subject: [PATCH 0291/1637] ui: Working flags --- .../ui/src/components/ExerciceFlags.svelte | 105 ++++++++++- frontend/ui/src/components/FlagKey.svelte | 173 ++++++++++++++++++ frontend/ui/src/components/FlagMCQ.svelte | 49 +++++ .../ui/src/components/TeamChangeName.svelte | 2 +- .../ui/src/routes/[theme]/[exercice].svelte | 14 +- frontend/ui/src/routes/[theme]/index.svelte | 6 +- frontend/ui/src/routes/register.svelte | 2 +- frontend/ui/src/stores/mythemes.js | 4 +- 8 files changed, 340 insertions(+), 15 deletions(-) create mode 100644 frontend/ui/src/components/FlagKey.svelte create mode 100644 frontend/ui/src/components/FlagMCQ.svelte diff --git a/frontend/ui/src/components/ExerciceFlags.svelte b/frontend/ui/src/components/ExerciceFlags.svelte index 92e7ceb3..1deeefcd 100644 --- a/frontend/ui/src/components/ExerciceFlags.svelte +++ b/frontend/ui/src/components/ExerciceFlags.svelte @@ -8,17 +8,88 @@ Icon, ListGroup, ListGroupItem, + Spinner, } from 'sveltestrap'; - export let exercice = {}; + import FlagKey from './FlagKey.svelte'; + import FlagMCQ from './FlagMCQ.svelte'; + + export let exercice = { }; export let flags = []; - function submitFlags(event) { - console.log(event); + export let refresh_my; + export let refresh_teams; + + function waitDiff(i) { + refresh_my((my) => { + if (my && my.exercices[exercice.id].tries != exercice.tries) { + submitInProgress = false; + refresh_teams(); + } else if (i > 0) { + setTimeout(waitDiff, 450, i-1); + } + }) + } + + let responses = { }; + async function submitFlags() { + submitInProgress = true; + sberr = ""; + message = ""; + + const response = await fetch( + "/submit/" + exercice.id, + { + method: "POST", + body: JSON.stringify(responses), + } + ) + + if (response.status < 300) { + const data = await response.json(); + messageClass = 'text-success'; + message = data.errmsg; + waitDiff(20); + } else { + submitInProgress = false; + + messageClass = 'text-danger'; + + let data = ""; + try { + data = await response.json(); + } catch(e) { + data = null; + } + + if (data && data.errmsg) + message = data.errmsg; + if (response.statys != 402) + sberr = "Oups !"; + } + } + + function resetResponses() { + responses = { + flags: { }, + mcqs: { }, + justifications: { }, + }; + } + + let last_exercice = null; + $: { + if (!last_exercice || last_exercice != exercice.id) { + last_exercice = exercice.id; + resetResponses() + } } let sberr = ""; let message = ""; + let messageClass = "text-danger"; + let timeouted = false; + let submitInProgress = false; @@ -40,7 +111,7 @@ {/if} {#if exercice.submitted || sberr} - + {#if !sberr} Votre solution a bien été envoyée ! {:else} @@ -48,7 +119,7 @@ {/if} {/if} - {#if exercice.timeouted} + {#if timeouted} Oops La requête a dépassé le délai d'attente. Vous devriez réessayer dans quelques instant… @@ -59,12 +130,34 @@ {#if !exercice.submitted || sberr}
- {JSON.stringify(flags)} + {#each flags as flag ((flag.type?flag.type:"i") + flag.id)} + {#if flag.type == "mcq"} + + {:else} + + {/if} + {/each} +
diff --git a/frontend/ui/src/components/FlagKey.svelte b/frontend/ui/src/components/FlagKey.svelte new file mode 100644 index 00000000..b227fd96 --- /dev/null +++ b/frontend/ui/src/components/FlagKey.svelte @@ -0,0 +1,173 @@ + + +
+ + {#if flag.found && flag.value} + {flag.value} + {/if} + {#if !flag.found} + {#each values as v, index} +
+ {#if !flag.choices} + {#if !flag.multiline} + + {:else} + + {/if} + {:else} + + {/if} + {#if flag.choices_cost > 0} + + {:else if flag.separator && !flag.nb_lines && index == values.length - 1} + + {/if} +
+ {/each} + {#if flag.help} + {flag.help} + {/if} + {:else} +
diff --git a/frontend/ui/src/components/FlagMCQ.svelte b/frontend/ui/src/components/FlagMCQ.svelte new file mode 100644 index 00000000..efddea16 --- /dev/null +++ b/frontend/ui/src/components/FlagMCQ.svelte @@ -0,0 +1,49 @@ + + +{#if flag.label} +

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

+{/if} +{#if !flag.found || flag.justify} + {#each Object.keys(flag.choices) as cid, index} +
+ + + {#if values[Number(cid)] && flag.justify && (!flag.choices[cid].justification || !flag.choices[cid].justification.solved)} + + {/if} + {#if flag.choices[cid].justification && flag.choices[cid].justification.solved} +
+ {/each} +{/if} +
diff --git a/frontend/ui/src/components/TeamChangeName.svelte b/frontend/ui/src/components/TeamChangeName.svelte index bf7808e0..fb08825b 100644 --- a/frontend/ui/src/components/TeamChangeName.svelte +++ b/frontend/ui/src/components/TeamChangeName.svelte @@ -20,7 +20,7 @@ messageClass = "info"; sberr = "Votre nom d'équipe a été changé avec succès."; message = ""; - } else { + } else if (i > 0) { setTimeout(gotoHomeOnDiff, 850, i-1); } }) diff --git a/frontend/ui/src/routes/[theme]/[exercice].svelte b/frontend/ui/src/routes/[theme]/[exercice].svelte index 739a2501..34589f52 100644 --- a/frontend/ui/src/routes/[theme]/[exercice].svelte +++ b/frontend/ui/src/routes/[theme]/[exercice].svelte @@ -18,6 +18,8 @@ props: { theme: context.theme, exercice: exercice, + refresh_my: context.refresh_my, + refresh_teams: context.refresh_teams, } }; } @@ -45,6 +47,9 @@ export let theme; export let exercice; + + export let refresh_my; + export let refresh_teams; @@ -147,8 +152,13 @@ {/if} - {#if !$my.exercices[exercice.id].solved} - + {#if !$my.exercices[exercice.id].solved_rank} + {:else} {/if} diff --git a/frontend/ui/src/routes/[theme]/index.svelte b/frontend/ui/src/routes/[theme]/index.svelte index 3a47e7b6..888cc432 100644 --- a/frontend/ui/src/routes/[theme]/index.svelte +++ b/frontend/ui/src/routes/[theme]/index.svelte @@ -38,19 +38,19 @@ on:click={goto(`/${theme.urlid}/${theme.exercices[k].urlid}`)} >
-
+
diff --git a/frontend/ui/src/routes/register.svelte b/frontend/ui/src/routes/register.svelte index bc4930b6..27f769b6 100644 --- a/frontend/ui/src/routes/register.svelte +++ b/frontend/ui/src/routes/register.svelte @@ -39,7 +39,7 @@ refresh_my((my) => { if (my && my.team_id) { goto('/'); - } else { + } else if (i > 0) { setTimeout(gotoHomeOnDiff, 650, i-1); } }) diff --git a/frontend/ui/src/stores/mythemes.js b/frontend/ui/src/stores/mythemes.js index 726b71b5..279d9bc7 100644 --- a/frontend/ui/src/stores/mythemes.js +++ b/frontend/ui/src/stores/mythemes.js @@ -11,7 +11,7 @@ export const myThemes = derived([my, themesStore], ([$my, $themesStore]) => { if ($my && $my.exercices) { for (let k in $themesStore.themes[key].exercices) { - if ($my.exercices[k] && $my.exercices[k].solved) { + if ($my.exercices[k] && $my.exercices[k].solved_rank) { themes[key].exercice_solved++; } } @@ -32,7 +32,7 @@ export const tags = derived([my, themesStore], ([$my, $themesStore]) => { else tags[tag].count += 1; - if ($my && $my.exercices && $my.exercices[k] && $my.exercices[k].solved) + if ($my && $my.exercices && $my.exercices[k] && $my.exercices[k].solved_rank) tags[tag].solved += 1; }); } From 6223d2be36af54fdf4f606933564fd95c935679b Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 31 Aug 2021 19:34:47 +0200 Subject: [PATCH 0292/1637] sync: Also import hints during speed sync --- admin/sync/full.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/admin/sync/full.go b/admin/sync/full.go index 22622e5e..f45d5f92 100644 --- a/admin/sync/full.go +++ b/admin/sync/full.go @@ -56,10 +56,12 @@ func SpeedySyncDeep(i Importer) (errs map[string][]string) { 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) + errs[theme.Name] = append(errs[theme.Name], ferrs...) DeepSyncProgress += exerciceStep / 2 - _, ferrs := SyncExerciceFlags(i, exercice) - errs[theme.Name] = append(errs[theme.Name], ferrs...) + _, herrs := SyncExerciceHints(i, exercice, flagsBindings) + errs[theme.Name] = append(errs[theme.Name], herrs...) } } } From dcb0cb315bd0f58916071098941d332f33baf350 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 31 Aug 2021 20:39:24 +0200 Subject: [PATCH 0293/1637] admin: Can modify help and order props in ui --- admin/static/views/exercice-flags.html | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/admin/static/views/exercice-flags.html b/admin/static/views/exercice-flags.html index adcd7f60..9c052b4d 100644 --- a/admin/static/views/exercice-flags.html +++ b/admin/static/views/exercice-flags.html @@ -32,6 +32,10 @@
+
+ + +
@@ -55,9 +59,12 @@
-
+
+
+ +
@@ -121,6 +128,9 @@
+
+ +
From 451b678e7328a8aade77e638cca32c4189d2a93f Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 31 Aug 2021 21:00:37 +0200 Subject: [PATCH 0294/1637] CI: Build frontend ui --- .drone.yml | 16 ++++++++++++++++ Dockerfile-nginx | 2 +- frontend/ui/package.json | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index d4c8757b..1258ea2b 100644 --- a/.drone.yml +++ b/.drone.yml @@ -58,6 +58,14 @@ steps: environment: CGO_ENABLED: 0 + - name: build frontend ui + image: node:lts-alpine + commands: + - apk --no-cache add python2 build-base + - cd frontend/ui + - npm install --network-timeout=100000 + - npm run build + - name: build dashboard image: golang:alpine commands: @@ -259,6 +267,14 @@ steps: environment: CGO_ENABLED: 0 + - name: build frontend ui + image: node:lts-alpine + commands: + - apk --no-cache add python2 build-base + - cd frontend/ui + - npm install --network-timeout=100000 + - npm run build + - name: build dashboard image: golang:alpine commands: diff --git a/Dockerfile-nginx b/Dockerfile-nginx index 6e7631a2..a919875d 100644 --- a/Dockerfile-nginx +++ b/Dockerfile-nginx @@ -6,7 +6,7 @@ RUN apk --no-cache add python2 build-base COPY frontend/ui/ . -RUN npm install --network-timeout100000 +RUN npm install --network-timeout 100000 RUN npm run build diff --git a/frontend/ui/package.json b/frontend/ui/package.json index 825914f5..e3c221a9 100644 --- a/frontend/ui/package.json +++ b/frontend/ui/package.json @@ -9,7 +9,6 @@ "format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ." }, "devDependencies": { - "@popperjs/core": "^2.9.3", "@sveltejs/kit": "next", "eslint": "^7.22.0", "eslint-config-prettier": "^8.1.0", @@ -21,6 +20,7 @@ }, "type": "module", "dependencies": { + "@popperjs/core": "^2.9.3", "@sveltejs/adapter-static": "^1.0.0-next.17", "bootstrap": "^5.1.0", "bootstrap-icons": "^1.5.0" From 23d5ea7c97b50aa7b1f35878a229ebca1389ad8b Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 31 Aug 2021 21:47:49 +0200 Subject: [PATCH 0295/1637] ui: Randomize themes list --- frontend/ui/package-lock.json | 5 ++++ frontend/ui/package.json | 3 ++- frontend/ui/src/components/NavThemes.svelte | 6 ++--- frontend/ui/src/routes/index.svelte | 5 ++-- frontend/ui/src/stores/mythemes.js | 27 +++++++++++++++++++++ 5 files changed, 39 insertions(+), 7 deletions(-) diff --git a/frontend/ui/package-lock.json b/frontend/ui/package-lock.json index 0c2f1ed7..21f3ade8 100644 --- a/frontend/ui/package-lock.json +++ b/frontend/ui/package-lock.json @@ -998,6 +998,11 @@ "mri": "^1.1.0" } }, + "seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" + }, "semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", diff --git a/frontend/ui/package.json b/frontend/ui/package.json index e3c221a9..371fa71a 100644 --- a/frontend/ui/package.json +++ b/frontend/ui/package.json @@ -23,6 +23,7 @@ "@popperjs/core": "^2.9.3", "@sveltejs/adapter-static": "^1.0.0-next.17", "bootstrap": "^5.1.0", - "bootstrap-icons": "^1.5.0" + "bootstrap-icons": "^1.5.0", + "seedrandom": "^3.0.5" } } diff --git a/frontend/ui/src/components/NavThemes.svelte b/frontend/ui/src/components/NavThemes.svelte index 3e5b0094..a225cda7 100644 --- a/frontend/ui/src/components/NavThemes.svelte +++ b/frontend/ui/src/components/NavThemes.svelte @@ -9,8 +9,8 @@ } from 'sveltestrap'; import { my } from '../stores/my.js'; - import { max_solved, themes } from '../stores/themes.js'; - import { myThemes } from '../stores/mythemes.js'; + import { max_solved } from '../stores/themes.js'; + import { myThemes, themes } from '../stores/mythemes.js'; @@ -33,7 +33,7 @@ {/if} - {#if $my && $my.team_id}{$myThemes[th].exercice_solved}/{/if}{$themes[th].exercice_count} + {#if $my && $my.team_id}{$myThemes[$themes[th].id].exercice_solved}/{/if}{$themes[th].exercice_count} {/each} diff --git a/frontend/ui/src/routes/index.svelte b/frontend/ui/src/routes/index.svelte index 5827bbd1..1373e0ac 100644 --- a/frontend/ui/src/routes/index.svelte +++ b/frontend/ui/src/routes/index.svelte @@ -16,8 +16,7 @@ import { my } from '../stores/my.js'; import { teams } from '../stores/teams.js'; - import { themes } from '../stores/themes.js'; - import { myThemes } from '../stores/mythemes.js'; + import { myThemes, themes } from '../stores/mythemes.js'; import { settings } from '../stores/settings.js'; @@ -52,7 +51,7 @@ {#each Object.keys($themes) as th, index} diff --git a/frontend/ui/src/stores/mythemes.js b/frontend/ui/src/stores/mythemes.js index 279d9bc7..ef23b6a4 100644 --- a/frontend/ui/src/stores/mythemes.js +++ b/frontend/ui/src/stores/mythemes.js @@ -1,5 +1,7 @@ import { derived } from 'svelte/store'; +import seedrandom from 'seedrandom'; + import { my } from './my.js'; import { themesStore } from './themes.js'; @@ -21,6 +23,31 @@ export const myThemes = derived([my, themesStore], ([$my, $themesStore]) => { return themes; }); +export const themes = derived( + [my, themesStore], + ([$my, $themesStore]) => { + const arr = []; + for (let th in $themesStore.themes) { + $themesStore.themes[th].id = th + arr.push($themesStore.themes[th]); + } + const size = arr.length; + const rng = new seedrandom($my && $my.team_id ? $my.team_id : 0); + const resp = []; + const keys = []; + + for(let i=0;i { const tags = {}; From a2554801959880ea46b22b1c7b23381e2d2cea01 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 31 Aug 2021 21:50:26 +0200 Subject: [PATCH 0296/1637] ui: Ensure themes menu kept in screen --- frontend/ui/src/components/NavThemes.svelte | 43 +++++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/frontend/ui/src/components/NavThemes.svelte b/frontend/ui/src/components/NavThemes.svelte index a225cda7..47539a5b 100644 --- a/frontend/ui/src/components/NavThemes.svelte +++ b/frontend/ui/src/components/NavThemes.svelte @@ -19,23 +19,32 @@ Scénarii - {#each Object.keys($themes) as th, index} - - {$themes[th].name} - {#if $max_solved > 1 && $themes[th].solved == $max_solved} - - +
+ {#each Object.keys($themes) as th, index} + + {$themes[th].name} + {#if $max_solved > 1 && $themes[th].solved == $max_solved} + + + + {/if} + {#if $themes[th].exercice_coeff_max > 1} + + + + {/if} + + {#if $my && $my.team_id}{$myThemes[$themes[th].id].exercice_solved}/{/if}{$themes[th].exercice_count} - {/if} - {#if $themes[th].exercice_coeff_max > 1} - - - - {/if} - - {#if $my && $my.team_id}{$myThemes[$themes[th].id].exercice_solved}/{/if}{$themes[th].exercice_count} - - - {/each} + + {/each} +
+ + From 3c42bef2983962056d9674cd849652176bea663e Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 1 Sep 2021 01:27:24 +0200 Subject: [PATCH 0297/1637] CI: Fix compilation problems --- .drone.yml | 2 ++ Dockerfile-nginx | 5 +++-- admin/main.go | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index 1258ea2b..7faaa81a 100644 --- a/.drone.yml +++ b/.drone.yml @@ -64,6 +64,7 @@ steps: - apk --no-cache add python2 build-base - cd frontend/ui - npm install --network-timeout=100000 + - sed -i 's!@popperjs/core/dist/esm/popper!@popperjs/core!' node_modules/sveltestrap/src/*.js node_modules/sveltestrap/src/*.svelte - npm run build - name: build dashboard @@ -273,6 +274,7 @@ steps: - apk --no-cache add python2 build-base - cd frontend/ui - npm install --network-timeout=100000 + - sed -i 's!@popperjs/core/dist/esm/popper!@popperjs/core!' node_modules/sveltestrap/src/*.js node_modules/sveltestrap/src/*.svelte - npm run build - name: build dashboard diff --git a/Dockerfile-nginx b/Dockerfile-nginx index a919875d..c1e3d77e 100644 --- a/Dockerfile-nginx +++ b/Dockerfile-nginx @@ -6,8 +6,9 @@ RUN apk --no-cache add python2 build-base COPY frontend/ui/ . -RUN npm install --network-timeout 100000 -RUN npm run build +RUN npm install --network-timeout=100000 && \ + sed -i 's!@popperjs/core/dist/esm/popper!@popperjs/core!' node_modules/sveltestrap/src/*.js node_modules/sveltestrap/src/*.svelte && \ + npm run build FROM nginx:stable-alpine diff --git a/admin/main.go b/admin/main.go index 99191fa9..ea9bb502 100644 --- a/admin/main.go +++ b/admin/main.go @@ -135,6 +135,7 @@ func main() { 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) } @@ -143,6 +144,7 @@ func main() { 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) } From 815f4b903796d328fd3a24869fcaaf7514fc9f75 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 1 Sep 2021 01:38:39 +0200 Subject: [PATCH 0298/1637] ui: Add a progress bar indicating total number of flags --- frontend/ui/src/components/ExerciceFlags.svelte | 11 +++++++++++ libfic/team_my.go | 15 ++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/frontend/ui/src/components/ExerciceFlags.svelte b/frontend/ui/src/components/ExerciceFlags.svelte index 1deeefcd..12c87377 100644 --- a/frontend/ui/src/components/ExerciceFlags.svelte +++ b/frontend/ui/src/components/ExerciceFlags.svelte @@ -8,6 +8,7 @@ Icon, ListGroup, ListGroupItem, + Progress, Spinner, } from 'sveltestrap'; @@ -97,6 +98,16 @@ Faire son rapport + {#if exercice.flags.length != exercice.nb_flags} + + {exercice.flags.length}/{exercice.nb_flags} + + {/if} {#if exercice.tries || exercice.submitted || sberr} {#if exercice.solved_time && exercice.tries} diff --git a/libfic/team_my.go b/libfic/team_my.go index 943d6699..c8718087 100644 --- a/libfic/team_my.go +++ b/libfic/team_my.go @@ -61,6 +61,7 @@ type myTeamExercice struct { Gain int `json:"gain"` Files []myTeamFile `json:"files,omitempty"` Flags []myTeamFlag `json:"flags,omitempty"` + NbFlags int `json:"nb_flags,omitempty"` SolveDist int64 `json:"solve_dist,omitempty"` SolvedTime *time.Time `json:"solved_time,omitempty"` SolvedRank int64 `json:"solved_rank,omitempty"` @@ -189,6 +190,13 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { return nil, err } else { for _, k := range flags { + exercice.NbFlags += 1 + + if !DisplayAllFlags && t != nil && !t.CanSeeFlag(k) { + // Dependancy missing, skip the flag for now + continue + } + flag := myTeamFlag{ Id: k.Id, Type: k.Type, @@ -196,11 +204,6 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { Help: k.Help, } - if !DisplayAllFlags && t != nil && !t.CanSeeFlag(k) { - // Dependancy missing, skip the flag for now - continue - } - // Retrieve solved state or solution for public iface if t == nil { flag.IgnoreCase = k.IgnoreCase @@ -255,6 +258,8 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { return nil, err } else { for _, mcq := range mcqs { + exercice.NbFlags += 1 + if !DisplayAllFlags && t != nil && !t.CanSeeFlag(mcq) { // Dependancy missing, skip the flag for now continue From e3057726e8b2d76ae2b44f52962dc8bd0a06487e Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 1 Sep 2021 01:41:22 +0200 Subject: [PATCH 0299/1637] ui: Implement hint discovery --- .../ui/src/components/ExerciceHints.svelte | 73 +++++++++++++++++-- 1 file changed, 67 insertions(+), 6 deletions(-) diff --git a/frontend/ui/src/components/ExerciceHints.svelte b/frontend/ui/src/components/ExerciceHints.svelte index 705b10a5..8adb6526 100644 --- a/frontend/ui/src/components/ExerciceHints.svelte +++ b/frontend/ui/src/components/ExerciceHints.svelte @@ -7,18 +7,73 @@ Icon, ListGroup, ListGroupItem, + Spinner, } from 'sveltestrap'; import { settings } from '../stores/settings.js'; export let hints = []; + export let exercice = {}; + export let refresh_my = null; + + let hints_submitted = {}; + + function waitDiff(i, hint) { + refresh_my((my) => { + let openedHint = false; + + if (my && my.exercices[exercice.id].hints) { + my.exercices[exercice.id].hints.forEach((h) => { + if (h.id == hint.id && (h.content || h.file)) { + openedHint = true; + } + }) + } + + if (openedHint) { + hints_submitted[hint.id] = false; + hinterror = ""; + } else if (i > 0) { + setTimeout(waitDiff, 650, i-1, hint); + } + }) + } + + async function openHint(hint) { + hints_submitted[hint.id] = true; + hinterror = ""; + + const response = await fetch( + "/openhint/" + exercice.id, + { + method: "POST", + body: JSON.stringify({ id: hint.id }), + } + ) + + if (response.status < 300) { + waitDiff(15, hint); + } else { + hints_submitted[hint.id] = false; + + let data = ""; + try { + data = await response.json(); + } catch(e) { + data = null; + } + + if (data && data.errmsg) + hinterror = data.errmsg; + } + } let hinterror = ""; {#if hints.length} - + Indices @@ -33,12 +88,18 @@ {#each hints as hint (hint.id)} {#if hint.file} -

+

+ +

{/if}
{#if !(hint.content || hint.file)} - {/if} @@ -48,7 +109,7 @@ Afficher {/if} -

{hint.name}

+

{hint.title}

{#if hint.file}

Cliquez ici pour télécharger l'indice.
@@ -59,7 +120,7 @@

{@html hint.content}

{:else}

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

{/if}
From 941e1c16d514dadbd500bdcf132208e7541d75f5 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 1 Sep 2021 01:42:56 +0200 Subject: [PATCH 0300/1637] ui: Fix loading problems when themes arrived to late --- frontend/ui/src/components/Header.svelte | 4 +- .../ui/src/routes/[theme]/[exercice].svelte | 4 +- .../ui/src/routes/[theme]/__layout.svelte | 7 +- frontend/ui/src/routes/__layout.svelte | 2 +- frontend/ui/src/stores/themes.js | 66 +++++++++---------- 5 files changed, 41 insertions(+), 42 deletions(-) diff --git a/frontend/ui/src/components/Header.svelte b/frontend/ui/src/components/Header.svelte index 44483098..d96f232b 100644 --- a/frontend/ui/src/components/Header.svelte +++ b/frontend/ui/src/components/Header.svelte @@ -83,7 +83,7 @@ {#if $my && $my.team_id} {$my.score} {$my.score === 1 ? 'point' : 'points'} - {#if $teams[$my.team_id].rank} + {#if $teams && $teams[$my.team_id].rank} – {$teams[$my.team_id].rank}e sur {Object.keys($teams).length} {/if} @@ -93,7 +93,7 @@ Inscription - {:else if $my.team_id} + {:else if $my.team_id && $teams} {$my.name} diff --git a/frontend/ui/src/routes/[theme]/[exercice].svelte b/frontend/ui/src/routes/[theme]/[exercice].svelte index 34589f52..f6de6fb8 100644 --- a/frontend/ui/src/routes/[theme]/[exercice].svelte +++ b/frontend/ui/src/routes/[theme]/[exercice].svelte @@ -53,14 +53,14 @@ - {exercice.title} - {$settings.title} + {exercice?exercice.title+" - ":""}{$settings.title} {#if exercice} {/if} -{#if !$my || !$my.exercices[exercice.id]} +{#if !$my || !exercice || !$my.exercices[exercice.id]} Vous n'avez pas encore accès à ce défi. diff --git a/frontend/ui/src/routes/[theme]/__layout.svelte b/frontend/ui/src/routes/[theme]/__layout.svelte index 9d5288e2..d7f5bcc5 100644 --- a/frontend/ui/src/routes/[theme]/__layout.svelte +++ b/frontend/ui/src/routes/[theme]/__layout.svelte @@ -7,9 +7,8 @@ const thms = get_store_value(themes); let theme = null; - for (let th in thms) { - if (thms[th].urlid === page.params.theme) { + if (thms[th] && thms[th].urlid === page.params.theme) { theme = thms[th]; break; } @@ -33,11 +32,11 @@ import { settings } from '../../stores/settings.js'; - export let theme = null; + export let theme; - {theme.name} - {$settings.title} + {theme?theme.name:""} - {$settings.title} {#if theme} diff --git a/frontend/ui/src/routes/__layout.svelte b/frontend/ui/src/routes/__layout.svelte index bb5786be..0f65d56c 100644 --- a/frontend/ui/src/routes/__layout.svelte +++ b/frontend/ui/src/routes/__layout.svelte @@ -57,7 +57,7 @@ } refresh_interval_themes = setInterval(refresh_themes, interval); - themesStore.update(await fetch('/themes.json'), cb); + await themesStore.update(await fetch('/themes.json'), cb); } let refresh_interval_my = null; diff --git a/frontend/ui/src/stores/themes.js b/frontend/ui/src/stores/themes.js index df71f704..f5b7089f 100644 --- a/frontend/ui/src/stores/themes.js +++ b/frontend/ui/src/stores/themes.js @@ -1,50 +1,50 @@ import { derived, writable } from 'svelte/store'; function createThemesStore() { - const { subscribe, set, update } = writable({themes: {}, exercices_idx: {}, max_solved: 0}); + const { subscribe, set, update } = writable({themes: null, exercices_idx: {}, max_solved: 0}); return { subscribe, - update: (res_themes, cb=null) => { + update: async (res_themes, cb=null) => { if (res_themes.status === 200) { - res_themes.json().then((themes) => { - let max_solved = 0; - const exercices_idx = {}; + const themes = await res_themes.json(); - for (let key in themes) { - const theme = themes[key]; + let max_solved = 0; + const exercices_idx = {}; - if (theme.solved > max_solved) { - max_solved = theme.solved; + for (let key in themes) { + const theme = themes[key]; + + if (theme.solved > max_solved) { + max_solved = theme.solved; + } + + themes[key].exercice_count = Object.keys(theme.exercices).length; + themes[key].exercice_coeff_max = 0; + themes[key].max_gain = 0; + let last_exercice = null; + for (let 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; } - themes[key].exercice_count = Object.keys(theme.exercices).length; - themes[key].exercice_coeff_max = 0; - themes[key].max_gain = 0; - let last_exercice = null; - for (let k in theme.exercices) { - const exercice = theme.exercices[k]; + if (last_exercice != null) + themes[key].exercices[last_exercice].next = k; + last_exercice = k; - themes[key].max_gain += exercice.gain; - if (themes[key].exercice_coeff_max < exercice.curcoeff) { - themes[key].exercice_coeff_max = exercice.curcoeff; - } + exercice.id = k; + exercices_idx[k] = exercice; + } + } - if (last_exercice != null) - themes[key].exercices[last_exercice].next = k; - last_exercice = k; + update((t) => (Object.assign(t, {themes, exercices_idx, max_solved}))); - exercice.id = k; - exercices_idx[k] = exercice; - } - } - - update((t) => (Object.assign(t, {themes, exercices_idx, max_solved}))); - - if (cb) { - cb(themes, exercices_idx, max_solved); - } - }); + if (cb) { + cb(themes, exercices_idx, max_solved); + } } }, }; From e6557c8c06177ebe7711ba6dff86ace09c3d19c3 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 1 Sep 2021 01:46:06 +0200 Subject: [PATCH 0301/1637] ui: Fix erasing of downloadable files --- frontend/ui/src/components/ExerciceDownloads.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/ui/src/components/ExerciceDownloads.svelte b/frontend/ui/src/components/ExerciceDownloads.svelte index d55dc543..a56b5abb 100644 --- a/frontend/ui/src/components/ExerciceDownloads.svelte +++ b/frontend/ui/src/components/ExerciceDownloads.svelte @@ -24,7 +24,7 @@ - {#each files as file (file.id)} + {#each files as file, index}

From 83a47af391a65430a1f33b43b9187f7c2720014f Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 1 Sep 2021 01:47:44 +0200 Subject: [PATCH 0302/1637] ui: Add theme from bootswatch --- frontend/ui/package-lock.json | 148 ++++++++++++++++-- frontend/ui/package.json | 3 + .../src/components/ExerciceDownloads.svelte | 17 +- .../ui/src/components/ExerciceFlags.svelte | 12 +- .../ui/src/components/ExerciceHints.svelte | 6 +- .../ui/src/components/ExerciceSolved.svelte | 2 +- .../ui/src/components/ExerciceVideo.svelte | 2 +- frontend/ui/src/components/Header.svelte | 4 +- frontend/ui/src/components/ThemeNav.svelte | 70 ++++----- frontend/ui/src/fic.scss | 24 +++ .../ui/src/routes/[theme]/[exercice].svelte | 19 ++- frontend/ui/src/routes/[theme]/index.svelte | 4 +- frontend/ui/src/routes/__layout.svelte | 2 +- frontend/ui/src/routes/edit.svelte | 2 +- frontend/ui/src/routes/rank.svelte | 2 +- frontend/ui/src/routes/rules.svelte | 2 +- frontend/ui/src/routes/tags/[tag].svelte | 2 +- password_paper/dingbat.log | 114 ++++++++++++++ 18 files changed, 355 insertions(+), 80 deletions(-) create mode 100644 frontend/ui/src/fic.scss create mode 100644 password_paper/dingbat.log diff --git a/frontend/ui/package-lock.json b/frontend/ui/package-lock.json index 21f3ade8..7ab00696 100644 --- a/frontend/ui/package-lock.json +++ b/frontend/ui/package-lock.json @@ -86,8 +86,7 @@ "@popperjs/core": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.9.3.tgz", - "integrity": "sha512-xDu17cEfh7Kid/d95kB6tZsLOmSWKCZKtprnhVepjsSaCij+lM3mItSJDuuHDMbCWTh8Ejmebwb+KONcCJ0eXQ==", - "dev": true + "integrity": "sha512-xDu17cEfh7Kid/d95kB6tZsLOmSWKCZKtprnhVepjsSaCij+lM3mItSJDuuHDMbCWTh8Ejmebwb+KONcCJ0eXQ==" }, "@rollup/pluginutils": { "version": "4.1.1", @@ -105,15 +104,15 @@ "integrity": "sha512-RKYNkQxtsMgt0wD8PhfXR1hGT1Tmq1E5eZeTr1KxIerczITRnWVT8LElfu/9Kusv44yYlyQtNc1mLoYqgloOQw==" }, "@sveltejs/kit": { - "version": "1.0.0-next.156", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.156.tgz", - "integrity": "sha512-YHT3sbPKpBGSYYFgEpXUx7JhN682wdXhc5INYZGqFfaGqkIOrThosz8qRsLQBfRbFnu0fzgWSsfj7FKq8YeFuA==", + "version": "1.0.0-next.160", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.160.tgz", + "integrity": "sha512-JmacpK+VsppUKDQu1B3oJ70SnE9BXb/PSAP3f91K/DMnaSEGj7vtHn/3e6l7Mop12t1PkBk5lImh/ULqBO+YfA==", "dev": true, "requires": { "@sveltejs/vite-plugin-svelte": "^1.0.0-next.16", "cheap-watch": "^1.0.3", "sade": "^1.7.4", - "vite": "^2.5.0" + "vite": "^2.5.2" } }, "@sveltejs/vite-plugin-svelte": { @@ -175,6 +174,16 @@ "color-convert": "^1.9.0" } }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -196,6 +205,12 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, "bootstrap": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.0.tgz", @@ -206,6 +221,11 @@ "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.5.0.tgz", "integrity": "sha512-44feMc7DE1Ccpsas/1wioN8ewFJNquvi5FewA06wLnqct7CwMdGDVy41ieHaacogzDqLfG8nADIvMNp9e4bfbA==" }, + "bootswatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bootswatch/-/bootswatch-5.1.0.tgz", + "integrity": "sha512-HwQjahQSq0u+ydqY0fFyl/GsJIHhBvzjBA8D2XvMvIrHdqYfL1LdD5bnnaEDoJugMCYGud9PQSgq/XkuyFFPMg==" + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -216,6 +236,15 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -279,6 +308,22 @@ "integrity": "sha512-xC5CruMhLzjPwJ5ecUxGu1uGmwJQykUhqd2QrCrYbwvsFYdRyviu6jG9+pccwDXJR/OpmOTOJ9yLFunVgQu9wg==", "dev": true }, + "chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -357,9 +402,9 @@ } }, "esbuild": { - "version": "0.12.22", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.22.tgz", - "integrity": "sha512-yWCr9RoFehpqoe/+MwZXJpYOEIt7KOEvNnjIeMZpMSyQt+KCBASM3y7yViiN5dJRphf1wGdUz1+M4rTtWd/ulA==", + "version": "0.12.24", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.24.tgz", + "integrity": "sha512-C0ibY+HsXzYB6L/pLWEiWjMpghKsIc58Q5yumARwBQsHl9DXPakW+5NI/Y9w4YXiz0PEP6XTGTT/OV4Nnsmb4A==", "dev": true }, "escape-string-regexp": { @@ -565,6 +610,15 @@ "flat-cache": "^3.0.4" } }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -691,6 +745,15 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, "is-core-module": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", @@ -721,6 +784,12 @@ "is-extglob": "^2.1.1" } }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -761,6 +830,12 @@ "integrity": "sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==", "dev": true }, + "klona": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz", + "integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==", + "dev": true + }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -840,6 +915,18 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "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 + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -937,6 +1024,15 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, "regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -998,6 +1094,25 @@ "mri": "^1.1.0" } }, + "sass": { + "version": "1.38.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.38.2.tgz", + "integrity": "sha512-Bz1fG6qiyF0FX6m/I+VxtdVKz1Dfmg/e9kfDy2PhWOkq3T384q2KxwIfP0fXpeI+EyyETdOauH+cRHQDFASllA==", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0" + } + }, + "sass-loader": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.1.0.tgz", + "integrity": "sha512-FVJZ9kxVRYNZTIe2xhw93n3xJNYZADr+q69/s98l9nTCrWASo+DR2Ot0s5xTKQDDEosUkatsGeHxcH4QBp5bSg==", + "dev": true, + "requires": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + } + }, "seedrandom": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", @@ -1178,6 +1293,15 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -1209,9 +1333,9 @@ "dev": true }, "vite": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-2.5.1.tgz", - "integrity": "sha512-FwmLbbz8MB1pBs9dKoRDgpiqoijif8hSK1+NNUYc12/cnf+pM2UFhhQ1rcpXgbMhm/5c2USZdVAf0FSkSxaFDA==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-2.5.2.tgz", + "integrity": "sha512-JK5uhiVyMqHiAJbgBa8rCvpP8bEhAE9dKDv1gCmP+EUP2FSPmEeW3WXlCXauPB3MDa8behPW+ntyNXqnGaxslg==", "dev": true, "requires": { "esbuild": "^0.12.17", diff --git a/frontend/ui/package.json b/frontend/ui/package.json index 371fa71a..0e8b6d29 100644 --- a/frontend/ui/package.json +++ b/frontend/ui/package.json @@ -15,6 +15,8 @@ "eslint-plugin-svelte3": "^3.2.0", "prettier": "~2.2.1", "prettier-plugin-svelte": "^2.2.0", + "sass": "^1.38.2", + "sass-loader": "^12.1.0", "svelte": "^3.34.0", "sveltestrap": "^5.6.2" }, @@ -24,6 +26,7 @@ "@sveltejs/adapter-static": "^1.0.0-next.17", "bootstrap": "^5.1.0", "bootstrap-icons": "^1.5.0", + "bootswatch": "^5.1.0", "seedrandom": "^3.0.5" } } diff --git a/frontend/ui/src/components/ExerciceDownloads.svelte b/frontend/ui/src/components/ExerciceDownloads.svelte index a56b5abb..aca38b08 100644 --- a/frontend/ui/src/components/ExerciceDownloads.svelte +++ b/frontend/ui/src/components/ExerciceDownloads.svelte @@ -13,8 +13,8 @@ {#if files.length} - - + + Téléchargements @@ -23,18 +23,19 @@ 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} - -

-
+ +

+ +

+

{file.name}

Taille : {file.size} - – - + b2sum : {file.checksum} diff --git a/frontend/ui/src/components/ExerciceFlags.svelte b/frontend/ui/src/components/ExerciceFlags.svelte index 12c87377..03524589 100644 --- a/frontend/ui/src/components/ExerciceFlags.svelte +++ b/frontend/ui/src/components/ExerciceFlags.svelte @@ -94,7 +94,7 @@ - + Faire son rapport @@ -109,20 +109,20 @@ {/if} {#if exercice.tries || exercice.submitted || sberr} - + {#if exercice.solved_time && exercice.tries} - + {exercice.tries} {exercice.tries==1?"tentative effectuée":"tentatives effectuées"}. Dernière solution envoyée à {exercice.solved_time}. {/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} @@ -131,7 +131,7 @@ {/if} {#if timeouted} - + Oops La requête a dépassé le délai d'attente. Vous devriez réessayer dans quelques instant… diff --git a/frontend/ui/src/components/ExerciceHints.svelte b/frontend/ui/src/components/ExerciceHints.svelte index 8adb6526..e92f6d94 100644 --- a/frontend/ui/src/components/ExerciceHints.svelte +++ b/frontend/ui/src/components/ExerciceHints.svelte @@ -86,13 +86,13 @@ {/if} {#each hints as hint (hint.id)} - + {#if hint.file}

{/if} -
+
{#if !(hint.content || hint.file)}
- +
+ (isOpen = !isOpen)} />
From b0a7daf1f44c80be8e7419f094133c554247cb92 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 2 Sep 2021 16:39:58 +0200 Subject: [PATCH 0316/1637] ui: Include badge on CardTheme --- frontend/ui/src/components/CardTheme.svelte | 24 +++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/frontend/ui/src/components/CardTheme.svelte b/frontend/ui/src/components/CardTheme.svelte index 4bccae05..75f3dd73 100644 --- a/frontend/ui/src/components/CardTheme.svelte +++ b/frontend/ui/src/components/CardTheme.svelte @@ -1,6 +1,7 @@ - - - -
- -
- -
- - -
-
-
-
- - - -
-

{{ title }} {{ authors }}

-

{{ settings.title }} {{ settings.authors }}

-
- -
- -
- - - - - - - - - - - diff --git a/frontend/static/js/angular-route.min.js b/frontend/static/js/angular-route.min.js deleted file mode 120000 index ae04a29f..00000000 --- a/frontend/static/js/angular-route.min.js +++ /dev/null @@ -1 +0,0 @@ -../../../admin/static/js/angular-route.min.js \ No newline at end of file diff --git a/frontend/static/js/angular-sanitize.min.js b/frontend/static/js/angular-sanitize.min.js deleted file mode 120000 index d0410452..00000000 --- a/frontend/static/js/angular-sanitize.min.js +++ /dev/null @@ -1 +0,0 @@ -../../../admin/static/js/angular-sanitize.min.js \ No newline at end of file diff --git a/frontend/static/js/angular.min.js b/frontend/static/js/angular.min.js deleted file mode 120000 index 5eba27d2..00000000 --- a/frontend/static/js/angular.min.js +++ /dev/null @@ -1 +0,0 @@ -../../../admin/static/js/angular.min.js \ No newline at end of file diff --git a/frontend/static/js/blake2b.js b/frontend/static/js/blake2b.js deleted file mode 100644 index f741df36..00000000 --- a/frontend/static/js/blake2b.js +++ /dev/null @@ -1,2594 +0,0 @@ -(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i= 0x100000000) { - o1++ - } - v[a] = o0 - v[a + 1] = o1 -} - -// 64-bit unsigned addition -// Sets v[a,a+1] += b -// b0 is the low 32 bits of b, b1 represents the high 32 bits -function ADD64AC (v, a, b0, b1) { - var o0 = v[a] + b0 - if (b0 < 0) { - o0 += 0x100000000 - } - var o1 = v[a + 1] + b1 - if (o0 >= 0x100000000) { - o1++ - } - v[a] = o0 - v[a + 1] = o1 -} - -// Little-endian byte access -function B2B_GET32 (arr, i) { - return (arr[i] ^ - (arr[i + 1] << 8) ^ - (arr[i + 2] << 16) ^ - (arr[i + 3] << 24)) -} - -// G Mixing function -// The ROTRs are inlined for speed -function B2B_G (a, b, c, d, ix, iy) { - var x0 = m[ix] - var x1 = m[ix + 1] - var y0 = m[iy] - var y1 = m[iy + 1] - - ADD64AA(v, a, b) // v[a,a+1] += v[b,b+1] ... in JS we must store a uint64 as two uint32s - ADD64AC(v, a, x0, x1) // v[a, a+1] += x ... x0 is the low 32 bits of x, x1 is the high 32 bits - - // v[d,d+1] = (v[d,d+1] xor v[a,a+1]) rotated to the right by 32 bits - var xor0 = v[d] ^ v[a] - var xor1 = v[d + 1] ^ v[a + 1] - v[d] = xor1 - v[d + 1] = xor0 - - ADD64AA(v, c, d) - - // v[b,b+1] = (v[b,b+1] xor v[c,c+1]) rotated right by 24 bits - xor0 = v[b] ^ v[c] - xor1 = v[b + 1] ^ v[c + 1] - v[b] = (xor0 >>> 24) ^ (xor1 << 8) - v[b + 1] = (xor1 >>> 24) ^ (xor0 << 8) - - ADD64AA(v, a, b) - ADD64AC(v, a, y0, y1) - - // v[d,d+1] = (v[d,d+1] xor v[a,a+1]) rotated right by 16 bits - xor0 = v[d] ^ v[a] - xor1 = v[d + 1] ^ v[a + 1] - v[d] = (xor0 >>> 16) ^ (xor1 << 16) - v[d + 1] = (xor1 >>> 16) ^ (xor0 << 16) - - ADD64AA(v, c, d) - - // v[b,b+1] = (v[b,b+1] xor v[c,c+1]) rotated right by 63 bits - xor0 = v[b] ^ v[c] - xor1 = v[b + 1] ^ v[c + 1] - v[b] = (xor1 >>> 31) ^ (xor0 << 1) - v[b + 1] = (xor0 >>> 31) ^ (xor1 << 1) -} - -// Initialization Vector -var BLAKE2B_IV32 = new Uint32Array([ - 0xF3BCC908, 0x6A09E667, 0x84CAA73B, 0xBB67AE85, - 0xFE94F82B, 0x3C6EF372, 0x5F1D36F1, 0xA54FF53A, - 0xADE682D1, 0x510E527F, 0x2B3E6C1F, 0x9B05688C, - 0xFB41BD6B, 0x1F83D9AB, 0x137E2179, 0x5BE0CD19 -]) - -var SIGMA8 = [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3, - 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4, - 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8, - 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13, - 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9, - 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11, - 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10, - 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5, - 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 -] - -// These are offsets into a uint64 buffer. -// Multiply them all by 2 to make them offsets into a uint32 buffer, -// because this is Javascript and we don't have uint64s -var SIGMA82 = new Uint8Array(SIGMA8.map(function (x) { return x * 2 })) - -// Compression function. 'last' flag indicates last block. -// Note we're representing 16 uint64s as 32 uint32s -var v = new Uint32Array(32) -var m = new Uint32Array(32) -function blake2bCompress (ctx, last) { - var i = 0 - - // init work variables - for (i = 0; i < 16; i++) { - v[i] = ctx.h[i] - v[i + 16] = BLAKE2B_IV32[i] - } - - // low 64 bits of offset - v[24] = v[24] ^ ctx.t - v[25] = v[25] ^ (ctx.t / 0x100000000) - // high 64 bits not supported, offset may not be higher than 2**53-1 - - // last block flag set ? - if (last) { - v[28] = ~v[28] - v[29] = ~v[29] - } - - // get little-endian words - for (i = 0; i < 32; i++) { - m[i] = B2B_GET32(ctx.b, 4 * i) - } - - // twelve rounds of mixing - // uncomment the DebugPrint calls to log the computation - // and match the RFC sample documentation - // util.debugPrint(' m[16]', m, 64) - for (i = 0; i < 12; i++) { - // util.debugPrint(' (i=' + (i < 10 ? ' ' : '') + i + ') v[16]', v, 64) - B2B_G(0, 8, 16, 24, SIGMA82[i * 16 + 0], SIGMA82[i * 16 + 1]) - B2B_G(2, 10, 18, 26, SIGMA82[i * 16 + 2], SIGMA82[i * 16 + 3]) - B2B_G(4, 12, 20, 28, SIGMA82[i * 16 + 4], SIGMA82[i * 16 + 5]) - B2B_G(6, 14, 22, 30, SIGMA82[i * 16 + 6], SIGMA82[i * 16 + 7]) - B2B_G(0, 10, 20, 30, SIGMA82[i * 16 + 8], SIGMA82[i * 16 + 9]) - B2B_G(2, 12, 22, 24, SIGMA82[i * 16 + 10], SIGMA82[i * 16 + 11]) - B2B_G(4, 14, 16, 26, SIGMA82[i * 16 + 12], SIGMA82[i * 16 + 13]) - B2B_G(6, 8, 18, 28, SIGMA82[i * 16 + 14], SIGMA82[i * 16 + 15]) - } - // util.debugPrint(' (i=12) v[16]', v, 64) - - for (i = 0; i < 16; i++) { - ctx.h[i] = ctx.h[i] ^ v[i] ^ v[i + 16] - } - // util.debugPrint('h[8]', ctx.h, 64) -} - -// Creates a BLAKE2b hashing context -// Requires an output length between 1 and 64 bytes -// Takes an optional Uint8Array key -function blake2bInit (outlen, key) { - if (outlen === 0 || outlen > 64) { - throw new Error('Illegal output length, expected 0 < length <= 64') - } - if (key && key.length > 64) { - throw new Error('Illegal key, expected Uint8Array with 0 < length <= 64') - } - - // state, 'param block' - var ctx = { - b: new Uint8Array(128), - h: new Uint32Array(16), - t: 0, // input count - c: 0, // pointer within buffer - outlen: outlen // output length in bytes - } - - // initialize hash state - for (var i = 0; i < 16; i++) { - ctx.h[i] = BLAKE2B_IV32[i] - } - var keylen = key ? key.length : 0 - ctx.h[0] ^= 0x01010000 ^ (keylen << 8) ^ outlen - - // key the hash, if applicable - if (key) { - blake2bUpdate(ctx, key) - // at the end - ctx.c = 128 - } - - return ctx -} - -// Updates a BLAKE2b streaming hash -// Requires hash context and Uint8Array (byte array) -function blake2bUpdate (ctx, input) { - for (var i = 0; i < input.length; i++) { - if (ctx.c === 128) { // buffer full ? - ctx.t += ctx.c // add counters - blake2bCompress(ctx, false) // compress (not last) - ctx.c = 0 // counter to zero - } - ctx.b[ctx.c++] = input[i] - } -} - -// Completes a BLAKE2b streaming hash -// Returns a Uint8Array containing the message digest -function blake2bFinal (ctx) { - ctx.t += ctx.c // mark last block offset - - while (ctx.c < 128) { // fill up with zeros - ctx.b[ctx.c++] = 0 - } - blake2bCompress(ctx, true) // final block flag = 1 - - // little endian convert and store - var out = new Uint8Array(ctx.outlen) - for (var i = 0; i < ctx.outlen; i++) { - out[i] = ctx.h[i >> 2] >> (8 * (i & 3)) - } - return out -} - -// Computes the BLAKE2B hash of a string or byte array, and returns a Uint8Array -// -// Returns a n-byte Uint8Array -// -// Parameters: -// - input - the input bytes, as a string, Buffer or Uint8Array -// - key - optional key Uint8Array, up to 64 bytes -// - outlen - optional output length in bytes, default 64 -function blake2b (input, key, outlen) { - // preprocess inputs - outlen = outlen || 64 - input = util.normalizeInput(input) - - // do the math - var ctx = blake2bInit(outlen, key) - blake2bUpdate(ctx, input) - return blake2bFinal(ctx) -} - -// Computes the BLAKE2B hash of a string or byte array -// -// Returns an n-byte hash in hex, all lowercase -// -// Parameters: -// - input - the input bytes, as a string, Buffer, or Uint8Array -// - key - optional key Uint8Array, up to 64 bytes -// - outlen - optional output length in bytes, default 64 -function blake2bHex (input, key, outlen) { - var output = blake2b(input, key, outlen) - return util.toHex(output) -} - -module.exports = { - blake2b: blake2b, - blake2bHex: blake2bHex, - blake2bInit: blake2bInit, - blake2bUpdate: blake2bUpdate, - blake2bFinal: blake2bFinal -} - -},{"./util":5}],2:[function(require,module,exports){ -// BLAKE2s hash function in pure Javascript -// Adapted from the reference implementation in RFC7693 -// Ported to Javascript by DC - https://github.com/dcposch - -var util = require('./util') - -// Little-endian byte access. -// Expects a Uint8Array and an index -// Returns the little-endian uint32 at v[i..i+3] -function B2S_GET32 (v, i) { - return v[i] ^ (v[i + 1] << 8) ^ (v[i + 2] << 16) ^ (v[i + 3] << 24) -} - -// Mixing function G. -function B2S_G (a, b, c, d, x, y) { - v[a] = v[a] + v[b] + x - v[d] = ROTR32(v[d] ^ v[a], 16) - v[c] = v[c] + v[d] - v[b] = ROTR32(v[b] ^ v[c], 12) - v[a] = v[a] + v[b] + y - v[d] = ROTR32(v[d] ^ v[a], 8) - v[c] = v[c] + v[d] - v[b] = ROTR32(v[b] ^ v[c], 7) -} - -// 32-bit right rotation -// x should be a uint32 -// y must be between 1 and 31, inclusive -function ROTR32 (x, y) { - return (x >>> y) ^ (x << (32 - y)) -} - -// Initialization Vector. -var BLAKE2S_IV = new Uint32Array([ - 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, - 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19]) - -var SIGMA = new Uint8Array([ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3, - 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4, - 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8, - 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13, - 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9, - 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11, - 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10, - 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5, - 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0]) - -// Compression function. "last" flag indicates last block -var v = new Uint32Array(16) -var m = new Uint32Array(16) -function blake2sCompress (ctx, last) { - var i = 0 - for (i = 0; i < 8; i++) { // init work variables - v[i] = ctx.h[i] - v[i + 8] = BLAKE2S_IV[i] - } - - v[12] ^= ctx.t // low 32 bits of offset - v[13] ^= (ctx.t / 0x100000000) // high 32 bits - if (last) { // last block flag set ? - v[14] = ~v[14] - } - - for (i = 0; i < 16; i++) { // get little-endian words - m[i] = B2S_GET32(ctx.b, 4 * i) - } - - // ten rounds of mixing - // uncomment the DebugPrint calls to log the computation - // and match the RFC sample documentation - // util.debugPrint(' m[16]', m, 32) - for (i = 0; i < 10; i++) { - // util.debugPrint(' (i=' + i + ') v[16]', v, 32) - B2S_G(0, 4, 8, 12, m[SIGMA[i * 16 + 0]], m[SIGMA[i * 16 + 1]]) - B2S_G(1, 5, 9, 13, m[SIGMA[i * 16 + 2]], m[SIGMA[i * 16 + 3]]) - B2S_G(2, 6, 10, 14, m[SIGMA[i * 16 + 4]], m[SIGMA[i * 16 + 5]]) - B2S_G(3, 7, 11, 15, m[SIGMA[i * 16 + 6]], m[SIGMA[i * 16 + 7]]) - B2S_G(0, 5, 10, 15, m[SIGMA[i * 16 + 8]], m[SIGMA[i * 16 + 9]]) - B2S_G(1, 6, 11, 12, m[SIGMA[i * 16 + 10]], m[SIGMA[i * 16 + 11]]) - B2S_G(2, 7, 8, 13, m[SIGMA[i * 16 + 12]], m[SIGMA[i * 16 + 13]]) - B2S_G(3, 4, 9, 14, m[SIGMA[i * 16 + 14]], m[SIGMA[i * 16 + 15]]) - } - // util.debugPrint(' (i=10) v[16]', v, 32) - - for (i = 0; i < 8; i++) { - ctx.h[i] ^= v[i] ^ v[i + 8] - } - // util.debugPrint('h[8]', ctx.h, 32) -} - -// Creates a BLAKE2s hashing context -// Requires an output length between 1 and 32 bytes -// Takes an optional Uint8Array key -function blake2sInit (outlen, key) { - if (!(outlen > 0 && outlen <= 32)) { - throw new Error('Incorrect output length, should be in [1, 32]') - } - var keylen = key ? key.length : 0 - if (key && !(keylen > 0 && keylen <= 32)) { - throw new Error('Incorrect key length, should be in [1, 32]') - } - - var ctx = { - h: new Uint32Array(BLAKE2S_IV), // hash state - b: new Uint32Array(64), // input block - c: 0, // pointer within block - t: 0, // input count - outlen: outlen // output length in bytes - } - ctx.h[0] ^= 0x01010000 ^ (keylen << 8) ^ outlen - - if (keylen > 0) { - blake2sUpdate(ctx, key) - ctx.c = 64 // at the end - } - - return ctx -} - -// Updates a BLAKE2s streaming hash -// Requires hash context and Uint8Array (byte array) -function blake2sUpdate (ctx, input) { - for (var i = 0; i < input.length; i++) { - if (ctx.c === 64) { // buffer full ? - ctx.t += ctx.c // add counters - blake2sCompress(ctx, false) // compress (not last) - ctx.c = 0 // counter to zero - } - ctx.b[ctx.c++] = input[i] - } -} - -// Completes a BLAKE2s streaming hash -// Returns a Uint8Array containing the message digest -function blake2sFinal (ctx) { - ctx.t += ctx.c // mark last block offset - while (ctx.c < 64) { // fill up with zeros - ctx.b[ctx.c++] = 0 - } - blake2sCompress(ctx, true) // final block flag = 1 - - // little endian convert and store - var out = new Uint8Array(ctx.outlen) - for (var i = 0; i < ctx.outlen; i++) { - out[i] = (ctx.h[i >> 2] >> (8 * (i & 3))) & 0xFF - } - return out -} - -// Computes the BLAKE2S hash of a string or byte array, and returns a Uint8Array -// -// Returns a n-byte Uint8Array -// -// Parameters: -// - input - the input bytes, as a string, Buffer, or Uint8Array -// - key - optional key Uint8Array, up to 32 bytes -// - outlen - optional output length in bytes, default 64 -function blake2s (input, key, outlen) { - // preprocess inputs - outlen = outlen || 32 - input = util.normalizeInput(input) - - // do the math - var ctx = blake2sInit(outlen, key) - blake2sUpdate(ctx, input) - return blake2sFinal(ctx) -} - -// Computes the BLAKE2S hash of a string or byte array -// -// Returns an n-byte hash in hex, all lowercase -// -// Parameters: -// - input - the input bytes, as a string, Buffer, or Uint8Array -// - key - optional key Uint8Array, up to 32 bytes -// - outlen - optional output length in bytes, default 64 -function blake2sHex (input, key, outlen) { - var output = blake2s(input, key, outlen) - return util.toHex(output) -} - -module.exports = { - blake2s: blake2s, - blake2sHex: blake2sHex, - blake2sInit: blake2sInit, - blake2sUpdate: blake2sUpdate, - blake2sFinal: blake2sFinal -} - -},{"./util":5}],3:[function(require,module,exports){ -var b2b = require('./blake2b') -var b2s = require('./blake2s') - -module.exports = { - blake2b: b2b.blake2b, - blake2bHex: b2b.blake2bHex, - blake2bInit: b2b.blake2bInit, - blake2bUpdate: b2b.blake2bUpdate, - blake2bFinal: b2b.blake2bFinal, - blake2s: b2s.blake2s, - blake2sHex: b2s.blake2sHex, - blake2sInit: b2s.blake2sInit, - blake2sUpdate: b2s.blake2sUpdate, - blake2sFinal: b2s.blake2sFinal -} - -},{"./blake2b":1,"./blake2s":2}],4:[function(require,module,exports){ -var blake = require('.') -window.b2sum = function(s) { - return blake.blake2bHex(s); -} - -},{".":3}],5:[function(require,module,exports){ -(function (Buffer){ -var ERROR_MSG_INPUT = 'Input must be an string, Buffer or Uint8Array' - -// For convenience, let people hash a string, not just a Uint8Array -function normalizeInput (input) { - var ret - if (input instanceof Uint8Array) { - ret = input - } else if (input instanceof Buffer) { - ret = new Uint8Array(input) - } else if (typeof (input) === 'string') { - ret = new Uint8Array(Buffer.from(input, 'utf8')) - } else { - throw new Error(ERROR_MSG_INPUT) - } - return ret -} - -// Converts a Uint8Array to a hexadecimal string -// For example, toHex([255, 0, 255]) returns "ff00ff" -function toHex (bytes) { - return Array.prototype.map.call(bytes, function (n) { - return (n < 16 ? '0' : '') + n.toString(16) - }).join('') -} - -// Converts any value in [0...2^32-1] to an 8-character hex string -function uint32ToHex (val) { - return (0x100000000 + val).toString(16).substring(1) -} - -// For debugging: prints out hash state in the same format as the RFC -// sample computation exactly, so that you can diff -function debugPrint (label, arr, size) { - var msg = '\n' + label + ' = ' - for (var i = 0; i < arr.length; i += 2) { - if (size === 32) { - msg += uint32ToHex(arr[i]).toUpperCase() - msg += ' ' - msg += uint32ToHex(arr[i + 1]).toUpperCase() - } else if (size === 64) { - msg += uint32ToHex(arr[i + 1]).toUpperCase() - msg += uint32ToHex(arr[i]).toUpperCase() - } else throw new Error('Invalid size ' + size) - if (i % 6 === 4) { - msg += '\n' + new Array(label.length + 4).join(' ') - } else if (i < arr.length - 2) { - msg += ' ' - } - } - console.log(msg) -} - -// For performance testing: generates N bytes of input, hashes M times -// Measures and prints MB/second hash performance each time -function testSpeed (hashFn, N, M) { - var startMs = new Date().getTime() - - var input = new Uint8Array(N) - for (var i = 0; i < N; i++) { - input[i] = i % 256 - } - var genMs = new Date().getTime() - console.log('Generated random input in ' + (genMs - startMs) + 'ms') - startMs = genMs - - for (i = 0; i < M; i++) { - var hashHex = hashFn(input) - var hashMs = new Date().getTime() - var ms = hashMs - startMs - startMs = hashMs - console.log('Hashed in ' + ms + 'ms: ' + hashHex.substring(0, 20) + '...') - console.log(Math.round(N / (1 << 20) / (ms / 1000) * 100) / 100 + ' MB PER SECOND') - } -} - -module.exports = { - normalizeInput: normalizeInput, - toHex: toHex, - debugPrint: debugPrint, - testSpeed: testSpeed -} - -}).call(this,require("buffer").Buffer) -},{"buffer":7}],6:[function(require,module,exports){ -'use strict' - -exports.byteLength = byteLength -exports.toByteArray = toByteArray -exports.fromByteArray = fromByteArray - -var lookup = [] -var revLookup = [] -var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array - -var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' -for (var i = 0, len = code.length; i < len; ++i) { - lookup[i] = code[i] - revLookup[code.charCodeAt(i)] = i -} - -// Support decoding URL-safe base64 strings, as Node.js does. -// See: https://en.wikipedia.org/wiki/Base64#URL_applications -revLookup['-'.charCodeAt(0)] = 62 -revLookup['_'.charCodeAt(0)] = 63 - -function getLens (b64) { - var len = b64.length - - if (len % 4 > 0) { - throw new Error('Invalid string. Length must be a multiple of 4') - } - - // Trim off extra bytes after placeholder bytes are found - // See: https://github.com/beatgammit/base64-js/issues/42 - var validLen = b64.indexOf('=') - if (validLen === -1) validLen = len - - var placeHoldersLen = validLen === len - ? 0 - : 4 - (validLen % 4) - - return [validLen, placeHoldersLen] -} - -// base64 is 4/3 + up to two characters of the original data -function byteLength (b64) { - var lens = getLens(b64) - var validLen = lens[0] - var placeHoldersLen = lens[1] - return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen -} - -function _byteLength (b64, validLen, placeHoldersLen) { - return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen -} - -function toByteArray (b64) { - var tmp - var lens = getLens(b64) - var validLen = lens[0] - var placeHoldersLen = lens[1] - - var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen)) - - var curByte = 0 - - // if there are placeholders, only get up to the last complete 4 chars - var len = placeHoldersLen > 0 - ? validLen - 4 - : validLen - - for (var i = 0; i < len; i += 4) { - tmp = - (revLookup[b64.charCodeAt(i)] << 18) | - (revLookup[b64.charCodeAt(i + 1)] << 12) | - (revLookup[b64.charCodeAt(i + 2)] << 6) | - revLookup[b64.charCodeAt(i + 3)] - arr[curByte++] = (tmp >> 16) & 0xFF - arr[curByte++] = (tmp >> 8) & 0xFF - arr[curByte++] = tmp & 0xFF - } - - if (placeHoldersLen === 2) { - tmp = - (revLookup[b64.charCodeAt(i)] << 2) | - (revLookup[b64.charCodeAt(i + 1)] >> 4) - arr[curByte++] = tmp & 0xFF - } - - if (placeHoldersLen === 1) { - tmp = - (revLookup[b64.charCodeAt(i)] << 10) | - (revLookup[b64.charCodeAt(i + 1)] << 4) | - (revLookup[b64.charCodeAt(i + 2)] >> 2) - arr[curByte++] = (tmp >> 8) & 0xFF - arr[curByte++] = tmp & 0xFF - } - - return arr -} - -function tripletToBase64 (num) { - return lookup[num >> 18 & 0x3F] + - lookup[num >> 12 & 0x3F] + - lookup[num >> 6 & 0x3F] + - lookup[num & 0x3F] -} - -function encodeChunk (uint8, start, end) { - var tmp - var output = [] - for (var i = start; i < end; i += 3) { - tmp = - ((uint8[i] << 16) & 0xFF0000) + - ((uint8[i + 1] << 8) & 0xFF00) + - (uint8[i + 2] & 0xFF) - output.push(tripletToBase64(tmp)) - } - return output.join('') -} - -function fromByteArray (uint8) { - var tmp - var len = uint8.length - var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes - var parts = [] - var maxChunkLength = 16383 // must be multiple of 3 - - // go through the array every three bytes, we'll deal with trailing stuff later - for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { - parts.push(encodeChunk( - uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength) - )) - } - - // pad the end with zeros, but make sure to not forget the extra bytes - if (extraBytes === 1) { - tmp = uint8[len - 1] - parts.push( - lookup[tmp >> 2] + - lookup[(tmp << 4) & 0x3F] + - '==' - ) - } else if (extraBytes === 2) { - tmp = (uint8[len - 2] << 8) + uint8[len - 1] - parts.push( - lookup[tmp >> 10] + - lookup[(tmp >> 4) & 0x3F] + - lookup[(tmp << 2) & 0x3F] + - '=' - ) - } - - return parts.join('') -} - -},{}],7:[function(require,module,exports){ -/*! - * The buffer module from node.js, for the browser. - * - * @author Feross Aboukhadijeh - * @license MIT - */ -/* eslint-disable no-proto */ - -'use strict' - -var base64 = require('base64-js') -var ieee754 = require('ieee754') - -exports.Buffer = Buffer -exports.SlowBuffer = SlowBuffer -exports.INSPECT_MAX_BYTES = 50 - -var K_MAX_LENGTH = 0x7fffffff -exports.kMaxLength = K_MAX_LENGTH - -/** - * If `Buffer.TYPED_ARRAY_SUPPORT`: - * === true Use Uint8Array implementation (fastest) - * === false Print warning and recommend using `buffer` v4.x which has an Object - * implementation (most compatible, even IE6) - * - * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, - * Opera 11.6+, iOS 4.2+. - * - * We report that the browser does not support typed arrays if the are not subclassable - * using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array` - * (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support - * for __proto__ and has a buggy typed array implementation. - */ -Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport() - -if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' && - typeof console.error === 'function') { - console.error( - 'This browser lacks typed array (Uint8Array) support which is required by ' + - '`buffer` v5.x. Use `buffer` v4.x if you require old browser support.' - ) -} - -function typedArraySupport () { - // Can typed array instances can be augmented? - try { - var arr = new Uint8Array(1) - arr.__proto__ = { __proto__: Uint8Array.prototype, foo: function () { return 42 } } - return arr.foo() === 42 - } catch (e) { - return false - } -} - -Object.defineProperty(Buffer.prototype, 'parent', { - enumerable: true, - get: function () { - if (!Buffer.isBuffer(this)) return undefined - return this.buffer - } -}) - -Object.defineProperty(Buffer.prototype, 'offset', { - enumerable: true, - get: function () { - if (!Buffer.isBuffer(this)) return undefined - return this.byteOffset - } -}) - -function createBuffer (length) { - if (length > K_MAX_LENGTH) { - throw new RangeError('The value "' + length + '" is invalid for option "size"') - } - // Return an augmented `Uint8Array` instance - var buf = new Uint8Array(length) - buf.__proto__ = Buffer.prototype - return buf -} - -/** - * The Buffer constructor returns instances of `Uint8Array` that have their - * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of - * `Uint8Array`, so the returned instances will have all the node `Buffer` methods - * and the `Uint8Array` methods. Square bracket notation works as expected -- it - * returns a single octet. - * - * The `Uint8Array` prototype remains unmodified. - */ - -function Buffer (arg, encodingOrOffset, length) { - // Common case. - if (typeof arg === 'number') { - if (typeof encodingOrOffset === 'string') { - throw new TypeError( - 'The "string" argument must be of type string. Received type number' - ) - } - return allocUnsafe(arg) - } - return from(arg, encodingOrOffset, length) -} - -// Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97 -if (typeof Symbol !== 'undefined' && Symbol.species != null && - Buffer[Symbol.species] === Buffer) { - Object.defineProperty(Buffer, Symbol.species, { - value: null, - configurable: true, - enumerable: false, - writable: false - }) -} - -Buffer.poolSize = 8192 // not used by this implementation - -function from (value, encodingOrOffset, length) { - if (typeof value === 'string') { - return fromString(value, encodingOrOffset) - } - - if (ArrayBuffer.isView(value)) { - return fromArrayLike(value) - } - - if (value == null) { - throw TypeError( - 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + - 'or Array-like Object. Received type ' + (typeof value) - ) - } - - if (isInstance(value, ArrayBuffer) || - (value && isInstance(value.buffer, ArrayBuffer))) { - return fromArrayBuffer(value, encodingOrOffset, length) - } - - if (typeof value === 'number') { - throw new TypeError( - 'The "value" argument must not be of type number. Received type number' - ) - } - - var valueOf = value.valueOf && value.valueOf() - if (valueOf != null && valueOf !== value) { - return Buffer.from(valueOf, encodingOrOffset, length) - } - - var b = fromObject(value) - if (b) return b - - if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null && - typeof value[Symbol.toPrimitive] === 'function') { - return Buffer.from( - value[Symbol.toPrimitive]('string'), encodingOrOffset, length - ) - } - - throw new TypeError( - 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + - 'or Array-like Object. Received type ' + (typeof value) - ) -} - -/** - * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError - * if value is a number. - * Buffer.from(str[, encoding]) - * Buffer.from(array) - * Buffer.from(buffer) - * Buffer.from(arrayBuffer[, byteOffset[, length]]) - **/ -Buffer.from = function (value, encodingOrOffset, length) { - return from(value, encodingOrOffset, length) -} - -// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug: -// https://github.com/feross/buffer/pull/148 -Buffer.prototype.__proto__ = Uint8Array.prototype -Buffer.__proto__ = Uint8Array - -function assertSize (size) { - if (typeof size !== 'number') { - throw new TypeError('"size" argument must be of type number') - } else if (size < 0) { - throw new RangeError('The value "' + size + '" is invalid for option "size"') - } -} - -function alloc (size, fill, encoding) { - assertSize(size) - if (size <= 0) { - return createBuffer(size) - } - if (fill !== undefined) { - // Only pay attention to encoding if it's a string. This - // prevents accidentally sending in a number that would - // be interpretted as a start offset. - return typeof encoding === 'string' - ? createBuffer(size).fill(fill, encoding) - : createBuffer(size).fill(fill) - } - return createBuffer(size) -} - -/** - * Creates a new filled Buffer instance. - * alloc(size[, fill[, encoding]]) - **/ -Buffer.alloc = function (size, fill, encoding) { - return alloc(size, fill, encoding) -} - -function allocUnsafe (size) { - assertSize(size) - return createBuffer(size < 0 ? 0 : checked(size) | 0) -} - -/** - * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance. - * */ -Buffer.allocUnsafe = function (size) { - return allocUnsafe(size) -} -/** - * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance. - */ -Buffer.allocUnsafeSlow = function (size) { - return allocUnsafe(size) -} - -function fromString (string, encoding) { - if (typeof encoding !== 'string' || encoding === '') { - encoding = 'utf8' - } - - if (!Buffer.isEncoding(encoding)) { - throw new TypeError('Unknown encoding: ' + encoding) - } - - var length = byteLength(string, encoding) | 0 - var buf = createBuffer(length) - - var actual = buf.write(string, encoding) - - if (actual !== length) { - // Writing a hex string, for example, that contains invalid characters will - // cause everything after the first invalid character to be ignored. (e.g. - // 'abxxcd' will be treated as 'ab') - buf = buf.slice(0, actual) - } - - return buf -} - -function fromArrayLike (array) { - var length = array.length < 0 ? 0 : checked(array.length) | 0 - var buf = createBuffer(length) - for (var i = 0; i < length; i += 1) { - buf[i] = array[i] & 255 - } - return buf -} - -function fromArrayBuffer (array, byteOffset, length) { - if (byteOffset < 0 || array.byteLength < byteOffset) { - throw new RangeError('"offset" is outside of buffer bounds') - } - - if (array.byteLength < byteOffset + (length || 0)) { - throw new RangeError('"length" is outside of buffer bounds') - } - - var buf - if (byteOffset === undefined && length === undefined) { - buf = new Uint8Array(array) - } else if (length === undefined) { - buf = new Uint8Array(array, byteOffset) - } else { - buf = new Uint8Array(array, byteOffset, length) - } - - // Return an augmented `Uint8Array` instance - buf.__proto__ = Buffer.prototype - return buf -} - -function fromObject (obj) { - if (Buffer.isBuffer(obj)) { - var len = checked(obj.length) | 0 - var buf = createBuffer(len) - - if (buf.length === 0) { - return buf - } - - obj.copy(buf, 0, 0, len) - return buf - } - - if (obj.length !== undefined) { - if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) { - return createBuffer(0) - } - return fromArrayLike(obj) - } - - if (obj.type === 'Buffer' && Array.isArray(obj.data)) { - return fromArrayLike(obj.data) - } -} - -function checked (length) { - // Note: cannot use `length < K_MAX_LENGTH` here because that fails when - // length is NaN (which is otherwise coerced to zero.) - if (length >= K_MAX_LENGTH) { - throw new RangeError('Attempt to allocate Buffer larger than maximum ' + - 'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes') - } - return length | 0 -} - -function SlowBuffer (length) { - if (+length != length) { // eslint-disable-line eqeqeq - length = 0 - } - return Buffer.alloc(+length) -} - -Buffer.isBuffer = function isBuffer (b) { - return b != null && b._isBuffer === true && - b !== Buffer.prototype // so Buffer.isBuffer(Buffer.prototype) will be false -} - -Buffer.compare = function compare (a, b) { - if (isInstance(a, Uint8Array)) a = Buffer.from(a, a.offset, a.byteLength) - if (isInstance(b, Uint8Array)) b = Buffer.from(b, b.offset, b.byteLength) - if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { - throw new TypeError( - 'The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array' - ) - } - - if (a === b) return 0 - - var x = a.length - var y = b.length - - for (var i = 0, len = Math.min(x, y); i < len; ++i) { - if (a[i] !== b[i]) { - x = a[i] - y = b[i] - break - } - } - - if (x < y) return -1 - if (y < x) return 1 - return 0 -} - -Buffer.isEncoding = function isEncoding (encoding) { - switch (String(encoding).toLowerCase()) { - case 'hex': - case 'utf8': - case 'utf-8': - case 'ascii': - case 'latin1': - case 'binary': - case 'base64': - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return true - default: - return false - } -} - -Buffer.concat = function concat (list, length) { - if (!Array.isArray(list)) { - throw new TypeError('"list" argument must be an Array of Buffers') - } - - if (list.length === 0) { - return Buffer.alloc(0) - } - - var i - if (length === undefined) { - length = 0 - for (i = 0; i < list.length; ++i) { - length += list[i].length - } - } - - var buffer = Buffer.allocUnsafe(length) - var pos = 0 - for (i = 0; i < list.length; ++i) { - var buf = list[i] - if (isInstance(buf, Uint8Array)) { - buf = Buffer.from(buf) - } - if (!Buffer.isBuffer(buf)) { - throw new TypeError('"list" argument must be an Array of Buffers') - } - buf.copy(buffer, pos) - pos += buf.length - } - return buffer -} - -function byteLength (string, encoding) { - if (Buffer.isBuffer(string)) { - return string.length - } - if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) { - return string.byteLength - } - if (typeof string !== 'string') { - throw new TypeError( - 'The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' + - 'Received type ' + typeof string - ) - } - - var len = string.length - var mustMatch = (arguments.length > 2 && arguments[2] === true) - if (!mustMatch && len === 0) return 0 - - // Use a for loop to avoid recursion - var loweredCase = false - for (;;) { - switch (encoding) { - case 'ascii': - case 'latin1': - case 'binary': - return len - case 'utf8': - case 'utf-8': - return utf8ToBytes(string).length - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return len * 2 - case 'hex': - return len >>> 1 - case 'base64': - return base64ToBytes(string).length - default: - if (loweredCase) { - return mustMatch ? -1 : utf8ToBytes(string).length // assume utf8 - } - encoding = ('' + encoding).toLowerCase() - loweredCase = true - } - } -} -Buffer.byteLength = byteLength - -function slowToString (encoding, start, end) { - var loweredCase = false - - // No need to verify that "this.length <= MAX_UINT32" since it's a read-only - // property of a typed array. - - // This behaves neither like String nor Uint8Array in that we set start/end - // to their upper/lower bounds if the value passed is out of range. - // undefined is handled specially as per ECMA-262 6th Edition, - // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization. - if (start === undefined || start < 0) { - start = 0 - } - // Return early if start > this.length. Done here to prevent potential uint32 - // coercion fail below. - if (start > this.length) { - return '' - } - - if (end === undefined || end > this.length) { - end = this.length - } - - if (end <= 0) { - return '' - } - - // Force coersion to uint32. This will also coerce falsey/NaN values to 0. - end >>>= 0 - start >>>= 0 - - if (end <= start) { - return '' - } - - if (!encoding) encoding = 'utf8' - - while (true) { - switch (encoding) { - case 'hex': - return hexSlice(this, start, end) - - case 'utf8': - case 'utf-8': - return utf8Slice(this, start, end) - - case 'ascii': - return asciiSlice(this, start, end) - - case 'latin1': - case 'binary': - return latin1Slice(this, start, end) - - case 'base64': - return base64Slice(this, start, end) - - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return utf16leSlice(this, start, end) - - default: - if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) - encoding = (encoding + '').toLowerCase() - loweredCase = true - } - } -} - -// This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package) -// to detect a Buffer instance. It's not possible to use `instanceof Buffer` -// reliably in a browserify context because there could be multiple different -// copies of the 'buffer' package in use. This method works even for Buffer -// instances that were created from another copy of the `buffer` package. -// See: https://github.com/feross/buffer/issues/154 -Buffer.prototype._isBuffer = true - -function swap (b, n, m) { - var i = b[n] - b[n] = b[m] - b[m] = i -} - -Buffer.prototype.swap16 = function swap16 () { - var len = this.length - if (len % 2 !== 0) { - throw new RangeError('Buffer size must be a multiple of 16-bits') - } - for (var i = 0; i < len; i += 2) { - swap(this, i, i + 1) - } - return this -} - -Buffer.prototype.swap32 = function swap32 () { - var len = this.length - if (len % 4 !== 0) { - throw new RangeError('Buffer size must be a multiple of 32-bits') - } - for (var i = 0; i < len; i += 4) { - swap(this, i, i + 3) - swap(this, i + 1, i + 2) - } - return this -} - -Buffer.prototype.swap64 = function swap64 () { - var len = this.length - if (len % 8 !== 0) { - throw new RangeError('Buffer size must be a multiple of 64-bits') - } - for (var i = 0; i < len; i += 8) { - swap(this, i, i + 7) - swap(this, i + 1, i + 6) - swap(this, i + 2, i + 5) - swap(this, i + 3, i + 4) - } - return this -} - -Buffer.prototype.toString = function toString () { - var length = this.length - if (length === 0) return '' - if (arguments.length === 0) return utf8Slice(this, 0, length) - return slowToString.apply(this, arguments) -} - -Buffer.prototype.toLocaleString = Buffer.prototype.toString - -Buffer.prototype.equals = function equals (b) { - if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') - if (this === b) return true - return Buffer.compare(this, b) === 0 -} - -Buffer.prototype.inspect = function inspect () { - var str = '' - var max = exports.INSPECT_MAX_BYTES - str = this.toString('hex', 0, max).replace(/(.{2})/g, '$1 ').trim() - if (this.length > max) str += ' ... ' - return '' -} - -Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) { - if (isInstance(target, Uint8Array)) { - target = Buffer.from(target, target.offset, target.byteLength) - } - if (!Buffer.isBuffer(target)) { - throw new TypeError( - 'The "target" argument must be one of type Buffer or Uint8Array. ' + - 'Received type ' + (typeof target) - ) - } - - if (start === undefined) { - start = 0 - } - if (end === undefined) { - end = target ? target.length : 0 - } - if (thisStart === undefined) { - thisStart = 0 - } - if (thisEnd === undefined) { - thisEnd = this.length - } - - if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) { - throw new RangeError('out of range index') - } - - if (thisStart >= thisEnd && start >= end) { - return 0 - } - if (thisStart >= thisEnd) { - return -1 - } - if (start >= end) { - return 1 - } - - start >>>= 0 - end >>>= 0 - thisStart >>>= 0 - thisEnd >>>= 0 - - if (this === target) return 0 - - var x = thisEnd - thisStart - var y = end - start - var len = Math.min(x, y) - - var thisCopy = this.slice(thisStart, thisEnd) - var targetCopy = target.slice(start, end) - - for (var i = 0; i < len; ++i) { - if (thisCopy[i] !== targetCopy[i]) { - x = thisCopy[i] - y = targetCopy[i] - break - } - } - - if (x < y) return -1 - if (y < x) return 1 - return 0 -} - -// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`, -// OR the last index of `val` in `buffer` at offset <= `byteOffset`. -// -// Arguments: -// - buffer - a Buffer to search -// - val - a string, Buffer, or number -// - byteOffset - an index into `buffer`; will be clamped to an int32 -// - encoding - an optional encoding, relevant is val is a string -// - dir - true for indexOf, false for lastIndexOf -function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) { - // Empty buffer means no match - if (buffer.length === 0) return -1 - - // Normalize byteOffset - if (typeof byteOffset === 'string') { - encoding = byteOffset - byteOffset = 0 - } else if (byteOffset > 0x7fffffff) { - byteOffset = 0x7fffffff - } else if (byteOffset < -0x80000000) { - byteOffset = -0x80000000 - } - byteOffset = +byteOffset // Coerce to Number. - if (numberIsNaN(byteOffset)) { - // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer - byteOffset = dir ? 0 : (buffer.length - 1) - } - - // Normalize byteOffset: negative offsets start from the end of the buffer - if (byteOffset < 0) byteOffset = buffer.length + byteOffset - if (byteOffset >= buffer.length) { - if (dir) return -1 - else byteOffset = buffer.length - 1 - } else if (byteOffset < 0) { - if (dir) byteOffset = 0 - else return -1 - } - - // Normalize val - if (typeof val === 'string') { - val = Buffer.from(val, encoding) - } - - // Finally, search either indexOf (if dir is true) or lastIndexOf - if (Buffer.isBuffer(val)) { - // Special case: looking for empty string/buffer always fails - if (val.length === 0) { - return -1 - } - return arrayIndexOf(buffer, val, byteOffset, encoding, dir) - } else if (typeof val === 'number') { - val = val & 0xFF // Search for a byte value [0-255] - if (typeof Uint8Array.prototype.indexOf === 'function') { - if (dir) { - return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset) - } else { - return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset) - } - } - return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir) - } - - throw new TypeError('val must be string, number or Buffer') -} - -function arrayIndexOf (arr, val, byteOffset, encoding, dir) { - var indexSize = 1 - var arrLength = arr.length - var valLength = val.length - - if (encoding !== undefined) { - encoding = String(encoding).toLowerCase() - if (encoding === 'ucs2' || encoding === 'ucs-2' || - encoding === 'utf16le' || encoding === 'utf-16le') { - if (arr.length < 2 || val.length < 2) { - return -1 - } - indexSize = 2 - arrLength /= 2 - valLength /= 2 - byteOffset /= 2 - } - } - - function read (buf, i) { - if (indexSize === 1) { - return buf[i] - } else { - return buf.readUInt16BE(i * indexSize) - } - } - - var i - if (dir) { - var foundIndex = -1 - for (i = byteOffset; i < arrLength; i++) { - if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) { - if (foundIndex === -1) foundIndex = i - if (i - foundIndex + 1 === valLength) return foundIndex * indexSize - } else { - if (foundIndex !== -1) i -= i - foundIndex - foundIndex = -1 - } - } - } else { - if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength - for (i = byteOffset; i >= 0; i--) { - var found = true - for (var j = 0; j < valLength; j++) { - if (read(arr, i + j) !== read(val, j)) { - found = false - break - } - } - if (found) return i - } - } - - return -1 -} - -Buffer.prototype.includes = function includes (val, byteOffset, encoding) { - return this.indexOf(val, byteOffset, encoding) !== -1 -} - -Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) { - return bidirectionalIndexOf(this, val, byteOffset, encoding, true) -} - -Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) { - return bidirectionalIndexOf(this, val, byteOffset, encoding, false) -} - -function hexWrite (buf, string, offset, length) { - offset = Number(offset) || 0 - var remaining = buf.length - offset - if (!length) { - length = remaining - } else { - length = Number(length) - if (length > remaining) { - length = remaining - } - } - - var strLen = string.length - - if (length > strLen / 2) { - length = strLen / 2 - } - for (var i = 0; i < length; ++i) { - var parsed = parseInt(string.substr(i * 2, 2), 16) - if (numberIsNaN(parsed)) return i - buf[offset + i] = parsed - } - return i -} - -function utf8Write (buf, string, offset, length) { - return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) -} - -function asciiWrite (buf, string, offset, length) { - return blitBuffer(asciiToBytes(string), buf, offset, length) -} - -function latin1Write (buf, string, offset, length) { - return asciiWrite(buf, string, offset, length) -} - -function base64Write (buf, string, offset, length) { - return blitBuffer(base64ToBytes(string), buf, offset, length) -} - -function ucs2Write (buf, string, offset, length) { - return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) -} - -Buffer.prototype.write = function write (string, offset, length, encoding) { - // Buffer#write(string) - if (offset === undefined) { - encoding = 'utf8' - length = this.length - offset = 0 - // Buffer#write(string, encoding) - } else if (length === undefined && typeof offset === 'string') { - encoding = offset - length = this.length - offset = 0 - // Buffer#write(string, offset[, length][, encoding]) - } else if (isFinite(offset)) { - offset = offset >>> 0 - if (isFinite(length)) { - length = length >>> 0 - if (encoding === undefined) encoding = 'utf8' - } else { - encoding = length - length = undefined - } - } else { - throw new Error( - 'Buffer.write(string, encoding, offset[, length]) is no longer supported' - ) - } - - var remaining = this.length - offset - if (length === undefined || length > remaining) length = remaining - - if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { - throw new RangeError('Attempt to write outside buffer bounds') - } - - if (!encoding) encoding = 'utf8' - - var loweredCase = false - for (;;) { - switch (encoding) { - case 'hex': - return hexWrite(this, string, offset, length) - - case 'utf8': - case 'utf-8': - return utf8Write(this, string, offset, length) - - case 'ascii': - return asciiWrite(this, string, offset, length) - - case 'latin1': - case 'binary': - return latin1Write(this, string, offset, length) - - case 'base64': - // Warning: maxLength not taken into account in base64Write - return base64Write(this, string, offset, length) - - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return ucs2Write(this, string, offset, length) - - default: - if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) - encoding = ('' + encoding).toLowerCase() - loweredCase = true - } - } -} - -Buffer.prototype.toJSON = function toJSON () { - return { - type: 'Buffer', - data: Array.prototype.slice.call(this._arr || this, 0) - } -} - -function base64Slice (buf, start, end) { - if (start === 0 && end === buf.length) { - return base64.fromByteArray(buf) - } else { - return base64.fromByteArray(buf.slice(start, end)) - } -} - -function utf8Slice (buf, start, end) { - end = Math.min(buf.length, end) - var res = [] - - var i = start - while (i < end) { - var firstByte = buf[i] - var codePoint = null - var bytesPerSequence = (firstByte > 0xEF) ? 4 - : (firstByte > 0xDF) ? 3 - : (firstByte > 0xBF) ? 2 - : 1 - - if (i + bytesPerSequence <= end) { - var secondByte, thirdByte, fourthByte, tempCodePoint - - switch (bytesPerSequence) { - case 1: - if (firstByte < 0x80) { - codePoint = firstByte - } - break - case 2: - secondByte = buf[i + 1] - if ((secondByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) - if (tempCodePoint > 0x7F) { - codePoint = tempCodePoint - } - } - break - case 3: - secondByte = buf[i + 1] - thirdByte = buf[i + 2] - if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) - if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { - codePoint = tempCodePoint - } - } - break - case 4: - secondByte = buf[i + 1] - thirdByte = buf[i + 2] - fourthByte = buf[i + 3] - if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) - if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { - codePoint = tempCodePoint - } - } - } - } - - if (codePoint === null) { - // we did not generate a valid codePoint so insert a - // replacement char (U+FFFD) and advance only 1 byte - codePoint = 0xFFFD - bytesPerSequence = 1 - } else if (codePoint > 0xFFFF) { - // encode to utf16 (surrogate pair dance) - codePoint -= 0x10000 - res.push(codePoint >>> 10 & 0x3FF | 0xD800) - codePoint = 0xDC00 | codePoint & 0x3FF - } - - res.push(codePoint) - i += bytesPerSequence - } - - return decodeCodePointsArray(res) -} - -// Based on http://stackoverflow.com/a/22747272/680742, the browser with -// the lowest limit is Chrome, with 0x10000 args. -// We go 1 magnitude less, for safety -var MAX_ARGUMENTS_LENGTH = 0x1000 - -function decodeCodePointsArray (codePoints) { - var len = codePoints.length - if (len <= MAX_ARGUMENTS_LENGTH) { - return String.fromCharCode.apply(String, codePoints) // avoid extra slice() - } - - // Decode in chunks to avoid "call stack size exceeded". - var res = '' - var i = 0 - while (i < len) { - res += String.fromCharCode.apply( - String, - codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) - ) - } - return res -} - -function asciiSlice (buf, start, end) { - var ret = '' - end = Math.min(buf.length, end) - - for (var i = start; i < end; ++i) { - ret += String.fromCharCode(buf[i] & 0x7F) - } - return ret -} - -function latin1Slice (buf, start, end) { - var ret = '' - end = Math.min(buf.length, end) - - for (var i = start; i < end; ++i) { - ret += String.fromCharCode(buf[i]) - } - return ret -} - -function hexSlice (buf, start, end) { - var len = buf.length - - if (!start || start < 0) start = 0 - if (!end || end < 0 || end > len) end = len - - var out = '' - for (var i = start; i < end; ++i) { - out += toHex(buf[i]) - } - return out -} - -function utf16leSlice (buf, start, end) { - var bytes = buf.slice(start, end) - var res = '' - for (var i = 0; i < bytes.length; i += 2) { - res += String.fromCharCode(bytes[i] + (bytes[i + 1] * 256)) - } - return res -} - -Buffer.prototype.slice = function slice (start, end) { - var len = this.length - start = ~~start - end = end === undefined ? len : ~~end - - if (start < 0) { - start += len - if (start < 0) start = 0 - } else if (start > len) { - start = len - } - - if (end < 0) { - end += len - if (end < 0) end = 0 - } else if (end > len) { - end = len - } - - if (end < start) end = start - - var newBuf = this.subarray(start, end) - // Return an augmented `Uint8Array` instance - newBuf.__proto__ = Buffer.prototype - return newBuf -} - -/* - * Need to make sure that buffer isn't trying to write out of bounds. - */ -function checkOffset (offset, ext, length) { - if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint') - if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length') -} - -Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) { - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) checkOffset(offset, byteLength, this.length) - - var val = this[offset] - var mul = 1 - var i = 0 - while (++i < byteLength && (mul *= 0x100)) { - val += this[offset + i] * mul - } - - return val -} - -Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) { - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) { - checkOffset(offset, byteLength, this.length) - } - - var val = this[offset + --byteLength] - var mul = 1 - while (byteLength > 0 && (mul *= 0x100)) { - val += this[offset + --byteLength] * mul - } - - return val -} - -Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 1, this.length) - return this[offset] -} - -Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 2, this.length) - return this[offset] | (this[offset + 1] << 8) -} - -Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 2, this.length) - return (this[offset] << 8) | this[offset + 1] -} - -Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - - return ((this[offset]) | - (this[offset + 1] << 8) | - (this[offset + 2] << 16)) + - (this[offset + 3] * 0x1000000) -} - -Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - - return (this[offset] * 0x1000000) + - ((this[offset + 1] << 16) | - (this[offset + 2] << 8) | - this[offset + 3]) -} - -Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) { - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) checkOffset(offset, byteLength, this.length) - - var val = this[offset] - var mul = 1 - var i = 0 - while (++i < byteLength && (mul *= 0x100)) { - val += this[offset + i] * mul - } - mul *= 0x80 - - if (val >= mul) val -= Math.pow(2, 8 * byteLength) - - return val -} - -Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) { - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) checkOffset(offset, byteLength, this.length) - - var i = byteLength - var mul = 1 - var val = this[offset + --i] - while (i > 0 && (mul *= 0x100)) { - val += this[offset + --i] * mul - } - mul *= 0x80 - - if (val >= mul) val -= Math.pow(2, 8 * byteLength) - - return val -} - -Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 1, this.length) - if (!(this[offset] & 0x80)) return (this[offset]) - return ((0xff - this[offset] + 1) * -1) -} - -Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 2, this.length) - var val = this[offset] | (this[offset + 1] << 8) - return (val & 0x8000) ? val | 0xFFFF0000 : val -} - -Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 2, this.length) - var val = this[offset + 1] | (this[offset] << 8) - return (val & 0x8000) ? val | 0xFFFF0000 : val -} - -Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - - return (this[offset]) | - (this[offset + 1] << 8) | - (this[offset + 2] << 16) | - (this[offset + 3] << 24) -} - -Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - - return (this[offset] << 24) | - (this[offset + 1] << 16) | - (this[offset + 2] << 8) | - (this[offset + 3]) -} - -Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - return ieee754.read(this, offset, true, 23, 4) -} - -Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - return ieee754.read(this, offset, false, 23, 4) -} - -Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 8, this.length) - return ieee754.read(this, offset, true, 52, 8) -} - -Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 8, this.length) - return ieee754.read(this, offset, false, 52, 8) -} - -function checkInt (buf, value, offset, ext, max, min) { - if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance') - if (value > max || value < min) throw new RangeError('"value" argument is out of bounds') - if (offset + ext > buf.length) throw new RangeError('Index out of range') -} - -Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { - value = +value - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) { - var maxBytes = Math.pow(2, 8 * byteLength) - 1 - checkInt(this, value, offset, byteLength, maxBytes, 0) - } - - var mul = 1 - var i = 0 - this[offset] = value & 0xFF - while (++i < byteLength && (mul *= 0x100)) { - this[offset + i] = (value / mul) & 0xFF - } - - return offset + byteLength -} - -Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) { - value = +value - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) { - var maxBytes = Math.pow(2, 8 * byteLength) - 1 - checkInt(this, value, offset, byteLength, maxBytes, 0) - } - - var i = byteLength - 1 - var mul = 1 - this[offset + i] = value & 0xFF - while (--i >= 0 && (mul *= 0x100)) { - this[offset + i] = (value / mul) & 0xFF - } - - return offset + byteLength -} - -Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) - this[offset] = (value & 0xff) - return offset + 1 -} - -Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) - this[offset] = (value & 0xff) - this[offset + 1] = (value >>> 8) - return offset + 2 -} - -Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) - this[offset] = (value >>> 8) - this[offset + 1] = (value & 0xff) - return offset + 2 -} - -Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) - this[offset + 3] = (value >>> 24) - this[offset + 2] = (value >>> 16) - this[offset + 1] = (value >>> 8) - this[offset] = (value & 0xff) - return offset + 4 -} - -Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) - this[offset] = (value >>> 24) - this[offset + 1] = (value >>> 16) - this[offset + 2] = (value >>> 8) - this[offset + 3] = (value & 0xff) - return offset + 4 -} - -Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) { - var limit = Math.pow(2, (8 * byteLength) - 1) - - checkInt(this, value, offset, byteLength, limit - 1, -limit) - } - - var i = 0 - var mul = 1 - var sub = 0 - this[offset] = value & 0xFF - while (++i < byteLength && (mul *= 0x100)) { - if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) { - sub = 1 - } - this[offset + i] = ((value / mul) >> 0) - sub & 0xFF - } - - return offset + byteLength -} - -Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) { - var limit = Math.pow(2, (8 * byteLength) - 1) - - checkInt(this, value, offset, byteLength, limit - 1, -limit) - } - - var i = byteLength - 1 - var mul = 1 - var sub = 0 - this[offset + i] = value & 0xFF - while (--i >= 0 && (mul *= 0x100)) { - if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) { - sub = 1 - } - this[offset + i] = ((value / mul) >> 0) - sub & 0xFF - } - - return offset + byteLength -} - -Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) - if (value < 0) value = 0xff + value + 1 - this[offset] = (value & 0xff) - return offset + 1 -} - -Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) - this[offset] = (value & 0xff) - this[offset + 1] = (value >>> 8) - return offset + 2 -} - -Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) - this[offset] = (value >>> 8) - this[offset + 1] = (value & 0xff) - return offset + 2 -} - -Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) - this[offset] = (value & 0xff) - this[offset + 1] = (value >>> 8) - this[offset + 2] = (value >>> 16) - this[offset + 3] = (value >>> 24) - return offset + 4 -} - -Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) - if (value < 0) value = 0xffffffff + value + 1 - this[offset] = (value >>> 24) - this[offset + 1] = (value >>> 16) - this[offset + 2] = (value >>> 8) - this[offset + 3] = (value & 0xff) - return offset + 4 -} - -function checkIEEE754 (buf, value, offset, ext, max, min) { - if (offset + ext > buf.length) throw new RangeError('Index out of range') - if (offset < 0) throw new RangeError('Index out of range') -} - -function writeFloat (buf, value, offset, littleEndian, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) { - checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) - } - ieee754.write(buf, value, offset, littleEndian, 23, 4) - return offset + 4 -} - -Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) { - return writeFloat(this, value, offset, true, noAssert) -} - -Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) { - return writeFloat(this, value, offset, false, noAssert) -} - -function writeDouble (buf, value, offset, littleEndian, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) { - checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) - } - ieee754.write(buf, value, offset, littleEndian, 52, 8) - return offset + 8 -} - -Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) { - return writeDouble(this, value, offset, true, noAssert) -} - -Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) { - return writeDouble(this, value, offset, false, noAssert) -} - -// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) -Buffer.prototype.copy = function copy (target, targetStart, start, end) { - if (!Buffer.isBuffer(target)) throw new TypeError('argument should be a Buffer') - if (!start) start = 0 - if (!end && end !== 0) end = this.length - if (targetStart >= target.length) targetStart = target.length - if (!targetStart) targetStart = 0 - if (end > 0 && end < start) end = start - - // Copy 0 bytes; we're done - if (end === start) return 0 - if (target.length === 0 || this.length === 0) return 0 - - // Fatal error conditions - if (targetStart < 0) { - throw new RangeError('targetStart out of bounds') - } - if (start < 0 || start >= this.length) throw new RangeError('Index out of range') - if (end < 0) throw new RangeError('sourceEnd out of bounds') - - // Are we oob? - if (end > this.length) end = this.length - if (target.length - targetStart < end - start) { - end = target.length - targetStart + start - } - - var len = end - start - - if (this === target && typeof Uint8Array.prototype.copyWithin === 'function') { - // Use built-in when available, missing from IE11 - this.copyWithin(targetStart, start, end) - } else if (this === target && start < targetStart && targetStart < end) { - // descending copy from end - for (var i = len - 1; i >= 0; --i) { - target[i + targetStart] = this[i + start] - } - } else { - Uint8Array.prototype.set.call( - target, - this.subarray(start, end), - targetStart - ) - } - - return len -} - -// Usage: -// buffer.fill(number[, offset[, end]]) -// buffer.fill(buffer[, offset[, end]]) -// buffer.fill(string[, offset[, end]][, encoding]) -Buffer.prototype.fill = function fill (val, start, end, encoding) { - // Handle string cases: - if (typeof val === 'string') { - if (typeof start === 'string') { - encoding = start - start = 0 - end = this.length - } else if (typeof end === 'string') { - encoding = end - end = this.length - } - if (encoding !== undefined && typeof encoding !== 'string') { - throw new TypeError('encoding must be a string') - } - if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) { - throw new TypeError('Unknown encoding: ' + encoding) - } - if (val.length === 1) { - var code = val.charCodeAt(0) - if ((encoding === 'utf8' && code < 128) || - encoding === 'latin1') { - // Fast path: If `val` fits into a single byte, use that numeric value. - val = code - } - } - } else if (typeof val === 'number') { - val = val & 255 - } - - // Invalid ranges are not set to a default, so can range check early. - if (start < 0 || this.length < start || this.length < end) { - throw new RangeError('Out of range index') - } - - if (end <= start) { - return this - } - - start = start >>> 0 - end = end === undefined ? this.length : end >>> 0 - - if (!val) val = 0 - - var i - if (typeof val === 'number') { - for (i = start; i < end; ++i) { - this[i] = val - } - } else { - var bytes = Buffer.isBuffer(val) - ? val - : Buffer.from(val, encoding) - var len = bytes.length - if (len === 0) { - throw new TypeError('The value "' + val + - '" is invalid for argument "value"') - } - for (i = 0; i < end - start; ++i) { - this[i + start] = bytes[i % len] - } - } - - return this -} - -// HELPER FUNCTIONS -// ================ - -var INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g - -function base64clean (str) { - // Node takes equal signs as end of the Base64 encoding - str = str.split('=')[0] - // Node strips out invalid characters like \n and \t from the string, base64-js does not - str = str.trim().replace(INVALID_BASE64_RE, '') - // Node converts strings with length < 2 to '' - if (str.length < 2) return '' - // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not - while (str.length % 4 !== 0) { - str = str + '=' - } - return str -} - -function toHex (n) { - if (n < 16) return '0' + n.toString(16) - return n.toString(16) -} - -function utf8ToBytes (string, units) { - units = units || Infinity - var codePoint - var length = string.length - var leadSurrogate = null - var bytes = [] - - for (var i = 0; i < length; ++i) { - codePoint = string.charCodeAt(i) - - // is surrogate component - if (codePoint > 0xD7FF && codePoint < 0xE000) { - // last char was a lead - if (!leadSurrogate) { - // no lead yet - if (codePoint > 0xDBFF) { - // unexpected trail - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - continue - } else if (i + 1 === length) { - // unpaired lead - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - continue - } - - // valid lead - leadSurrogate = codePoint - - continue - } - - // 2 leads in a row - if (codePoint < 0xDC00) { - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - leadSurrogate = codePoint - continue - } - - // valid surrogate pair - codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000 - } else if (leadSurrogate) { - // valid bmp char, but last char was a lead - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - } - - leadSurrogate = null - - // encode utf8 - if (codePoint < 0x80) { - if ((units -= 1) < 0) break - bytes.push(codePoint) - } else if (codePoint < 0x800) { - if ((units -= 2) < 0) break - bytes.push( - codePoint >> 0x6 | 0xC0, - codePoint & 0x3F | 0x80 - ) - } else if (codePoint < 0x10000) { - if ((units -= 3) < 0) break - bytes.push( - codePoint >> 0xC | 0xE0, - codePoint >> 0x6 & 0x3F | 0x80, - codePoint & 0x3F | 0x80 - ) - } else if (codePoint < 0x110000) { - if ((units -= 4) < 0) break - bytes.push( - codePoint >> 0x12 | 0xF0, - codePoint >> 0xC & 0x3F | 0x80, - codePoint >> 0x6 & 0x3F | 0x80, - codePoint & 0x3F | 0x80 - ) - } else { - throw new Error('Invalid code point') - } - } - - return bytes -} - -function asciiToBytes (str) { - var byteArray = [] - for (var i = 0; i < str.length; ++i) { - // Node's code seems to be doing this and not & 0x7F.. - byteArray.push(str.charCodeAt(i) & 0xFF) - } - return byteArray -} - -function utf16leToBytes (str, units) { - var c, hi, lo - var byteArray = [] - for (var i = 0; i < str.length; ++i) { - if ((units -= 2) < 0) break - - c = str.charCodeAt(i) - hi = c >> 8 - lo = c % 256 - byteArray.push(lo) - byteArray.push(hi) - } - - return byteArray -} - -function base64ToBytes (str) { - return base64.toByteArray(base64clean(str)) -} - -function blitBuffer (src, dst, offset, length) { - for (var i = 0; i < length; ++i) { - if ((i + offset >= dst.length) || (i >= src.length)) break - dst[i + offset] = src[i] - } - return i -} - -// ArrayBuffer or Uint8Array objects from other contexts (i.e. iframes) do not pass -// the `instanceof` check but they should be treated as of that type. -// See: https://github.com/feross/buffer/issues/166 -function isInstance (obj, type) { - return obj instanceof type || - (obj != null && obj.constructor != null && obj.constructor.name != null && - obj.constructor.name === type.name) -} -function numberIsNaN (obj) { - // For IE11 support - return obj !== obj // eslint-disable-line no-self-compare -} - -},{"base64-js":6,"ieee754":8}],8:[function(require,module,exports){ -exports.read = function (buffer, offset, isLE, mLen, nBytes) { - var e, m - var eLen = (nBytes * 8) - mLen - 1 - var eMax = (1 << eLen) - 1 - var eBias = eMax >> 1 - var nBits = -7 - var i = isLE ? (nBytes - 1) : 0 - var d = isLE ? -1 : 1 - var s = buffer[offset + i] - - i += d - - e = s & ((1 << (-nBits)) - 1) - s >>= (-nBits) - nBits += eLen - for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {} - - m = e & ((1 << (-nBits)) - 1) - e >>= (-nBits) - nBits += mLen - for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {} - - if (e === 0) { - e = 1 - eBias - } else if (e === eMax) { - return m ? NaN : ((s ? -1 : 1) * Infinity) - } else { - m = m + Math.pow(2, mLen) - e = e - eBias - } - return (s ? -1 : 1) * m * Math.pow(2, e - mLen) -} - -exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { - var e, m, c - var eLen = (nBytes * 8) - mLen - 1 - var eMax = (1 << eLen) - 1 - var eBias = eMax >> 1 - var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0) - var i = isLE ? 0 : (nBytes - 1) - var d = isLE ? 1 : -1 - var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 - - value = Math.abs(value) - - if (isNaN(value) || value === Infinity) { - m = isNaN(value) ? 1 : 0 - e = eMax - } else { - e = Math.floor(Math.log(value) / Math.LN2) - if (value * (c = Math.pow(2, -e)) < 1) { - e-- - c *= 2 - } - if (e + eBias >= 1) { - value += rt / c - } else { - value += rt * Math.pow(2, 1 - eBias) - } - if (value * c >= 2) { - e++ - c /= 2 - } - - if (e + eBias >= eMax) { - m = 0 - e = eMax - } else if (e + eBias >= 1) { - m = ((value * c) - 1) * Math.pow(2, mLen) - e = e + eBias - } else { - m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) - e = 0 - } - } - - for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} - - e = (e << mLen) | m - eLen += mLen - for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} - - buffer[offset + i - d] |= s * 128 -} - -},{}]},{},[4]); diff --git a/frontend/static/js/bootstrap.min.js b/frontend/static/js/bootstrap.min.js deleted file mode 120000 index 47696515..00000000 --- a/frontend/static/js/bootstrap.min.js +++ /dev/null @@ -1 +0,0 @@ -../../../admin/static/js/bootstrap.min.js \ No newline at end of file diff --git a/frontend/static/js/challenge.js b/frontend/static/js/challenge.js deleted file mode 100644 index 2a012242..00000000 --- a/frontend/static/js/challenge.js +++ /dev/null @@ -1,887 +0,0 @@ -angular.module("FICApp", ["ngRoute", "ngSanitize"]) - .config(function($routeProvider, $locationProvider) { - $routeProvider - .when("/rules", { - controller: "HomeController", - templateUrl: "views/rules.html" - }) - .when("/edit", { - controller: "MyTeamController", - templateUrl: "views/team-edit.html" - }) - .when("/issue/:eid", { - controller: "IssueController", - templateUrl: "views/issue.html" - }) - .when("/issues", { - controller: "IssueController", - templateUrl: "views/issue.html" - }) - .when("/issues/:iid", { - controller: "IssueController", - templateUrl: "views/issue.html" - }) - .when("/rank", { - controller: "RankController", - templateUrl: "views/rank.html" - }) - .when("/register", { - controller: "RegisterController", - templateUrl: "views/register.html" - }) - .when("/videos", { - controller: "VideosController", - templateUrl: "views/videos.html" - }) - .when("/tags/:tag", { - controller: "TagController", - templateUrl: "views/tag.html" - }) - .when("/:theme", { - controller: "ExerciceController", - templateUrl: "views/theme.html" - }) - .when("/:theme/:exercice", { - controller: "ExerciceController", - templateUrl: function(e) { - if (e.theme == "BlueMoney" && e.exercice == "JackSpearrow") - return "views/defi-SE.html" - else - return "views/defi.html"; - } - }) - .when("/", { - controller: "HomeController", - templateUrl: "views/home.html" - }) - .otherwise({ - redirectTo: "/" - }); - $locationProvider.html5Mode(true); - }) - .run(function($rootScope) { - $rootScope.current_theme = 0; - $rootScope.current_exercice = 0; - $rootScope.current_tag = undefined; - $rootScope.notify_field = 0; - $rootScope.issues_known_responses = 0; - - if ('Notification' in window) - Notification.requestPermission(function(result) { - if (result == "granted") { - if (localStorage && localStorage.notification) - $rootScope.notify_field = localStorage.notification; - else - $rootScope.notify_field = 63; - } - else - $rootScope.notify_field = 0; - }); - }) - .controller("DataController", function($sce, $scope, $http, $rootScope, $interval, $location) { - var actMenu = function() { - if ($scope.my && $scope.themes) { - var tags = {}; - angular.forEach($scope.themes, function(theme, key) { - $scope.themes[key].exercice_solved = 0; - var last_exercice = null; - angular.forEach(theme.exercices, function(exercice, k) { - if (last_exercice != null) - last_exercice.next = k; - last_exercice = exercice; - - if ($scope.my.exercices && $scope.my.exercices[k] && $scope.my.exercices[k].solved) { - $scope.themes[key].exercice_solved++; - } - angular.forEach(exercice.tags, function(tag) { - if (!tags[tag]) - tags[tag] = {count: 1, solved: 0}; - else - tags[tag].count += 1; - if ($scope.my.exercices && $scope.my.exercices[k] && $scope.my.exercices[k].solved) - tags[tag].solved += 1; - }); - }); - }); - $scope.tags = tags; - $scope.tagsl = Object.keys(tags).sort(); - } - } - - var refreshSettingsInterval - var refreshSettings = function() { - if (refreshSettingsInterval) - $interval.cancel(refreshSettingsInterval); - refreshSettingsInterval = $interval(refreshSettings, Math.floor(Math.random() * 24000) + 32000); - - $http.get("settings.json").then(function(response) { - var time = $rootScope.recvTime(response); - if (response.data.start) - response.data.start = new Date(response.data.start); - if (response.data.end) - response.data.end = new Date(response.data.end); - if (response.data.generation) - response.data.generation = new Date(response.data.generation); - if (response.data.activateTime) - response.data.activateTime = new Date(response.data.activateTime); - if ($rootScope.settings === undefined || response.data.activateTime === undefined || response.data.activateTime <= new Date(Date.now() + (time.cu - time.he))) { - $rootScope.settings = response.data; - if (response.data.eventKindness && refreshEventsInterval) { - $interval.cancel(refreshEventsInterval); - refreshEventsInterval = null; - } - else if (!response.data.eventKindness && !refreshEventsInterval) { - refreshEvents(); - } - } - else - $rootScope.settings.activateTime = response.data.activateTime; - }); - } - - var refreshIssuesInterval - var refreshIssues = function() { - if (refreshIssuesInterval) - $interval.cancel(refreshIssuesInterval); - refreshIssuesInterval = $interval(refreshIssues, Math.floor(Math.random() * 24000) + 32000); - - $http.get("issues.json").then(function(response) { - $rootScope.issues_idx = {}; - $rootScope.issues_nb_responses = 0; - $rootScope.issues_need_info = 0; - $rootScope.issues = response.data; - $rootScope.issues.forEach(function(issue) { - $rootScope.issues_idx[issue.id] = issue; - $rootScope.issues_nb_responses += issue.texts.length; - if (issue.state == 'need-info') $rootScope.issues_need_info++; - }) - }, function(error) {}); - } - - var refreshThemesInterval - var refreshThemes = function() { - if (refreshThemesInterval) - $interval.cancel(refreshThemesInterval); - refreshThemesInterval = $interval(refreshThemes, Math.floor(Math.random() * 24000) + 32000); - - $http.get("themes.json").then(function(response) { - $scope.themes = response.data; - $scope.max_gain = 0; - $scope.max_solved = 0; - $scope.themesUrl = {}; - $scope.exercicesUrl = {}; - angular.forEach(response.data, function(theme, key) { - $scope.themesUrl[theme.urlid] = key; - this[key].exercice_count = Object.keys(theme.exercices).length; - this[key].exercice_coeff_max = 0; - this[key].gain = 0; - this[key].solved = 0; - angular.forEach(theme.exercices, function(ex, k) { - $scope.exercicesUrl[theme.urlid + "/" + ex.urlid] = k; - this.gain += ex.gain; - this.solved += ex.solved; - this.exercice_coeff_max = Math.max(this.exercice_coeff_max, ex.curcoeff); - }, theme); - $scope.max_gain += theme.gain; - $scope.max_solved = Math.max($scope.max_solved, theme.solved); - }, response.data); - actMenu(); - }); - } - - var refreshTeamsInterval; - $rootScope.refreshTeams = function() { - if (refreshTeamsInterval) - $interval.cancel(refreshTeamsInterval); - refreshTeamsInterval = $interval($rootScope.refreshTeams, Math.floor(Math.random() * 24000) + 32000); - - $http.get("teams.json").then(function(response) { - 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; - this.push(team); - }, $scope.rank); - }); - } - - var refreshEventsInterval; - var eventsLastRefresh; - var eventsLastTreated; - var refreshEvents = function() { - if (refreshEventsInterval) - $interval.cancel(refreshEventsInterval); - var refreshRate = 1200; - if ($rootScope.notify_field == 0 && eventsLastTreated) - refreshRate = 30000; - if ($scope.my && !$scope.my.team_id) - return; - refreshEventsInterval = $interval(refreshEvents, Math.floor(Math.random() * refreshRate * 2) + refreshRate); - - if (!eventsLastTreated) { - eventsLastTreated = $rootScope.getSrvTime(); - if (!eventsLastTreated) - return; - } - - $http.get("events.json").then(function(response) { - if (eventsLastRefresh != undefined && eventsLastRefresh == response.headers()["last-modified"]) - return; - eventsLastRefresh = response.headers()["last-modified"]; - - var maxTimeSeen = eventsLastTreated; - for (var i = response.data.length - 1; i >= 0; i--) { - var event = response.data[i]; - - event.time = new Date(event.time); - if (event.time <= eventsLastTreated) - continue; - else if (event.time > maxTimeSeen) - maxTimeSeen = event.time; - - // Determine the kind of event - var kind = 1; - - if (event.txt.match(/<\/strong> qui vient de nous rejoindre/)) { - kind = 64; - } else { - var res = event.txt.match(/(\w+) le \d+e<\/sup><\/strong> défi [^&]+/) - if (res) { - if (res[1] == "résolu") - kind = 8; - else if (res[1] == "pour") - kind = 16; - else if (res[1] == "tente") - kind = 32; - } - } - if (kind != 1) { - if ($scope.my && $scope.my.name && event.txt.indexOf($scope.my.name) >= 0) - kind |= 2; - else - kind |= 4; - } - - if ((kind & $rootScope.notify_field) == kind) { - var notification = new Notification("Challenge forensic", {body: event.txt.replace(/&#(\d+);/g, function(match, dec) {return String.fromCharCode(dec);}).replace(/(<([^>]+)>)/ig,""), badge: "/img/icon-" + event.kind + ".ico", icon: "/img/icon-" + event.kind + ".ico"}); - notification.onclick = function(ev) { - $location.url("edit"); - }; - setTimeout(notification.close.bind(notification), 4000); - } - }; - - if (maxTimeSeen > eventsLastTreated) - eventsLastTreated = maxTimeSeen; - }); - } - - var refreshMyInterval; - var refreshMy = function() { - if (refreshMyInterval) - $interval.cancel(refreshMyInterval); - refreshMyInterval = $interval(refreshMy, Math.floor(Math.random() * 24000) + 24000); - - $http.get("my.json").then(function(response) { - $rootScope.recvMy(response.data); - }, function(response) { - if (!$scope.my && response.status == 404) { - $location.url("register"); - } - }); - } - $rootScope.recvMy = function(data) { - if (data.team_id == 0) { - angular.forEach(data.exercices, function(exercice, eid) { - angular.forEach(exercice.hints, function(hint, hid) { - if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].hints[hid] && $scope.my.exercices[eid].hints[hid].hidden !== undefined) - data.exercices[eid].hints[hid].hidden = $scope.my.exercices[eid].hints[hid].hidden; - else - data.exercices[eid].hints[hid].hidden = true; - }); - }); - } - angular.forEach(data.exercices, function(exercice, eid) { - if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].submitted) - data.exercices[eid].timeouted = true; - if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].solved !== undefined) - data.exercices[eid].solved = $scope.my.exercices[eid].solved; - if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].solved_time !== undefined && data.exercices[eid].solved_time === undefined) - data.exercices[eid].solved_time = $scope.my.exercices[eid].solved_time; - if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].tries !== undefined && data.exercices[eid].tries === undefined) - data.exercices[eid].tries = $scope.my.exercices[eid].tries; - angular.forEach(exercice.flags, function(flag, fid) { - if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].flags && $scope.my.exercices[eid].flags[fid] && $scope.my.exercices[eid].flags[fid].found !== undefined) - data.exercices[eid].flags[fid].found = $scope.my.exercices[eid].flags[fid].found; - if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].flags && $scope.my.exercices[eid].flags[fid] && $scope.my.exercices[eid].flags[fid].value !== undefined) - data.exercices[eid].flags[fid].value = $scope.my.exercices[eid].flags[fid].value; - if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].flags && $scope.my.exercices[eid].flags[fid] && $scope.my.exercices[eid].flags[fid].values !== undefined) - data.exercices[eid].flags[fid].values = $scope.my.exercices[eid].flags[fid].values; - else if (data.exercices[eid].flags[fid].nb_lines) - data.exercices[eid].flags[fid].values = Array(data.exercices[eid].flags[fid].nb_lines); - else - data.exercices[eid].flags[fid].values = [""]; - }); - angular.forEach(exercice.mcqs, function(mcq, mid) { - if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].mcqs && $scope.my.exercices[eid].mcqs[mid] && $scope.my.exercices[eid].mcqs[mid].solved !== undefined) - data.exercices[eid].mcqs[mid].solved = $scope.my.exercices[eid].mcqs[mid].solved; - }); - }); - angular.forEach(data.exercices, function(exercice, eid) { - angular.forEach(exercice.mcqs, function(mcq, mid) { - angular.forEach(mcq.choices, function(choice, cid) { - if (!(choice instanceof Object)) - this[cid] = { - label: choice, - }; - - this[cid].disabled = mcq.solved || mcq.part_solved || (this[cid].justification && this[cid].justification.solved); - - if (!this[cid].disabled) - this[cid].value = $scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].mcqs && $scope.my.exercices[eid].mcqs[mid] && $scope.my.exercices[eid].mcqs[mid].choices[cid] && $scope.my.exercices[eid].mcqs[mid].choices[cid].value - - if (mcq.justify) { - if (!this[cid].justification) - this[cid].justification = {}; - - if (!this[cid].justification.label) { - this[cid].justification.label = "Flag correspondant"; - this[cid].justification.help = "Trouvez et validez les choix du QCM pour avoir des indications supplémentaires"; - } - - if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].mcqs[mid] && $scope.my.exercices[eid].mcqs[mid].choices[cid] && $scope.my.exercices[eid].mcqs[mid].choices[cid].justification) { - if ($scope.my.exercices[eid].mcqs[mid].choices[cid].justification.value !== undefined) - data.exercices[eid].mcqs[mid].choices[cid].justification.value = $scope.my.exercices[eid].mcqs[mid].choices[cid].justification.value; - if ($scope.my.exercices[eid].mcqs[mid].choices[cid].justification.values !== undefined) - data.exercices[eid].mcqs[mid].choices[cid].justification.values = $scope.my.exercices[eid].mcqs[mid].choices[cid].justification.values; - else - data.exercices[eid].mcqs[mid].choices[cid].justification.values = [""]; - } - else - data.exercices[eid].mcqs[mid].choices[cid].justification.values = [""]; - } - }, mcq.choices); - }); - }); - $scope.my = data; - angular.forEach($scope.my.exercices, function(exercice, eid) { - exercice.solved = exercice.solved_rank > 0; - if (exercice.video_uri) { - exercice.video_uri = $sce.trustAsResourceUrl(exercice.video_uri); - } - }); - actMenu(); - } - - $rootScope.refresh = function(justMy, justSettings) { - if (!justMy) { - refreshSettings(); - refreshThemes(); - $rootScope.refreshTeams(); - refreshEvents(); - refreshIssues(); - } - else if (justSettings) { - refreshSettings(); - } - refreshMy(); - } - $rootScope.refreshIssues = refreshIssues; - $rootScope.refresh(); - }) - .controller("ExerciceController", function($scope, $routeParams, $http, $rootScope, $timeout) { - if (window.scrollY > 112) - window.scrollTo(window.scrollX, 112); - - $rootScope.current_tag = undefined; - - $scope.$watch("themesUrl", function(themesUrl) { - if (themesUrl != undefined) - $rootScope.current_theme = themesUrl[$routeParams.theme]; - }) - - if ($routeParams.exercice) { - $scope.$watch("exercicesUrl", function(exercicesUrl) { - if (exercicesUrl != undefined) - $rootScope.current_exercice = exercicesUrl[$routeParams.theme + "/" + $routeParams.exercice]; - }) - $scope.$watchGroup(["themes", "current_theme", "current_exercice"], function(newValues) { - var themes = newValues[0]; - var current_theme = newValues[1]; - var current_exercice = newValues[2]; - if (themes != undefined && themes[current_theme] != undefined && themes[current_theme].exercices != undefined && themes[current_theme].exercices[current_exercice] != undefined) - $rootScope.title = themes[current_theme].name + " > " + themes[current_theme].exercices[current_exercice].title; - }); - } else { - $rootScope.current_exercice = 0; - $scope.$watchGroup(["themes", "current_theme"], function(newValues) { - var themes = newValues[0]; - var current_theme = newValues[1]; - if (themes != undefined && themes[current_theme] != undefined) - $rootScope.title = themes[current_theme].name; - }); - } - - var cbh; - $scope.hsubmit = function(hint) { - hint.submitted = true; - $scope.hinterror = null; - $http({ url: "openhint/" + $rootScope.current_exercice, method: "POST", data: { id: hint.id } }).then(function(response) { - var checkDiffHint = function() { - $http.get("my.json").then(function(response) { - var my = response.data; - angular.forEach(my.exercices[$rootScope.current_exercice].hints, function(h,hid){ - if (hint.id == h.id) { - if (hint.content != h.content) { - $rootScope.recvMy(my); - } else { - if (cbh) - $timeout.cancel(cbh); - cbh = $timeout(checkDiffHint, 750); - } - } - }); - }); - }; - checkDiffHint(); - }, function(response) { - $scope.hinterror = response.data.errmsg; - hint.submitted = false; - }); - }; - }) - .controller("SubmissionController", function($scope, $http, $rootScope, $timeout) { - $scope.sberr = null; - - var cbs; - var cbd; - - $scope.ssubmit = function() { - var resp = {} - var check = undefined - $scope.sberr = null; - - if ($scope.my.exercices[$rootScope.current_exercice].flags && Object.keys($scope.my.exercices[$rootScope.current_exercice].flags).length) - { - resp["flags"] = {}; - angular.forEach($scope.my.exercices[$rootScope.current_exercice].flags, function(flag,kid) { - if (check === undefined) check = true; - - check &= treatFlagKey(flag) || flag.found; - if (flag.soluce === undefined) { - check = undefined; - if (flag.found == null) { - resp["flags"][kid] = flag.value; - } - } - }); - } - - if ($scope.my.exercices[$rootScope.current_exercice].mcqs && Object.keys($scope.my.exercices[$rootScope.current_exercice].mcqs).length) - { - resp["mcqs"] = {}; - angular.forEach($scope.my.exercices[$rootScope.current_exercice].mcqs, function(mcq) { - var soluce = ""; - if (mcq.solved == null) { - angular.forEach(mcq.choices, function(choice, cid) { - if (mcq.soluce !== undefined) { - if (check === undefined) check = true; - - soluce += choice.value ? "t" : "f"; - } else { - if (choice.value) { - resp["mcqs"][cid] = choice.value; - if (choice.justification !== undefined) { - if (resp["justifications"] == undefined) - resp["justifications"] = {}; - treatFlagKey(choice.justification); - resp["justifications"][cid] = choice.justification.value; - } - } - } - }); - if (mcq.soluce !== undefined) { - if (mcq.soluce == b2sum(soluce)) - mcq.solved = new Date(); - check &= mcq.solved; - } - } - }); - } - - if (check !== undefined) - { - if (!$scope.my.exercices[$rootScope.current_exercice].tries) - $scope.my.exercices[$rootScope.current_exercice].tries = 0; - $scope.my.exercices[$rootScope.current_exercice].tries += 1; - $scope.my.exercices[$rootScope.current_exercice].solved_time = new Date(); - - if (check) { - $scope.my.exercices[$rootScope.current_exercice].solved = true; - } - return; - } - - $http({ url: "submit/" + $rootScope.current_exercice, method: "POST", data: resp }).then(function(response) { - $scope.messageClass = {"text-success": true}; - $scope.message = response.data.errmsg; - $scope.sberr = null; - - angular.forEach($scope.flags, function(flag,kid) { - flag.value = ""; - }); - - var checkDiff = function() { - $http.get("my.json").then(function(response) { - var my = response.data; - if ($scope.my.exercices[$rootScope.current_exercice].tries != my.exercices[$rootScope.current_exercice].tries || $scope.my.exercices[$rootScope.current_exercice].solved_time != my.exercices[$rootScope.current_exercice].solved_time) { - $scope.my.exercices[$rootScope.current_exercice].submitted = false; - $rootScope.recvMy(my); - $rootScope.refreshTeams(); - } else { - if (cbd) - $timeout.cancel(cbd); - cbd = $timeout(checkDiff, 750); - } - }); - }; - checkDiff(); - }, function(response) { - if (response.status >= 500) { - $scope.my.exercices[$rootScope.current_exercice].submitted = false; - } - $scope.messageClass = {"text-danger": true}; - $scope.sberr = "Oups !"; - $scope.message = response.data.errmsg; - }); - $scope.my.exercices[$rootScope.current_exercice].timeouted = false; - $scope.my.exercices[$rootScope.current_exercice].submitted = true; - }; - - $scope.wantchoices = function(kid) { - $scope.my.exercices[$rootScope.current_exercice].flags[kid].wcsubmitted = true; - $http({ url: "wantchoices/" + $rootScope.current_exercice, method: "POST", data: { id: Math.floor(kid) } }).then(function(response) { - var checkDiffWC = function() { - $http.get("my.json").then(function(response) { - var my = response.data; - if (my.exercices[$rootScope.current_exercice].flags[kid].choices) - $rootScope.recvMy(my); - else { - if (cbd) - $timeout.cancel(cbd); - cbd = $timeout(checkDiffWC, 750); - } - }); - }; - checkDiffWC(); - }, function(response) { - $scope.messageClass = {"text-danger": true}; - $scope.sberr = "Oups !"; - $scope.message = response.data.errmsg; - $scope.my.exercices[$rootScope.current_exercice].flags[kid].wcsubmitted = false; - }); - } - }) - .controller("IssueController", function($scope, $http, $rootScope, $routeParams) { - $rootScope.current_tag = undefined; - $rootScope.current_exercice = $routeParams.eid; - $rootScope.issues_known_responses = $rootScope.issues_nb_responses; - - $scope.issue = { - id: parseInt($routeParams.iid, 10), - id_exercice: parseInt($routeParams.eid, 10), - subject: "", - description: "", - } - - $scope.isubmit = function() { - $rootScope.sberr = ""; - if (!$scope.issue.id && $scope.issue.subject.length < 3) { - $rootScope.messageClass = {"text-danger": true}; - $rootScope.sberr = "L'objet de votre rapport d'anomalie est trop court !"; - return false; - } - - $http({ - url: "submit/issue", - method: "POST", - data: $scope.issue - }).then(function(response) { - $rootScope.messageClass = {"text-success": true}; - $rootScope.message = response.data.errmsg; - $scope.issue.subject = ""; - $scope.issue.description = ""; - setTimeout($rootScope.refreshIssues, 1750); - }, function(response) { - $rootScope.messageClass = {"text-danger": true}; - $rootScope.message = response.data.errmsg; - if (response.status != 402) { - $rootScope.sberr = "Une erreur est survenue lors de l'envoi. Veuillez réessayer dans quelques instants."; - } - }); - }; - }) - .controller("MyTeamController", function($scope, $http, $rootScope, $timeout, $location) { - $rootScope.current_theme = 0; - $rootScope.current_exercice = 0; - $rootScope.current_tag = undefined; - if ($scope.my) { - $rootScope.title = $scope.my.name; - $rootScope.authors = $scope.my.members.map(function (cur) { - return cur.firstname.capitalize() + " " + cur.lastname.capitalize(); - }).join(", "); - } - $rootScope.message = ""; - $rootScope.sberr = ""; - - $scope.notify = { - info: ($rootScope.notify_field & 1) != 1, - team: ($rootScope.notify_field & 2) != 2, - others: ($rootScope.notify_field & 4) != 4, - solve: ($rootScope.notify_field & 8) != 8, - hint: ($rootScope.notify_field & 16) != 16, - tries: ($rootScope.notify_field & 32) != 32, - rename: ($rootScope.notify_field & 64) != 64, - } - - $scope.alternotify = function() { - $rootScope.notify_field = - ($scope.notify.info?0:1) | - ($scope.notify.team?0:2) | - ($scope.notify.others?0:4) | - ($scope.notify.solve?0:8) | - ($scope.notify.hint?0:16) | - ($scope.notify.tries?0:32) | - ($scope.notify.rename?0:64); - if ($rootScope.notify_field != 0) - localStorage.notification = $rootScope.notify_field; - else - delete localStorage.notification; - $location.url("."); - } - - var cbt; - - $scope.tsubmit = function() { - $rootScope.sberr = ""; - if ($scope.my.newName.length < 1) { - $rootScope.messageClass = {"text-danger": true}; - $rootScope.sberr = "Nom d'équipe invalide: pas d'entrée."; - return false; - } - else if ($scope.my.newName.length > 32) { - $rootScope.messageClass = {"text-danger": true}; - $rootScope.sberr = "Nom d'équipe invalide: pas plus de 32 caractères."; - return false; - } - else if (!$scope.my.newName.match(/^[A-Za-z0-9 àéèêëîïôùûü_-]+$/)) { - $rootScope.messageClass = {"text-danger": true}; - $rootScope.sberr = "Nom d'équipe invalide: seuls les caractères alpha-numériques sont autorisés."; - return false; - } - - $http({ - url: "submit/name", - method: "POST", - data: {newName: $scope.my.newName} - }).then(function(response) { - $rootScope.messageClass = {"text-success": true}; - $rootScope.message = response.data.errmsg; - - var checkDiff = function() { - $http.get("my.json").then(function(response) { - if ($scope.my.name != response.data.name) { - $scope.my.newName = undefined; - $rootScope.message = ""; - $rootScope.recvMy(response.data); - $rootScope.refreshTeams(); - } else { - if (cbt) - $timeout.cancel(cbt); - cbt = $timeout(checkDiff, 750); - } - }); - }; - checkDiff(); - }, function(response) { - $rootScope.messageClass = {"text-danger": true}; - $rootScope.message = response.data.errmsg; - if (response.status != 402) { - $rootScope.sberr = "Une erreur est survenue lors de l'envoi. Veuillez réessayer dans quelques instants."; - } - }); - }; - }) - .controller("RegisterController", function($scope, $rootScope, $location, $http, $interval) { - $rootScope.current_theme = 0; - $rootScope.current_exercice = 0; - $rootScope.current_tag = undefined; - $rootScope.title = "Bienvenue au challenge forensic !"; - $rootScope.authors = null; - - $scope.form = {"teamName": "", "jTeam": 0, "members": [{}]}; - - $scope.AddMember = function() { - $scope.form.members.push({}); - } - $scope.RemoveMember = function(k) { - $scope.form.members.splice(k, 1); - } - $scope.Validate = function() { - if ($scope.form.teamName.length <= 3) { - $('#teamName').addClass("is-invalid") - return; - } else { - $('#teamName').removeClass("is-invalid") - $('#vldBtn').removeClass("input-group-btn"); - $('#vldBtn').css("display", "none"); - $('#jvldBtn').css("display", "none"); - $scope.partR = true; - $scope.partJ = false; - } - } - $scope.JValidate = function() { - if (!$scope.teams[$scope.form.jTeam]) { - $('#jTeam').addClass("is-invalid") - return; - } else { - $('#jTeam').removeClass("is-invalid") - $('#jvldBtn').removeClass("input-group-btn"); - $('#jvldBtn').css("display", "none"); - $('#vldBtn').css("display", "none"); - $scope.partR = false; - $scope.partJ = true; - } - } - - var commonsubmit = function(registration) { - // Remove empty members - $scope.form.members = $scope.form.members.filter(function(m) { - return ((m.lastname != undefined && m.lastname != "") || (m.firstname != undefined && m.firstname != "") || (m.nickname != undefined && m.nickname != "")); - }); - - if ($scope.form.members.length == 0) { - $scope.messageClass = {"text-danger": true}; - $scope.message = "Veuillez ajouter au moins un membre dans votre équipe !"; - - $scope.form.members.push({}); - return; - } - - $scope.form.jTeam = parseInt($scope.form.jTeam); - - $http({ - url: "registration", - method: "POST", - data: $scope.form - }).then(function(response) { - $scope.messageClass = {"text-success": true}; - $scope.message = response.data.errmsg; - - $interval(function(){ - $http.get("my.json").then(function(response) { - $rootScope.refresh(); - }); - }, 1500); - - }, function(response) { - $scope.messageClass = {"text-danger": true}; - if (response.data && response.data.errmsg) - $scope.message = response.data.errmsg; - else - $scope.message = "Une erreur est survenue lors de l'inscription de l'équipe. Veuillez réessayer dans quelques instants."; - }); - } - - $scope.rsubmit = function() { - if (!$scope.partR) - return $scope.Validate(); - else - return commonsubmit(true); - } - - $scope.jsubmit = function() { - if (!$scope.partJ) - return $scope.JValidate(); - else - return commonsubmit(false); - } - - $scope.$watch("my", function(my){ - if (my) - $location.url("/"); - }); - }) - .controller("TagController", function($scope, $rootScope, $routeParams, $location) { - $rootScope.current_theme = 0; - $rootScope.current_exercice = 0; - $rootScope.current_tag = $routeParams.tag; - $rootScope.title = "Challenges " + $routeParams.tag; - $rootScope.authors = ""; - - $scope.tag = $routeParams.tag; - $scope.exercices = []; - - $scope.$watch("themes", function(themes) { - $scope.exercices = []; - angular.forEach(themes, function(theme, tid) { - angular.forEach(theme.exercices, function(exercice, eid) { - if (exercice.tags.indexOf($scope.tag) >= 0) - $scope.exercices.push({"exercice": exercice, "theme": theme, "eid": eid, "tid": tid}); - }) - }) - }) - - $scope.goDefi = function() { - $location.url(this.ex.theme.urlid + "/" + this.ex.exercice.urlid); - } - - }) - .controller("RankController", function($scope, $rootScope) { - $rootScope.current_theme = 0; - $rootScope.current_exercice = 0; - $rootScope.current_tag = undefined; - $rootScope.title = "Classement général"; - $rootScope.authors = ""; - - $scope.fields = ["rank", "name", "score"]; - $scope.rankOrder = "rank"; - $scope.reverse = false; - $scope.order = function(fld) { - if ($scope.rankOrder == fld) { - $scope.reverse = !$scope.reverse; - } else { - $scope.rankOrder = fld; - $scope.reverse = (fld == "score"); - } - }; - }) - .controller("VideosController", function($scope, $rootScope) { - $rootScope.current_theme = 0; - $rootScope.current_exercice = 0; - $rootScope.current_tag = undefined; - $rootScope.title = "Vidéos de résolution"; - $rootScope.authors = ""; - }) - .controller("HomeController", function($scope, $rootScope, $location) { - $rootScope.current_theme = 0; - $rootScope.current_exercice = 0; - $rootScope.current_tag = undefined; - $rootScope.title = "Bienvenue au challenge forensic de l'ÉPITA !"; - $rootScope.authors = ""; - - $scope.goTheme = function() { - $location.url(this.theme.urlid); - } - }); - -function sready() { - if ($("#solution").val().length) { - $("#sbmt").removeClass("disabled"); - } else { - $("#sbmt").addClass("disabled"); - } -}; diff --git a/frontend/static/js/common.js b/frontend/static/js/common.js deleted file mode 120000 index c978b8f5..00000000 --- a/frontend/static/js/common.js +++ /dev/null @@ -1 +0,0 @@ -../../../admin/static/js/common.js \ No newline at end of file diff --git a/frontend/static/js/d3.v3.min.js b/frontend/static/js/d3.v3.min.js deleted file mode 120000 index 58337036..00000000 --- a/frontend/static/js/d3.v3.min.js +++ /dev/null @@ -1 +0,0 @@ -../../../admin/static/js/d3.v3.min.js \ No newline at end of file diff --git a/frontend/static/js/i18n b/frontend/static/js/i18n deleted file mode 120000 index 0bb28e47..00000000 --- a/frontend/static/js/i18n +++ /dev/null @@ -1 +0,0 @@ -../../../admin/static/js/i18n/ \ No newline at end of file diff --git a/frontend/static/js/jquery.min.js b/frontend/static/js/jquery.min.js deleted file mode 120000 index 3065bbb1..00000000 --- a/frontend/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/frontend/static/robots.txt b/frontend/static/robots.txt deleted file mode 100644 index 1f53798b..00000000 --- a/frontend/static/robots.txt +++ /dev/null @@ -1,2 +0,0 @@ -User-agent: * -Disallow: / diff --git a/frontend/static/views/defi-SE.html b/frontend/static/views/defi-SE.html deleted file mode 100644 index 62bc3774..00000000 --- a/frontend/static/views/defi-SE.html +++ /dev/null @@ -1,130 +0,0 @@ - - -
- Vous n'avez pas encore accès à cet exercice. -
-
-

-

-
-
- Rapporter une anomalie sur cet exercice -

{{ themes[current_theme].exercices[current_exercice].title }}

- #{{ tag }} -

-
-
-

-

- - - - b2sum : {{ hint.content }} - Débloquer cet indice vous fera perdre . - - -

-
-
-
-
-
    -
  • Gain : {{ 1 + settings.firstBlood | coeff }} prem's {{ themes[current_theme].exercices[current_exercice].curcoeff * settings.exerciceCurrentCoefficient | coeff }} bonus
  • -
  • Tenté par : (cumulant )
  • -
  • Résolu par :
  • -
-
- -
-
-
-
- NSEC – Réinitialisation de mot de passe -
-
    -
  • . Dernière tentative envoyée à {{ my.exercices[current_exercice].solved_time | date:"mediumTime" }}. .
  • -
  • Votre demande de réinitialisation de mot de passe a bien été envoyée !{{ sberr }} {{ message }}
  • -
  • Oops La requête a dépassé le délai d'attente. Vous devriez réessayer dans quelques instant…
  • -
-
- - - -
-

{{ mcq.title }}

-
-
- - - - - -
-
-
-
- -
- -
- -
-
- -
-
- NSEC – Réinitialisation de mot de passe -
-
-

-
-

- Vous êtes la {{ my.exercices[current_exercice].solved_rank }} équipe à avoir résolu ce défi à {{ my.exercices[current_exercice].solved_time | date:"mediumTime" }}. Vous avez marqué ! -

-

- Bravo, vous avez résolu ce défi à {{ my.exercices[current_exercice].solved_time | date:"mediumTime" }}. Vous marquez ! -

-
- Se connecter -
-
- -
-
- Solution du défi -
-
-
- -
-
-
- -
-
-
-
diff --git a/frontend/static/views/defi.html b/frontend/static/views/defi.html deleted file mode 100644 index 7ea96a4f..00000000 --- a/frontend/static/views/defi.html +++ /dev/null @@ -1,149 +0,0 @@ - - -
- Vous n'avez pas encore accès à cet exercice. -
-
-

-

-
-
-

{{ themes[current_theme].exercices[current_exercice].title }}

- #{{ tag }} -

-
-
- Rapporter une anomalie sur cet exercice - Voir les éléments QA sur cet exercice -
    -
  • Gain : {{ 1 + settings.firstBlood | coeff }} prem's {{ themes[current_theme].exercices[current_exercice].curcoeff * settings.exerciceCurrentCoefficient | coeff }} bonus
  • -
  • Tenté par : (cumulant )
  • -
  • Résolu par :
  • -
-
- -
- -
-
-
- 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 ! -

-
- -
- - -
- -
-
-
- Faire son rapport -
-
    -
  • . Dernière solution envoyée à {{ my.exercices[current_exercice].solved_time | date:"mediumTime" }}. .
  • -
  • Votre solution a bien été envoyée !{{ sberr }} {{ message }}
  • -
  • Oops La requête a dépassé le délai d'attente. Vous devriez réessayer dans quelques instant…
  • -
-
-
- - -
-

{{ mcq.title }}

-
-
- - - - - -
-
-
-
- -
- -
-
-
-
- -
-
- Défi réussi ! -
-
-

- Vous êtes la {{ my.exercices[current_exercice].solved_rank }} équipe à avoir résolu ce défi à {{ my.exercices[current_exercice].solved_time | date:"mediumTime" }}. Vous avez marqué ! -

-

- Bravo, vous avez résolu ce défi à {{ my.exercices[current_exercice].solved_time | date:"mediumTime" }}. Vous marquez ! -

-
-

-
- Passer au défi suivant -
-
- -
-
- Solution du défi -
-
-
- -
-
-
-
-
diff --git a/frontend/static/views/home.html b/frontend/static/views/home.html deleted file mode 100644 index f3e0b28c..00000000 --- a/frontend/static/views/home.html +++ /dev/null @@ -1,27 +0,0 @@ -
- 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 ! -
-
- Félicitations et , {{ member.firstname | capitalize }} {{ member.lastname | capitalize }} ! vous êtes maintenant connecté à l'espace de votre équipe {{ teams[my.team_id].name }}. Vous pouvez changer ce nom dès maintenant en vous rendant sur la page de votre équipe. -
-
- Les membres de votre équipe ne sont pas encore enregistrés. Passez voir l'équipe serveur pour corriger cela. -
-
- Votre équipe n'est pas encore enregistrée. Rendez-vous sur cette page pour procéder à votre inscription. -
-
- 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. -
- -
-
-
-
-
- {{ theme.name }} -
-

-
-
-
diff --git a/frontend/static/views/issue.html b/frontend/static/views/issue.html deleted file mode 100644 index a7bb9647..00000000 --- a/frontend/static/views/issue.html +++ /dev/null @@ -1,80 +0,0 @@ - -
- - - - - - - - - - - - - - - - - - -
ObjetÉtat / PrioritéGéré parMessages
{{ issue.subject }} (challenge {{ issue.exercice }}){{ issue.state }} / {{ issue.priority }}{{ issue.assignee }} -

- Vous -  à {{ text.date | date:"mediumTime" }} : - {{ text.cnt }} -

-
- -
-
- -
-
Rapporter une anomalie sur un exercice
-
-

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

-
-
- -
-
Rapporter une anomalie sur un exercice
-
Répondre à un rapport d'anomalie
-
-

Votre rapport a bien été envoyé !{{ sberr }} {{ message }}

-
- -
- -
- -
-
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- - -
-
-
diff --git a/frontend/static/views/rank.html b/frontend/static/views/rank.html deleted file mode 100644 index e5218d9f..00000000 --- a/frontend/static/views/rank.html +++ /dev/null @@ -1,22 +0,0 @@ -
-
- -
- - - - - - - - - - - - - -
- {{ field | rankTitle }} - -
{{ team.rank }}{{ team.name }}{{ team.score | number:0 }}
-
diff --git a/frontend/static/views/register.html b/frontend/static/views/register.html deleted file mode 100644 index f8f7c32d..00000000 --- a/frontend/static/views/register.html +++ /dev/null @@ -1,135 +0,0 @@ -
-

- Félicitations ! vous êtes maintenant authentifié auprès de notre - serveur ! -

-
- 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. -
-
- -
-

- Félicitations ! vous êtes maintenant authentifié·e auprès de - notre serveur ! -

-

- 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 : -

-
- -
- -
-
- - - - -
- Veuillez indiquer un nom d'équipe valide. -
-
-
-
- -
-

- Membres d'équipe - -

-

- Chef d'équipe -

-

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

- Félicitations ! vous êtes maintenant authentifié·e auprès de - notre serveur ! -

-

- Si votre équipe est déjà créée, rejoignez-là ! -

-

- 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 : -

-
- -
- -
-
- - - - -
- Veuillez sélectionner une équipe valide. -
-
-
-
- -
-

- Vos informations -

-

-
-
- -
-
- -
-
- -
-
- -
-
- -
- -
-
diff --git a/frontend/static/views/rules.html b/frontend/static/views/rules.html deleted file mode 100644 index 347b9000..00000000 --- a/frontend/static/views/rules.html +++ /dev/null @@ -1,128 +0,0 @@ -
-
-
-

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. -

-
- -

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. -

- -

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
21 à 30
31 à 40
41 à 50
......
-
-
-
-
-

- Par exemple : -

-
    -
  • À 10 tentatives, vous aurez perdu .
  • -
  • À 15 tentatives, vous aurez perdu en tout  : {{ settings.submissionCostBase }} * 5.
  • -
  • 25 tentatives vous coûteront en tout  : {{ settings.submissionCostBase }} * 10 + {{ settings.submissionCostBase * 2}} * 5.
  • -
  • 50 tentatives vous coûteront en tout  : {{ settings.submissionCostBase }} * 10 + {{ settings.submissionCostBase * 2 }} * 10 + {{ settings.submissionCostBase * 3 }} * 10 + {{ settings.submissionCostBase * 4 }} * 10.
  • -
-

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

-
- -

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. -

- -

Prem's

-

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

- -

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/static/views/tag.html b/frontend/static/views/tag.html deleted file mode 100644 index e76d4d6b..00000000 --- a/frontend/static/views/tag.html +++ /dev/null @@ -1,16 +0,0 @@ -
-
-
- -
-
diff --git a/frontend/static/views/team-edit.html b/frontend/static/views/team-edit.html deleted file mode 100644 index 9cbc44d9..00000000 --- a/frontend/static/views/team-edit.html +++ /dev/null @@ -1,103 +0,0 @@ -
-
- -
-
Votre équipe est composée de :
-
- Passez voir l'équipe serveur pour compléter ces informations. -
-
    -
  • - {{ member.firstname | capitalize }} - {{ member.nickname }} - {{ member.lastname | capitalize }} - - {{ member.company}} -
  • -
-
- -
-
Changer de nom d'équipe
-
-

Votre demande a bien été envoyée !{{ sberr }} {{ message }}

-
-
- -
-
- -
- -
-
-
-
-
-
-
- -
-
- -
-
Gestion des notifications
-
-

- Ces paramètres ne seront valable que pour votre navigateur. -

- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- - -
-
- -
-
diff --git a/frontend/static/views/theme.html b/frontend/static/views/theme.html deleted file mode 100644 index 50b0215e..00000000 --- a/frontend/static/views/theme.html +++ /dev/null @@ -1,49 +0,0 @@ -
-
-

-

-
- -
diff --git a/frontend/static/views/videos.html b/frontend/static/views/videos.html deleted file mode 100644 index 32077719..00000000 --- a/frontend/static/views/videos.html +++ /dev/null @@ -1,10 +0,0 @@ -
- -

- {{ theme.name }} -

- - {{ exercice.title }} - -
-
diff --git a/frontend/static/welcome.html b/frontend/static/welcome.html deleted file mode 100644 index 4107e50e..00000000 --- a/frontend/static/welcome.html +++ /dev/null @@ -1,185 +0,0 @@ - - - - - Challenge Forensic - - - - - - - - - - - - -
-
-

Bienvenue !

-

- - Vous n'êtes pas encore connecté en tant qu'équipe sur notre serveur. - -


-

- Bienvenue dans cette première épreuve du challenge forensic ! - Votre première activité consiste à accéder au site dédié à cet - événement ; ce guide est là pour vous y aider. -

-
-

- Important : La clef USB qui vous a été donnée - contient des fichiers permettant votre authentification auprès de - nos serveurs. Ne la laissez pas sans surveillance ! -

-
- -
-
-

Installation du certificat client

-
-
-

- Le certificat client est envoyé à notre serveur pour vous - identifier et vous authentifier. Votre certificat et votre clef - privée sont contenus sur la clef USB que nous vous avons donnée, dans - un fichier .p12, protégé avec le mot de passe qui vous a - été fourni sur papier. -

- -

- Choisissez la procédure correspondant à votre navigateur ou système - d'exploitation : -

-
    -
  1. Mozilla Firefox
  2. -
  3. Chromium/Google Chrome
  4. -
      -
    1. Sous Microsoft Windows
    2. -
    3. Sous Mac OS
    4. -
    5. Sous GNU/Linux, FreeBSD ou OpenBSD
    6. -
    -
  5. Internet Explorer/Edge
  6. -
  7. Safari
  8. -
-
- -

- Si malgré tout, vous n'arrivez pas à accéder à l'espace de votre - équipe ou si votre clef USB est illisible, n'hésitez pas à nous - solliciter ! -

-

- Bon courage ! -

- -
-
- -
-
-

Mozilla Firefox

-
-
-
    -
  1. Ouvrez la fenêtre des préférences du navigateur.
  2. -
  3. Choisissez la catégorie Vie privée et sécurité (Avancé dans les versions plus anciennes).
  4. -
  5. Sélectionnez l'onglet Certificats.
  6. -
  7. Cliquez sur Afficher les certificats.
  8. -
  9. Sélectionnez l'onglet Vos certificats.
  10. -
  11. Cliquez sur Importer… et sélectionnez votre certificat client.
  12. -
- -
-
- -
-
-

Chromium/Google Chrome

-
-
-

Sous Microsoft Windows

-

- Le navigateur utilise les paramètres du système ; suivez les - instructions concernant Internet - Explorer. -

- -

Sous Mac OS

-
    -
  1. Ouvrez le menu des préférences du navigateur.
  2. -
  3. Cliquez sur Afficher les paramètres avancés.
  4. -
  5. Dans la section HTTPS/SSL, cliquez sur Gérer les certificats. Le trousseau d'accès se lance.
  6. -
  7. Dans le menu Fichier, sélectionnez Importer des éléments… et sélectionnez votre certificat client.
  8. -
  9. Choisissez un trousseau.
  10. -
- -

Sous GNU/Linux, FreeBSD ou OpenBSD

-
    -
  1. Ouvrez le menu des préférences du navigateur.
  2. -
  3. Cliquez sur Afficher les paramètres avancés.
  4. -
  5. Dans la section HTTPS/SSL, cliquez sur Gérer les certificats.
  6. -
  7. Sélectionnez l'onglet Vos certificats.
  8. -
  9. Cliquez sur Importer… et sélectionnez votre certificat client.
  10. -
-
-
- -
-
-

Internet Explorer

-
-
-
    -
  1. Double-cliquez sur le fichier .p12 présent sur votre clef USB. L'assistant d'importation du certificat apparaît.
  2. -
  3. Cliquez sur Suivant.
  4. -
  5. Cliquez sur Suivant.
  6. -
  7. Entrez le mot de passe fourni sur papier puis cliquez sur Suivant.
  8. -
  9. Cliquez sur Suivant (le certificat sera automatiquement placé dans le magasin Personnel).
  10. -
  11. Cliquez sur Terminer.
  12. -
- -

- Selon votre version de Windows, votre système peut ensuite vous - demander de définir un mot de passe pour protéger ce certificat. -

- -

- Microsoft Internet Explorer : Aucune version - de Microsoft Internet Explorer (nom d'« Internet Explorer » - jusqu'à sa version 9 comprise) n'est supportée par notre serveur. -

-
-
- -
-
-

Safari

-
-
-
    -
  1. Double-cliquez sur le fichier .p12 présent sur votre clef USB.
  2. -
  3. Entrez le mot de passe fourni sur papier puis cliquez sur Suivant.
  4. -
-
-
-
- - - diff --git a/frontend/static/welcome.json b/frontend/static/welcome.json deleted file mode 100644 index 15f5719b..00000000 --- a/frontend/static/welcome.json +++ /dev/null @@ -1 +0,0 @@ -{"errmsg": "Vous n'avez pas les droits nécessaires pour effectuer cette action."} diff --git a/htdocs-frontend b/htdocs-frontend deleted file mode 120000 index 4346ea21..00000000 --- a/htdocs-frontend +++ /dev/null @@ -1 +0,0 @@ -frontend/static/ \ No newline at end of file diff --git a/qa/static/css/fic.css b/qa/static/css/fic.css deleted file mode 120000 index 5f8e4077..00000000 --- a/qa/static/css/fic.css +++ /dev/null @@ -1 +0,0 @@ -../../../frontend/static/css/fic.css \ No newline at end of file diff --git a/qa/static/css/fic.css b/qa/static/css/fic.css new file mode 100644 index 00000000..fc12b00d --- /dev/null +++ b/qa/static/css/fic.css @@ -0,0 +1,387 @@ +@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('../img/logo-epita-bw.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/qa/static/css/glyphicon.css b/qa/static/css/glyphicon.css index 6d65cb92..14cd8c56 120000 --- a/qa/static/css/glyphicon.css +++ b/qa/static/css/glyphicon.css @@ -1 +1 @@ -../../../frontend/static/css/glyphicon.css \ No newline at end of file +../../../admin/static/css/glyphicon.css \ No newline at end of file diff --git a/qa/static/favicon.ico b/qa/static/favicon.ico index 060d10ef..b92aceba 120000 --- a/qa/static/favicon.ico +++ b/qa/static/favicon.ico @@ -1 +1 @@ -../../frontend/static/favicon.ico \ No newline at end of file +../../frontend/ui/static/favicon.ico \ No newline at end of file diff --git a/qa/static/fonts b/qa/static/fonts index 5431051e..0ef2f8d8 120000 --- a/qa/static/fonts +++ b/qa/static/fonts @@ -1 +1 @@ -../../frontend/static/fonts/ \ No newline at end of file +../../admin/static/fonts/ \ No newline at end of file diff --git a/qa/static/img/comcyber.png b/qa/static/img/comcyber.png deleted file mode 120000 index 0df83a18..00000000 --- a/qa/static/img/comcyber.png +++ /dev/null @@ -1 +0,0 @@ -../../../frontend/static/img/comcyber.png \ No newline at end of file diff --git a/qa/static/img/epita.png b/qa/static/img/epita.png deleted file mode 120000 index 110ea62c..00000000 --- a/qa/static/img/epita.png +++ /dev/null @@ -1 +0,0 @@ -../../../frontend/static/img/epita.png \ No newline at end of file diff --git a/qa/static/img/fic.png b/qa/static/img/fic.png index 00ba2748..838a2cc9 120000 --- a/qa/static/img/fic.png +++ b/qa/static/img/fic.png @@ -1 +1 @@ -../../../frontend/static/img/fic.png \ No newline at end of file +../../../frontend/ui/static/img/fic.png \ No newline at end of file diff --git a/qa/static/img/srs.png b/qa/static/img/srs.png deleted file mode 120000 index a599744d..00000000 --- a/qa/static/img/srs.png +++ /dev/null @@ -1 +0,0 @@ -../../../frontend/static/img/srs.png \ No newline at end of file diff --git a/qa/static/js/angular-route.min.js b/qa/static/js/angular-route.min.js index 407db24d..ae04a29f 120000 --- a/qa/static/js/angular-route.min.js +++ b/qa/static/js/angular-route.min.js @@ -1 +1 @@ -../../../frontend/static/js/angular-route.min.js \ No newline at end of file +../../../admin/static/js/angular-route.min.js \ No newline at end of file diff --git a/qa/static/js/angular-sanitize.min.js b/qa/static/js/angular-sanitize.min.js index 7e14fe0a..d0410452 120000 --- a/qa/static/js/angular-sanitize.min.js +++ b/qa/static/js/angular-sanitize.min.js @@ -1 +1 @@ -../../../frontend/static/js/angular-sanitize.min.js \ No newline at end of file +../../../admin/static/js/angular-sanitize.min.js \ No newline at end of file diff --git a/qa/static/js/angular.min.js b/qa/static/js/angular.min.js index 86b88964..5eba27d2 120000 --- a/qa/static/js/angular.min.js +++ b/qa/static/js/angular.min.js @@ -1 +1 @@ -../../../frontend/static/js/angular.min.js \ No newline at end of file +../../../admin/static/js/angular.min.js \ No newline at end of file diff --git a/qa/static/js/bootstrap.min.js b/qa/static/js/bootstrap.min.js index d074c9db..47696515 120000 --- a/qa/static/js/bootstrap.min.js +++ b/qa/static/js/bootstrap.min.js @@ -1 +1 @@ -../../../frontend/static/js/bootstrap.min.js \ No newline at end of file +../../../admin/static/js/bootstrap.min.js \ No newline at end of file diff --git a/qa/static/js/common.js b/qa/static/js/common.js index 4e3fce35..c978b8f5 120000 --- a/qa/static/js/common.js +++ b/qa/static/js/common.js @@ -1 +1 @@ -../../../frontend/static/js/common.js \ No newline at end of file +../../../admin/static/js/common.js \ No newline at end of file diff --git a/qa/static/js/d3.v3.min.js b/qa/static/js/d3.v3.min.js index d2407e47..58337036 120000 --- a/qa/static/js/d3.v3.min.js +++ b/qa/static/js/d3.v3.min.js @@ -1 +1 @@ -../../../frontend/static/js/d3.v3.min.js \ No newline at end of file +../../../admin/static/js/d3.v3.min.js \ No newline at end of file diff --git a/qa/static/js/i18n b/qa/static/js/i18n index dab94408..0bb28e47 120000 --- a/qa/static/js/i18n +++ b/qa/static/js/i18n @@ -1 +1 @@ -../../../frontend/static/js/i18n/ \ No newline at end of file +../../../admin/static/js/i18n/ \ No newline at end of file diff --git a/qa/static/js/jquery.min.js b/qa/static/js/jquery.min.js index 022463c1..3065bbb1 120000 --- a/qa/static/js/jquery.min.js +++ b/qa/static/js/jquery.min.js @@ -1 +1 @@ -../../../frontend/static/js/jquery.min.js \ No newline at end of file +../../../admin/static/js/jquery.min.js \ No newline at end of file From 56d8d493041e2a87eded25f33f8f5834650870de Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 9 Sep 2021 18:20:03 +0200 Subject: [PATCH 0367/1637] ui: Prepare ui for public interface --- frontend/ui/package-lock.json | 5 + frontend/ui/package.json | 1 + .../ui/src/components/ExerciceFlags.svelte | 103 +++++++++++++----- .../ui/src/components/ExerciceHints.svelte | 7 +- .../ui/src/routes/[theme]/[exercice].svelte | 4 +- frontend/ui/src/routes/__layout.svelte | 23 +++- frontend/ui/src/stores/my.js | 6 + 7 files changed, 120 insertions(+), 29 deletions(-) diff --git a/frontend/ui/package-lock.json b/frontend/ui/package-lock.json index 7ab00696..aed2c386 100644 --- a/frontend/ui/package-lock.json +++ b/frontend/ui/package-lock.json @@ -707,6 +707,11 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "hash-wasm": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/hash-wasm/-/hash-wasm-4.9.0.tgz", + "integrity": "sha512-7SW7ejyfnRxuOc7ptQHSf4LDoZaWOivfzqw+5rpcQku0nHfmicPKE51ra9BiRLAmT8+gGLestr1XroUkqdjL6w==" + }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", diff --git a/frontend/ui/package.json b/frontend/ui/package.json index 0e8b6d29..019c9eb3 100644 --- a/frontend/ui/package.json +++ b/frontend/ui/package.json @@ -27,6 +27,7 @@ "bootstrap": "^5.1.0", "bootstrap-icons": "^1.5.0", "bootswatch": "^5.1.0", + "hash-wasm": "^4.9.0", "seedrandom": "^3.0.5" } } diff --git a/frontend/ui/src/components/ExerciceFlags.svelte b/frontend/ui/src/components/ExerciceFlags.svelte index 1c5063c9..4ae8026d 100644 --- a/frontend/ui/src/components/ExerciceFlags.svelte +++ b/frontend/ui/src/components/ExerciceFlags.svelte @@ -12,6 +12,10 @@ Spinner, } from 'sveltestrap'; + import { blake2b } from 'hash-wasm'; + + import { my } from '../stores/my.js'; + import DateFormat from './DateFormat.svelte'; import FlagKey from './FlagKey.svelte'; import FlagMCQ from './FlagMCQ.svelte'; @@ -36,42 +40,89 @@ }) } + export let forcesolved = false; let responses = { }; async function submitFlags() { submitInProgress = true; sberr = ""; message = ""; - const response = await fetch( - "submit/" + exercice.id, - { - method: "POST", - headers: {'Accept': 'application/json'}, - body: JSON.stringify(responses), - } - ) + if ($my && $my.team_id === 0) { + let allGoodResponse = true; + for (const f in flags) { + const flag = flags[f]; + + let soluce = ""; + if (flag.type == "mcq") { + for (const c in flag.choices) { + soluce += responses.mcqs[c] ? "t" : "f"; + } + } else { + soluce = responses.flags[flag.id]; + } + + if (flag.ignore_case) { + soluce = soluce.toLowerCase(); + } + + if (flag.validator_regexp) { + let re = new RegExp(flag.validator_regexp, flag.ignore_case?'i':''); + soluce = soluce.match(re).slice(1).join("+"); + } + + if (await blake2b(soluce) == flag.soluce) { + flags[f].found = new Date(); + } else if (!flag.found) { + allGoodResponse = false; + } + flags = flags; + } + + if (allGoodResponse) { + forcesolved = true; + } + + if (exercice.tries) { + exercice.tries += 1; + } else { + exercice.tries = 1; + } + exercice.solved_time = new Date(); + exercice = exercice; - if (response.status < 300) { - const data = await response.json(); - messageClass = 'text-success'; - message = data.errmsg; - waitDiff(20); - } else { submitInProgress = false; + } else { + const response = await fetch( + "submit/" + exercice.id, + { + method: "POST", + headers: {'Accept': 'application/json'}, + body: JSON.stringify(responses), + } + ) - messageClass = 'text-danger'; - - let data = ""; - try { - data = await response.json(); - } catch(e) { - data = null; - } - - if (data && data.errmsg) + if (response.status < 300) { + const data = await response.json(); + messageClass = 'text-success'; message = data.errmsg; - if (response.statys != 402) - sberr = "Oups !"; + waitDiff(20); + } else { + submitInProgress = false; + + messageClass = 'text-danger'; + + let data = ""; + try { + data = await response.json(); + } catch(e) { + data = null; + } + + if (data && data.errmsg) + message = data.errmsg; + if (response.statys != 402) + sberr = "Oups !"; + } } } diff --git a/frontend/ui/src/components/ExerciceHints.svelte b/frontend/ui/src/components/ExerciceHints.svelte index e8a8063d..70b1e0cb 100644 --- a/frontend/ui/src/components/ExerciceHints.svelte +++ b/frontend/ui/src/components/ExerciceHints.svelte @@ -39,6 +39,11 @@ }) } + function showHint(hint) { + hint.hidden = false; + hints = hints; // Force Svelte update + } + async function openHint(hint) { hints_submitted[hint.id] = true; hinterror = ""; @@ -122,7 +127,7 @@ {/if} {#if !hint.file && hint.hidden} - diff --git a/frontend/ui/src/routes/[theme]/[exercice].svelte b/frontend/ui/src/routes/[theme]/[exercice].svelte index 41452c6b..7b8a0cb6 100644 --- a/frontend/ui/src/routes/[theme]/[exercice].svelte +++ b/frontend/ui/src/routes/[theme]/[exercice].svelte @@ -47,6 +47,7 @@ export let theme; export let exercice; + let solved = {}; export let refresh_my; export let refresh_teams; @@ -158,11 +159,12 @@ {/if} - {#if !$my.exercices[exercice.id].solved_rank} + {#if !$my.exercices[exercice.id].solved_rank && !solved[exercice.id]} {:else} diff --git a/frontend/ui/src/routes/__layout.svelte b/frontend/ui/src/routes/__layout.svelte index 438dead2..56aa0ec4 100644 --- a/frontend/ui/src/routes/__layout.svelte +++ b/frontend/ui/src/routes/__layout.svelte @@ -5,6 +5,8 @@ import { themesStore } from '../stores/themes.js'; import { settings, time } from '../stores/settings.js'; + let stop_refresh = false; + let refresh_interval_settings = null; async function refresh_settings(cb=null, interval=null) { if (refresh_interval_settings) @@ -12,6 +14,9 @@ if (interval === null) { interval = Math.floor(Math.random() * 24000) + 32000; } + if (stop_refresh) { + return; + } refresh_interval_settings = setInterval(refresh_settings, interval); if (!cb) { @@ -43,6 +48,9 @@ if (interval === null) { interval = Math.floor(Math.random() * 24000) + 32000; } + if (stop_refresh) { + return; + } refresh_interval_teams = setInterval(refresh_teams, interval); teamsStore.update(await fetch('teams.json', {headers: {'Accept': 'application/json'}}), cb); @@ -55,6 +63,9 @@ if (interval === null) { interval = Math.floor(Math.random() * 24000) + 32000; } + if (stop_refresh) { + return; + } refresh_interval_themes = setInterval(refresh_themes, interval); await themesStore.update(await fetch('themes.json', {headers: {'Accept': 'application/json'}}), cb); @@ -67,6 +78,9 @@ if (interval === null) { interval = Math.floor(Math.random() * 24000) + 24000; } + if (stop_refresh) { + return; + } refresh_interval_my = setInterval(refresh_my, interval); my.update(await fetch('my.json', {headers: {'Accept': 'application/json'}}), cb); @@ -79,6 +93,9 @@ if (interval === null) { interval = Math.floor(Math.random() * 24000) + 32000; } + if (stop_refresh) { + return; + } refresh_interval_issues = setInterval(refresh_issues, interval); issuesStore.update(await fetch('issues.json', {headers: {'Accept': 'application/json'}}), cb); @@ -88,7 +105,11 @@ await refresh_settings(); await refresh_themes(); refresh_teams(); - refresh_my(); + refresh_my((my) => { + if (my && my.team_id === 0) { + stop_refresh = true; + } + }); refresh_issues(); return { diff --git a/frontend/ui/src/stores/my.js b/frontend/ui/src/stores/my.js index 72f4be00..7500cd52 100644 --- a/frontend/ui/src/stores/my.js +++ b/frontend/ui/src/stores/my.js @@ -10,6 +10,12 @@ function createMyStore() { res_my.json().then((my) => { for (let k in my.exercices) { my.exercices[k].id = k; + + 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))); From 2e4513c00fb99399819f3c92fedb593e5f87f24b Mon Sep 17 00:00:00 2001 From: MCharpy Date: Sun, 5 Sep 2021 19:53:58 +0200 Subject: [PATCH 0368/1637] fic: Tag now in Title format Signed-off-by: MCharpy --- libfic/exercice_tag.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libfic/exercice_tag.go b/libfic/exercice_tag.go index 22c4088b..860dd1d4 100644 --- a/libfic/exercice_tag.go +++ b/libfic/exercice_tag.go @@ -1,5 +1,9 @@ package fic +import ( + "strings" +) + // GetTags returns tags associated with this exercice. func (e Exercice) GetTags() (tags []string, err error) { if rows, errr := DBQuery("SELECT tag FROM exercice_tags WHERE id_exercice = ?", e.Id); errr != nil { @@ -22,6 +26,7 @@ func (e Exercice) GetTags() (tags []string, err error) { // AddTag assign a new tag to the exercice and registers it into the database. func (e Exercice) AddTag(tag string) (string, error) { + tag = strings.Title(tag) if _, err := DBExec("INSERT INTO exercice_tags (id_exercice, tag) VALUES (?, ?)", e.Id, tag); err != nil { return "", err } else { From 8d9269c635a0c3675aa01ba1da86587f5b88a71b Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 24 Sep 2021 18:43:55 +0200 Subject: [PATCH 0369/1637] ui: Fix bootstrap 5 embed items --- frontend/ui/src/components/ExerciceVideo.svelte | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/frontend/ui/src/components/ExerciceVideo.svelte b/frontend/ui/src/components/ExerciceVideo.svelte index 5411f8d6..1c9ddc13 100644 --- a/frontend/ui/src/components/ExerciceVideo.svelte +++ b/frontend/ui/src/components/ExerciceVideo.svelte @@ -17,10 +17,8 @@ Solution du défi - -
- - + + From e29802b7317293bcf219a1bab77b510b429c8c92 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 24 Sep 2021 19:05:08 +0200 Subject: [PATCH 0370/1637] WIP Try to built a new htdocs-frontend tarball --- .drone.yml | 2 +- Dockerfile-dashboard | 10 +++++----- Dockerfile-frontend | 2 -- Dockerfile-qa | 10 +++++----- frontend/ui/src/components/ExerciceVideo.svelte | 1 + 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/.drone.yml b/.drone.yml index 6ba90a6d..f9e293d3 100644 --- a/.drone.yml +++ b/.drone.yml @@ -54,7 +54,6 @@ steps: image: golang:alpine commands: - go build -v -o deploy/frontend-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/frontend - - tar chjf deploy/htdocs-frontend.tar.bz2 htdocs-frontend environment: CGO_ENABLED: 0 @@ -66,6 +65,7 @@ steps: - npm install --network-timeout=100000 - sed -i 's!@popperjs/core/dist/esm/popper!@popperjs/core!' node_modules/sveltestrap/src/*.js node_modules/sveltestrap/src/*.svelte - npm run build + - tar chjf ../../deploy/htdocs-frontend.tar.bz2 build - name: build dashboard image: golang:alpine diff --git a/Dockerfile-dashboard b/Dockerfile-dashboard index b1511697..147075ee 100644 --- a/Dockerfile-dashboard +++ b/Dockerfile-dashboard @@ -24,9 +24,9 @@ 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 dashboard/static/css/bootstrap.min.css frontend/static/css/fic.css frontend/static/css/glyphicon.css /srv/htdocs-dashboard/css/ -COPY frontend/static/fonts /srv/htdocs-dashboard/fonts -COPY frontend/static/img/ dashboard/static/img/logo-epita-bw.png dashboard/static/img/sii.png /srv/htdocs-dashboard/img/ -COPY dashboard/static/js/dashboard.js frontend/static/js/angular.min.js dashboard/static/js/angular-animate.min.js frontend/static/js/angular-route.min.js frontend/static/js/angular-sanitize.min.js frontend/static/js/bootstrap.min.js frontend/static/js/common.js frontend/static/js/d3.v3.min.js frontend/static/js/jquery.min.js /srv/htdocs-dashboard/js/ +COPY dashboard/static/index.html frontend/ui/static/favicon.ico /srv/htdocs-dashboard/ +COPY admin/static/css/bootstrap.min.css qa/static/css/fic.css admin/static/css/glyphicon.css /srv/htdocs-dashboard/css/ +COPY admin/static/fonts /srv/htdocs-dashboard/fonts +COPY frontend/ui/static/img/ dashboard/static/img/logo-epita-bw.png dashboard/static/img/sii.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-frontend b/Dockerfile-frontend index 7eb9f23d..5fc196fe 100644 --- a/Dockerfile-frontend +++ b/Dockerfile-frontend @@ -24,6 +24,4 @@ CMD ["--bind=:8080"] COPY entrypoint-frontend.sh /usr/sbin/entrypoint.sh -VOLUME /srv/htdocs-frontend/ - COPY --from=gobuild /go/src/srs.epita.fr/fic-server/frontend/frontend /srv/frontend diff --git a/Dockerfile-qa b/Dockerfile-qa index ec451bb2..728c73b5 100644 --- a/Dockerfile-qa +++ b/Dockerfile-qa @@ -24,9 +24,9 @@ ENTRYPOINT ["/srv/qa", "--bind=:8083"] VOLUME /srv/htdocs-qa/ COPY --from=gobuild /go/src/srs.epita.fr/fic-server/qa/qa /srv/qa -COPY qa/static/index.html /srv/htdocs-qa/ -COPY qa/static/css/bootstrap.min.css frontend/static/css/fic.css frontend/static/css/glyphicon.css /srv/htdocs-qa/css/ -COPY frontend/static/fonts /srv/htdocs-qa/fonts -COPY frontend/static/img/ /srv/htdocs-qa/img/ -COPY qa/static/js/qa.js frontend/static/js/angular.min.js qa/static/js/angular-resource.min.js frontend/static/js/angular-route.min.js frontend/static/js/angular-sanitize.min.js frontend/static/js/bootstrap.min.js frontend/static/js/common.js frontend/static/js/i18n frontend/static/js/jquery.min.js /srv/htdocs-qa/js/ +COPY qa/static/index.html frontend/ui/static/favicon.ico /srv/htdocs-qa/ +COPY admin/static/css/bootstrap.min.css qa/static/css/fic.css admin/static/css/glyphicon.css /srv/htdocs-qa/css/ +COPY admin/static/fonts /srv/htdocs-qa/fonts +COPY frontend/ui/static/img/ /srv/htdocs-qa/img/ +COPY qa/static/js/qa.js admin/static/js/angular.min.js qa/static/js/angular-resource.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/i18n admin/static/js/jquery.min.js /srv/htdocs-qa/js/ COPY qa/static/views/ /srv/htdocs-qa/views/ diff --git a/frontend/ui/src/components/ExerciceVideo.svelte b/frontend/ui/src/components/ExerciceVideo.svelte index 1c9ddc13..a13c20c8 100644 --- a/frontend/ui/src/components/ExerciceVideo.svelte +++ b/frontend/ui/src/components/ExerciceVideo.svelte @@ -21,4 +21,5 @@ + From 744aabf82131bd7fc349556b617f697bb8f49393 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 28 Sep 2021 08:06:56 +0000 Subject: [PATCH 0371/1637] chore(deps): update github.com/studio-b12/gowebdav commit hash to a3a8697 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 3b359410..293c7c6f 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/go-sql-driver/mysql v1.6.0 github.com/julienschmidt/httprouter v1.3.0 github.com/russross/blackfriday/v2 v2.1.0 - github.com/studio-b12/gowebdav v0.0.0-20210630100626-7ff61aa87be8 + github.com/studio-b12/gowebdav v0.0.0-20210917133250-a3a86976a1df golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f diff --git a/go.sum b/go.sum index 188936e1..b37af3ce 100644 --- a/go.sum +++ b/go.sum @@ -119,6 +119,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/studio-b12/gowebdav v0.0.0-20210630100626-7ff61aa87be8 h1:ipNUBPHSUmHhhcLhvqC2vGZsJPzVuJap8rJx3uGAEco= github.com/studio-b12/gowebdav v0.0.0-20210630100626-7ff61aa87be8/go.mod h1:gCcfDlA1Y7GqOaeEKw5l9dOGx1VLdc/HuQSlQAaZ30s= +github.com/studio-b12/gowebdav v0.0.0-20210917133250-a3a86976a1df h1:C+J/LwTqP8gRPt1MdSzBNZP0OYuDm5wsmDKgwpLjYzo= +github.com/studio-b12/gowebdav v0.0.0-20210917133250-a3a86976a1df/go.mod h1:gCcfDlA1Y7GqOaeEKw5l9dOGx1VLdc/HuQSlQAaZ30s= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= From 315fb1efae0601a8f74411adbfa84b9fe9d73068 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 9 Sep 2021 10:08:44 +0000 Subject: [PATCH 0372/1637] chore(deps): update dependency prettier to ~2.4.0 --- frontend/ui/package-lock.json | 46 +++++++++++++++++------------------ frontend/ui/package.json | 2 +- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/frontend/ui/package-lock.json b/frontend/ui/package-lock.json index aed2c386..5e8180bf 100644 --- a/frontend/ui/package-lock.json +++ b/frontend/ui/package-lock.json @@ -104,21 +104,21 @@ "integrity": "sha512-RKYNkQxtsMgt0wD8PhfXR1hGT1Tmq1E5eZeTr1KxIerczITRnWVT8LElfu/9Kusv44yYlyQtNc1mLoYqgloOQw==" }, "@sveltejs/kit": { - "version": "1.0.0-next.160", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.160.tgz", - "integrity": "sha512-JmacpK+VsppUKDQu1B3oJ70SnE9BXb/PSAP3f91K/DMnaSEGj7vtHn/3e6l7Mop12t1PkBk5lImh/ULqBO+YfA==", + "version": "1.0.0-next.165", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.165.tgz", + "integrity": "sha512-yCNDj5C7pAWNFT7U194SlR44JBUjlfjgsDVTwQejRCEn0/+1yGhxr32Q6seBCuRNjJHYFzNSfMvxc/Ui0Y80KA==", "dev": true, "requires": { - "@sveltejs/vite-plugin-svelte": "^1.0.0-next.16", + "@sveltejs/vite-plugin-svelte": "^1.0.0-next.22", "cheap-watch": "^1.0.3", "sade": "^1.7.4", - "vite": "^2.5.2" + "vite": "^2.5.6" } }, "@sveltejs/vite-plugin-svelte": { - "version": "1.0.0-next.19", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.19.tgz", - "integrity": "sha512-q9hHkMzodScwDq64pNaWhekpj97vWg3wO9T0rqd8bC2EsrBQs2uD1qMJvDqlNd63AbO2uSJMGo+TQ0Xt2xgyYg==", + "version": "1.0.0-next.22", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.22.tgz", + "integrity": "sha512-B1O/GMIxpE4iJ3/FBUJ3oYGKHhJ99G9uFARsb8kVeReLDPhtZGXTM+7brLDefJuedbKkRkGEEkbGOVuEuEBo3g==", "dev": true, "requires": { "@rollup/pluginutils": "^4.1.1", @@ -303,9 +303,9 @@ } }, "cheap-watch": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cheap-watch/-/cheap-watch-1.0.3.tgz", - "integrity": "sha512-xC5CruMhLzjPwJ5ecUxGu1uGmwJQykUhqd2QrCrYbwvsFYdRyviu6jG9+pccwDXJR/OpmOTOJ9yLFunVgQu9wg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cheap-watch/-/cheap-watch-1.0.4.tgz", + "integrity": "sha512-QR/9FrtRL5fjfUJBhAKCdi0lSRQ3rVRRum3GF9wDKp2TJbEIMGhUEr2yU8lORzm9Isdjx7/k9S0DFDx+z5VGtw==", "dev": true }, "chokidar": { @@ -340,9 +340,9 @@ "dev": true }, "colorette": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.3.0.tgz", - "integrity": "sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", "dev": true }, "concat-map": { @@ -402,9 +402,9 @@ } }, "esbuild": { - "version": "0.12.24", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.24.tgz", - "integrity": "sha512-C0ibY+HsXzYB6L/pLWEiWjMpghKsIc58Q5yumARwBQsHl9DXPakW+5NI/Y9w4YXiz0PEP6XTGTT/OV4Nnsmb4A==", + "version": "0.12.25", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.25.tgz", + "integrity": "sha512-woie0PosbRSoN8gQytrdCzUbS2ByKgO8nD1xCZkEup3D9q92miCze4PqEI9TZDYAuwn6CruEnQpJxgTRWdooAg==", "dev": true }, "escape-string-regexp": { @@ -1006,9 +1006,9 @@ "dev": true }, "prettier": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", - "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.0.tgz", + "integrity": "sha512-DsEPLY1dE5HF3BxCRBmD4uYZ+5DCbvatnolqTqcxEgKVZnL2kUfyu7b8pPQ5+hTBkdhU9SLUmK0/pHb07RE4WQ==", "dev": true }, "prettier-plugin-svelte": { @@ -1338,9 +1338,9 @@ "dev": true }, "vite": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-2.5.2.tgz", - "integrity": "sha512-JK5uhiVyMqHiAJbgBa8rCvpP8bEhAE9dKDv1gCmP+EUP2FSPmEeW3WXlCXauPB3MDa8behPW+ntyNXqnGaxslg==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-2.5.6.tgz", + "integrity": "sha512-P++qzXuOPhTql8iDamsatlJfD7/yGi8NCNwzyqkB2p0jrNJC567WEdXiKn3hQ+ZV8amQmB2dTH6svo3Z2tJ6MQ==", "dev": true, "requires": { "esbuild": "^0.12.17", diff --git a/frontend/ui/package.json b/frontend/ui/package.json index 019c9eb3..691e8482 100644 --- a/frontend/ui/package.json +++ b/frontend/ui/package.json @@ -13,7 +13,7 @@ "eslint": "^7.22.0", "eslint-config-prettier": "^8.1.0", "eslint-plugin-svelte3": "^3.2.0", - "prettier": "~2.2.1", + "prettier": "~2.4.0", "prettier-plugin-svelte": "^2.2.0", "sass": "^1.38.2", "sass-loader": "^12.1.0", From 87583fbd17520d0cbf7cf5cd1614305392e0aec2 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 22 Oct 2021 10:14:39 +0200 Subject: [PATCH 0373/1637] ui: Update modules --- configs/nginx-chbase.sh | 2 +- frontend/ui/package-lock.json | 294 +++++++++++++----- frontend/ui/package.json | 24 +- frontend/ui/src/components/Header.svelte | 2 +- .../ui/src/routes/[theme]/[exercice].svelte | 14 +- .../ui/src/routes/[theme]/__layout.svelte | 6 +- frontend/ui/src/routes/[theme]/index.svelte | 4 +- frontend/ui/src/routes/__layout.svelte | 6 +- frontend/ui/src/routes/edit.svelte | 4 +- frontend/ui/src/routes/issues.svelte | 4 +- frontend/ui/src/routes/register.svelte | 4 +- frontend/ui/src/routes/tags/[tag].svelte | 2 +- frontend/ui/svelte.config.js | 3 + 13 files changed, 255 insertions(+), 114 deletions(-) diff --git a/configs/nginx-chbase.sh b/configs/nginx-chbase.sh index d7fd73ef..a4772eeb 100755 --- a/configs/nginx-chbase.sh +++ b/configs/nginx-chbase.sh @@ -24,7 +24,7 @@ run() { then sed -ri "s@${CURRENT_BASE}_app/@${NEWBASE}_app/@g" ${FILE} else - sed -ri "s@(href|src)=\"${CURRENT_BASE}@\1=\"${NEWBASE}@g;s@\\\$http.get\(\"${CURRENT_BASE}@\$http.get\(\"${NEWBASE}@g;s@\\\$http\((.*)\"${CURRENT_BASE}@\$http(\1\"${NEWBASE}@g;s@from \"${CURRENT_BASE}_app@from \"${NEWBASE}_app@g;s@\`${CURRENT_BASE}_app/\\\$@\`${NEWBASE}_app/\\\$@g;s@paths: \{\"base\":\"${CURRENT_BASE%/}\",\"assets\":\"${CURRENT_BASE%/}\"\},@paths: {\"base\":\"${NEWBASE%/}\",\"assets\":\"${NEWBASE%/}\"},@" ${FILE} + sed -ri "s@(href|src)=\"${CURRENT_BASE}@\1=\"${NEWBASE}@g;s@\\\$http.get\(\"${CURRENT_BASE}@\$http.get\(\"${NEWBASE}@g;s@\\\$http\((.*)\"${CURRENT_BASE}@\$http(\1\"${NEWBASE}@g;s@\"${CURRENT_BASE}_app@\"${NEWBASE}_app@g;s@\`${CURRENT_BASE}_app/\\\$@\`${NEWBASE}_app/\\\$@g;s@paths: \{\"base\":\"${CURRENT_BASE%/}\",\"assets\":\"${CURRENT_BASE%/}\"\},@paths: {\"base\":\"${NEWBASE%/}\",\"assets\":\"${NEWBASE%/}\"},@" ${FILE} fi fi } diff --git a/frontend/ui/package-lock.json b/frontend/ui/package-lock.json index 5e8180bf..3ee297f7 100644 --- a/frontend/ui/package-lock.json +++ b/frontend/ui/package-lock.json @@ -84,9 +84,9 @@ "dev": true }, "@popperjs/core": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.9.3.tgz", - "integrity": "sha512-xDu17cEfh7Kid/d95kB6tZsLOmSWKCZKtprnhVepjsSaCij+lM3mItSJDuuHDMbCWTh8Ejmebwb+KONcCJ0eXQ==" + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.10.2.tgz", + "integrity": "sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ==" }, "@rollup/pluginutils": { "version": "4.1.1", @@ -99,26 +99,26 @@ } }, "@sveltejs/adapter-static": { - "version": "1.0.0-next.17", - "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-1.0.0-next.17.tgz", - "integrity": "sha512-RKYNkQxtsMgt0wD8PhfXR1hGT1Tmq1E5eZeTr1KxIerczITRnWVT8LElfu/9Kusv44yYlyQtNc1mLoYqgloOQw==" + "version": "1.0.0-next.21", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-1.0.0-next.21.tgz", + "integrity": "sha512-B4+QoUVAaANKx+mHntG8SqF45zbj3Ct4Akg/cGauo6COyfKZRhO5OsMa+wPuT2TKJBZC4eEDK0p+p9nyQBkxKQ==" }, "@sveltejs/kit": { - "version": "1.0.0-next.165", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.165.tgz", - "integrity": "sha512-yCNDj5C7pAWNFT7U194SlR44JBUjlfjgsDVTwQejRCEn0/+1yGhxr32Q6seBCuRNjJHYFzNSfMvxc/Ui0Y80KA==", + "version": "1.0.0-next.188", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.188.tgz", + "integrity": "sha512-eDssOlwcAmkUR3SGh3PK4LHBfV3mMY9vhsMohMUPV7oJO3fydOgvDykqzxK9wc1NgNB/TIBCm+1sps2vetePHw==", "dev": true, "requires": { - "@sveltejs/vite-plugin-svelte": "^1.0.0-next.22", - "cheap-watch": "^1.0.3", + "@sveltejs/vite-plugin-svelte": "^1.0.0-next.30", + "cheap-watch": "^1.0.4", "sade": "^1.7.4", - "vite": "^2.5.6" + "vite": "^2.6.10" } }, "@sveltejs/vite-plugin-svelte": { - "version": "1.0.0-next.22", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.22.tgz", - "integrity": "sha512-B1O/GMIxpE4iJ3/FBUJ3oYGKHhJ99G9uFARsb8kVeReLDPhtZGXTM+7brLDefJuedbKkRkGEEkbGOVuEuEBo3g==", + "version": "1.0.0-next.30", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.30.tgz", + "integrity": "sha512-YQqdMxjL1VgSFk4/+IY3yLwuRRapPafPiZTiaGEq1psbJYSNYUWx9F1zMm32GMsnogg3zn99mGJOqe3ld3HZSg==", "dev": true, "requires": { "@rollup/pluginutils": "^4.1.1", @@ -212,19 +212,19 @@ "dev": true }, "bootstrap": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.0.tgz", - "integrity": "sha512-bs74WNI9BgBo3cEovmdMHikSKoXnDgA6VQjJ7TyTotU6L7d41ZyCEEelPwkYEzsG/Zjv3ie9IE3EMAje0W9Xew==" + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz", + "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==" }, "bootstrap-icons": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.5.0.tgz", - "integrity": "sha512-44feMc7DE1Ccpsas/1wioN8ewFJNquvi5FewA06wLnqct7CwMdGDVy41ieHaacogzDqLfG8nADIvMNp9e4bfbA==" + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.6.1.tgz", + "integrity": "sha512-MNpF89+njCdVJePDRbCd2DrUusqIyNsPlBrdKqBEXAvFZpwb+Gc8k2VlyF2ueiDQn1PoeTSg9UqQNgx8tGqHAA==" }, "bootswatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bootswatch/-/bootswatch-5.1.0.tgz", - "integrity": "sha512-HwQjahQSq0u+ydqY0fFyl/GsJIHhBvzjBA8D2XvMvIrHdqYfL1LdD5bnnaEDoJugMCYGud9PQSgq/XkuyFFPMg==" + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bootswatch/-/bootswatch-5.1.3.tgz", + "integrity": "sha512-NmZFN6rOCoXWQ/PkzmD8FFWDe24kocX9OXWHNVaLxVVnpqpAzEbMFsf8bAfKwVtpNXibasZCzv09B5fLieAh2g==" }, "brace-expansion": { "version": "1.1.11", @@ -339,12 +339,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "colorette": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", - "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", - "dev": true - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -402,10 +396,148 @@ } }, "esbuild": { - "version": "0.12.25", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.25.tgz", - "integrity": "sha512-woie0PosbRSoN8gQytrdCzUbS2ByKgO8nD1xCZkEup3D9q92miCze4PqEI9TZDYAuwn6CruEnQpJxgTRWdooAg==", - "dev": true + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.8.tgz", + "integrity": "sha512-A4af7G7YZLfG5OnARJRMtlpEsCkq/zHZQXewgPA864l9D6VjjbH1SuFYK/OSV6BtHwDGkdwyRrX0qQFLnMfUcw==", + "dev": true, + "requires": { + "esbuild-android-arm64": "0.13.8", + "esbuild-darwin-64": "0.13.8", + "esbuild-darwin-arm64": "0.13.8", + "esbuild-freebsd-64": "0.13.8", + "esbuild-freebsd-arm64": "0.13.8", + "esbuild-linux-32": "0.13.8", + "esbuild-linux-64": "0.13.8", + "esbuild-linux-arm": "0.13.8", + "esbuild-linux-arm64": "0.13.8", + "esbuild-linux-mips64le": "0.13.8", + "esbuild-linux-ppc64le": "0.13.8", + "esbuild-netbsd-64": "0.13.8", + "esbuild-openbsd-64": "0.13.8", + "esbuild-sunos-64": "0.13.8", + "esbuild-windows-32": "0.13.8", + "esbuild-windows-64": "0.13.8", + "esbuild-windows-arm64": "0.13.8" + } + }, + "esbuild-android-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.8.tgz", + "integrity": "sha512-AilbChndywpk7CdKkNSZ9klxl+9MboLctXd9LwLo3b0dawmOF/i/t2U5d8LM6SbT1Xw36F8yngSUPrd8yPs2RA==", + "dev": true, + "optional": true + }, + "esbuild-darwin-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.8.tgz", + "integrity": "sha512-b6sdiT84zV5LVaoF+UoMVGJzR/iE2vNUfUDfFQGrm4LBwM/PWXweKpuu6RD9mcyCq18cLxkP6w/LD/w9DtX3ng==", + "dev": true, + "optional": true + }, + "esbuild-darwin-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.8.tgz", + "integrity": "sha512-R8YuPiiJayuJJRUBG4H0VwkEKo6AvhJs2m7Tl0JaIer3u1FHHXwGhMxjJDmK+kXwTFPriSysPvcobXC/UrrZCQ==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.8.tgz", + "integrity": "sha512-zBn6urrn8FnKC+YSgDxdof9jhPCeU8kR/qaamlV4gI8R3KUaUK162WYM7UyFVAlj9N0MyD3AtB+hltzu4cysTw==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.8.tgz", + "integrity": "sha512-pWW2slN7lGlkx0MOEBoUGwRX5UgSCLq3dy2c8RIOpiHtA87xAUpDBvZK10MykbT+aMfXc0NI2lu1X+6kI34xng==", + "dev": true, + "optional": true + }, + "esbuild-linux-32": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.8.tgz", + "integrity": "sha512-T0I0ueeKVO/Is0CAeSEOG9s2jeNNb8jrrMwG9QBIm3UU18MRB60ERgkS2uV3fZ1vP2F8i3Z2e3Zju4lg9dhVmw==", + "dev": true, + "optional": true + }, + "esbuild-linux-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.8.tgz", + "integrity": "sha512-Bm8SYmFtvfDCIu9sjKppFXzRXn2BVpuCinU1ChTuMtdKI/7aPpXIrkqBNOgPTOQO9AylJJc1Zw6EvtKORhn64w==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.8.tgz", + "integrity": "sha512-4/HfcC40LJ4GPyboHA+db0jpFarTB628D1ifU+/5bunIgY+t6mHkJWyxWxAAE8wl/ZIuRYB9RJFdYpu1AXGPdg==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.8.tgz", + "integrity": "sha512-X4pWZ+SL+FJ09chWFgRNO3F+YtvAQRcWh0uxKqZSWKiWodAB20flsW/OWFYLXBKiVCTeoGMvENZS/GeVac7+tQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-mips64le": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.8.tgz", + "integrity": "sha512-o7e0D+sqHKT31v+mwFircJFjwSKVd2nbkHEn4l9xQ1hLR+Bv8rnt3HqlblY3+sBdlrOTGSwz0ReROlKUMJyldA==", + "dev": true, + "optional": true + }, + "esbuild-linux-ppc64le": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.8.tgz", + "integrity": "sha512-eZSQ0ERsWkukJp2px/UWJHVNuy0lMoz/HZcRWAbB6reoaBw7S9vMzYNUnflfL3XA6WDs+dZn3ekHE4Y2uWLGig==", + "dev": true, + "optional": true + }, + "esbuild-netbsd-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.8.tgz", + "integrity": "sha512-gZX4kP7gVvOrvX0ZwgHmbuHczQUwqYppxqtoyC7VNd80t5nBHOFXVhWo2Ad/Lms0E8b+wwgI/WjZFTCpUHOg9Q==", + "dev": true, + "optional": true + }, + "esbuild-openbsd-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.8.tgz", + "integrity": "sha512-afzza308X4WmcebexbTzAgfEWt9MUkdTvwIa8xOu4CM2qGbl2LanqEl8/LUs8jh6Gqw6WsicEK52GPrS9wvkcw==", + "dev": true, + "optional": true + }, + "esbuild-sunos-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.8.tgz", + "integrity": "sha512-mWPZibmBbuMKD+LDN23LGcOZ2EawMYBONMXXHmbuxeT0XxCNwadbCVwUQ/2p5Dp5Kvf6mhrlIffcnWOiCBpiVw==", + "dev": true, + "optional": true + }, + "esbuild-windows-32": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.8.tgz", + "integrity": "sha512-QsZ1HnWIcnIEApETZWw8HlOhDSWqdZX2SylU7IzGxOYyVcX7QI06ety/aDcn437mwyO7Ph4RrbhB+2ntM8kX8A==", + "dev": true, + "optional": true + }, + "esbuild-windows-64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.8.tgz", + "integrity": "sha512-76Fb57B9eE/JmJi1QmUW0tRLQZfGo0it+JeYoCDTSlbTn7LV44ecOHIMJSSgZADUtRMWT9z0Kz186bnaB3amSg==", + "dev": true, + "optional": true + }, + "esbuild-windows-arm64": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.8.tgz", + "integrity": "sha512-HW6Mtq5eTudllxY2YgT62MrVcn7oq2o8TAoAvDUhyiEmRmDY8tPwAhb1vxw5/cdkbukM3KdMYtksnUhF/ekWeg==", + "dev": true, + "optional": true }, "escape-string-regexp": { "version": "4.0.0", @@ -468,9 +600,9 @@ "dev": true }, "eslint-plugin-svelte3": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-svelte3/-/eslint-plugin-svelte3-3.2.0.tgz", - "integrity": "sha512-qdWB1QN21dEozsJFdR8XlEhMnsS6aKHjsXWuNmchYwxoet5I6QdCr1Xcq62++IzRBMCNCeH4waXqSOAdqrZzgA==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-svelte3/-/eslint-plugin-svelte3-3.2.1.tgz", + "integrity": "sha512-YoBR9mLoKCjGghJ/gvpnFZKaMEu/VRcuxpSRS8KuozuEo7CdBH7bmBHa6FmMm0i4kJnOyx+PVsaptz96K6H/4Q==", "dev": true }, "eslint-scope": { @@ -760,9 +892,9 @@ } }, "is-core-module": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", - "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", "dev": true, "requires": { "has": "^1.0.3" @@ -897,9 +1029,9 @@ } }, "mri": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.6.tgz", - "integrity": "sha512-oi1b3MfbyGa7FJMP9GmLTttni5JoICpYBRlq+x5V16fZbLsnL9N3wFqqIm/nIG43FjUFkFh9Epzp/kzUGUnJxQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", "dev": true }, "ms": { @@ -909,9 +1041,9 @@ "dev": true }, "nanoid": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", - "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "version": "3.1.30", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz", + "integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==", "dev": true }, "natural-compare": { @@ -982,6 +1114,12 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, "picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -989,13 +1127,13 @@ "dev": true }, "postcss": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.6.tgz", - "integrity": "sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==", + "version": "8.3.11", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.11.tgz", + "integrity": "sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA==", "dev": true, "requires": { - "colorette": "^1.2.2", - "nanoid": "^3.1.23", + "nanoid": "^3.1.30", + "picocolors": "^1.0.0", "source-map-js": "^0.6.2" } }, @@ -1006,15 +1144,15 @@ "dev": true }, "prettier": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.0.tgz", - "integrity": "sha512-DsEPLY1dE5HF3BxCRBmD4uYZ+5DCbvatnolqTqcxEgKVZnL2kUfyu7b8pPQ5+hTBkdhU9SLUmK0/pHb07RE4WQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", + "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", "dev": true }, "prettier-plugin-svelte": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-2.3.1.tgz", - "integrity": "sha512-F1/r6OYoBq8Zgurhs1MN25tdrhPw0JW5JjioPRqpxbYdmrZ3gY/DzHGs0B6zwd4DLyRsfGB2gqhxUCbHt/D1fw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-2.4.0.tgz", + "integrity": "sha512-JwJ9bOz4XHLQtiLnX4mTSSDUdhu12WH8sTwy/XTDCSyPlah6IcV7NWeYBZscPEcceu2YnW8Y9sJCP40Z2UH9GA==", "dev": true }, "progress": { @@ -1082,9 +1220,9 @@ } }, "rollup": { - "version": "2.56.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.56.3.tgz", - "integrity": "sha512-Au92NuznFklgQCUcV96iXlxUbHuB1vQMaH76DHl5M11TotjOHwqk9CwcrT78+Tnv4FN9uTBxq6p4EJoYkpyekg==", + "version": "2.58.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.58.0.tgz", + "integrity": "sha512-NOXpusKnaRpbS7ZVSzcEXqxcLDOagN6iFS8p45RkoiMqPHDLwJm758UF05KlMoCRbLBTZsPOIa887gZJ1AiXvw==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -1100,18 +1238,18 @@ } }, "sass": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.38.2.tgz", - "integrity": "sha512-Bz1fG6qiyF0FX6m/I+VxtdVKz1Dfmg/e9kfDy2PhWOkq3T384q2KxwIfP0fXpeI+EyyETdOauH+cRHQDFASllA==", + "version": "1.43.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.43.3.tgz", + "integrity": "sha512-BJnLngqWpMeS65UvlYYEuCb3/fLxDxhHtOB/gWPxs6NKrslTxGt3ZxwIvOe/0Jm4tWwM/+tIpE3wj4dLEhPDeQ==", "dev": true, "requires": { "chokidar": ">=3.0.0 <4.0.0" } }, "sass-loader": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.1.0.tgz", - "integrity": "sha512-FVJZ9kxVRYNZTIe2xhw93n3xJNYZADr+q69/s98l9nTCrWASo+DR2Ot0s5xTKQDDEosUkatsGeHxcH4QBp5bSg==", + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.2.0.tgz", + "integrity": "sha512-qducnp5vSV+8A8MZxuH6zV0MUg4MOVISScl2wDTCAn/2WJX+9Auxh92O/rnkdR2bvi5QxZBafnzkzRrWGZvm7w==", "dev": true, "requires": { "klona": "^2.0.4", @@ -1238,9 +1376,9 @@ } }, "svelte": { - "version": "3.42.3", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.42.3.tgz", - "integrity": "sha512-pbdtdNZEx2GBqSM6XEgPoHbwtvWBwFLt/1bRmzsyXZO+i424wFnPe7O5B3GOJDPFSxPRztumAW3mL5LPzecWUg==", + "version": "3.44.0", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.44.0.tgz", + "integrity": "sha512-zWACSJBSncGiDvFfYOMFGNV5zDLOlyhftmO5yOZ0lEtQMptpElaRtl39MWz1+lYCpwUq4F3Q2lTzI9TrTL+eMA==", "dev": true }, "svelte-hmr": { @@ -1250,9 +1388,9 @@ "dev": true }, "sveltestrap": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/sveltestrap/-/sveltestrap-5.6.2.tgz", - "integrity": "sha512-g+WhMNsQjdGtSIMGmfVrMVP5Pz0d+o2L49GdTZnKnd9CEeZHcgWObn7+DDnAIxxjQUsaCgdZPgPVSD8/bU8/zA==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/sveltestrap/-/sveltestrap-5.6.3.tgz", + "integrity": "sha512-/geTKJbPmJGzwHFKYC3NkUNDk/GKxrppgdSxcg58w/qcxs0S6RiN4PaQ1tgBKsdSrZDfbHfkFF+dybHAyUlV0A==", "dev": true, "requires": { "@popperjs/core": "^2.9.2" @@ -1338,16 +1476,16 @@ "dev": true }, "vite": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-2.5.6.tgz", - "integrity": "sha512-P++qzXuOPhTql8iDamsatlJfD7/yGi8NCNwzyqkB2p0jrNJC567WEdXiKn3hQ+ZV8amQmB2dTH6svo3Z2tJ6MQ==", + "version": "2.6.10", + "resolved": "https://registry.npmjs.org/vite/-/vite-2.6.10.tgz", + "integrity": "sha512-XbevwpDJMs3lKiGEj0UQScsOCpwHIjFgfzPnFVkPgnxsF9oPv1uGyckLg58XkXv6LnO46KN9yZqJzINFmAxtUg==", "dev": true, "requires": { - "esbuild": "^0.12.17", + "esbuild": "^0.13.2", "fsevents": "~2.3.2", - "postcss": "^8.3.6", + "postcss": "^8.3.8", "resolve": "^1.20.0", - "rollup": "^2.38.5" + "rollup": "^2.57.0" } }, "which": { diff --git a/frontend/ui/package.json b/frontend/ui/package.json index 691e8482..1c4b1d48 100644 --- a/frontend/ui/package.json +++ b/frontend/ui/package.json @@ -12,21 +12,21 @@ "@sveltejs/kit": "next", "eslint": "^7.22.0", "eslint-config-prettier": "^8.1.0", - "eslint-plugin-svelte3": "^3.2.0", - "prettier": "~2.4.0", - "prettier-plugin-svelte": "^2.2.0", - "sass": "^1.38.2", - "sass-loader": "^12.1.0", - "svelte": "^3.34.0", - "sveltestrap": "^5.6.2" + "eslint-plugin-svelte3": "^3.2.1", + "prettier": "^2.4.1", + "prettier-plugin-svelte": "^2.4.0", + "sass": "^1.43.3", + "sass-loader": "^12.2.0", + "svelte": "^3.44.0", + "sveltestrap": "^5.6.3" }, "type": "module", "dependencies": { - "@popperjs/core": "^2.9.3", - "@sveltejs/adapter-static": "^1.0.0-next.17", - "bootstrap": "^5.1.0", - "bootstrap-icons": "^1.5.0", - "bootswatch": "^5.1.0", + "@popperjs/core": "^2.10.2", + "@sveltejs/adapter-static": "^1.0.0-next.21", + "bootstrap": "^5.1.3", + "bootstrap-icons": "^1.6.1", + "bootswatch": "^5.1.3", "hash-wasm": "^4.9.0", "seedrandom": "^3.0.5" } diff --git a/frontend/ui/src/components/Header.svelte b/frontend/ui/src/components/Header.svelte index 9ebbd90a..884c4201 100644 --- a/frontend/ui/src/components/Header.svelte +++ b/frontend/ui/src/components/Header.svelte @@ -64,7 +64,7 @@ - {#if $settings && $settings.end - $settings.start >= 0} + {#if $settings && $settings.end - $settings.start >= 0 && $teams && Object.keys($teams).length} diff --git a/frontend/ui/src/routes/[theme]/[exercice].svelte b/frontend/ui/src/routes/[theme]/[exercice].svelte index 7b8a0cb6..e4ebeda4 100644 --- a/frontend/ui/src/routes/[theme]/[exercice].svelte +++ b/frontend/ui/src/routes/[theme]/[exercice].svelte @@ -3,12 +3,12 @@ import { themes } from '../../stores/themes.js'; - export async function load({ page, fetch, session, context }) { + export async function load({ page, fetch, session, stuff }) { let exercice = null; - for (let ex in context.theme.exercices) { - if (context.theme.exercices[ex].urlid === page.params.exercice) { - exercice = context.theme.exercices[ex]; + for (let ex in stuff.theme.exercices) { + if (stuff.theme.exercices[ex].urlid === page.params.exercice) { + exercice = stuff.theme.exercices[ex]; exercice.id = ex; break; } @@ -16,10 +16,10 @@ return { props: { - theme: context.theme, + theme: stuff.theme, exercice: exercice, - refresh_my: context.refresh_my, - refresh_teams: context.refresh_teams, + refresh_my: stuff.refresh_my, + refresh_teams: stuff.refresh_teams, } }; } diff --git a/frontend/ui/src/routes/[theme]/__layout.svelte b/frontend/ui/src/routes/[theme]/__layout.svelte index a4b23ce3..fc0e2632 100644 --- a/frontend/ui/src/routes/[theme]/__layout.svelte +++ b/frontend/ui/src/routes/[theme]/__layout.svelte @@ -3,7 +3,7 @@ import { themes } from '../../stores/themes.js'; - export async function load({ page, fetch, session, context }) { + export async function load({ page, fetch, session, stuff }) { const thms = get_store_value(themes); let theme = null; @@ -15,8 +15,8 @@ } return { - context: { - ...context, + stuff: { + ...stuff, theme: theme, }, props: { theme: theme, diff --git a/frontend/ui/src/routes/[theme]/index.svelte b/frontend/ui/src/routes/[theme]/index.svelte index 2c7ff2eb..a08adb31 100644 --- a/frontend/ui/src/routes/[theme]/index.svelte +++ b/frontend/ui/src/routes/[theme]/index.svelte @@ -1,8 +1,8 @@ -
diff --git a/admin/static/views/settings.html b/admin/static/views/settings.html index 2e7741c9..93efeb36 100644 --- a/admin/static/views/settings.html +++ b/admin/static/views/settings.html @@ -229,7 +229,7 @@
-
+
Synchronisation et suppressions de masse
@@ -243,7 +243,7 @@
-
+
diff --git a/fickit-backend.yml b/fickit-backend.yml index 12699337..7bf5372b 100644 --- a/fickit-backend.yml +++ b/fickit-backend.yml @@ -159,7 +159,7 @@ services: - /var/lib/fic/backups - name: fic-admin image: nemunaire/fic-admin:latest - command: ["/srv/admin", "-bind=:8081", "-baseurl=/admin/", "-localimport=/mnt/fic", "-timestampCheck=/srv/submissions"] + command: ["/srv/admin", "-4real", "-bind=:8081", "-baseurl=/admin/", "-localimport=/mnt/fic", "-timestampCheck=/srv/submissions"] env: - MYSQL_HOST=db - FICCA_PASS=jee8AhloAith1aesCeQu5ahgIegaeM4K diff --git a/settings/settings.go b/settings/settings.go index c1bfbab7..36c00562 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -28,6 +28,8 @@ type FICSettings struct { Authors string `json:"authors"` // VideoLink is the link to explaination videos when the challenge is over. VideosLink string `json:"videoslink"` + // WorkInProgress indicates if the current challenge is under development or if it is in production. + WorkInProgress bool `json:"wip,omitempty"` // Start is the departure time (expected or effective). Start time.Time `json:"start"` From b1315a9eeb978a4d60701d79d32c488b8cca2167 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 21 Jan 2022 09:17:46 +0100 Subject: [PATCH 0415/1637] admin: Auto change default value for unlockedChallengeDepth --- admin/static/js/app.js | 7 +++++++ admin/static/views/settings.html | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index d45f17a4..fe5b305f 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -507,6 +507,13 @@ angular.module("FICApp") $scope.configro = ROSettings.get(); $scope.duration = 360; + $scope.exerciceDependChange = function() { + if ($scope.config.enableExerciceDepend) + $scope.config.unlockedChallengeDepth = 0; + else + $scope.config.unlockedChallengeDepth = -1; + }; + var needRefreshSyncReportWhenReady = false; var refreshSyncReport = function() { needRefreshSyncReportWhenReady = false; diff --git a/admin/static/views/settings.html b/admin/static/views/settings.html index 93efeb36..9b57e9be 100644 --- a/admin/static/views/settings.html +++ b/admin/static/views/settings.html @@ -184,9 +184,9 @@
From ce41ab76ebb291c9dcc2eca7b81e4551e0db2551 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 21 Jan 2022 09:59:13 +0100 Subject: [PATCH 0416/1637] Upgrade bootstrap to 4.6.1 This fixes a bug with toast --- admin/static/css/bootstrap.min.css | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/admin/static/css/bootstrap.min.css b/admin/static/css/bootstrap.min.css index e6b49777..6ee59568 100644 --- a/admin/static/css/bootstrap.min.css +++ b/admin/static/css/bootstrap.min.css @@ -1,7 +1,7 @@ /*! - * Bootstrap v4.2.1 (https://getbootstrap.com/) - * Copyright 2011-2018 The Bootstrap Authors - * Copyright 2011-2018 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/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",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",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{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;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([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}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}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}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}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}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[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}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}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-family:inherit;font-weight:500;line-height:1.2;color:inherit}.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:80%;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:80%;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-break: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{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.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%}.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%}.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%}.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%}.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%}.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;background-color:transparent}.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 .table{background-color:#fff}.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{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:#212529;border-color:#32383e}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#212529}.table-dark td,.table-dark th,.table-dark thead th{border-color:#32383e}.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{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;-ms-overflow-style:-ms-autohiding-scrollbar}.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;-ms-overflow-style:-ms-autohiding-scrollbar}.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;-ms-overflow-style:-ms-autohiding-scrollbar}.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;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(2.25rem + 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 screen and (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}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-top:.375rem;padding-bottom:.375rem;margin-bottom:0;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.8125rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(2.875rem + 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{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:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;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-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:2.25rem;background-repeat:no-repeat;background-position:center right calc(2.25rem / 4);background-size:calc(2.25rem / 2) calc(2.25rem / 2);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' 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")}.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)}.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:2.25rem;background-position:top calc(2.25rem / 4) right calc(2.25rem / 4)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:3.4375rem;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' 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") no-repeat center right 1.75rem/1.125rem 1.125rem}.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)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip{display:block}.form-control-file.is-valid~.valid-feedback,.form-control-file.is-valid~.valid-tooltip,.was-validated .form-control-file:valid~.valid-feedback,.was-validated .form-control-file:valid~.valid-tooltip{display:block}.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~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.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~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.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:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;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-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:2.25rem;background-repeat:no-repeat;background-position:center right calc(2.25rem / 4);background-size:calc(2.25rem / 2) calc(2.25rem / 2);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23d9534f' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E")}.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)}.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:2.25rem;background-position:top calc(2.25rem / 4) right calc(2.25rem / 4)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:3.4375rem;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23d9534f' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E") no-repeat center right 1.75rem/1.125rem 1.125rem}.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)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip{display:block}.form-control-file.is-invalid~.invalid-feedback,.form-control-file.is-invalid~.invalid-tooltip,.was-validated .form-control-file:invalid~.invalid-feedback,.was-validated .form-control-file:invalid~.invalid-tooltip{display:block}.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~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.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~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.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;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 screen and (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{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{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{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{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{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{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{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{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}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline;box-shadow:none}.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 screen and (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 screen and (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.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-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-right{right:0;left:auto}}.dropdown-menu-left{right:auto;left:0}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}}.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:first-child{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.dropdown-item:last-child{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;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%;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(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.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-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(2.875rem + 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.8125rem + 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>.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-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.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;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;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{color:#6c757d}.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:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background-repeat:no-repeat;background-position:center center;background-size:50% 50%}.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' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 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' 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' 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 screen and (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(2.25rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;background-color:#fff;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(128,189,255,.5)}.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{opacity:0}.custom-select-sm{height:calc(1.8125rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(2.875rem + 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(2.25rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(2.25rem + 2px);margin:0;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{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(2.25rem + 2px);padding:.375rem .75rem;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:2.25rem;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:calc(1rem + .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;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 screen and (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{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;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 screen and (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{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;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{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 screen and (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-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{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{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-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{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:not(:disabled):not(.disabled){cursor:pointer}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{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{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.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{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{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.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{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{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.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{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{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.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{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{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.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 viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' 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 viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' 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:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;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;color:inherit;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-header+.list-group .list-group-item:first-child{border-top: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}.card-img{width:100%;border-radius:calc(.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{display:-ms-flexbox;display:flex;-ms-flex:1 0 0%;flex:1 0 0%;-ms-flex-direction:column;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{-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:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:first-child .card-header,.card-group>.card:first-child .card-img-top{border-top-right-radius:0}.card-group>.card:first-child .card-footer,.card-group>.card:first-child .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:last-child .card-header,.card-group>.card:last-child .card-img-top{border-top-left-radius:0}.card-group>.card:last-child .card-footer,.card-group>.card:last-child .card-img-bottom{border-bottom-left-radius:0}.card-group>.card:only-child{border-radius:.25rem}.card-group>.card:only-child .card-header,.card-group>.card:only-child .card-img-top{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card-group>.card:only-child .card-footer,.card-group>.card:only-child .card-img-bottom{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-group>.card:not(:first-child):not(:last-child):not(:only-child){border-radius:0}.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-footer,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-header,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-top{border-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 .card{overflow:hidden}.accordion .card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion .card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion .card:first-of-type{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion .card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.accordion .card .card-header{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{display:inline-block;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:2;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-link:not(:disabled):not(.disabled){cursor:pointer}.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:1;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}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}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}.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;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;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;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media screen and (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:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}.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}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{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;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item:focus,.list-group-item:hover{z-index:1;text-decoration:none}.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-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush .list-group-item:last-child{margin-bottom:-1px}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{margin-bottom:0;border-bottom: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){cursor:pointer}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);border-radius:.25rem;box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0}.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)}.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 screen and (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - (.5rem * 2))}.modal-dialog-centered::before{display:block;height:calc(100vh - (.5rem * 2));content:""}.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 #e9ecef;border-top-left-radius:.3rem;border-top-right-radius:.3rem}.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-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid #e9ecef;border-bottom-right-radius:.3rem;border-bottom-left-radius:.3rem}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.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-centered{min-height:calc(100% - (1.75rem * 2))}.modal-dialog-centered::before{height:calc(100vh - (1.75rem * 2))}.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",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;word-spacing:normal;white-space: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",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;word-spacing:normal;white-space: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) * -1)}.bs-popover-auto[x-placement^=top] .arrow::after,.bs-popover-auto[x-placement^=top] .arrow::before,.bs-popover-top .arrow::after,.bs-popover-top .arrow::before{border-width:.5rem .5rem 0}.bs-popover-auto[x-placement^=top] .arrow::before,.bs-popover-top .arrow::before{bottom: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-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) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right] .arrow::after,.bs-popover-auto[x-placement^=right] .arrow::before,.bs-popover-right .arrow::after,.bs-popover-right .arrow::before{border-width:.5rem .5rem .5rem 0}.bs-popover-auto[x-placement^=right] .arrow::before,.bs-popover-right .arrow::before{left: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-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) * -1)}.bs-popover-auto[x-placement^=bottom] .arrow::after,.bs-popover-auto[x-placement^=bottom] .arrow::before,.bs-popover-bottom .arrow::after,.bs-popover-bottom .arrow::before{border-width:0 .5rem .5rem .5rem}.bs-popover-auto[x-placement^=bottom] .arrow::before,.bs-popover-bottom .arrow::before{top:0;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom] .arrow::after,.bs-popover-bottom .arrow::after{top:1px;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) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left] .arrow::after,.bs-popover-auto[x-placement^=left] .arrow::before,.bs-popover-left .arrow::after,.bs-popover-left .arrow::before{border-width:.5rem 0 .5rem .5rem}.bs-popover-auto[x-placement^=left] .arrow::before,.bs-popover-left .arrow::before{right:0;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left] .arrow::after,.bs-popover-left .arrow::after{right:1px;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;color:inherit;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 screen and (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:0s .6s opacity}@media screen and (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%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media screen and (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:transparent no-repeat center center;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%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' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-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 screen and (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:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.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}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.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{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-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-3by4::before{padding-top:133.333333%}.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}}.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;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}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}.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-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}thead{display:table-header-group}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}} + * Bootstrap v4.6.1 (https://getbootstrap.com/) + * Copyright 2011-2021 The Bootstrap Authors + * Copyright 2011-2021 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:80%;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:80%;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:80%;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:80%;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}}.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}.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:#adb5bd solid 1px}.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;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{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{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;word-spacing:normal;white-space: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;word-spacing:normal;white-space: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 From 76ccd25ae399ccf635b03a77a122d95faf12a6a0 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 21 Jan 2022 10:40:36 +0100 Subject: [PATCH 0417/1637] ui: Detect my refresh with more accuracy --- frontend/ui/src/components/ExerciceFlags.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/ui/src/components/ExerciceFlags.svelte b/frontend/ui/src/components/ExerciceFlags.svelte index 4ae8026d..9fc7a9a8 100644 --- a/frontend/ui/src/components/ExerciceFlags.svelte +++ b/frontend/ui/src/components/ExerciceFlags.svelte @@ -28,7 +28,7 @@ function waitDiff(i) { refresh_my((my) => { - if (my && my.exercices[exercice.id].solved_time != exercice.solved_time) { + 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)) { submitInProgress = false; refresh_teams(); } else if (i > 0) { From 63b0ad4885289a2536d9666f21863d285ad57e57 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 21 Jan 2022 10:46:13 +0100 Subject: [PATCH 0418/1637] ui: Increase time between checks --- frontend/ui/src/components/ExerciceFlags.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/ui/src/components/ExerciceFlags.svelte b/frontend/ui/src/components/ExerciceFlags.svelte index 9fc7a9a8..efdc8178 100644 --- a/frontend/ui/src/components/ExerciceFlags.svelte +++ b/frontend/ui/src/components/ExerciceFlags.svelte @@ -32,7 +32,7 @@ submitInProgress = false; refresh_teams(); } else if (i > 0) { - setTimeout(waitDiff, 450, i-1); + setTimeout(waitDiff, (12-i)*50+440, i-1); } else { timeouted = true; submitInProgress = false; @@ -105,7 +105,7 @@ const data = await response.json(); messageClass = 'text-success'; message = data.errmsg; - waitDiff(20); + waitDiff(13); } else { submitInProgress = false; From 892bb9946189f6aa0cc4940827bfeeb79215300d Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 21 Jan 2022 11:09:16 +0100 Subject: [PATCH 0419/1637] admin: Disable PKI regeneration in prod --- admin/static/views/pki.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/static/views/pki.html b/admin/static/views/pki.html index 245571ce..30f22686 100644 --- a/admin/static/views/pki.html +++ b/admin/static/views/pki.html @@ -129,7 +129,7 @@ Générée Introuvable - +
From b98e23d0607f663c8ff94663c782182c37677350 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 21 Jan 2022 12:12:35 +0100 Subject: [PATCH 0420/1637] =?UTF-8?q?Include=20=C3=A7=20and=20=C3=87=20in?= =?UTF-8?q?=20urlid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libfic/utils.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libfic/utils.go b/libfic/utils.go index e82e862c..0faf9e2c 100644 --- a/libfic/utils.go +++ b/libfic/utils.go @@ -14,6 +14,11 @@ func ToURLid(str string) string { re_A := regexp.MustCompile("[ÀÁÂÄĀ]") str = re_A.ReplaceAllLiteralString(str, "A") + re_c := regexp.MustCompile("[ç]") + str = re_c.ReplaceAllLiteralString(str, "c") + re_C := regexp.MustCompile("[Ç]") + str = re_C.ReplaceAllLiteralString(str, "C") + re_e := regexp.MustCompile("[éèêëȩē]") str = re_e.ReplaceAllLiteralString(str, "e") re_E := regexp.MustCompile("[ÉÈÊËĒ]") From 01b05aaed0e43922702e2b62ff6e9684834fd65c Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 21 Jan 2022 13:06:37 +0100 Subject: [PATCH 0421/1637] Implement label only flag --- admin/sync/exercice_defines.go | 1 + admin/sync/exercice_keys.go | 45 ++++- .../ui/src/components/ExerciceFlags.svelte | 6 +- libfic/db.go | 32 +++ libfic/flag.go | 14 ++ libfic/flag_label.go | 187 ++++++++++++++++++ libfic/mcq_justification.go | 4 +- libfic/team.go | 5 +- libfic/team_my.go | 28 ++- 9 files changed, 311 insertions(+), 11 deletions(-) create mode 100644 libfic/flag_label.go diff --git a/admin/sync/exercice_defines.go b/admin/sync/exercice_defines.go index f7841e33..0fabe9e7 100644 --- a/admin/sync/exercice_defines.go +++ b/admin/sync/exercice_defines.go @@ -49,6 +49,7 @@ type ExerciceFlag struct { NeedFlag []ExerciceDependency `toml:"need_flag,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"` diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index 4c028d95..c7ffc972 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -95,11 +95,37 @@ func getRawKey(input interface{}, validatorRe string, ordered bool, showLines bo return } +func buildLabelFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int) (f *fic.FlagLabel, errs []string) { + if len(flag.Label) == 0 { + errs = append(errs, fmt.Sprintf("%q: flag #%d: Label cannot be empty.", path.Base(exercice.Path), flagline)) + return + } + + if flag.Raw != nil { + errs = append(errs, fmt.Sprintf("%q: flag #%d: raw cannot be defined.", path.Base(exercice.Path), flagline)) + } + + if len(flag.Choice) != 0 { + errs = append(errs, fmt.Sprintf("%q: flag #%d: choices cannot be defined.", path.Base(exercice.Path), flagline)) + } + + f = &fic.FlagLabel{ + Order: int8(flagline), + Label: flag.Label, + Variant: flag.Variant, + } + return +} + func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defaultLabel string) (f *fic.Flag, choices []*fic.FlagChoice, errs []string) { if len(flag.Label) == 0 { flag.Label = defaultLabel } + if len(flag.Variant) != 0 { + errs = append(errs, fmt.Sprintf("%q: flag #%d: variant is not defined for this kind of flag.", path.Base(exercice.Path), flagline)) + } + if flag.Label[0] == '`' { errs = append(errs, fmt.Sprintf("%q: flag #%d: Label should not begin with `.", path.Base(exercice.Path), flagline)) flag.Label = flag.Label[1:] @@ -221,6 +247,8 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl switch strings.ToLower(flag.Type) { case "": flag.Type = "key" + case "label": + flag.Type = "label" case "key": flag.Type = "key" case "number": @@ -273,7 +301,18 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl } } - if flag.Type == "key" || strings.HasPrefix(flag.Type, "number") || flag.Type == "text" || flag.Type == "ucq" || flag.Type == "radio" || flag.Type == "vector" { + if flag.Type == "label" { + addedFlag, berrs := buildLabelFlag(exercice, flag, nline+1) + if len(berrs) > 0 { + errs = append(errs, berrs...) + } + if addedFlag != nil { + ret = append(ret, importFlag{ + 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") if len(berrs) > 0 { errs = append(errs, berrs...) @@ -296,6 +335,10 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl hasOne := false isJustified := false + if len(flag.Variant) != 0 { + errs = append(errs, fmt.Sprintf("%q: flag #%d: variant is not defined for this kind of flag.", path.Base(exercice.Path), nline+1)) + } + if !flag.NoShuffle { rand.Shuffle(len(flag.Choice), func(i, j int) { flag.Choice[i], flag.Choice[j] = flag.Choice[j], flag.Choice[i] diff --git a/frontend/ui/src/components/ExerciceFlags.svelte b/frontend/ui/src/components/ExerciceFlags.svelte index efdc8178..c176db57 100644 --- a/frontend/ui/src/components/ExerciceFlags.svelte +++ b/frontend/ui/src/components/ExerciceFlags.svelte @@ -198,7 +198,11 @@
{#each flags as flag ((flag.type?flag.type:"i") + flag.id)} - {#if flag.type == "mcq"} + {#if !flag.type && !flag.id} +
+ +
+ {:else if flag.type == "mcq"} = 3 && len(spl[0]) == 0 { var idChoice int64 diff --git a/libfic/team.go b/libfic/team.go index 96cc19e5..9260d3fc 100644 --- a/libfic/team.go +++ b/libfic/team.go @@ -312,7 +312,10 @@ func (t *Team) GetSolvedRank(e *Exercice) (nb int64, err error) { // HasPartiallySolved checks if the Team already has unlocked the given flag and returns the validation's timestamp. func (t *Team) HasPartiallySolved(f Flag) (tm *time.Time) { - if k, ok := f.(*FlagKey); ok { + if _, ok := f.(*FlagLabel); ok { + now := time.Now() + return &now + } else if k, ok := f.(*FlagKey); ok { DBQueryRow("SELECT MIN(time) FROM flag_found WHERE id_team = ? AND id_flag = ?", t.Id, k.Id).Scan(&tm) } else if m, ok := f.(*MCQ); ok { DBQueryRow("SELECT MIN(time) FROM mcq_found WHERE id_team = ? AND id_mcq = ?", t.Id, m.Id).Scan(&tm) diff --git a/libfic/team_my.go b/libfic/team_my.go index f860e021..f516df7f 100644 --- a/libfic/team_my.go +++ b/libfic/team_my.go @@ -31,7 +31,7 @@ type myTeamHint struct { Cost int64 `json:"cost"` } type myTeamFlag struct { - Id int `json:"id"` + Id int `json:"id,omitempty"` order int8 `json:"order"` Label string `json:"label"` Type string `json:"type,omitempty"` @@ -50,6 +50,7 @@ type myTeamFlag struct { Justify bool `json:"justify,omitempty"` Choices map[string]interface{} `json:"choices,omitempty"` ChoicesCost int64 `json:"choices_cost,omitempty"` + Variant string `json:"variant,omitempty"` Min *float64 `json:"min,omitempty"` Max *float64 `json:"max,omitempty"` Step *float64 `json:"step,omitempty"` @@ -142,10 +143,8 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { } else { exercice.Tries, stime = t.CountTries(e) exercice.SolvedTime = stime - if exercice.Tries > 0 { - if DisplayMCQBadCount { - exercice.SolveDist = t.LastTryDist(e) - } + if DisplayMCQBadCount && exercice.Tries > 0 { + exercice.SolveDist = t.LastTryDist(e) } } @@ -194,6 +193,23 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { justifiedMCQ := map[int]myTeamFlag{} + if labels, err := e.GetFlagLabels(); err != nil { + return nil, err + } else { + for _, l := range labels { + if !DisplayAllFlags && t != nil && !t.CanSeeFlag(l) { + // Dependancy missing, skip the flag for now + continue + } + + exercice.Flags = append(exercice.Flags, myTeamFlag{ + order: l.Order, + Label: l.Label, + Variant: l.Variant, + }) + } + } + if flags, err := e.GetFlagKeys(); err != nil { return nil, err } else { @@ -257,7 +273,7 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { flag.Multiline = k.Multiline - var fl FlagLabel + var fl FlagMCQLabel if fl, err = k.GetMCQJustification(); err == nil { k.Label = fl.Label } From d4a81aa6603e469fd7fc184a9eebeeeae7ad2169 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 22 Jan 2022 07:51:02 +0100 Subject: [PATCH 0422/1637] sync: Improve git sync reliability --- admin/sync/importer_gitbin.go | 63 +++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/admin/sync/importer_gitbin.go b/admin/sync/importer_gitbin.go index eb83c2e0..f2062c63 100644 --- a/admin/sync/importer_gitbin.go +++ b/admin/sync/importer_gitbin.go @@ -5,9 +5,12 @@ package sync import ( "bytes" + "errors" "fmt" "log" + "os" "os/exec" + "path" "strings" ) @@ -77,20 +80,68 @@ func (i GitImporter) Init() error { func (i GitImporter) Sync() error { log.Println("Synchronizing local git repository...") - cmdremote := exec.Command("git", "-C", i.li.Base, "pull", "--rebase", "--force", "-X", "theirs", "--recurse-submodules=yes", "origin") - stdout, err := cmdremote.CombinedOutput() + cmdfetch := exec.Command("git", "-C", i.li.Base, "fetch", "origin") + stdout, err := cmdfetch.CombinedOutput() if err != nil { - log.Printf("Local git repository synchronization failed: %s\n%s", err, stdout) + log.Printf("Git repository fetch failed: %s\n%s", err, stdout) return fmt.Errorf("%w:\n%s", err, stdout) } - cmdsubmodule := exec.Command("git", "-C", i.li.Base, "submodule", "foreach", "--recursive", "git", "lfs", "pull") - stdout, err = cmdsubmodule.CombinedOutput() + cmdclean := exec.Command("git", "-C", i.li.Base, "clean", "-xfde", "*_MERGED") + stdout, err = cmdclean.CombinedOutput() if err != nil { - log.Printf("Local LFS synchronization failed: %s\n%s", err, stdout) + 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) + } + } + + cmdreset := exec.Command("git", "-C", i.li.Base, "reset", "--hard", "origin/master") + 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 + cmdsubreset := exec.Command("git", "-C", i.li.Base, "submodule", "foreach", "--recursive", "git", "reset", "--hard") + stdout, err = cmdsubreset.CombinedOutput() + if err != nil { + log.Printf("Local git repository submodule reset failed: %s\n%s", err, stdout) + return fmt.Errorf("%w:\n%s", err, stdout) + } + + cmdsubupdate := exec.Command("git", "-C", i.li.Base, "submodule", "update", "--init", "--recursive") + stdout, err = cmdsubupdate.CombinedOutput() + if err != nil { + log.Printf("Local git repository submodule update failed: %s\n%s", err, stdout) + return fmt.Errorf("%w:\n%s", err, stdout) + } + + 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 } From 86748b36c84219f5452c3d2e379e137dbe12bfa9 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 22 Jan 2022 08:10:53 +0100 Subject: [PATCH 0423/1637] sync: Use git reset --hard --recurse-submodule --- admin/sync/importer_gitbin.go | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/admin/sync/importer_gitbin.go b/admin/sync/importer_gitbin.go index f2062c63..6dc32583 100644 --- a/admin/sync/importer_gitbin.go +++ b/admin/sync/importer_gitbin.go @@ -104,7 +104,7 @@ func (i GitImporter) Sync() error { } } - cmdreset := exec.Command("git", "-C", i.li.Base, "reset", "--hard", "origin/master") + cmdreset := exec.Command("git", "-C", i.li.Base, "reset", "--hard", "--recurse-submodule", "origin/master") stdout, err = cmdreset.CombinedOutput() if err != nil { log.Printf("Local git repository reset failed: %s\n%s", err, stdout) @@ -113,20 +113,6 @@ func (i GitImporter) Sync() error { if _, err := os.Stat(path.Join(i.li.Base, ".gitmodules")); !errors.Is(err, os.ErrNotExist) { // Treat submodules - cmdsubreset := exec.Command("git", "-C", i.li.Base, "submodule", "foreach", "--recursive", "git", "reset", "--hard") - stdout, err = cmdsubreset.CombinedOutput() - if err != nil { - log.Printf("Local git repository submodule reset failed: %s\n%s", err, stdout) - return fmt.Errorf("%w:\n%s", err, stdout) - } - - cmdsubupdate := exec.Command("git", "-C", i.li.Base, "submodule", "update", "--init", "--recursive") - stdout, err = cmdsubupdate.CombinedOutput() - if err != nil { - log.Printf("Local git repository submodule update failed: %s\n%s", err, stdout) - return fmt.Errorf("%w:\n%s", err, stdout) - } - cmdsublfs := exec.Command("git", "-C", i.li.Base, "submodule", "foreach", "--recursive", "git", "lfs", "pull") stdout, err = cmdsublfs.CombinedOutput() if err != nil { From 3e8ab0bd1a6326af957fee91bdb14fc87883fc40 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 23 Jan 2022 16:10:01 +0000 Subject: [PATCH 0424/1637] chore(deps): update golang.org/x/crypto commit hash to 5e0467b --- go.mod | 2 +- go.sum | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index d151cdbb..fb91ed23 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/julienschmidt/httprouter v1.3.0 github.com/russross/blackfriday/v2 v2.1.0 github.com/studio-b12/gowebdav v0.0.0-20210917133250-a3a86976a1df - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 + golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f gopkg.in/fsnotify.v1 v1.4.7 diff --git a/go.sum b/go.sum index 935626f8..5024fce2 100644 --- a/go.sum +++ b/go.sum @@ -188,6 +188,8 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s3 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce h1:Roh6XWxHFKrPgC/EQhVubSAGQ6Ozk6IdxHSzt1mR0EI= +golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -250,6 +252,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxW golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210326060303-6b1517762897 h1:KrsHThm5nFk34YtATK1LsThyGhGbGe1olrte/HInHvs= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -296,6 +300,7 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= From b1961c7102fda2fd582b55c40cf636d32a696b81 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 23 Jan 2022 16:10:25 +0000 Subject: [PATCH 0425/1637] chore(deps): update module github.com/burntsushi/toml to v1 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index fb91ed23..d49bcdc5 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module srs.epita.fr/fic-server go 1.16 require ( - github.com/BurntSushi/toml v0.4.1 + github.com/BurntSushi/toml v1.0.0 github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/go-git/go-git/v5 v5.4.2 github.com/go-sql-driver/mysql v1.6.0 diff --git a/go.sum b/go.sum index 5024fce2..2303d768 100644 --- a/go.sum +++ b/go.sum @@ -34,6 +34,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU= +github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 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= From 59ff2e5a3a9e19c491fe2cbd8c7d99a61b9775a0 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 31 Jan 2022 20:19:35 +0000 Subject: [PATCH 0426/1637] chore(deps): update golang.org/x/crypto commit hash to 30dcbda --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index d49bcdc5..58e22ebd 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/julienschmidt/httprouter v1.3.0 github.com/russross/blackfriday/v2 v2.1.0 github.com/studio-b12/gowebdav v0.0.0-20210917133250-a3a86976a1df - golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce + golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f gopkg.in/fsnotify.v1 v1.4.7 diff --git a/go.sum b/go.sum index 2303d768..cfadd464 100644 --- a/go.sum +++ b/go.sum @@ -192,6 +192,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5U golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce h1:Roh6XWxHFKrPgC/EQhVubSAGQ6Ozk6IdxHSzt1mR0EI= golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 h1:71vQrMauZZhcTVK6KdYM+rklehEEwb3E+ZhaE5jrPrE= +golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= From 1d36de028d87a01fb86b8c82f97307561f6e0ef1 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 31 Jan 2022 22:15:00 +0000 Subject: [PATCH 0427/1637] chore(deps): update github.com/studio-b12/gowebdav commit hash to c7b1ff8 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 58e22ebd..bc285c4a 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/go-sql-driver/mysql v1.6.0 github.com/julienschmidt/httprouter v1.3.0 github.com/russross/blackfriday/v2 v2.1.0 - github.com/studio-b12/gowebdav v0.0.0-20210917133250-a3a86976a1df + github.com/studio-b12/gowebdav v0.0.0-20220128162035-c7b1ff8a5e62 golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f diff --git a/go.sum b/go.sum index cfadd464..0b041327 100644 --- a/go.sum +++ b/go.sum @@ -168,6 +168,8 @@ github.com/studio-b12/gowebdav v0.0.0-20210630100626-7ff61aa87be8 h1:ipNUBPHSUmH github.com/studio-b12/gowebdav v0.0.0-20210630100626-7ff61aa87be8/go.mod h1:gCcfDlA1Y7GqOaeEKw5l9dOGx1VLdc/HuQSlQAaZ30s= github.com/studio-b12/gowebdav v0.0.0-20210917133250-a3a86976a1df h1:C+J/LwTqP8gRPt1MdSzBNZP0OYuDm5wsmDKgwpLjYzo= github.com/studio-b12/gowebdav v0.0.0-20210917133250-a3a86976a1df/go.mod h1:gCcfDlA1Y7GqOaeEKw5l9dOGx1VLdc/HuQSlQAaZ30s= +github.com/studio-b12/gowebdav v0.0.0-20220128162035-c7b1ff8a5e62 h1:b2nJXyPCa9HY7giGM+kYcnQ71m14JnGdQabMPmyt++8= +github.com/studio-b12/gowebdav v0.0.0-20220128162035-c7b1ff8a5e62/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE= 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/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= From 5d36c8a2c25225af0da6fefd680840eadf86504f Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 2 Feb 2022 15:29:46 +0100 Subject: [PATCH 0428/1637] ui: Update node packages --- frontend/ui/package-lock.json | 374 ++++++++++-------- frontend/ui/package.json | 22 +- frontend/ui/src/hooks.js | 6 + .../ui/src/routes/[theme]/[exercice].svelte | 4 +- .../ui/src/routes/[theme]/__layout.svelte | 4 +- frontend/ui/src/routes/[theme]/index.svelte | 2 +- frontend/ui/src/routes/__layout.svelte | 2 +- frontend/ui/src/routes/edit.svelte | 2 +- frontend/ui/src/routes/index.svelte | 2 +- frontend/ui/src/routes/issues.svelte | 6 +- frontend/ui/src/routes/register.svelte | 2 +- frontend/ui/src/routes/tags/[tag].svelte | 4 +- frontend/ui/svelte.config.js | 2 +- 13 files changed, 236 insertions(+), 196 deletions(-) create mode 100644 frontend/ui/src/hooks.js diff --git a/frontend/ui/package-lock.json b/frontend/ui/package-lock.json index 563d3067..c86a0a20 100644 --- a/frontend/ui/package-lock.json +++ b/frontend/ui/package-lock.json @@ -84,14 +84,14 @@ "dev": true }, "@popperjs/core": { - "version": "2.10.2", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.10.2.tgz", - "integrity": "sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ==" + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz", + "integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==" }, "@rollup/pluginutils": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.1.tgz", - "integrity": "sha512-clDjivHqWGXi7u+0d2r2sBi4Ie6VLEAzWMIkvJLnDmxoOhBYOTfzGbOQBA32THHm11/LiJbd01tJUpJsbshSWQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.2.tgz", + "integrity": "sha512-ROn4qvkxP9SyPeHaf7uQC/GPFY6L/OWy9+bd9AwcjOAWQwxRscoEyAUD8qCY5o5iL4jqQwoLk2kaTKJPb/HwzQ==", "dev": true, "requires": { "estree-walker": "^2.0.1", @@ -99,34 +99,46 @@ } }, "@sveltejs/adapter-static": { - "version": "1.0.0-next.21", - "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-1.0.0-next.21.tgz", - "integrity": "sha512-B4+QoUVAaANKx+mHntG8SqF45zbj3Ct4Akg/cGauo6COyfKZRhO5OsMa+wPuT2TKJBZC4eEDK0p+p9nyQBkxKQ==" + "version": "1.0.0-next.26", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-1.0.0-next.26.tgz", + "integrity": "sha512-LXR0HkPygZ+m9wJhFqbYWbJ0jquhgUK6vL/8AwnqbAZGGtQFloMpf49WOANk7MiLBeY6L97W5jPLSxHiDW3T0Q==", + "requires": { + "tiny-glob": "^0.2.9" + } }, "@sveltejs/kit": { - "version": "1.0.0-next.196", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.196.tgz", - "integrity": "sha512-z7sA/2/3Il5biibjPXCYXJC11TyFhgCvMaJvvbtMnu2l3EmOmJBMS+r2djGptzfFsugSVNwGQFn8+ldWQWq3jA==", + "version": "1.0.0-next.256", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.256.tgz", + "integrity": "sha512-H0g/zOxyOoRKNieovgD+jr5Mq9GhD1SjRu0u3P87L6Ci9K0BBsf2y2Ry41RgxfrP92S87YnY1dMK8e1rm5PLNA==", "dev": true, "requires": { - "@sveltejs/vite-plugin-svelte": "^1.0.0-next.30", - "cheap-watch": "^1.0.4", + "@sveltejs/vite-plugin-svelte": "^1.0.0-next.32", "sade": "^1.7.4", - "vite": "^2.6.12" + "vite": "^2.7.2" } }, "@sveltejs/vite-plugin-svelte": { - "version": "1.0.0-next.30", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.30.tgz", - "integrity": "sha512-YQqdMxjL1VgSFk4/+IY3yLwuRRapPafPiZTiaGEq1psbJYSNYUWx9F1zMm32GMsnogg3zn99mGJOqe3ld3HZSg==", + "version": "1.0.0-next.36", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.36.tgz", + "integrity": "sha512-X7lTiioTGC3ri5M299fxc2dimuKU7f22zTXcmD+NrF+fO9/b7YNfLeQQwWV7Tvv9REysMlR4G2HQF6+lY62p/Q==", "dev": true, "requires": { - "@rollup/pluginutils": "^4.1.1", - "debug": "^4.3.2", + "@rollup/pluginutils": "^4.1.2", + "debug": "^4.3.3", "kleur": "^4.1.4", "magic-string": "^0.25.7", - "require-relative": "^0.8.7", - "svelte-hmr": "^0.14.7" + "svelte-hmr": "^0.14.9" + }, + "dependencies": { + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + } } }, "acorn": { @@ -217,9 +229,9 @@ "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==" }, "bootstrap-icons": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.7.0.tgz", - "integrity": "sha512-x2ORQ3lv1QdSnIzzQdU8mf83fbCq4FIaUinrAWw+a/d186vO3Ph7qUfqzvQDBA41AD6IoxdRHD6zUt9IXC1J6A==" + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.8.0.tgz", + "integrity": "sha512-plaZQb8tReUULAwQ9M98PyWh5H912eKGVC6etMtc6VqmPmp9Eq0s7Wd0qvPoPLHh0VhXxzdLk1ta5W7lwPIdCQ==" }, "bootswatch": { "version": "5.1.3", @@ -302,16 +314,10 @@ } } }, - "cheap-watch": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cheap-watch/-/cheap-watch-1.0.4.tgz", - "integrity": "sha512-QR/9FrtRL5fjfUJBhAKCdi0lSRQ3rVRRum3GF9wDKp2TJbEIMGhUEr2yU8lORzm9Isdjx7/k9S0DFDx+z5VGtw==", - "dev": true - }, "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -396,146 +402,146 @@ } }, "esbuild": { - "version": "0.13.13", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.13.tgz", - "integrity": "sha512-Z17A/R6D0b4s3MousytQ/5i7mTCbaF+Ua/yPfoe71vdTv4KBvVAvQ/6ytMngM2DwGJosl8WxaD75NOQl2QF26Q==", + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.15.tgz", + "integrity": "sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw==", "dev": true, "requires": { - "esbuild-android-arm64": "0.13.13", - "esbuild-darwin-64": "0.13.13", - "esbuild-darwin-arm64": "0.13.13", - "esbuild-freebsd-64": "0.13.13", - "esbuild-freebsd-arm64": "0.13.13", - "esbuild-linux-32": "0.13.13", - "esbuild-linux-64": "0.13.13", - "esbuild-linux-arm": "0.13.13", - "esbuild-linux-arm64": "0.13.13", - "esbuild-linux-mips64le": "0.13.13", - "esbuild-linux-ppc64le": "0.13.13", - "esbuild-netbsd-64": "0.13.13", - "esbuild-openbsd-64": "0.13.13", - "esbuild-sunos-64": "0.13.13", - "esbuild-windows-32": "0.13.13", - "esbuild-windows-64": "0.13.13", - "esbuild-windows-arm64": "0.13.13" + "esbuild-android-arm64": "0.13.15", + "esbuild-darwin-64": "0.13.15", + "esbuild-darwin-arm64": "0.13.15", + "esbuild-freebsd-64": "0.13.15", + "esbuild-freebsd-arm64": "0.13.15", + "esbuild-linux-32": "0.13.15", + "esbuild-linux-64": "0.13.15", + "esbuild-linux-arm": "0.13.15", + "esbuild-linux-arm64": "0.13.15", + "esbuild-linux-mips64le": "0.13.15", + "esbuild-linux-ppc64le": "0.13.15", + "esbuild-netbsd-64": "0.13.15", + "esbuild-openbsd-64": "0.13.15", + "esbuild-sunos-64": "0.13.15", + "esbuild-windows-32": "0.13.15", + "esbuild-windows-64": "0.13.15", + "esbuild-windows-arm64": "0.13.15" } }, "esbuild-android-arm64": { - "version": "0.13.13", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.13.tgz", - "integrity": "sha512-T02aneWWguJrF082jZworjU6vm8f4UQ+IH2K3HREtlqoY9voiJUwHLRL6khRlsNLzVglqgqb7a3HfGx7hAADCQ==", + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.15.tgz", + "integrity": "sha512-m602nft/XXeO8YQPUDVoHfjyRVPdPgjyyXOxZ44MK/agewFFkPa8tUo6lAzSWh5Ui5PB4KR9UIFTSBKh/RrCmg==", "dev": true, "optional": true }, "esbuild-darwin-64": { - "version": "0.13.13", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.13.tgz", - "integrity": "sha512-wkaiGAsN/09X9kDlkxFfbbIgR78SNjMOfUhoel3CqKBDsi9uZhw7HBNHNxTzYUK8X8LAKFpbODgcRB3b/I8gHA==", + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.15.tgz", + "integrity": "sha512-ihOQRGs2yyp7t5bArCwnvn2Atr6X4axqPpEdCFPVp7iUj4cVSdisgvEKdNR7yH3JDjW6aQDw40iQFoTqejqxvQ==", "dev": true, "optional": true }, "esbuild-darwin-arm64": { - "version": "0.13.13", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.13.tgz", - "integrity": "sha512-b02/nNKGSV85Gw9pUCI5B48AYjk0vFggDeom0S6QMP/cEDtjSh1WVfoIFNAaLA0MHWfue8KBwoGVsN7rBshs4g==", + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.15.tgz", + "integrity": "sha512-i1FZssTVxUqNlJ6cBTj5YQj4imWy3m49RZRnHhLpefFIh0To05ow9DTrXROTE1urGTQCloFUXTX8QfGJy1P8dQ==", "dev": true, "optional": true }, "esbuild-freebsd-64": { - "version": "0.13.13", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.13.tgz", - "integrity": "sha512-ALgXYNYDzk9YPVk80A+G4vz2D22Gv4j4y25exDBGgqTcwrVQP8rf/rjwUjHoh9apP76oLbUZTmUmvCMuTI1V9A==", + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.15.tgz", + "integrity": "sha512-G3dLBXUI6lC6Z09/x+WtXBXbOYQZ0E8TDBqvn7aMaOCzryJs8LyVXKY4CPnHFXZAbSwkCbqiPuSQ1+HhrNk7EA==", "dev": true, "optional": true }, "esbuild-freebsd-arm64": { - "version": "0.13.13", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.13.tgz", - "integrity": "sha512-uFvkCpsZ1yqWQuonw5T1WZ4j59xP/PCvtu6I4pbLejhNo4nwjW6YalqnBvBSORq5/Ifo9S/wsIlVHzkzEwdtlw==", + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.15.tgz", + "integrity": "sha512-KJx0fzEDf1uhNOZQStV4ujg30WlnwqUASaGSFPhznLM/bbheu9HhqZ6mJJZM32lkyfGJikw0jg7v3S0oAvtvQQ==", "dev": true, "optional": true }, "esbuild-linux-32": { - "version": "0.13.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.13.tgz", - "integrity": "sha512-yxR9BBwEPs9acVEwTrEE2JJNHYVuPQC9YGjRfbNqtyfK/vVBQYuw8JaeRFAvFs3pVJdQD0C2BNP4q9d62SCP4w==", + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.15.tgz", + "integrity": "sha512-ZvTBPk0YWCLMCXiFmD5EUtB30zIPvC5Itxz0mdTu/xZBbbHJftQgLWY49wEPSn2T/TxahYCRDWun5smRa0Tu+g==", "dev": true, "optional": true }, "esbuild-linux-64": { - "version": "0.13.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.13.tgz", - "integrity": "sha512-kzhjlrlJ+6ESRB/n12WTGll94+y+HFeyoWsOrLo/Si0s0f+Vip4b8vlnG0GSiS6JTsWYAtGHReGczFOaETlKIw==", + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.15.tgz", + "integrity": "sha512-eCKzkNSLywNeQTRBxJRQ0jxRCl2YWdMB3+PkWFo2BBQYC5mISLIVIjThNtn6HUNqua1pnvgP5xX0nHbZbPj5oA==", "dev": true, "optional": true }, "esbuild-linux-arm": { - "version": "0.13.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.13.tgz", - "integrity": "sha512-hXub4pcEds+U1TfvLp1maJ+GHRw7oizvzbGRdUvVDwtITtjq8qpHV5Q5hWNNn6Q+b3b2UxF03JcgnpzCw96nUQ==", + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.15.tgz", + "integrity": "sha512-wUHttDi/ol0tD8ZgUMDH8Ef7IbDX+/UsWJOXaAyTdkT7Yy9ZBqPg8bgB/Dn3CZ9SBpNieozrPRHm0BGww7W/jA==", "dev": true, "optional": true }, "esbuild-linux-arm64": { - "version": "0.13.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.13.tgz", - "integrity": "sha512-KMrEfnVbmmJxT3vfTnPv/AiXpBFbbyExH13BsUGy1HZRPFMi5Gev5gk8kJIZCQSRfNR17aqq8sO5Crm2KpZkng==", + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.15.tgz", + "integrity": "sha512-bYpuUlN6qYU9slzr/ltyLTR9YTBS7qUDymO8SV7kjeNext61OdmqFAzuVZom+OLW1HPHseBfJ/JfdSlx8oTUoA==", "dev": true, "optional": true }, "esbuild-linux-mips64le": { - "version": "0.13.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.13.tgz", - "integrity": "sha512-cJT9O1LYljqnnqlHaS0hdG73t7hHzF3zcN0BPsjvBq+5Ad47VJun+/IG4inPhk8ta0aEDK6LdP+F9299xa483w==", + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.15.tgz", + "integrity": "sha512-KlVjIG828uFPyJkO/8gKwy9RbXhCEUeFsCGOJBepUlpa7G8/SeZgncUEz/tOOUJTcWMTmFMtdd3GElGyAtbSWg==", "dev": true, "optional": true }, "esbuild-linux-ppc64le": { - "version": "0.13.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.13.tgz", - "integrity": "sha512-+rghW8st6/7O6QJqAjVK3eXzKkZqYAw6LgHv7yTMiJ6ASnNvghSeOcIvXFep3W2oaJc35SgSPf21Ugh0o777qQ==", + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.15.tgz", + "integrity": "sha512-h6gYF+OsaqEuBjeesTBtUPw0bmiDu7eAeuc2OEH9S6mV9/jPhPdhOWzdeshb0BskRZxPhxPOjqZ+/OqLcxQwEQ==", "dev": true, "optional": true }, "esbuild-netbsd-64": { - "version": "0.13.13", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.13.tgz", - "integrity": "sha512-A/B7rwmzPdzF8c3mht5TukbnNwY5qMJqes09ou0RSzA5/jm7Jwl/8z853ofujTFOLhkNHUf002EAgokzSgEMpQ==", + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.15.tgz", + "integrity": "sha512-3+yE9emwoevLMyvu+iR3rsa+Xwhie7ZEHMGDQ6dkqP/ndFzRHkobHUKTe+NCApSqG5ce2z4rFu+NX/UHnxlh3w==", "dev": true, "optional": true }, "esbuild-openbsd-64": { - "version": "0.13.13", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.13.tgz", - "integrity": "sha512-szwtuRA4rXKT3BbwoGpsff6G7nGxdKgUbW9LQo6nm0TVCCjDNDC/LXxT994duIW8Tyq04xZzzZSW7x7ttDiw1w==", + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.15.tgz", + "integrity": "sha512-wTfvtwYJYAFL1fSs8yHIdf5GEE4NkbtbXtjLWjM3Cw8mmQKqsg8kTiqJ9NJQe5NX/5Qlo7Xd9r1yKMMkHllp5g==", "dev": true, "optional": true }, "esbuild-sunos-64": { - "version": "0.13.13", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.13.tgz", - "integrity": "sha512-ihyds9O48tVOYF48iaHYUK/boU5zRaLOXFS+OOL3ceD39AyHo46HVmsJLc7A2ez0AxNZCxuhu+P9OxfPfycTYQ==", + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.15.tgz", + "integrity": "sha512-lbivT9Bx3t1iWWrSnGyBP9ODriEvWDRiweAs69vI+miJoeKwHWOComSRukttbuzjZ8r1q0mQJ8Z7yUsDJ3hKdw==", "dev": true, "optional": true }, "esbuild-windows-32": { - "version": "0.13.13", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.13.tgz", - "integrity": "sha512-h2RTYwpG4ldGVJlbmORObmilzL8EECy8BFiF8trWE1ZPHLpECE9//J3Bi+W3eDUuv/TqUbiNpGrq4t/odbayUw==", + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.15.tgz", + "integrity": "sha512-fDMEf2g3SsJ599MBr50cY5ve5lP1wyVwTe6aLJsM01KtxyKkB4UT+fc5MXQFn3RLrAIAZOG+tHC+yXObpSn7Nw==", "dev": true, "optional": true }, "esbuild-windows-64": { - "version": "0.13.13", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.13.tgz", - "integrity": "sha512-oMrgjP4CjONvDHe7IZXHrMk3wX5Lof/IwFEIbwbhgbXGBaN2dke9PkViTiXC3zGJSGpMvATXVplEhlInJ0drHA==", + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.15.tgz", + "integrity": "sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ==", "dev": true, "optional": true }, "esbuild-windows-arm64": { - "version": "0.13.13", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.13.tgz", - "integrity": "sha512-6fsDfTuTvltYB5k+QPah/x7LrI2+OLAJLE3bWLDiZI6E8wXMQU+wLqtEO/U/RvJgVY1loPs5eMpUBpVajczh1A==", + "version": "0.13.15", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.15.tgz", + "integrity": "sha512-zzvyCVVpbwQQATaf3IG8mu1IwGEiDxKkYUdA4FpoCHi1KtPa13jeScYDjlW0Qh+ebWzpKfR2ZwvqAQkSWNcKjA==", "dev": true, "optional": true }, @@ -600,9 +606,9 @@ "dev": true }, "eslint-plugin-svelte3": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-svelte3/-/eslint-plugin-svelte3-3.2.1.tgz", - "integrity": "sha512-YoBR9mLoKCjGghJ/gvpnFZKaMEu/VRcuxpSRS8KuozuEo7CdBH7bmBHa6FmMm0i4kJnOyx+PVsaptz96K6H/4Q==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-svelte3/-/eslint-plugin-svelte3-3.4.0.tgz", + "integrity": "sha512-MIQUTuRv3o7LyQ+360qOc9mLT35j1I5YzHr04g/UDcvJTpg0X/kHWELY99ve869Rp/9wjqD7I26Aq5H8OH5RIg==", "dev": true }, "eslint-scope": { @@ -824,6 +830,16 @@ "type-fest": "^0.20.2" } }, + "globalyzer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", + "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==" + }, + "globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==" + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -850,6 +866,12 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, + "immutable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", + "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", + "dev": true + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -892,9 +914,9 @@ } }, "is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", "dev": true, "requires": { "has": "^1.0.3" @@ -1041,9 +1063,9 @@ "dev": true }, "nanoid": { - "version": "3.1.30", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz", - "integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", + "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", "dev": true }, "natural-compare": { @@ -1121,20 +1143,20 @@ "dev": true }, "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "postcss": { - "version": "8.3.11", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.11.tgz", - "integrity": "sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA==", + "version": "8.4.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.6.tgz", + "integrity": "sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==", "dev": true, "requires": { - "nanoid": "^3.1.30", + "nanoid": "^3.2.0", "picocolors": "^1.0.0", - "source-map-js": "^0.6.2" + "source-map-js": "^1.0.2" } }, "prelude-ls": { @@ -1144,15 +1166,15 @@ "dev": true }, "prettier": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", - "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", + "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", "dev": true }, "prettier-plugin-svelte": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-2.4.0.tgz", - "integrity": "sha512-JwJ9bOz4XHLQtiLnX4mTSSDUdhu12WH8sTwy/XTDCSyPlah6IcV7NWeYBZscPEcceu2YnW8Y9sJCP40Z2UH9GA==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-2.6.0.tgz", + "integrity": "sha512-NPSRf6Y5rufRlBleok/pqg4+1FyGsL0zYhkYP6hnueeL1J/uCm3OfOZPsLX4zqD9VAdcXfyEL2PYqGv8ZoOSfA==", "dev": true }, "progress": { @@ -1188,20 +1210,15 @@ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true }, - "require-relative": { - "version": "0.8.7", - "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", - "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", - "dev": true - }, "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", "dev": true, "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } }, "resolve-from": { @@ -1220,36 +1237,38 @@ } }, "rollup": { - "version": "2.60.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.60.0.tgz", - "integrity": "sha512-cHdv9GWd58v58rdseC8e8XIaPUo8a9cgZpnCMMDGZFDZKEODOiPPEQFXLriWr/TjXzhPPmG5bkAztPsOARIcGQ==", + "version": "2.67.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.67.0.tgz", + "integrity": "sha512-W83AaERwvDiHwHEF/dfAfS3z1Be5wf7n+pO3ZAO5IQadCT2lBTr7WQ2MwZZe+nodbD+n3HtC4OCOAdsOPPcKZQ==", "dev": true, "requires": { "fsevents": "~2.3.2" } }, "sade": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/sade/-/sade-1.7.4.tgz", - "integrity": "sha512-y5yauMD93rX840MwUJr7C1ysLFBgMspsdTo4UVrDg3fXDvtwOyIqykhVAAm6fk/3au77773itJStObgK+LKaiA==", + "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, "requires": { "mri": "^1.1.0" } }, "sass": { - "version": "1.43.4", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.43.4.tgz", - "integrity": "sha512-/ptG7KE9lxpGSYiXn7Ar+lKOv37xfWsZRtFYal2QHNigyVQDx685VFT/h7ejVr+R8w7H4tmUgtulsKl5YpveOg==", + "version": "1.49.7", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.49.7.tgz", + "integrity": "sha512-13dml55EMIR2rS4d/RDHHP0sXMY3+30e1TKsyXaSz3iLWVoDWEoboY8WzJd5JMnxrRHffKO3wq2mpJ0jxRJiEQ==", "dev": true, "requires": { - "chokidar": ">=3.0.0 <4.0.0" + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" } }, "sass-loader": { - "version": "12.3.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.3.0.tgz", - "integrity": "sha512-6l9qwhdOb7qSrtOu96QQ81LVl8v6Dp9j1w3akOm0aWHyrTYtagDt5+kS32N4yq4hHk3M+rdqoRMH+lIdqvW6HA==", + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.4.0.tgz", + "integrity": "sha512-7xN+8khDIzym1oL9XyS6zP6Ges+Bo2B2xbPrjdMHEYyV3AQYhd/wXeru++3ODHF0zMjYmVadblSKrPrjEkL8mg==", "dev": true, "requires": { "klona": "^2.0.4", @@ -1323,9 +1342,9 @@ } }, "source-map-js": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", - "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "dev": true }, "sourcemap-codec": { @@ -1375,22 +1394,28 @@ "has-flag": "^3.0.0" } }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, "svelte": { - "version": "3.44.1", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.44.1.tgz", - "integrity": "sha512-4DrCEJoBvdR689efHNSxIQn2pnFwB7E7j2yLEJtHE/P8hxwZWIphCtJ8are7bjl/iVMlcEf5uh5pJ68IwR09vQ==", + "version": "3.46.3", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.46.3.tgz", + "integrity": "sha512-mTOXvv74CVQqJHqoIZDprVfRKVVmYNadXP0VKnOEA54223kLGCr1nMrifS4Zx29qMt/xRB38Eq1D7dDH/fM8fQ==", "dev": true }, "svelte-hmr": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.14.7.tgz", - "integrity": "sha512-pDrzgcWSoMaK6AJkBWkmgIsecW0GChxYZSZieIYfCP0v2oPyx2CYU/zm7TBIcjLVUPP714WxmViE9Thht4etog==", + "version": "0.14.9", + "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.14.9.tgz", + "integrity": "sha512-bKE9+4qb4sAnA+TKHiYurUl970rjA0XmlP9TEP7K/ncyWz3m81kA4HOgmlZK/7irGK7gzZlaPDI3cmf8fp/+tg==", "dev": true }, "sveltestrap": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/sveltestrap/-/sveltestrap-5.6.3.tgz", - "integrity": "sha512-/geTKJbPmJGzwHFKYC3NkUNDk/GKxrppgdSxcg58w/qcxs0S6RiN4PaQ1tgBKsdSrZDfbHfkFF+dybHAyUlV0A==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/sveltestrap/-/sveltestrap-5.8.3.tgz", + "integrity": "sha512-6Ldzz62BwfqBaz9UaGwXg0E6PfXZc24vAg/nEGaJlB7Bv071ctT07EylqEhXdCoS1JE+NnhT2ByyP2SxJseYHQ==", "dev": true, "requires": { "@popperjs/core": "^2.9.2" @@ -1436,6 +1461,15 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "tiny-glob": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", + "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", + "requires": { + "globalyzer": "0.1.0", + "globrex": "^0.1.2" + } + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1476,16 +1510,16 @@ "dev": true }, "vite": { - "version": "2.6.14", - "resolved": "https://registry.npmjs.org/vite/-/vite-2.6.14.tgz", - "integrity": "sha512-2HA9xGyi+EhY2MXo0+A2dRsqsAG3eFNEVIo12olkWhOmc8LfiM+eMdrXf+Ruje9gdXgvSqjLI9freec1RUM5EA==", + "version": "2.7.13", + "resolved": "https://registry.npmjs.org/vite/-/vite-2.7.13.tgz", + "integrity": "sha512-Mq8et7f3aK0SgSxjDNfOAimZGW9XryfHRa/uV0jseQSilg+KhYDSoNb9h1rknOy6SuMkvNDLKCYAYYUMCE+IgQ==", "dev": true, "requires": { - "esbuild": "^0.13.2", + "esbuild": "^0.13.12", "fsevents": "~2.3.2", - "postcss": "^8.3.8", + "postcss": "^8.4.5", "resolve": "^1.20.0", - "rollup": "^2.57.0" + "rollup": "^2.59.0" } }, "which": { diff --git a/frontend/ui/package.json b/frontend/ui/package.json index b85aa3ca..399b4a60 100644 --- a/frontend/ui/package.json +++ b/frontend/ui/package.json @@ -9,23 +9,23 @@ "format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ." }, "devDependencies": { - "@sveltejs/kit": "^1.0.0-next.196", + "@sveltejs/kit": "^1.0.0-next.256", "eslint": "^7.22.0", "eslint-config-prettier": "^8.1.0", - "eslint-plugin-svelte3": "^3.2.1", - "prettier": "^2.4.1", - "prettier-plugin-svelte": "^2.4.0", - "sass": "^1.43.4", - "sass-loader": "^12.3.0", - "svelte": "^3.44.1", - "sveltestrap": "^5.6.3" + "eslint-plugin-svelte3": "^3.4.0", + "prettier": "^2.5.1", + "prettier-plugin-svelte": "^2.6.0", + "sass": "^1.49.7", + "sass-loader": "^12.4.0", + "svelte": "^3.46.3", + "sveltestrap": "^5.8.3" }, "type": "module", "dependencies": { - "@popperjs/core": "^2.10.2", - "@sveltejs/adapter-static": "^1.0.0-next.21", + "@popperjs/core": "^2.11.2", + "@sveltejs/adapter-static": "^1.0.0-next.26", "bootstrap": "^5.1.3", - "bootstrap-icons": "^1.7.0", + "bootstrap-icons": "^1.8.0", "bootswatch": "^5.1.3", "hash-wasm": "^4.9.0", "seedrandom": "^3.0.5" diff --git a/frontend/ui/src/hooks.js b/frontend/ui/src/hooks.js new file mode 100644 index 00000000..edd43793 --- /dev/null +++ b/frontend/ui/src/hooks.js @@ -0,0 +1,6 @@ +export async function handle({ event, resolve }) { + const response = await resolve(event, { + ssr: false, + }); + return response; +} diff --git a/frontend/ui/src/routes/[theme]/[exercice].svelte b/frontend/ui/src/routes/[theme]/[exercice].svelte index 5b8e07cf..ed5b88df 100644 --- a/frontend/ui/src/routes/[theme]/[exercice].svelte +++ b/frontend/ui/src/routes/[theme]/[exercice].svelte @@ -3,11 +3,11 @@ import { themes } from '../../stores/themes.js'; - export async function load({ page, fetch, session, stuff }) { + export async function load({ params, stuff }) { let exercice = null; for (let ex in stuff.theme.exercices) { - if (stuff.theme.exercices[ex].urlid === page.params.exercice) { + if (stuff.theme.exercices[ex].urlid === params.exercice) { exercice = stuff.theme.exercices[ex]; exercice.id = ex; break; diff --git a/frontend/ui/src/routes/[theme]/__layout.svelte b/frontend/ui/src/routes/[theme]/__layout.svelte index fc0e2632..dca44ac2 100644 --- a/frontend/ui/src/routes/[theme]/__layout.svelte +++ b/frontend/ui/src/routes/[theme]/__layout.svelte @@ -3,12 +3,12 @@ import { themes } from '../../stores/themes.js'; - export async function load({ page, fetch, session, stuff }) { + export async function load({ params, stuff }) { const thms = get_store_value(themes); let theme = null; for (let th in thms) { - if (thms[th] && thms[th].urlid === page.params.theme) { + if (thms[th] && thms[th].urlid === params.theme) { theme = thms[th]; break; } diff --git a/frontend/ui/src/routes/[theme]/index.svelte b/frontend/ui/src/routes/[theme]/index.svelte index a08adb31..a808c068 100644 --- a/frontend/ui/src/routes/[theme]/index.svelte +++ b/frontend/ui/src/routes/[theme]/index.svelte @@ -1,5 +1,5 @@ @@ -53,9 +54,9 @@ Classement Vidéos diff --git a/frontend/ui/src/routes/[theme]/[exercice].svelte b/frontend/ui/src/routes/[theme]/[exercice].svelte index ed5b88df..0e5fd189 100644 --- a/frontend/ui/src/routes/[theme]/[exercice].svelte +++ b/frontend/ui/src/routes/[theme]/[exercice].svelte @@ -42,6 +42,7 @@ import ExerciceVideo from '../../components/ExerciceVideo.svelte'; import ThemeNav from '../../components/ThemeNav.svelte'; + import { challengeInfo } from '../../stores/challengeinfo.js'; import { my } from '../../stores/my.js'; import { settings } from '../../stores/settings.js'; @@ -54,7 +55,7 @@ - {exercice?exercice.title+" - ":""}{$settings.title} + {exercice?exercice.title+" - ":""}{$challengeInfo.title} {#if exercice} diff --git a/frontend/ui/src/routes/[theme]/__layout.svelte b/frontend/ui/src/routes/[theme]/__layout.svelte index dca44ac2..1cc12bb7 100644 --- a/frontend/ui/src/routes/[theme]/__layout.svelte +++ b/frontend/ui/src/routes/[theme]/__layout.svelte @@ -30,13 +30,13 @@ Container, } from 'sveltestrap'; - import { settings } from '../../stores/settings.js'; + import { challengeInfo } from '../../stores/challengeinfo.js'; export let theme; - {theme?theme.name:""} - {$settings.title} + {theme?theme.name:""} - {$challengeInfo.title} {#if theme} diff --git a/frontend/ui/src/routes/__layout.svelte b/frontend/ui/src/routes/__layout.svelte index 9fd2f9af..8d59cb6e 100644 --- a/frontend/ui/src/routes/__layout.svelte +++ b/frontend/ui/src/routes/__layout.svelte @@ -1,4 +1,5 @@ - {$settings.title} + {$challengeInfo.title} + diff --git a/frontend/ui/src/routes/rank.svelte b/frontend/ui/src/routes/rank.svelte index 568365a8..1a85a2ba 100644 --- a/frontend/ui/src/routes/rank.svelte +++ b/frontend/ui/src/routes/rank.svelte @@ -12,7 +12,7 @@ import { my } from '../stores/my.js'; import { rank } from '../stores/teams.js'; - import { settings } from '../stores/settings.js'; + import { challengeInfo } from '../stores/challengeinfo.js'; import CardTheme from '../components/CardTheme.svelte'; @@ -21,7 +21,7 @@

- {$settings.title} + {$challengeInfo.title} Classement

diff --git a/frontend/ui/src/routes/rules.svelte b/frontend/ui/src/routes/rules.svelte index 02d09e19..e32da6dd 100644 --- a/frontend/ui/src/routes/rules.svelte +++ b/frontend/ui/src/routes/rules.svelte @@ -6,12 +6,13 @@ Icon, } from 'sveltestrap'; + import { challengeInfo } from '../stores/challengeinfo.js'; import { settings } from '../stores/settings.js';

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

diff --git a/frontend/ui/src/stores/challengeinfo.js b/frontend/ui/src/stores/challengeinfo.js new file mode 100644 index 00000000..ad984b53 --- /dev/null +++ b/frontend/ui/src/stores/challengeinfo.js @@ -0,0 +1,22 @@ +import { readable, writable } from 'svelte/store'; + +function createChallengeStore() { + const { subscribe, set, update } = writable({}); + + return { + subscribe, + 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/libfic/zqsd.go b/libfic/zqsd.go index 66118fa1..6bd0cd23 100644 --- a/libfic/zqsd.go +++ b/libfic/zqsd.go @@ -20,7 +20,7 @@ type ChallengeZQDS struct { type SessionZQDS struct { Name string `json:"name"` Description string `json:"description"` - Scenario string `json:"scenario"` + Scenario string `json:"scenario,omitempty"` YourMission string `json:"your_mission"` Rules string `json:"rules"` Start time.Time `json:"start"` @@ -28,7 +28,7 @@ type SessionZQDS struct { Challenges []*ChallengeZQDS `json:"challenges"` } -func GenZQDSSessionFile(s *settings.Settings) (*SessionZQDS, error) { +func GenZQDSSessionFile(c *settings.ChallengeInfo, s *settings.Settings) (*SessionZQDS, error) { themes, err := GetThemes() if err != nil { return nil, err @@ -58,11 +58,10 @@ func GenZQDSSessionFile(s *settings.Settings) (*SessionZQDS, error) { } return &SessionZQDS{ - Name: "Challenge Forensic", - Description: "", - Scenario: "", - YourMission: "", - Rules: "", + Name: c.Title, + Description: c.Description, + Rules: c.Rules, + YourMission: c.YourMission, Start: s.Start, End: s.End, Challenges: challenges, diff --git a/settings/challenge.go b/settings/challenge.go new file mode 100644 index 00000000..6c39b5ed --- /dev/null +++ b/settings/challenge.go @@ -0,0 +1,59 @@ +package settings + +import ( + "encoding/json" + "os" +) + +// ChallengeFile is the expected name of the file containing the challenge infos. +const ChallengeFile = "challenge.json" + +// ChallengeInfo stores common descriptions and informations about the challenge. +type ChallengeInfo struct { + // Title is the displayed name of the challenge. + Title string `json:"title"` + // Authors is the group name of people making the challenge. + Authors string `json:"authors"` + // VideoLink is the link to explaination videos when the challenge is over. + VideosLink string `json:"videoslink"` + + // Description gives an overview of the challenge. + Description string `json:"description"` + // Rules tell the player some help. + Rules string `json:"rules"` + // YourMission is a small introduction to understand the goals. + YourMission string `json:"your_mission"` +} + +// ReadChallenge parses the file at the given location. +func ReadChallengeInfo(path string) (*ChallengeInfo, error) { + var s ChallengeInfo + if fd, err := os.Open(path); err != nil { + return nil, err + } else { + defer fd.Close() + jdec := json.NewDecoder(fd) + + if err := jdec.Decode(&s); err != nil { + return &s, err + } + + return &s, nil + } +} + +// SaveChallenge saves challenge at the given location. +func SaveChallengeInfo(path string, s *ChallengeInfo) 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 + } +} diff --git a/settings/settings.go b/settings/settings.go index b2d74d24..a16f5207 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -22,12 +22,6 @@ var SettingsDir string = "./SETTINGS" // Settings represents the settings panel. type Settings struct { - // Title is the displayed name of the challenge. - Title string `json:"title"` - // Authors is the group name of people making the challenge. - Authors string `json:"authors"` - // VideoLink is the link to explaination videos when the challenge is over. - VideosLink string `json:"videoslink"` // WorkInProgress indicates if the current challenge is under development or if it is in production. WorkInProgress bool `json:"wip,omitempty"` From c713a0a25d8a4ffb8c49054751af0bfa58d3d62c Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 1 May 2022 22:50:16 +0200 Subject: [PATCH 0468/1637] ui: Update node modules --- frontend/ui/jsconfig.json | 1 + frontend/ui/package-lock.json | 319 ++++++++++++++++++---------------- frontend/ui/package.json | 24 +-- frontend/ui/src/app.html | 2 +- frontend/ui/svelte.config.js | 3 - 5 files changed, 186 insertions(+), 163 deletions(-) diff --git a/frontend/ui/jsconfig.json b/frontend/ui/jsconfig.json index 3757b0e2..fe1b5b6b 100644 --- a/frontend/ui/jsconfig.json +++ b/frontend/ui/jsconfig.json @@ -1,4 +1,5 @@ { + "extends": "./.svelte-kit/tsconfig.json", "compilerOptions": { "baseUrl": ".", "paths": { diff --git a/frontend/ui/package-lock.json b/frontend/ui/package-lock.json index c86a0a20..60515b96 100644 --- a/frontend/ui/package-lock.json +++ b/frontend/ui/package-lock.json @@ -84,14 +84,14 @@ "dev": true }, "@popperjs/core": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz", - "integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==" + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz", + "integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==" }, "@rollup/pluginutils": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.2.tgz", - "integrity": "sha512-ROn4qvkxP9SyPeHaf7uQC/GPFY6L/OWy9+bd9AwcjOAWQwxRscoEyAUD8qCY5o5iL4jqQwoLk2kaTKJPb/HwzQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", "dev": true, "requires": { "estree-walker": "^2.0.1", @@ -99,41 +99,42 @@ } }, "@sveltejs/adapter-static": { - "version": "1.0.0-next.26", - "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-1.0.0-next.26.tgz", - "integrity": "sha512-LXR0HkPygZ+m9wJhFqbYWbJ0jquhgUK6vL/8AwnqbAZGGtQFloMpf49WOANk7MiLBeY6L97W5jPLSxHiDW3T0Q==", + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-1.0.0-next.29.tgz", + "integrity": "sha512-0hjGnfT3BRyoHnzJ2w0/xL+xICRpKneDTm45ZzggiRrc0r71WJfF6toGeg8N4QUQnj8EJ3Itm453gsS1kt7VUQ==", "requires": { "tiny-glob": "^0.2.9" } }, "@sveltejs/kit": { - "version": "1.0.0-next.256", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.256.tgz", - "integrity": "sha512-H0g/zOxyOoRKNieovgD+jr5Mq9GhD1SjRu0u3P87L6Ci9K0BBsf2y2Ry41RgxfrP92S87YnY1dMK8e1rm5PLNA==", + "version": "1.0.0-next.324", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.0.0-next.324.tgz", + "integrity": "sha512-/CGW9rQpHQLBb2EcMw08yelD/C9hTsypymctUWdhryMTI8n1VWb0gkUcSHsz8n8oAAbKLXqwyHqeLATfcIMg2w==", "dev": true, "requires": { "@sveltejs/vite-plugin-svelte": "^1.0.0-next.32", + "chokidar": "^3.5.3", "sade": "^1.7.4", - "vite": "^2.7.2" + "vite": "^2.9.0" } }, "@sveltejs/vite-plugin-svelte": { - "version": "1.0.0-next.36", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.36.tgz", - "integrity": "sha512-X7lTiioTGC3ri5M299fxc2dimuKU7f22zTXcmD+NrF+fO9/b7YNfLeQQwWV7Tvv9REysMlR4G2HQF6+lY62p/Q==", + "version": "1.0.0-next.42", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.42.tgz", + "integrity": "sha512-I8ILzfjVQuOkl6eDHif6/QJhOEBnsA40u6/0RDWK0mujwOr+MfWCWEZEnrTKqa6YIVMO+uktfoknu61chbAIeg==", "dev": true, "requires": { - "@rollup/pluginutils": "^4.1.2", - "debug": "^4.3.3", + "@rollup/pluginutils": "^4.2.1", + "debug": "^4.3.4", "kleur": "^4.1.4", - "magic-string": "^0.25.7", - "svelte-hmr": "^0.14.9" + "magic-string": "^0.26.1", + "svelte-hmr": "^0.14.11" }, "dependencies": { "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -229,9 +230,9 @@ "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==" }, "bootstrap-icons": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.8.0.tgz", - "integrity": "sha512-plaZQb8tReUULAwQ9M98PyWh5H912eKGVC6etMtc6VqmPmp9Eq0s7Wd0qvPoPLHh0VhXxzdLk1ta5W7lwPIdCQ==" + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.8.1.tgz", + "integrity": "sha512-IXUqislddPJfwq6H+2nTkHyr9epO9h6u1AG0OZCx616w+TgzeoCjfmI3qJMQqt1J586gN2IxzB4M99Ip4sTZ1w==" }, "bootswatch": { "version": "5.1.3", @@ -402,146 +403,170 @@ } }, "esbuild": { - "version": "0.13.15", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.15.tgz", - "integrity": "sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw==", + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.38.tgz", + "integrity": "sha512-12fzJ0fsm7gVZX1YQ1InkOE5f9Tl7cgf6JPYXRJtPIoE0zkWAbHdPHVPPaLi9tYAcEBqheGzqLn/3RdTOyBfcA==", "dev": true, "requires": { - "esbuild-android-arm64": "0.13.15", - "esbuild-darwin-64": "0.13.15", - "esbuild-darwin-arm64": "0.13.15", - "esbuild-freebsd-64": "0.13.15", - "esbuild-freebsd-arm64": "0.13.15", - "esbuild-linux-32": "0.13.15", - "esbuild-linux-64": "0.13.15", - "esbuild-linux-arm": "0.13.15", - "esbuild-linux-arm64": "0.13.15", - "esbuild-linux-mips64le": "0.13.15", - "esbuild-linux-ppc64le": "0.13.15", - "esbuild-netbsd-64": "0.13.15", - "esbuild-openbsd-64": "0.13.15", - "esbuild-sunos-64": "0.13.15", - "esbuild-windows-32": "0.13.15", - "esbuild-windows-64": "0.13.15", - "esbuild-windows-arm64": "0.13.15" + "esbuild-android-64": "0.14.38", + "esbuild-android-arm64": "0.14.38", + "esbuild-darwin-64": "0.14.38", + "esbuild-darwin-arm64": "0.14.38", + "esbuild-freebsd-64": "0.14.38", + "esbuild-freebsd-arm64": "0.14.38", + "esbuild-linux-32": "0.14.38", + "esbuild-linux-64": "0.14.38", + "esbuild-linux-arm": "0.14.38", + "esbuild-linux-arm64": "0.14.38", + "esbuild-linux-mips64le": "0.14.38", + "esbuild-linux-ppc64le": "0.14.38", + "esbuild-linux-riscv64": "0.14.38", + "esbuild-linux-s390x": "0.14.38", + "esbuild-netbsd-64": "0.14.38", + "esbuild-openbsd-64": "0.14.38", + "esbuild-sunos-64": "0.14.38", + "esbuild-windows-32": "0.14.38", + "esbuild-windows-64": "0.14.38", + "esbuild-windows-arm64": "0.14.38" } }, + "esbuild-android-64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.38.tgz", + "integrity": "sha512-aRFxR3scRKkbmNuGAK+Gee3+yFxkTJO/cx83Dkyzo4CnQl/2zVSurtG6+G86EQIZ+w+VYngVyK7P3HyTBKu3nw==", + "dev": true, + "optional": true + }, "esbuild-android-arm64": { - "version": "0.13.15", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.15.tgz", - "integrity": "sha512-m602nft/XXeO8YQPUDVoHfjyRVPdPgjyyXOxZ44MK/agewFFkPa8tUo6lAzSWh5Ui5PB4KR9UIFTSBKh/RrCmg==", + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.38.tgz", + "integrity": "sha512-L2NgQRWuHFI89IIZIlpAcINy9FvBk6xFVZ7xGdOwIm8VyhX1vNCEqUJO3DPSSy945Gzdg98cxtNt8Grv1CsyhA==", "dev": true, "optional": true }, "esbuild-darwin-64": { - "version": "0.13.15", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.15.tgz", - "integrity": "sha512-ihOQRGs2yyp7t5bArCwnvn2Atr6X4axqPpEdCFPVp7iUj4cVSdisgvEKdNR7yH3JDjW6aQDw40iQFoTqejqxvQ==", + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.38.tgz", + "integrity": "sha512-5JJvgXkX87Pd1Og0u/NJuO7TSqAikAcQQ74gyJ87bqWRVeouky84ICoV4sN6VV53aTW+NE87qLdGY4QA2S7KNA==", "dev": true, "optional": true }, "esbuild-darwin-arm64": { - "version": "0.13.15", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.15.tgz", - "integrity": "sha512-i1FZssTVxUqNlJ6cBTj5YQj4imWy3m49RZRnHhLpefFIh0To05ow9DTrXROTE1urGTQCloFUXTX8QfGJy1P8dQ==", + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.38.tgz", + "integrity": "sha512-eqF+OejMI3mC5Dlo9Kdq/Ilbki9sQBw3QlHW3wjLmsLh+quNfHmGMp3Ly1eWm981iGBMdbtSS9+LRvR2T8B3eQ==", "dev": true, "optional": true }, "esbuild-freebsd-64": { - "version": "0.13.15", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.15.tgz", - "integrity": "sha512-G3dLBXUI6lC6Z09/x+WtXBXbOYQZ0E8TDBqvn7aMaOCzryJs8LyVXKY4CPnHFXZAbSwkCbqiPuSQ1+HhrNk7EA==", + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.38.tgz", + "integrity": "sha512-epnPbhZUt93xV5cgeY36ZxPXDsQeO55DppzsIgWM8vgiG/Rz+qYDLmh5ts3e+Ln1wA9dQ+nZmVHw+RjaW3I5Ig==", "dev": true, "optional": true }, "esbuild-freebsd-arm64": { - "version": "0.13.15", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.15.tgz", - "integrity": "sha512-KJx0fzEDf1uhNOZQStV4ujg30WlnwqUASaGSFPhznLM/bbheu9HhqZ6mJJZM32lkyfGJikw0jg7v3S0oAvtvQQ==", + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.38.tgz", + "integrity": "sha512-/9icXUYJWherhk+y5fjPI5yNUdFPtXHQlwP7/K/zg8t8lQdHVj20SqU9/udQmeUo5pDFHMYzcEFfJqgOVeKNNQ==", "dev": true, "optional": true }, "esbuild-linux-32": { - "version": "0.13.15", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.15.tgz", - "integrity": "sha512-ZvTBPk0YWCLMCXiFmD5EUtB30zIPvC5Itxz0mdTu/xZBbbHJftQgLWY49wEPSn2T/TxahYCRDWun5smRa0Tu+g==", + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.38.tgz", + "integrity": "sha512-QfgfeNHRFvr2XeHFzP8kOZVnal3QvST3A0cgq32ZrHjSMFTdgXhMhmWdKzRXP/PKcfv3e2OW9tT9PpcjNvaq6g==", "dev": true, "optional": true }, "esbuild-linux-64": { - "version": "0.13.15", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.15.tgz", - "integrity": "sha512-eCKzkNSLywNeQTRBxJRQ0jxRCl2YWdMB3+PkWFo2BBQYC5mISLIVIjThNtn6HUNqua1pnvgP5xX0nHbZbPj5oA==", + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.38.tgz", + "integrity": "sha512-uuZHNmqcs+Bj1qiW9k/HZU3FtIHmYiuxZ/6Aa+/KHb/pFKr7R3aVqvxlAudYI9Fw3St0VCPfv7QBpUITSmBR1Q==", "dev": true, "optional": true }, "esbuild-linux-arm": { - "version": "0.13.15", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.15.tgz", - "integrity": "sha512-wUHttDi/ol0tD8ZgUMDH8Ef7IbDX+/UsWJOXaAyTdkT7Yy9ZBqPg8bgB/Dn3CZ9SBpNieozrPRHm0BGww7W/jA==", + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.38.tgz", + "integrity": "sha512-FiFvQe8J3VKTDXG01JbvoVRXQ0x6UZwyrU4IaLBZeq39Bsbatd94Fuc3F1RGqPF5RbIWW7RvkVQjn79ejzysnA==", "dev": true, "optional": true }, "esbuild-linux-arm64": { - "version": "0.13.15", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.15.tgz", - "integrity": "sha512-bYpuUlN6qYU9slzr/ltyLTR9YTBS7qUDymO8SV7kjeNext61OdmqFAzuVZom+OLW1HPHseBfJ/JfdSlx8oTUoA==", + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.38.tgz", + "integrity": "sha512-HlMGZTEsBrXrivr64eZ/EO0NQM8H8DuSENRok9d+Jtvq8hOLzrxfsAT9U94K3KOGk2XgCmkaI2KD8hX7F97lvA==", "dev": true, "optional": true }, "esbuild-linux-mips64le": { - "version": "0.13.15", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.15.tgz", - "integrity": "sha512-KlVjIG828uFPyJkO/8gKwy9RbXhCEUeFsCGOJBepUlpa7G8/SeZgncUEz/tOOUJTcWMTmFMtdd3GElGyAtbSWg==", + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.38.tgz", + "integrity": "sha512-qd1dLf2v7QBiI5wwfil9j0HG/5YMFBAmMVmdeokbNAMbcg49p25t6IlJFXAeLzogv1AvgaXRXvgFNhScYEUXGQ==", "dev": true, "optional": true }, "esbuild-linux-ppc64le": { - "version": "0.13.15", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.15.tgz", - "integrity": "sha512-h6gYF+OsaqEuBjeesTBtUPw0bmiDu7eAeuc2OEH9S6mV9/jPhPdhOWzdeshb0BskRZxPhxPOjqZ+/OqLcxQwEQ==", + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.38.tgz", + "integrity": "sha512-mnbEm7o69gTl60jSuK+nn+pRsRHGtDPfzhrqEUXyCl7CTOCLtWN2bhK8bgsdp6J/2NyS/wHBjs1x8aBWwP2X9Q==", + "dev": true, + "optional": true + }, + "esbuild-linux-riscv64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.38.tgz", + "integrity": "sha512-+p6YKYbuV72uikChRk14FSyNJZ4WfYkffj6Af0/Tw63/6TJX6TnIKE+6D3xtEc7DeDth1fjUOEqm+ApKFXbbVQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-s390x": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.38.tgz", + "integrity": "sha512-0zUsiDkGJiMHxBQ7JDU8jbaanUY975CdOW1YDrurjrM0vWHfjv9tLQsW9GSyEb/heSK1L5gaweRjzfUVBFoybQ==", "dev": true, "optional": true }, "esbuild-netbsd-64": { - "version": "0.13.15", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.15.tgz", - "integrity": "sha512-3+yE9emwoevLMyvu+iR3rsa+Xwhie7ZEHMGDQ6dkqP/ndFzRHkobHUKTe+NCApSqG5ce2z4rFu+NX/UHnxlh3w==", + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.38.tgz", + "integrity": "sha512-cljBAApVwkpnJZfnRVThpRBGzCi+a+V9Ofb1fVkKhtrPLDYlHLrSYGtmnoTVWDQdU516qYI8+wOgcGZ4XIZh0Q==", "dev": true, "optional": true }, "esbuild-openbsd-64": { - "version": "0.13.15", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.15.tgz", - "integrity": "sha512-wTfvtwYJYAFL1fSs8yHIdf5GEE4NkbtbXtjLWjM3Cw8mmQKqsg8kTiqJ9NJQe5NX/5Qlo7Xd9r1yKMMkHllp5g==", + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.38.tgz", + "integrity": "sha512-CDswYr2PWPGEPpLDUO50mL3WO/07EMjnZDNKpmaxUPsrW+kVM3LoAqr/CE8UbzugpEiflYqJsGPLirThRB18IQ==", "dev": true, "optional": true }, "esbuild-sunos-64": { - "version": "0.13.15", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.15.tgz", - "integrity": "sha512-lbivT9Bx3t1iWWrSnGyBP9ODriEvWDRiweAs69vI+miJoeKwHWOComSRukttbuzjZ8r1q0mQJ8Z7yUsDJ3hKdw==", + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.38.tgz", + "integrity": "sha512-2mfIoYW58gKcC3bck0j7lD3RZkqYA7MmujFYmSn9l6TiIcAMpuEvqksO+ntBgbLep/eyjpgdplF7b+4T9VJGOA==", "dev": true, "optional": true }, "esbuild-windows-32": { - "version": "0.13.15", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.15.tgz", - "integrity": "sha512-fDMEf2g3SsJ599MBr50cY5ve5lP1wyVwTe6aLJsM01KtxyKkB4UT+fc5MXQFn3RLrAIAZOG+tHC+yXObpSn7Nw==", + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.38.tgz", + "integrity": "sha512-L2BmEeFZATAvU+FJzJiRLFUP+d9RHN+QXpgaOrs2klshoAm1AE6Us4X6fS9k33Uy5SzScn2TpcgecbqJza1Hjw==", "dev": true, "optional": true }, "esbuild-windows-64": { - "version": "0.13.15", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.15.tgz", - "integrity": "sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ==", + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.38.tgz", + "integrity": "sha512-Khy4wVmebnzue8aeSXLC+6clo/hRYeNIm0DyikoEqX+3w3rcvrhzpoix0S+MF9vzh6JFskkIGD7Zx47ODJNyCw==", "dev": true, "optional": true }, "esbuild-windows-arm64": { - "version": "0.13.15", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.15.tgz", - "integrity": "sha512-zzvyCVVpbwQQATaf3IG8mu1IwGEiDxKkYUdA4FpoCHi1KtPa13jeScYDjlW0Qh+ebWzpKfR2ZwvqAQkSWNcKjA==", + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.38.tgz", + "integrity": "sha512-k3FGCNmHBkqdJXuJszdWciAH77PukEyDsdIryEHn9cKLQFxzhT39dSumeTuggaQcXY57UlmLGIkklWZo2qzHpw==", "dev": true, "optional": true }, @@ -600,15 +625,15 @@ } }, "eslint-config-prettier": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", - "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", "dev": true }, "eslint-plugin-svelte3": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-svelte3/-/eslint-plugin-svelte3-3.4.0.tgz", - "integrity": "sha512-MIQUTuRv3o7LyQ+360qOc9mLT35j1I5YzHr04g/UDcvJTpg0X/kHWELY99ve869Rp/9wjqD7I26Aq5H8OH5RIg==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-svelte3/-/eslint-plugin-svelte3-3.4.1.tgz", + "integrity": "sha512-7p59WG8qV8L6wLdl4d/c3mdjkgVglQCdv5XOTk/iNPBKXuuV+Q0eFP5Wa6iJd/G2M1qR3BkLPEzaANOqKAZczw==", "dev": true }, "eslint-scope": { @@ -914,9 +939,9 @@ } }, "is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", "dev": true, "requires": { "has": "^1.0.3" @@ -1033,12 +1058,12 @@ } }, "magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.1.tgz", + "integrity": "sha512-ndThHmvgtieXe8J/VGPjG+Apu7v7ItcD5mhEIvOscWjPF/ccOiLxHaSuCAS2G+3x4GKsAbT8u7zdyamupui8Tg==", "dev": true, "requires": { - "sourcemap-codec": "^1.4.4" + "sourcemap-codec": "^1.4.8" } }, "minimatch": { @@ -1063,9 +1088,9 @@ "dev": true }, "nanoid": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", - "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", "dev": true }, "natural-compare": { @@ -1149,12 +1174,12 @@ "dev": true }, "postcss": { - "version": "8.4.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.6.tgz", - "integrity": "sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==", + "version": "8.4.13", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.13.tgz", + "integrity": "sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==", "dev": true, "requires": { - "nanoid": "^3.2.0", + "nanoid": "^3.3.3", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } @@ -1166,15 +1191,15 @@ "dev": true }, "prettier": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", - "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz", + "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", "dev": true }, "prettier-plugin-svelte": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-2.6.0.tgz", - "integrity": "sha512-NPSRf6Y5rufRlBleok/pqg4+1FyGsL0zYhkYP6hnueeL1J/uCm3OfOZPsLX4zqD9VAdcXfyEL2PYqGv8ZoOSfA==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-2.7.0.tgz", + "integrity": "sha512-fQhhZICprZot2IqEyoiUYLTRdumULGRvw0o4dzl5jt0jfzVWdGqeYW27QTWAeXhoupEZJULmNoH3ueJwUWFLIA==", "dev": true }, "progress": { @@ -1237,9 +1262,9 @@ } }, "rollup": { - "version": "2.67.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.67.0.tgz", - "integrity": "sha512-W83AaERwvDiHwHEF/dfAfS3z1Be5wf7n+pO3ZAO5IQadCT2lBTr7WQ2MwZZe+nodbD+n3HtC4OCOAdsOPPcKZQ==", + "version": "2.71.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.71.1.tgz", + "integrity": "sha512-lMZk3XfUBGjrrZQpvPSoXcZSfKcJ2Bgn+Z0L1MoW2V8Wh7BVM+LOBJTPo16yul2MwL59cXedzW1ruq3rCjSRgw==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -1255,9 +1280,9 @@ } }, "sass": { - "version": "1.49.7", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.49.7.tgz", - "integrity": "sha512-13dml55EMIR2rS4d/RDHHP0sXMY3+30e1TKsyXaSz3iLWVoDWEoboY8WzJd5JMnxrRHffKO3wq2mpJ0jxRJiEQ==", + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.51.0.tgz", + "integrity": "sha512-haGdpTgywJTvHC2b91GSq+clTKGbtkkZmVAb82jZQN/wTy6qs8DdFm2lhEQbEwrY0QDRgSQ3xDurqM977C3noA==", "dev": true, "requires": { "chokidar": ">=3.0.0 <4.0.0", @@ -1266,9 +1291,9 @@ } }, "sass-loader": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.4.0.tgz", - "integrity": "sha512-7xN+8khDIzym1oL9XyS6zP6Ges+Bo2B2xbPrjdMHEYyV3AQYhd/wXeru++3ODHF0zMjYmVadblSKrPrjEkL8mg==", + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", + "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", "dev": true, "requires": { "klona": "^2.0.4", @@ -1401,21 +1426,21 @@ "dev": true }, "svelte": { - "version": "3.46.3", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.46.3.tgz", - "integrity": "sha512-mTOXvv74CVQqJHqoIZDprVfRKVVmYNadXP0VKnOEA54223kLGCr1nMrifS4Zx29qMt/xRB38Eq1D7dDH/fM8fQ==", + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.48.0.tgz", + "integrity": "sha512-fN2YRm/bGumvjUpu6yI3BpvZnpIm9I6A7HR4oUNYd7ggYyIwSA/BX7DJ+UXXffLp6XNcUijyLvttbPVCYa/3xQ==", "dev": true }, "svelte-hmr": { - "version": "0.14.9", - "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.14.9.tgz", - "integrity": "sha512-bKE9+4qb4sAnA+TKHiYurUl970rjA0XmlP9TEP7K/ncyWz3m81kA4HOgmlZK/7irGK7gzZlaPDI3cmf8fp/+tg==", + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.14.11.tgz", + "integrity": "sha512-R9CVfX6DXxW1Kn45Jtmx+yUe+sPhrbYSUp7TkzbW0jI5fVPn6lsNG9NEs5dFg5qRhFNAoVdRw5qQDLALNKhwbQ==", "dev": true }, "sveltestrap": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/sveltestrap/-/sveltestrap-5.8.3.tgz", - "integrity": "sha512-6Ldzz62BwfqBaz9UaGwXg0E6PfXZc24vAg/nEGaJlB7Bv071ctT07EylqEhXdCoS1JE+NnhT2ByyP2SxJseYHQ==", + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/sveltestrap/-/sveltestrap-5.9.0.tgz", + "integrity": "sha512-ZSiYKYrKhDMhhbamnAFK3RK/uqUdcLgjae5Fk3GYdv6Ccth0tN2y6vSg+Vp/PBTYc51u08ZwnYvt8SfWSRNCMA==", "dev": true, "requires": { "@popperjs/core": "^2.9.2" @@ -1510,15 +1535,15 @@ "dev": true }, "vite": { - "version": "2.7.13", - "resolved": "https://registry.npmjs.org/vite/-/vite-2.7.13.tgz", - "integrity": "sha512-Mq8et7f3aK0SgSxjDNfOAimZGW9XryfHRa/uV0jseQSilg+KhYDSoNb9h1rknOy6SuMkvNDLKCYAYYUMCE+IgQ==", + "version": "2.9.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-2.9.6.tgz", + "integrity": "sha512-3IffdrByHW95Yjv0a13TQOQfJs7L5dVlSPuTt432XLbRMriWbThqJN2k/IS6kXn5WY4xBLhK9XoaWay1B8VzUw==", "dev": true, "requires": { - "esbuild": "^0.13.12", + "esbuild": "^0.14.27", "fsevents": "~2.3.2", - "postcss": "^8.4.5", - "resolve": "^1.20.0", + "postcss": "^8.4.12", + "resolve": "^1.22.0", "rollup": "^2.59.0" } }, diff --git a/frontend/ui/package.json b/frontend/ui/package.json index 399b4a60..eb76ac4b 100644 --- a/frontend/ui/package.json +++ b/frontend/ui/package.json @@ -9,23 +9,23 @@ "format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ." }, "devDependencies": { - "@sveltejs/kit": "^1.0.0-next.256", + "@sveltejs/kit": "^1.0.0-next.324", "eslint": "^7.22.0", - "eslint-config-prettier": "^8.1.0", - "eslint-plugin-svelte3": "^3.4.0", - "prettier": "^2.5.1", - "prettier-plugin-svelte": "^2.6.0", - "sass": "^1.49.7", - "sass-loader": "^12.4.0", - "svelte": "^3.46.3", - "sveltestrap": "^5.8.3" + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-svelte3": "^3.4.1", + "prettier": "^2.6.2", + "prettier-plugin-svelte": "^2.7.0", + "sass": "^1.51.0", + "sass-loader": "^12.6.0", + "svelte": "^3.48.0", + "sveltestrap": "^5.9.0" }, "type": "module", "dependencies": { - "@popperjs/core": "^2.11.2", - "@sveltejs/adapter-static": "^1.0.0-next.26", + "@popperjs/core": "^2.11.5", + "@sveltejs/adapter-static": "^1.0.0-next.29", "bootstrap": "^5.1.3", - "bootstrap-icons": "^1.8.0", + "bootstrap-icons": "^1.8.1", "bootswatch": "^5.1.3", "hash-wasm": "^4.9.0", "seedrandom": "^3.0.5" diff --git a/frontend/ui/src/app.html b/frontend/ui/src/app.html index 181c9bbb..75a3e648 100644 --- a/frontend/ui/src/app.html +++ b/frontend/ui/src/app.html @@ -10,6 +10,6 @@ %svelte.head% -
%svelte.body%
+
%svelte.body%
diff --git a/frontend/ui/svelte.config.js b/frontend/ui/svelte.config.js index c1e0fb1a..55fe534b 100644 --- a/frontend/ui/svelte.config.js +++ b/frontend/ui/svelte.config.js @@ -9,9 +9,6 @@ const config = { paths: { // base: '/2021', }, - - // hydrate the
element in src/app.html - target: '#svelte' } }; From 48ee5321a8af7f85120793343b15fb315624a6aa Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 2 May 2022 10:59:43 +0200 Subject: [PATCH 0469/1637] admin: Handle challenge info on settings page --- admin/static/js/app.js | 15 +- admin/static/views/settings.html | 481 +++++++++++++++++-------------- 2 files changed, 286 insertions(+), 210 deletions(-) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 67bc4c12..3464351a 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -223,6 +223,11 @@ angular.module("FICApp") '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'}, @@ -494,7 +499,7 @@ angular.module("FICApp") $scope.monitor = Monitor.get(); }) - .controller("SettingsController", function($scope, $rootScope, Settings, ROSettings, $location, $http, $interval) { + .controller("SettingsController", function($scope, $rootScope, Settings, SettingsChallenge, ROSettings, $location, $http, $interval) { $scope.displayDangerousActions = false; $scope.config = Settings.get(); $scope.config.$promise.then(function(response) { @@ -505,6 +510,7 @@ angular.module("FICApp") $rootScope.settings.activateTime = new Date(response.activateTime); }) $scope.configro = ROSettings.get(); + $scope.challenge = SettingsChallenge.get(); $scope.duration = 360; $scope.exerciceDependChange = function() { @@ -542,6 +548,13 @@ angular.module("FICApp") }, 1500); $scope.$on('$destroy', function () { $interval.cancel(progressInterval); }); + $scope.saveChallengeInfo = function() { + 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!'; } var nStart = this.config.start; diff --git a/admin/static/views/settings.html b/admin/static/views/settings.html index 89825f64..7f529d25 100644 --- a/admin/static/views/settings.html +++ b/admin/static/views/settings.html @@ -1,219 +1,282 @@ - -
- -

Paramètres

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

+ Paramètres +

-
-
+
+ -
- -
- -
-
- -
-
-
- -
- min -
+
+ +
+
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+ +
+
+
+ +
+ min +
+
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+
+ +
+
+ + + Propagation dans : {{ activateTimeCountDown | timer }} + Il restera : {{ timeRemaining - activateTimeCountDown | timer }} + +
+
+ +
+
-
-
+ -
- -
- -
- -
- - -
- -
- - -
- -
- - -
- -
-
- -
- -
- -
- -
- -
-
- -
-
-
- - +
+
+ +

Infos challenge

- - Propagation dans : {{ activateTimeCountDown | timer }} - Il restera : {{ timeRemaining - activateTimeCountDown | timer }} - -
-
- -
-
+
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+
+
+
+

Options

+
+
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+
-
-
- Options - -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
-
-
From c525acff2041a5a153ff148bf16972ecf6fde6d7 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 2 May 2022 11:21:34 +0200 Subject: [PATCH 0470/1637] settings: Add challenge subtitle --- admin/static/views/settings.html | 7 +++++++ settings/challenge.go | 2 ++ 2 files changed, 9 insertions(+) diff --git a/admin/static/views/settings.html b/admin/static/views/settings.html index 7f529d25..be10932c 100644 --- a/admin/static/views/settings.html +++ b/admin/static/views/settings.html @@ -120,6 +120,13 @@
+
+ +
+ +
+
+
diff --git a/settings/challenge.go b/settings/challenge.go index 6c39b5ed..456a85a3 100644 --- a/settings/challenge.go +++ b/settings/challenge.go @@ -12,6 +12,8 @@ const ChallengeFile = "challenge.json" type ChallengeInfo struct { // Title is the displayed name of the challenge. Title string `json:"title"` + // SubTitle is appended to the title. + SubTitle string `json:"subtitle"` // Authors is the group name of people making the challenge. Authors string `json:"authors"` // VideoLink is the link to explaination videos when the challenge is over. From 53e70b1eba7c7377bf1911d624c564d092de3950 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 2 May 2022 11:39:27 +0200 Subject: [PATCH 0471/1637] admin: Can reset challengeInfo --- admin/api/settings.go | 35 ++++++++++++++++++++++++++++++++ admin/static/js/app.js | 1 + admin/static/views/settings.html | 3 ++- 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/admin/api/settings.go b/admin/api/settings.go index 9ac78cff..d3adb39f 100644 --- a/admin/api/settings.go +++ b/admin/api/settings.go @@ -127,6 +127,39 @@ func ResetSettings() error { }) } +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(_ httprouter.Params, body []byte) (interface{}, error) { var m map[string]string if err := json.Unmarshal(body, &m); err != nil { @@ -143,6 +176,8 @@ func reset(_ httprouter.Params, body []byte) (interface{}, error) { return true, fic.ResetGame() } else if t == "settings" { return true, ResetSettings() + } else if t == "challengeInfo" { + return true, ResetChallengeInfo() } else { return nil, errors.New("Unknown reset type") } diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 3464351a..1fc9519c 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -603,6 +603,7 @@ angular.module("FICApp") $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.", diff --git a/admin/static/views/settings.html b/admin/static/views/settings.html index be10932c..d402c3d2 100644 --- a/admin/static/views/settings.html +++ b/admin/static/views/settings.html @@ -108,7 +108,8 @@
- + +

Infos challenge

From 1313f2caf926324fa05b53320e0649ee8ad18c8d Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 2 May 2022 12:08:05 +0200 Subject: [PATCH 0472/1637] Include ID and dependancy in zqds file --- libfic/zqsd.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/libfic/zqsd.go b/libfic/zqsd.go index 6bd0cd23..e14cd19b 100644 --- a/libfic/zqsd.go +++ b/libfic/zqsd.go @@ -7,11 +7,13 @@ import ( ) type ChallengeZQDS struct { + ID int64 `json:"id,omitempty"` Name string `json:"name"` Type string `json:"type,omitempty"` Category string `json:"category"` Points int64 `json:"points"` State string `json:"state"` + LockedBy []int64 `json:"locked_by,omitempty"` FirstBloodBonus []float64 `json:"first_blood_bonus,omitempty"` Description string `json:"description"` Flags []string `json:"flags,omitempty"` @@ -43,12 +45,22 @@ func GenZQDSSessionFile(c *settings.ChallengeInfo, s *settings.Settings) (*Sessi } for _, ex := range exos { + state := "visible" + lockedby := []int64{} + + if ex.Depend != nil { + state = "locked" + lockedby = append(lockedby, *ex.Depend) + } + challenges = append(challenges, &ChallengeZQDS{ + ID: ex.Id, Name: ex.Title, Type: "first_blood", Category: th.Name, Points: ex.Gain, - State: "visible", + State: state, + LockedBy: lockedby, FirstBloodBonus: []float64{ float64(ex.Gain) * (1 + s.FirstBlood), }, From 3fa53bc877f0f569152244075e57b68ebac0973d Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 7 May 2022 03:26:25 +0000 Subject: [PATCH 0473/1637] chore(deps): update golang.org/x/crypto digest to 2cf3ade --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index b28491f9..8f0650c7 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/julienschmidt/httprouter v1.3.0 github.com/russross/blackfriday/v2 v2.1.0 github.com/studio-b12/gowebdav v0.0.0-20220128162035-c7b1ff8a5e62 - golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f + golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9 golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 gopkg.in/fsnotify.v1 v1.4.7 diff --git a/go.sum b/go.sum index ba151ce5..2ffc96a7 100644 --- a/go.sum +++ b/go.sum @@ -228,6 +228,8 @@ golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNy golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f h1:OeJjE6G4dgCY4PIXvIRQbE8+RX+uXZyGhUy/ksMGJoc= golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 h1:NvGWuYG8dkDHFSKksI1P9faiVJ9rayE6l0+ouWVIDs8= +golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= From 9cf33ee7555be82d891850d1cbb17ba5f53ea9e5 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 10 May 2022 10:23:28 +0200 Subject: [PATCH 0474/1637] repochecker: Revert binary file checks --- repochecker/main.go | 8 ++++---- repochecker/update.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/repochecker/main.go b/repochecker/main.go index 43f86b55..fd2621b3 100644 --- a/repochecker/main.go +++ b/repochecker/main.go @@ -21,7 +21,7 @@ import ( var ( ignoreBinaryFileUnder = 1500000 skipFileChecks = false - strictBinaryFile = false + skipBinaryFileCheck = false ) func formatFileSize(size int) string { @@ -85,10 +85,10 @@ func searchBinaryInGit(edir string) (ret []string) { } continue } else if fsize, err = strconv.Atoi(fields[3]); err == nil && fsize < ignoreBinaryFileUnder { - if _, ok := alreadySeen[fname]; !ok || !strictBinaryFile { + if _, ok := alreadySeen[fname]; !ok || skipBinaryFileCheck { continue } - } else if _, ok := alreadySeen[fname]; !ok && !strictBinaryFile { + } else if _, ok := alreadySeen[fname]; !ok && skipBinaryFileCheck { alreadySeen[fname] = fmt.Sprintf("%s (commit %s) (size %s)", fname, commit[:7], formatFileSize(fsize)) continue } @@ -168,7 +168,7 @@ func main() { flag.BoolVar(&fic.StrongDigest, "strongdigest", fic.StrongDigest, "Are BLAKE2b digests required or is SHA-1 good enough?") flag.BoolVar(&skipFileChecks, "skipfiledigests", skipFileChecks, "Don't perform DIGESTS checks on file to speed up the checks") flag.BoolVar(&sync.LogMissingResolution, "skipresolution", sync.LogMissingResolution, "Don't fail if resolution.mp4 is absent") - flag.BoolVar(&strictBinaryFile, "strict-binary-file", strictBinaryFile, "In Git-LFS check, don't warn files") + flag.BoolVar(&skipBinaryFileCheck, "skip-binary-file", skipBinaryFileCheck, "In Git-LFS check, don't warn files") flag.IntVar(&ignoreBinaryFileUnder, "skip-binary-files-under", ignoreBinaryFileUnder, "In Git-LFS check, don't warn files under this size") flag.Parse() diff --git a/repochecker/update.go b/repochecker/update.go index 8144228c..a17c2d9a 100644 --- a/repochecker/update.go +++ b/repochecker/update.go @@ -10,7 +10,7 @@ import ( "net/http" ) -const version = 11 +const version = 12 func init() { go checkUpdate() From a9b3a45fb1b38cece56b954282dd6e846c0ff63d Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 11 May 2022 20:25:50 +0000 Subject: [PATCH 0475/1637] chore(deps): update golang.org/x/crypto digest to c6db032 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 8f0650c7..86cd1a48 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/julienschmidt/httprouter v1.3.0 github.com/russross/blackfriday/v2 v2.1.0 github.com/studio-b12/gowebdav v0.0.0-20220128162035-c7b1ff8a5e62 - golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 + golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88 golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9 golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 gopkg.in/fsnotify.v1 v1.4.7 diff --git a/go.sum b/go.sum index 2ffc96a7..438e1fb3 100644 --- a/go.sum +++ b/go.sum @@ -230,6 +230,8 @@ golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f h1:OeJjE6G4dgCY4PIXvIRQbE golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 h1:NvGWuYG8dkDHFSKksI1P9faiVJ9rayE6l0+ouWVIDs8= golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88 h1:Tgea0cVUD0ivh5ADBX4WwuI12DUd2to3nCYe2eayMIw= +golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= From 5a5e02e53302a5f5c8f2a7c1ee2beb5413247820 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 13 May 2022 21:28:53 +0000 Subject: [PATCH 0476/1637] chore(deps): update golang.org/x/crypto digest to 4661260 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 86cd1a48..915fab31 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/julienschmidt/httprouter v1.3.0 github.com/russross/blackfriday/v2 v2.1.0 github.com/studio-b12/gowebdav v0.0.0-20220128162035-c7b1ff8a5e62 - golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88 + golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9 golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9 golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 gopkg.in/fsnotify.v1 v1.4.7 diff --git a/go.sum b/go.sum index 438e1fb3..837a69d7 100644 --- a/go.sum +++ b/go.sum @@ -232,6 +232,8 @@ golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 h1:NvGWuYG8dkDHFSKksI1P9f golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88 h1:Tgea0cVUD0ivh5ADBX4WwuI12DUd2to3nCYe2eayMIw= golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9 h1:NUzdAbFtCJSXU20AOXgeqaUwg8Ypg4MPYmL+d+rsB5c= +golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= From c25c11e70ab1783a919cf276c7c6819a7d195a40 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 16 May 2022 17:27:52 +0000 Subject: [PATCH 0477/1637] chore(deps): update golang.org/x/crypto digest to 403b017 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 915fab31..568f246a 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/julienschmidt/httprouter v1.3.0 github.com/russross/blackfriday/v2 v2.1.0 github.com/studio-b12/gowebdav v0.0.0-20220128162035-c7b1ff8a5e62 - golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9 + golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8 golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9 golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 gopkg.in/fsnotify.v1 v1.4.7 diff --git a/go.sum b/go.sum index 837a69d7..3a792777 100644 --- a/go.sum +++ b/go.sum @@ -234,6 +234,8 @@ golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88 h1:Tgea0cVUD0ivh5ADBX4Wwu golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9 h1:NUzdAbFtCJSXU20AOXgeqaUwg8Ypg4MPYmL+d+rsB5c= golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8 h1:y+mHpWoQJNAHt26Nhh6JP7hvM71IRZureyvZhoVALIs= +golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= From 3c0751a78a441ae87023255a6cc5f4b91cf08fe1 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 14 May 2022 07:10:55 +0200 Subject: [PATCH 0478/1637] sync: Fix division by zero --- admin/sync/full.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/sync/full.go b/admin/sync/full.go index d684d276..39102758 100644 --- a/admin/sync/full.go +++ b/admin/sync/full.go @@ -110,7 +110,7 @@ func SyncDeep(i Importer) (errs map[string][]string) { errs["_date"] = []string{fmt.Sprintf("%v", startTime)} errs["_themes"] = SyncThemes(i) - if themes, err := fic.GetThemes(); err == nil { + if themes, err := fic.GetThemes(); err == nil && len(themes) > 0 { DeepSyncProgress = 2 var themeStep uint8 = uint8(250) / uint8(len(themes)) From 9d639a0315cf360713e572172c3b34cf49893c7e Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 16 May 2022 11:37:44 +0200 Subject: [PATCH 0479/1637] sync: Non-empty directory without .git is Fatal --- admin/sync/importer_gitbin.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/admin/sync/importer_gitbin.go b/admin/sync/importer_gitbin.go index 6dc32583..f7b0b779 100644 --- a/admin/sync/importer_gitbin.go +++ b/admin/sync/importer_gitbin.go @@ -66,6 +66,8 @@ func (i GitImporter) Init() error { 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.Fatal(i.li.Base, "is not a valid git repository and it cannot be initialized because it's not empty.") } // Check if the .git directory exists, change the origin remote to our From 83468ad72346fc4ede68d4cb8009466afb485792 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 16 May 2022 11:38:13 +0200 Subject: [PATCH 0480/1637] admin: Fix toast with yes/no after sync --- admin/static/js/app.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 1fc9519c..47ac1810 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -635,10 +635,10 @@ angular.module("FICApp") $scope.deepSyncInProgress = true; $http.post(url).then(function() { $scope.deepSyncInProgress = false; - $scope.addToast('success', 'Synchronisation intégrale terminée.', 'Voir le rapport.', 15000); + $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.', 15000); + $scope.addToast('warning', 'Synchronisation intégrale terminée.', 'Voir le rapport.', null, null, 15000); }); }); }; @@ -648,10 +648,10 @@ angular.module("FICApp") $scope.deepSyncInProgress = true; $http.post("api/sync/speed").then(function() { $scope.deepSyncInProgress = false; - $scope.addToast('success', 'Synchronisation profonde rapide terminée.', 'Voir le rapport.', 15000); + $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.', 15000); + $scope.addToast('warning', 'Synchronisation profinde rapide terminée.', 'Voir le rapport.', null, null, 15000); }); }); }; From 8b3fbdb64a522d406d182bda5550d7c4964e0098 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 16 May 2022 11:38:46 +0200 Subject: [PATCH 0481/1637] admin: Use gin-gonic as router --- admin/api/certificate.go | 387 ++++++++++----- admin/api/claim.go | 428 ++++++++++++----- admin/api/events.go | 149 ++++-- admin/api/exercice.go | 886 +++++++++++++++++++++++++---------- admin/api/file.go | 204 +++++--- admin/api/handlers.go | 352 -------------- admin/api/health.go | 54 ++- admin/api/monitor.go | 18 +- admin/api/password.go | 130 +++-- admin/api/public.go | 59 ++- admin/api/qa.go | 96 ++-- admin/api/router.go | 23 +- admin/api/settings.go | 151 +++--- admin/api/sync.go | 191 ++++++++ admin/api/team.go | 587 +++++++++++++++-------- admin/api/theme.go | 307 +++++------- admin/api/version.go | 12 +- admin/app.go | 71 +++ admin/main.go | 18 +- admin/static.go | 98 ++-- admin/static/js/app.js | 4 +- admin/sync/exercice_files.go | 20 +- admin/sync/exercice_hints.go | 26 +- admin/sync/exercice_keys.go | 27 +- admin/sync/exercices.go | 33 +- admin/sync/themes.go | 24 +- go.mod | 1 + go.sum | 30 ++ libfic/file.go | 6 + libfic/flag_key.go | 7 + libfic/hint.go | 11 + libfic/mcq.go | 10 +- 32 files changed, 2785 insertions(+), 1635 deletions(-) delete mode 100644 admin/api/handlers.go create mode 100644 admin/api/sync.go create mode 100644 admin/app.go diff --git a/admin/api/certificate.go b/admin/api/certificate.go index ebb58a3f..6ad3ac37 100644 --- a/admin/api/certificate.go +++ b/admin/api/certificate.go @@ -7,13 +7,13 @@ import ( "crypto/x509/pkix" "encoding/base32" "encoding/base64" - "encoding/json" "errors" "fmt" "io/ioutil" "log" "math" "math/big" + "net/http" "os" "path" "strconv" @@ -23,91 +23,178 @@ import ( "srs.epita.fr/fic-server/admin/pki" "srs.epita.fr/fic-server/libfic" - "github.com/julienschmidt/httprouter" + "github.com/gin-gonic/gin" ) var TeamsDir string -func init() { - router.GET("/api/htpasswd", apiHandler( - func(httprouter.Params, []byte) (interface{}, error) { - return genHtpasswd(true) - })) - router.POST("/api/htpasswd", apiHandler( - func(httprouter.Params, []byte) (interface{}, error) { - if htpasswd, err := genHtpasswd(true); err != nil { - return nil, err - } else if err := ioutil.WriteFile(path.Join(pki.PKIDir, "shared", "ficpasswd"), []byte(htpasswd), 0644); err != nil { - return nil, err - } else { - return true, nil - } - })) - router.DELETE("/api/htpasswd", apiHandler( - func(httprouter.Params, []byte) (interface{}, error) { - if err := os.Remove(path.Join(pki.PKIDir, "shared", "ficpasswd")); err != nil { - return nil, err - } else { - return true, nil - } - })) - router.GET("/api/htpasswd.apr1", apiHandler( - func(httprouter.Params, []byte) (interface{}, error) { - return genHtpasswd(false) - })) - router.GET("/api/ca/", apiHandler(infoCA)) - router.GET("/api/ca.pem", apiHandler(getCAPEM)) - router.POST("/api/ca/new", apiHandler( - func(_ httprouter.Params, body []byte) (interface{}, error) { - var upki PKISettings - if err := json.Unmarshal(body, &upki); err != nil { - return nil, err - } - return true, pki.GenerateCA(upki.NotBefore, upki.NotAfter) - })) +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 + } - router.GET("/api/teams/:tid/certificates", apiHandler(teamHandler( - func(team *fic.Team, _ []byte) (interface{}, error) { - if serials, err := pki.GetTeamSerials(TeamsDir, team.Id); err != nil { - return nil, err - } 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) - } + 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) } - return certs, nil } - }))) + c.JSON(http.StatusOK, certs) + } + }) - router.GET("/api/teams/:tid/associations", apiHandler(teamHandler( - func(team *fic.Team, _ []byte) (interface{}, error) { - return pki.GetTeamAssociations(TeamsDir, team.Id) - }))) - router.POST("/api/teams/:tid/associations/:assoc", apiHandler(teamAssocHandler( - func(team *fic.Team, assoc string, _ []byte) (interface{}, error) { - if err := os.Symlink(fmt.Sprintf("%d", team.Id), path.Join(TeamsDir, assoc)); err != nil { - return nil, err - } - return "\"" + assoc + "\"", nil - }))) - router.DELETE("/api/teams/:tid/associations/:assoc", apiHandler(teamAssocHandler( - func(team *fic.Team, assoc string, _ []byte) (interface{}, error) { - return "null", pki.DeleteTeamAssociation(TeamsDir, assoc) - }))) + router.GET("/associations", func(c *gin.Context) { + team := c.MustGet("team").(*fic.Team) - router.GET("/api/certs/", apiHandler(getCertificates)) - router.POST("/api/certs/", apiHandler(generateClientCert)) - router.DELETE("/api/certs/", apiHandler(func(_ httprouter.Params, _ []byte) (interface{}, error) { return fic.ClearCertificates() })) + 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 + } - router.HEAD("/api/certs/:certid", apiHandler(certificateHandler(getTeamP12File))) - router.GET("/api/certs/:certid", apiHandler(certificateHandler(getTeamP12File))) - router.PUT("/api/certs/:certid", apiHandler(certificateHandler(updateCertificateAssociation))) - router.DELETE("/api/certs/:certid", apiHandler(certificateHandler( - func(cert *fic.Certificate, _ []byte) (interface{}, error) { return cert.Revoke() }))) + 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) { @@ -187,13 +274,14 @@ type PKISettings struct { PublicKeyAlgorithm x509.PublicKeyAlgorithm `json:"publicKeyAlgorithm"` } -func infoCA(_ httprouter.Params, _ []byte) (interface{}, error) { +func infoCA(c *gin.Context) { _, cacert, err := pki.LoadCA() if err != nil { - return nil, err + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "CA not found"}) + return } - return PKISettings{ + c.JSON(http.StatusOK, PKISettings{ Version: cacert.Version, SerialNumber: cacert.SerialNumber, Issuer: cacert.Issuer, @@ -202,47 +290,78 @@ func infoCA(_ httprouter.Params, _ []byte) (interface{}, error) { NotAfter: cacert.NotAfter, SignatureAlgorithm: cacert.SignatureAlgorithm, PublicKeyAlgorithm: cacert.PublicKeyAlgorithm, - }, nil + }) } -func getCAPEM(_ httprouter.Params, _ []byte) (interface{}, error) { +func getCAPEM(c *gin.Context) { if _, err := os.Stat(pki.CACertPath()); os.IsNotExist(err) { - return nil, errors.New("Unable to locate the CA root certificate. Have you generated it?") + 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 { - return nil, err + log.Println("Unable to open CA root certificate:", err) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) + return } else { defer fd.Close() - return ioutil.ReadAll(fd) + + 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(cert *fic.Certificate, _ []byte) (interface{}, error) { +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 { - return nil, err + log.Println("Unable to WriteP12:", err.Error()) + c.AbortWithError(http.StatusInternalServerError, err) + return } } if _, err := os.Stat(pki.ClientP12Path(cert.Id)); os.IsNotExist(err) { - return nil, errors.New("Unable to locate the p12. Have you generated it?") + 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 { - return nil, err + log.Println("Unable to open ClientP12Path:", err.Error()) + c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("Unable to open the p12: %w", err.Error())) + return } else { defer fd.Close() - return ioutil.ReadAll(fd) + + 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.Error())) + return + } + + c.Data(http.StatusOK, "application/x-pkcs12", data) } } -func generateClientCert(_ httprouter.Params, _ []byte) (interface{}, error) { +func generateClientCert(c *gin.Context) { // First, generate a new, unique, serial var serial_gen [8]byte if _, err := rand.Read(serial_gen[:]); err != nil { - return nil, err + 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 { - return nil, err + 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 } } @@ -253,23 +372,35 @@ func generateClientCert(_ httprouter.Params, _ []byte) (interface{}, error) { // Let's pick a random password password, err := fic.GeneratePassword() if err != nil { - return nil, err + 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 { - return nil, err + 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 { - return nil, err + 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) - return 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}, err + 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 { @@ -280,35 +411,42 @@ type CertExported struct { Revoked *time.Time `json:"revoked"` } -func getCertificates(_ httprouter.Params, _ []byte) (interface{}, error) { - if certificates, err := fic.GetCertificates(); err != nil { - return nil, err - } else { - 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}) - } - return ret, nil +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(cert *fic.Certificate, body []byte) (interface{}, error) { +func updateCertificateAssociation(c *gin.Context) { + cert := c.MustGet("cert").(*fic.Certificate) + var uc CertUploaded - if err := json.Unmarshal(body, &uc); err != nil { - return nil, err + 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)) @@ -316,19 +454,26 @@ func updateCertificateAssociation(cert *fic.Certificate, body []byte) (interface if uc.Team != nil { srcLinkPath := fmt.Sprintf("%d", *uc.Team) if err := os.Symlink(srcLinkPath, dstLinkPath); err != nil { - return nil, err + 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 { - return nil, err + 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 - ut.Update() + _, err := ut.Update() + if err != nil { + log.Println("Unable to UpdateTeam after updateCertificateAssociation:", err.Error()) + } } } else { os.Remove(dstLinkPath) } - return cert, nil + c.JSON(http.StatusOK, cert) } diff --git a/admin/api/claim.go b/admin/api/claim.go index 08f7b8d7..fd0bc89d 100644 --- a/admin/api/claim.go +++ b/admin/api/claim.go @@ -2,62 +2,152 @@ package api import ( "encoding/json" - "errors" "fmt" "io/ioutil" + "log" + "net/http" "path" + "strconv" "time" "srs.epita.fr/fic-server/libfic" - "github.com/julienschmidt/httprouter" + "github.com/gin-gonic/gin" ) -func init() { - router.GET("/api/teams/:tid/issue.json", apiHandler(teamHandler( - func(team *fic.Team, _ []byte) (interface{}, error) { - return team.MyIssueFile() - }))) - +func declareClaimsRoutes(router *gin.RouterGroup) { // Tasks - router.GET("/api/claims", apiHandler(getClaims)) - router.POST("/api/claims", apiHandler(newClaim)) - router.DELETE("/api/claims", apiHandler(clearClaims)) - router.GET("/api/teams/:tid/claims", apiHandler(teamHandler(getTeamClaims))) - router.GET("/api/exercices/:eid/claims", apiHandler(exerciceHandler(getExerciceClaims))) - router.GET("/api/themes/:thid/exercices/:eid/claims", apiHandler(exerciceHandler(getExerciceClaims))) + router.GET("/claims", getClaims) + router.POST("/claims", newClaim) + router.DELETE("/claims", clearClaims) - router.GET("/api/claims/:cid", apiHandler(claimHandler(showClaim))) - router.PUT("/api/claims/:cid", apiHandler(claimHandler(updateClaim))) - router.POST("/api/claims/:cid", apiHandler(claimHandler(addClaimDescription))) - router.DELETE("/api/claims/:cid", apiHandler(claimHandler(deleteClaim))) + apiClaimsRoutes := router.Group("/claims/:cid") + apiClaimsRoutes.Use(ClaimHandler) + apiClaimsRoutes.GET("", showClaim) + apiClaimsRoutes.PUT("", updateClaim) + apiClaimsRoutes.POST("", addClaimDescription) + apiClaimsRoutes.DELETE("", deleteClaim) - router.GET("/api/claims/:cid/last_update", apiHandler(claimHandler(getClaimLastUpdate))) - router.PUT("/api/claims/:cid/descriptions", apiHandler(claimHandler(updateClaimDescription))) + apiClaimsRoutes.GET("/last_update", getClaimLastUpdate) + apiClaimsRoutes.PUT("/descriptions", updateClaimDescription) // Assignees - router.GET("/api/claims-assignees", apiHandler(getAssignees)) - router.POST("/api/claims-assignees", apiHandler(newAssignee)) + router.GET("/claims-assignees", getAssignees) + router.POST("/claims-assignees", newAssignee) - router.GET("/api/claims-assignees/:aid", apiHandler(claimAssigneeHandler(showClaimAssignee))) - router.PUT("/api/claims-assignees/:aid", apiHandler(claimAssigneeHandler(updateClaimAssignee))) - router.DELETE("/api/claims-assignees/:aid", apiHandler(claimAssigneeHandler(deleteClaimAssignee))) + 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 getClaims(_ httprouter.Params, _ []byte) (interface{}, error) { - return fic.GetClaims() +func declareExerciceClaimsRoutes(router *gin.RouterGroup) { + router.GET("/claims", getExerciceClaims) } -func getTeamClaims(team *fic.Team, _ []byte) (interface{}, error) { - return team.GetClaims() +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 getExerciceClaims(exercice *fic.Exercice, _ []byte) (interface{}, error) { - return exercice.GetClaims() +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 getClaimLastUpdate(claim *fic.Claim, _ []byte) (interface{}, error) { - return claim.GetLastUpdate() +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 { @@ -76,20 +166,26 @@ type ClaimExported struct { Descriptions []*fic.ClaimDescription `json:"descriptions"` } -func showClaim(claim *fic.Claim, _ []byte) (interface{}, error) { +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 { - return nil, fmt.Errorf("Unable to find associated team: %w", err) + 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 { - return nil, fmt.Errorf("Unable to find associated exercice: %w", err) + 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 { - return nil, fmt.Errorf("Unable to find associated assignee: %w", err) + 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 { - return nil, fmt.Errorf("Unable to find claim's descriptions: %w", err) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to find claim's descriptions: %s", err.Error())}) + return } e.LastUpdate = e.Creation @@ -107,7 +203,8 @@ func showClaim(claim *fic.Claim, _ []byte) (interface{}, error) { e.Creation = claim.Creation e.State = claim.State e.Priority = claim.Priority - return e, nil + + c.JSON(http.StatusOK, e) } type ClaimUploaded struct { @@ -115,20 +212,24 @@ type ClaimUploaded struct { Whoami *int64 `json:"whoami"` } -func newClaim(_ httprouter.Params, body []byte) (interface{}, error) { +func newClaim(c *gin.Context) { var uc ClaimUploaded - if err := json.Unmarshal(body, &uc); err != nil { - return nil, fmt.Errorf("Unable to decode JSON: %w", err) + err := c.ShouldBindJSON(&uc) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } if uc.Subject == "" { - return nil, errors.New("Claim's subject cannot be empty.") + 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 { - return nil, fmt.Errorf("Unable to get associated team: %w", err) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to get associated team: %s", err.Error())}) + return } else { t = team } @@ -139,7 +240,8 @@ func newClaim(_ httprouter.Params, body []byte) (interface{}, error) { var e *fic.Exercice if uc.IdExercice != nil { if exercice, err := fic.GetExercice(*uc.IdExercice); err != nil { - return nil, fmt.Errorf("Unable to get associated exercice: %w", err) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to get associated exercice: %s", err.Error())}) + return } else { e = exercice } @@ -150,7 +252,8 @@ func newClaim(_ httprouter.Params, body []byte) (interface{}, error) { var a *fic.ClaimAssignee if uc.IdAssignee != nil { if assignee, err := fic.GetAssignee(*uc.IdAssignee); err != nil { - return nil, fmt.Errorf("Unable to get associated assignee: %w", err) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to get associated assignee: %s", err.Error())}) + return } else { a = assignee } @@ -162,11 +265,25 @@ func newClaim(_ httprouter.Params, body []byte) (interface{}, error) { uc.Priority = "medium" } - return fic.NewClaim(uc.Subject, t, e, a, uc.Priority) + 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(_ httprouter.Params, _ []byte) (interface{}, error) { - return fic.ClearClaims() +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 { @@ -180,122 +297,189 @@ func generateTeamIssuesFile(team fic.Team) error { return nil } -func addClaimDescription(claim *fic.Claim, body []byte) (interface{}, error) { +func addClaimDescription(c *gin.Context) { + claim := c.MustGet("claim").(*fic.Claim) + var ud fic.ClaimDescription - if err := json.Unmarshal(body, &ud); err != nil { - return nil, fmt.Errorf("Unable to decode JSON: %w", err) + err := c.ShouldBindJSON(&ud) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } - if assignee, err := fic.GetAssignee(ud.IdAssignee); err != nil { - return nil, fmt.Errorf("Unable to get associated assignee: %w", err) - } else if description, err := claim.AddDescription(ud.Content, assignee, ud.Publish); err != nil { - return nil, fmt.Errorf("Unable to add description: %w", err) - } else { - if team, _ := claim.GetTeam(); team != nil { - err = generateTeamIssuesFile(*team) + 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()) } - - return description, err } + + c.JSON(http.StatusOK, description) } -func updateClaimDescription(claim *fic.Claim, body []byte) (interface{}, error) { +func updateClaimDescription(c *gin.Context) { + claim := c.MustGet("claim").(*fic.Claim) + var ud fic.ClaimDescription - if err := json.Unmarshal(body, &ud); err != nil { - return nil, fmt.Errorf("Unable to decode JSON: %w", err) + err := c.ShouldBindJSON(&ud) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } if _, err := ud.Update(); err != nil { - return nil, fmt.Errorf("Unable to update description: %w", err) - } else { - if team, _ := claim.GetTeam(); team != nil { - err = generateTeamIssuesFile(*team) - } - - return ud, err + 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(claim *fic.Claim, body []byte) (interface{}, error) { +func updateClaim(c *gin.Context) { + claim := c.MustGet("claim").(*fic.Claim) + var uc ClaimUploaded - if err := json.Unmarshal(body, &uc); err != nil { - return nil, fmt.Errorf("Unable to decode JSON: %w", err) + err := c.ShouldBindJSON(&uc) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } uc.Id = claim.Id - if _, err := uc.Update(); err != nil { - return nil, fmt.Errorf("Unable to update claim: %w", err) - } else { - 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) - } + _, 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) - } + 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) } + } 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) - } + if team, _ := claim.GetTeam(); team != nil { + err = generateTeamIssuesFile(*team) + } - return uc, err + 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 deleteClaim(claim *fic.Claim, _ []byte) (interface{}, error) { - return claim.Delete() -} - -func getAssignees(_ httprouter.Params, _ []byte) (interface{}, error) { - return fic.GetAssignees() -} - -func showClaimAssignee(assignee *fic.ClaimAssignee, _ []byte) (interface{}, error) { - return assignee, nil -} -func newAssignee(_ httprouter.Params, body []byte) (interface{}, error) { - var ua fic.ClaimAssignee - if err := json.Unmarshal(body, &ua); err != nil { - return nil, fmt.Errorf("Unable to decode JSON: %w", err) +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 } - return fic.NewClaimAssignee(ua.Name) + c.JSON(http.StatusOK, assignees) } -func updateClaimAssignee(assignee *fic.ClaimAssignee, body []byte) (interface{}, error) { +func showClaimAssignee(c *gin.Context) { + c.JSON(http.StatusOK, c.MustGet("claim-assignee").(*fic.ClaimAssignee)) +} +func newAssignee(c *gin.Context) { var ua fic.ClaimAssignee - if err := json.Unmarshal(body, &ua); err != nil { - return nil, fmt.Errorf("Unable to decode JSON: %w", err) + 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 { - return nil, fmt.Errorf("Unable to update claim assignee: %w", err) - } else { - return ua, 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(assignee *fic.ClaimAssignee, _ []byte) (interface{}, error) { - return assignee.Delete() +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 index 0c35c6b3..17500535 100644 --- a/admin/api/events.go +++ b/admin/api/events.go @@ -3,22 +3,45 @@ package api import ( "encoding/json" "io/ioutil" + "log" + "net/http" "path" + "strconv" "srs.epita.fr/fic-server/libfic" - "github.com/julienschmidt/httprouter" + "github.com/gin-gonic/gin" ) -func init() { - router.GET("/api/events/", apiHandler(getEvents)) - router.GET("/api/events.json", apiHandler(getLastEvents)) - router.POST("/api/events/", apiHandler(newEvent)) - router.DELETE("/api/events/", apiHandler(clearEvents)) +func declareEventsRoutes(router *gin.RouterGroup) { + router.GET("/events", getEvents) + router.GET("/events.json", getLastEvents) + router.POST("/events", newEvent) + router.DELETE("/events", clearEvents) - router.GET("/api/events/:evid", apiHandler(eventHandler(showEvent))) - router.PUT("/api/events/:evid", apiHandler(eventHandler(updateEvent))) - router.DELETE("/api/events/:evid", apiHandler(eventHandler(deleteEvent))) + 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 { @@ -33,65 +56,99 @@ func genEventsFile() error { return nil } -func getEvents(_ httprouter.Params, _ []byte) (interface{}, error) { - if evts, err := fic.GetEvents(); err != nil { - return nil, err - } else { - return evts, 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(_ httprouter.Params, _ []byte) (interface{}, error) { - if evts, err := fic.GetLastEvents(); err != nil { - return nil, err - } else { - return evts, nil +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 showEvent(event *fic.Event, _ []byte) (interface{}, error) { - return event, nil -} - -func newEvent(_ httprouter.Params, body []byte) (interface{}, error) { +func newEvent(c *gin.Context) { var ue fic.Event - if err := json.Unmarshal(body, &ue); err != nil { - return nil, err + err := c.ShouldBindJSON(&ue) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } - if event, err := fic.NewEvent(ue.Text, ue.Kind); err != nil { - return nil, err - } else { - genEventsFile() - return event, nil + 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(_ httprouter.Params, _ []byte) (interface{}, error) { - return fic.ClearEvents() +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 updateEvent(event *fic.Event, body []byte) (interface{}, error) { +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 - if err := json.Unmarshal(body, &ue); err != nil { - return nil, err + 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 { - return nil, err - } else { - genEventsFile() - return ue, 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(event *fic.Event, _ []byte) (interface{}, error) { - if i, err := event.Delete(); err != nil { - return i, err - } else { - genEventsFile() - return i, err +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 index c53dc931..e7b98c43 100644 --- a/admin/api/exercice.go +++ b/admin/api/exercice.go @@ -1,121 +1,254 @@ package api import ( - "encoding/json" - "errors" "fmt" + "log" + "net/http" + "strconv" "strings" "time" "srs.epita.fr/fic-server/admin/sync" "srs.epita.fr/fic-server/libfic" - "github.com/julienschmidt/httprouter" + "github.com/gin-gonic/gin" ) -func init() { - router.GET("/api/exercices/", apiHandler(listExercices)) - router.GET("/api/resolutions.json", apiHandler(exportResolutionMovies)) - - router.GET("/api/exercices/:eid", apiHandler(exerciceHandler(showExercice))) - router.PUT("/api/exercices/:eid", apiHandler(exerciceHandler(updateExercice))) - router.PATCH("/api/exercices/:eid", apiHandler(exerciceHandler(partUpdateExercice))) - router.DELETE("/api/exercices/:eid", apiHandler(exerciceHandler(deleteExercice))) - - router.GET("/api/exercices/:eid/stats.json", apiHandler(exerciceHandler(getExerciceStats))) - router.GET("/api/exercices_stats.json", apiHandler(getExercicesStats)) - - router.GET("/api/exercices/:eid/history.json", apiHandler(exerciceHandler(getExerciceHistory))) - router.PATCH("/api/exercices/:eid/history.json", apiHandler(exerciceHandler(updateExerciceHistory))) - router.DELETE("/api/exercices/:eid/history.json", apiHandler(exerciceHandler(delExerciceHistory))) - - router.GET("/api/exercices/:eid/hints", apiHandler(exerciceHandler(listExerciceHints))) - router.POST("/api/exercices/:eid/hints", apiHandler(exerciceHandler(createExerciceHint))) - router.GET("/api/exercices/:eid/hints/:hid", apiHandler(hintHandler(showExerciceHint))) - router.PUT("/api/exercices/:eid/hints/:hid", apiHandler(hintHandler(updateExerciceHint))) - router.DELETE("/api/exercices/:eid/hints/:hid", apiHandler(hintHandler(deleteExerciceHint))) - router.GET("/api/exercices/:eid/hints/:hid/dependancies", apiHandler(hintHandler(showExerciceHintDeps))) - - router.GET("/api/exercices/:eid/flags", apiHandler(exerciceHandler(listExerciceFlags))) - router.POST("/api/exercices/:eid/flags", apiHandler(exerciceHandler(createExerciceFlag))) - router.GET("/api/exercices/:eid/flags/:kid", apiHandler(flagKeyHandler(showExerciceFlag))) - router.PUT("/api/exercices/:eid/flags/:kid", apiHandler(flagKeyHandler(updateExerciceFlag))) - router.POST("/api/exercices/:eid/flags/:kid/try", apiHandler(flagKeyHandler(tryExerciceFlag))) - router.DELETE("/api/exercices/:eid/flags/:kid", apiHandler(flagKeyHandler(deleteExerciceFlag))) - router.GET("/api/exercices/:eid/flags/:kid/dependancies", apiHandler(flagKeyHandler(showExerciceFlagDeps))) - router.GET("/api/exercices/:eid/flags/:kid/choices/", apiHandler(flagKeyHandler(listFlagChoices))) - router.GET("/api/exercices/:eid/flags/:kid/choices/:cid", apiHandler(choiceHandler(showFlagChoice))) - router.POST("/api/exercices/:eid/flags/:kid/choices/", apiHandler(flagKeyHandler(createFlagChoice))) - router.PUT("/api/exercices/:eid/flags/:kid/choices/:cid", apiHandler(choiceHandler(updateFlagChoice))) - router.DELETE("/api/exercices/:eid/flags/:kid/choices/:cid", apiHandler(choiceHandler(deleteFlagChoice))) - - router.GET("/api/exercices/:eid/quiz", apiHandler(exerciceHandler(listExerciceQuiz))) - router.GET("/api/exercices/:eid/quiz/:qid", apiHandler(quizHandler(showExerciceQuiz))) - router.PUT("/api/exercices/:eid/quiz/:qid", apiHandler(quizHandler(updateExerciceQuiz))) - router.DELETE("/api/exercices/:eid/quiz/:qid", apiHandler(quizHandler(deleteExerciceQuiz))) - router.GET("/api/exercices/:eid/quiz/:qid/dependancies", apiHandler(quizHandler(showExerciceQuizDeps))) - - router.GET("/api/exercices/:eid/tags", apiHandler(exerciceHandler(listExerciceTags))) - router.POST("/api/exercices/:eid/tags", apiHandler(exerciceHandler(addExerciceTag))) - router.PUT("/api/exercices/:eid/tags", apiHandler(exerciceHandler(updateExerciceTags))) - - // Remote - router.GET("/api/remote/themes/:thid/exercices/:exid", apiHandler(sync.ApiGetRemoteExercice)) - router.GET("/api/remote/themes/:thid/exercices/:exid/hints", apiHandler(sync.ApiGetRemoteExerciceHints)) - router.GET("/api/remote/themes/:thid/exercices/:exid/flags", apiHandler(sync.ApiGetRemoteExerciceFlags)) - - // Synchronize - router.POST("/api/sync/themes/:thid/exercices/:eid", apiHandler(themedExerciceHandler( - func(theme *fic.Theme, exercice *fic.Exercice, _ []byte) (interface{}, error) { - _, _, errs := sync.SyncExercice(sync.GlobalImporter, theme, exercice.Path, nil) - return errs, nil - }))) - router.POST("/api/sync/exercices/:eid/hints", apiHandler(exerciceHandler( - func(exercice *fic.Exercice, _ []byte) (interface{}, error) { - _, errs := sync.SyncExerciceHints(sync.GlobalImporter, exercice, sync.ExerciceFlagsMap(sync.GlobalImporter, exercice)) - return errs, nil - }))) - router.POST("/api/sync/exercices/:eid/flags", apiHandler(exerciceHandler( - func(exercice *fic.Exercice, _ []byte) (interface{}, error) { - _, errs := sync.SyncExerciceFlags(sync.GlobalImporter, exercice) - _, herrs := sync.SyncExerciceHints(sync.GlobalImporter, exercice, sync.ExerciceFlagsMap(sync.GlobalImporter, exercice)) - return append(errs, herrs...), nil - }))) - - router.POST("/api/sync/exercices/:eid/fixurlid", apiHandler(exerciceHandler( - func(exercice *fic.Exercice, _ []byte) (interface{}, error) { - if exercice.FixURLId() { - return exercice.Update() - } - return 0, nil - }))) +func declareGlobalExercicesRoutes(router *gin.RouterGroup) { + router.GET("/resolutions.json", exportResolutionMovies) + router.GET("/exercices_stats.json", getExercicesStats) } -func listExercices(_ httprouter.Params, body []byte) (interface{}, error) { - // List all exercices - return fic.GetExercices() +func declareExercicesRoutes(router *gin.RouterGroup) { + router.GET("/exercices", listExercices) + + apiExercicesRoutes := router.Group("/exercices/:eid") + apiExercicesRoutes.Use(ExerciceHandler) + apiExercicesRoutes.GET("", showExercice) + apiExercicesRoutes.PUT("", updateExercice) + apiExercicesRoutes.PATCH("", partUpdateExercice) + apiExercicesRoutes.DELETE("", deleteExercice) + + apiExercicesRoutes.GET("/stats.json", getExerciceStats) + + apiExercicesRoutes.GET("/history.json", getExerciceHistory) + apiExercicesRoutes.PATCH("/history.json", updateExerciceHistory) + apiExercicesRoutes.DELETE("/history.json", 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("/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) + + 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/hints", sync.ApiGetRemoteExerciceHints) + router.GET("/remote/themes/:thid/exercices/:exid/flags", sync.ApiGetRemoteExerciceFlags) +} + +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 + } + } + + 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) + } } // Generate the csv to export with: // curl -s http://127.0.0.1:8081/api/resolutions.json | jq -r ".[] | [ .theme,.title, @uri \"https://fic.srs.epita.fr/resolution/\\(.videoURI)\" ] | join(\";\")" -func exportResolutionMovies(_ httprouter.Params, body []byte) (interface{}, error) { - if exercices, err := fic.GetExercices(); err != nil { - return nil, err - } else { - export := []map[string]string{} - for _, exercice := range exercices { - if theme, err := fic.GetTheme(exercice.IdTheme); err != nil { - return nil, err - } else { - export = append(export, map[string]string{ - "videoURI": exercice.VideoURI, - "theme": theme.Name, - "title": exercice.Title, - }) - } - } - return export, nil +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 { + if theme, err := fic.GetTheme(exercice.IdTheme); err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) + return + } else { + export = append(export, map[string]string{ + "videoURI": exercice.VideoURI, + "theme": theme.Name, + "title": exercice.Title, + }) + } + } + + c.JSON(http.StatusOK, export) } func loadFlags(n func() ([]fic.Flag, error)) (interface{}, error) { @@ -146,28 +279,73 @@ func loadFlags(n func() ([]fic.Flag, error)) (interface{}, error) { } } -func listExerciceHints(exercice *fic.Exercice, body []byte) (interface{}, error) { - return exercice.GetHints() +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(exercice *fic.Exercice, body []byte) (interface{}, error) { - return exercice.GetFlagKeys() +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(flag *fic.FlagKey, _ *fic.Exercice, body []byte) (interface{}, error) { - return flag.GetChoices() +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(exercice *fic.Exercice, body []byte) (interface{}, error) { - return exercice.GetMCQ() +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(exercice *fic.Exercice, body []byte) (interface{}, error) { - return exercice, nil +func showExercice(c *gin.Context) { + c.JSON(http.StatusOK, c.MustGet("exercice").(*fic.Exercice)) } -func getExerciceHistory(exercice *fic.Exercice, body []byte) (interface{}, error) { - return exercice.GetHistory() +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 { @@ -179,33 +357,37 @@ type exerciceStats struct { MCQSolved []int64 `json:"mcq_solved"` } -func getExerciceStats(e *fic.Exercice, body []byte) (interface{}, error) { - return exerciceStats{ +func getExerciceStats(c *gin.Context) { + e := c.MustGet("exercice").(*fic.Exercice) + + c.JSON(http.StatusOK, exerciceStats{ TeamTries: e.TriedTeamCount(), TotalTries: e.TriedCount(), SolvedCount: e.SolvedCount(), FlagSolved: e.FlagSolved(), MCQSolved: e.MCQSolved(), - }, nil + }) } -func getExercicesStats(_ httprouter.Params, body []byte) (interface{}, error) { - if exercices, err := fic.GetExercices(); err != nil { - return nil, err - } else { - 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(), - }) - } - return ret, nil +func getExercicesStats(c *gin.Context) { + exercices, err := fic.GetExercices() + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) } + + 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) } type uploadedExerciceHistory struct { @@ -216,51 +398,93 @@ type uploadedExerciceHistory struct { Coeff float32 } -func updateExerciceHistory(exercice *fic.Exercice, body []byte) (interface{}, error) { +func updateExerciceHistory(c *gin.Context) { + exercice := c.MustGet("exercice").(*fic.Exercice) + var uh uploadedExerciceHistory - if err := json.Unmarshal(body, &uh); err != nil { - return nil, err + err := c.ShouldBindJSON(&uh) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } - return exercice.UpdateHistoryItem(uh.Coeff, uh.IdTeam, uh.Kind, uh.Time, uh.Secondary) -} - -func delExerciceHistory(exercice *fic.Exercice, body []byte) (interface{}, error) { - var uh uploadedExerciceHistory - if err := json.Unmarshal(body, &uh); err != nil { - return nil, err + _, 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 } - return exercice.DelHistoryItem(uh.IdTeam, uh.Kind, uh.Time, uh.Secondary) + c.JSON(http.StatusOK, uh) } -func deleteExercice(exercice *fic.Exercice, _ []byte) (interface{}, error) { - return exercice.DeleteCascade() +func delExerciceHistory(c *gin.Context) { + exercice := c.MustGet("exercice").(*fic.Exercice) + + 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 + } + + c.JSON(http.StatusOK, true) } -func updateExercice(exercice *fic.Exercice, body []byte) (interface{}, error) { +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 - if err := json.Unmarshal(body, &ue); err != nil { - return nil, err + 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 { - return nil, errors.New("Exercice's title not filled") + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Exercice's title not filled"}) + return } if _, err := ue.Update(); err != nil { - return nil, err + log.Println("Unable to updateExercice:", err.Error()) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "An error occurs during exercice update"}) + return } - return ue, nil + c.JSON(http.StatusOK, ue) } -func partUpdateExercice(exercice *fic.Exercice, body []byte) (interface{}, error) { +func partUpdateExercice(c *gin.Context) { + exercice := c.MustGet("exercice").(*fic.Exercice) + var ue fic.Exercice - if err := json.Unmarshal(body, &ue); err != nil { - return nil, err + err := c.ShouldBindJSON(&ue) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } if len(ue.Title) > 0 { @@ -312,33 +536,49 @@ func partUpdateExercice(exercice *fic.Exercice, body []byte) (interface{}, error } if _, err := exercice.Update(); err != nil { - return nil, err + log.Println("Unable to partUpdateExercice:", err.Error()) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during exercice update."}) + return } - return exercice, nil + c.JSON(http.StatusOK, exercice) } -func createExercice(theme *fic.Theme, body []byte) (interface{}, error) { +func createExercice(c *gin.Context) { + theme := c.MustGet("theme").(*fic.Theme) + // Create a new exercice var ue fic.Exercice - if err := json.Unmarshal(body, &ue); err != nil { - return nil, err + err := c.ShouldBindJSON(&ue) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } if len(ue.Title) == 0 { - return nil, errors.New("Title not filled") + 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 { - return nil, err + log.Println("Unable to createExercice:", err.Error()) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during exercice creation."}) + return } else { depend = d } } - return theme.AddExercice(ue.Title, ue.URLId, ue.Path, ue.Statement, ue.Overview, ue.Headline, depend, ue.Gain, ue.VideoURI, ue.Resolution, ue.SeeAlso, ue.Finished) + exercice, err := theme.AddExercice(ue.Title, 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 { @@ -349,53 +589,98 @@ type uploadedHint struct { URI string } -func createExerciceHint(exercice *fic.Exercice, body []byte) (interface{}, error) { +func createExerciceHint(c *gin.Context) { + exercice := c.MustGet("exercice").(*fic.Exercice) + var uh uploadedHint - if err := json.Unmarshal(body, &uh); err != nil { - return nil, err + err := c.ShouldBindJSON(&uh) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } if len(uh.Content) != 0 { - return exercice.AddHint(uh.Title, uh.Content, uh.Cost) + 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 { - return sync.ImportFile(sync.GlobalImporter, uh.URI, + 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 { - return nil, errors.New("Hint's content not filled") + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Hint's content not filled"}) + return } } -func showExerciceHint(hint *fic.EHint, body []byte) (interface{}, error) { - return hint, nil +func showExerciceHint(c *gin.Context) { + c.JSON(http.StatusOK, c.MustGet("hint").(*fic.EHint)) } -func showExerciceHintDeps(hint *fic.EHint, body []byte) (interface{}, error) { - return loadFlags(hint.GetDepends) +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(hint *fic.EHint, body []byte) (interface{}, error) { +func updateExerciceHint(c *gin.Context) { + hint := c.MustGet("hint").(*fic.EHint) + var uh fic.EHint - if err := json.Unmarshal(body, &uh); err != nil { - return nil, err + 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 { - return nil, errors.New("Hint's title not filled") + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Hint's title not filled"}) + return } if _, err := uh.Update(); err != nil { - return nil, err + log.Println("Unable to updateExerciceHint:", err.Error()) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to update hint."}) + return } - return uh, nil + c.JSON(http.StatusOK, uh) } -func deleteExerciceHint(hint *fic.EHint, _ []byte) (interface{}, error) { - return hint.Delete() +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 { @@ -412,14 +697,17 @@ type uploadedFlag struct { ChoicesCost int64 `json:"choices_cost"` } -func createExerciceFlag(exercice *fic.Exercice, body []byte) (interface{}, error) { +func createExerciceFlag(c *gin.Context) { var uk uploadedFlag - if err := json.Unmarshal(body, &uk); err != nil { - return nil, err + err := c.ShouldBindJSON(&uk) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } if len(uk.Flag) == 0 { - return nil, errors.New("Flag not filled") + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Flag not filled"}) + return } var vre *string = nil @@ -427,38 +715,66 @@ func createExerciceFlag(exercice *fic.Exercice, body []byte) (interface{}, error vre = uk.ValidatorRe } - return exercice.AddRawFlagKey(uk.Label, uk.Type, uk.Placeholder, uk.IgnoreCase, uk.NoTrim, uk.Multiline, vre, uk.SortReGroups, []byte(uk.Flag), uk.ChoicesCost) + 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) + 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(flag *fic.FlagKey, _ *fic.Exercice, body []byte) (interface{}, error) { - return flag, nil +func showExerciceFlag(c *gin.Context) { + c.JSON(http.StatusOK, c.MustGet("flag-key").(*fic.FlagKey)) } -func showExerciceFlagDeps(flag *fic.FlagKey, _ *fic.Exercice, body []byte) (interface{}, error) { - return loadFlags(flag.GetDepends) +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 tryExerciceFlag(flag *fic.FlagKey, _ *fic.Exercice, body []byte) (interface{}, error) { +func tryExerciceFlag(c *gin.Context) { + flag := c.MustGet("flag-key").(*fic.FlagKey) + var uk uploadedFlag - if err := json.Unmarshal(body, &uk); err != nil { - return nil, err + err := c.ShouldBindJSON(&uk) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } if len(uk.Flag) == 0 { - return nil, errors.New("Empty submission") + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Empty submission"}) + return } if flag.Check([]byte(uk.Flag)) == 0 { - return true, nil - } else { - return nil, errors.New("Bad submission") + c.AbortWithStatusJSON(http.StatusOK, true) + return } + + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Bad submission"}) } -func updateExerciceFlag(flag *fic.FlagKey, exercice *fic.Exercice, body []byte) (interface{}, error) { +func updateExerciceFlag(c *gin.Context) { + flag := c.MustGet("flag-key").(*fic.FlagKey) + var uk uploadedFlag - if err := json.Unmarshal(body, &uk); err != nil { - return nil, err + err := c.ShouldBindJSON(&uk) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } if len(uk.Label) == 0 { @@ -474,7 +790,9 @@ func updateExerciceFlag(flag *fic.FlagKey, exercice *fic.Exercice, body []byte) var err error flag.Checksum, err = flag.ComputeChecksum([]byte(uk.Flag)) if err != nil { - return nil, err + 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 @@ -488,37 +806,63 @@ func updateExerciceFlag(flag *fic.FlagKey, exercice *fic.Exercice, body []byte) } if _, err := flag.Update(); err != nil { - return nil, err + log.Println("Unable to updateExerciceFlag:", err.Error()) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to update flag."}) + return } - return flag, nil + c.JSON(http.StatusOK, flag) } -func deleteExerciceFlag(flag *fic.FlagKey, _ *fic.Exercice, _ []byte) (interface{}, error) { - return flag.Delete() +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(flag *fic.FlagKey, exercice *fic.Exercice, body []byte) (interface{}, error) { +func createFlagChoice(c *gin.Context) { + flag := c.MustGet("flag-key").(*fic.FlagKey) + var uc fic.FlagChoice - if err := json.Unmarshal(body, &uc); err != nil { - return nil, err + 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 } - return flag.AddChoice(&uc) + 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(choice *fic.FlagChoice, _ *fic.Exercice, body []byte) (interface{}, error) { - return choice, nil +func showFlagChoice(c *gin.Context) { + c.JSON(http.StatusOK, c.MustGet("flag-choice").(*fic.FlagChoice)) } -func updateFlagChoice(choice *fic.FlagChoice, _ *fic.Exercice, body []byte) (interface{}, error) { +func updateFlagChoice(c *gin.Context) { + choice := c.MustGet("flag-choice").(*fic.FlagChoice) + var uc fic.FlagChoice - if err := json.Unmarshal(body, &uc); err != nil { - return nil, err + err := c.ShouldBindJSON(&uc) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } if len(uc.Label) == 0 { @@ -530,34 +874,60 @@ func updateFlagChoice(choice *fic.FlagChoice, _ *fic.Exercice, body []byte) (int choice.Value = uc.Value if _, err := choice.Update(); err != nil { - return nil, err + log.Println("Unable to updateFlagChoice:", err.Error()) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to update flag choice."}) + return } - return choice, nil + c.JSON(http.StatusOK, choice) } -func deleteFlagChoice(choice *fic.FlagChoice, _ *fic.Exercice, _ []byte) (interface{}, error) { - return choice.Delete() +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(quiz *fic.MCQ, _ *fic.Exercice, body []byte) (interface{}, error) { - return quiz, nil +func showExerciceQuiz(c *gin.Context) { + c.JSON(http.StatusOK, c.MustGet("flag-quiz").(*fic.MCQ)) } -func showExerciceQuizDeps(quiz *fic.MCQ, _ *fic.Exercice, body []byte) (interface{}, error) { - return loadFlags(quiz.GetDepends) +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 updateExerciceQuiz(quiz *fic.MCQ, exercice *fic.Exercice, body []byte) (interface{}, error) { +func updateExerciceQuiz(c *gin.Context) { + quiz := c.MustGet("flag-quiz").(*fic.MCQ) + var uq fic.MCQ - if err := json.Unmarshal(body, &uq); err != nil { - return nil, err + 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 { - return nil, err + 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 @@ -572,7 +942,9 @@ func updateExerciceQuiz(quiz *fic.MCQ, exercice *fic.Exercice, body []byte) (int cur.Label = next.Label cur.Response = next.Response if _, err := cur.Update(); err != nil { - return nil, err + log.Println("Unable to update MCQ entry:", err.Error()) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to update some MCQ entry"}) + return } } @@ -582,7 +954,9 @@ func updateExerciceQuiz(quiz *fic.MCQ, exercice *fic.Exercice, body []byte) (int if seen == false { if _, err := cur.Delete(); err != nil { - return nil, err + 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) } @@ -595,48 +969,78 @@ func updateExerciceQuiz(quiz *fic.MCQ, exercice *fic.Exercice, body []byte) (int // Add new choices for _, choice := range uq.Entries { if choice.Id == 0 { - if c, err := quiz.AddEntry(choice); err != nil { - return nil, err + 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, c) + quiz.Entries = append(quiz.Entries, ch) } } } - return quiz, nil + c.JSON(http.StatusOK, quiz) } -func deleteExerciceQuiz(quiz *fic.MCQ, _ *fic.Exercice, _ []byte) (interface{}, error) { +func deleteExerciceQuiz(c *gin.Context) { + quiz := c.MustGet("flag-quiz").(*fic.MCQ) + for _, choice := range quiz.Entries { if _, err := choice.Delete(); err != nil { - return nil, err + 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 } } - return quiz.Delete() + _, 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(exercice *fic.Exercice, _ []byte) (interface{}, error) { - return exercice.GetTags() +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(exercice *fic.Exercice, body []byte) (interface{}, error) { +func addExerciceTag(c *gin.Context) { + exercice := c.MustGet("exercice").(*fic.Exercice) + var ut []string - if err := json.Unmarshal(body, &ut); err != nil { - return nil, err + 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 { - return nil, err + log.Println("Unable to addExerciceTag:", err.Error()) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to add some tag."}) + return } } - return ut, nil + c.JSON(http.StatusOK, ut) } -func updateExerciceTags(exercice *fic.Exercice, body []byte) (interface{}, error) { +func updateExerciceTags(c *gin.Context) { + exercice := c.MustGet("exercice").(*fic.Exercice) + exercice.WipeTags() - return addExerciceTag(exercice, body) + addExerciceTag(c) } diff --git a/admin/api/file.go b/admin/api/file.go index 8edd5424..f164ebb2 100644 --- a/admin/api/file.go +++ b/admin/api/file.go @@ -2,43 +2,79 @@ package api import ( "encoding/hex" - "encoding/json" "fmt" + "log" + "net/http" + "strconv" "srs.epita.fr/fic-server/admin/sync" "srs.epita.fr/fic-server/libfic" - "github.com/julienschmidt/httprouter" + "github.com/gin-gonic/gin" ) -func init() { - router.GET("/api/files/", apiHandler(listFiles)) - router.DELETE("/api/files/", apiHandler(clearFiles)) - - router.GET("/api/files/:fileid", apiHandler(fileHandler(showFile))) - router.PUT("/api/files/:fileid", apiHandler(fileHandler(updateFile))) - router.DELETE("/api/files/:fileid", apiHandler(fileHandler(deleteFile))) - - router.DELETE("/api/files/:fileid/dependancies/:depid", apiHandler(fileDependancyHandler(deleteFileDep))) - - router.GET("/api/exercices/:eid/files", apiHandler(exerciceHandler(listExerciceFiles))) - router.POST("/api/exercices/:eid/files", apiHandler(exerciceHandler(createExerciceFile))) - - router.GET("/api/exercices/:eid/files/:fid", apiHandler(exerciceFileHandler(showFile))) - router.PUT("/api/exercices/:eid/files/:fid", apiHandler(exerciceFileHandler(updateFile))) - router.DELETE("/api/exercices/:eid/files/:fid", apiHandler(exerciceFileHandler(deleteFile))) +func declareFilesGlobalRoutes(router *gin.RouterGroup) { + router.DELETE("/files/", clearFiles) // Remote - router.GET("/api/remote/themes/:thid/exercices/:exid/files", apiHandler(sync.ApiGetRemoteExerciceFiles)) + 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 - router.POST("/api/files/:fileid/check", apiHandler(fileHandler(checkFile))) + apiFilesRoutes.POST("/check", checkFile) +} - // Synchronize - router.POST("/api/sync/exercices/:eid/files", apiHandler(exerciceHandler( - func(exercice *fic.Exercice, _ []byte) (interface{}, error) { - return sync.SyncExerciceFiles(sync.GlobalImporter, exercice), nil - }))) +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 { @@ -87,20 +123,35 @@ func genFileList(in []*fic.EFile, e error) (out []APIFile, err error) { return } -func listFiles(_ httprouter.Params, body []byte) (interface{}, error) { - return genFileList(fic.GetFiles()) +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 listExerciceFiles(exercice *fic.Exercice, body []byte) (interface{}, error) { - return genFileList(exercice.GetFiles()) +func clearFiles(c *gin.Context) { + _, err := fic.ClearFiles() + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) + return + } + + c.JSON(http.StatusOK, true) } -func clearFiles(_ httprouter.Params, _ []byte) (interface{}, error) { - return fic.ClearFiles() -} - -func showFile(file *fic.EFile, _ []byte) (interface{}, error) { - return file, nil +func showFile(c *gin.Context) { + c.JSON(http.StatusOK, c.MustGet("file").(*fic.EFile)) } type uploadedFile struct { @@ -108,45 +159,92 @@ type uploadedFile struct { Digest string } -func createExerciceFile(exercice *fic.Exercice, body []byte) (interface{}, error) { - var uf uploadedFile - if err := json.Unmarshal(body, &uf); err != nil { - return nil, err +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 } - return sync.ImportFile(sync.GlobalImporter, uf.URI, + 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 { - return exercice.ImportFile(filePath, origin, digest) + return exercice.(*fic.Exercice).ImportFile(filePath, origin, digest) } }) + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) + return + } + + c.JSON(http.StatusOK, ret) } -func updateFile(file *fic.EFile, body []byte) (interface{}, error) { +func updateFile(c *gin.Context) { + file := c.MustGet("file").(*fic.EFile) + var uf fic.EFile - if err := json.Unmarshal(body, &uf); err != nil { - return nil, err + 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 { - return nil, err - } else { - return uf, 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(file *fic.EFile, _ []byte) (interface{}, error) { - return file.Delete() +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(file *fic.EFile, depid int, _ []byte) (interface{}, error) { - return true, file.DeleteDepend(&fic.FlagKey{Id: depid}) +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(file *fic.EFile, _ []byte) (interface{}, error) { - return true, file.CheckFileOnDisk() +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) } diff --git a/admin/api/handlers.go b/admin/api/handlers.go deleted file mode 100644 index 302ab441..00000000 --- a/admin/api/handlers.go +++ /dev/null @@ -1,352 +0,0 @@ -package api - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "log" - "net/http" - "strconv" - "strings" - "time" - - "srs.epita.fr/fic-server/libfic" - - "github.com/julienschmidt/httprouter" -) - -type DispatchFunction func(httprouter.Params, []byte) (interface{}, error) - -func apiHandler(f DispatchFunction) func(http.ResponseWriter, *http.Request, httprouter.Params) { - return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - if addr := r.Header.Get("X-Forwarded-For"); addr != "" { - r.RemoteAddr = addr - } - log.Printf("%s \"%s %s\" [%s]\n", r.RemoteAddr, r.Method, r.URL.Path, r.UserAgent()) - - // Read the body - if r.ContentLength < 0 || r.ContentLength > 6553600 { - http.Error(w, fmt.Sprintf("{errmsg:\"Request too large or request size unknown\"}"), http.StatusRequestEntityTooLarge) - return - } - var body []byte - if r.ContentLength > 0 { - tmp := make([]byte, 1024) - for { - n, err := r.Body.Read(tmp) - for j := 0; j < n; j++ { - body = append(body, tmp[j]) - } - if err != nil || n <= 0 { - break - } - } - } - - var ret interface{} - var err error = nil - - ret, err = f(ps, body) - - // Format response - resStatus := http.StatusOK - if err != nil { - ret = map[string]string{"errmsg": err.Error()} - resStatus = http.StatusBadRequest - log.Println(r.RemoteAddr, resStatus, err.Error()) - } - - if ret == nil { - ret = map[string]string{"errmsg": "Page not found"} - resStatus = http.StatusNotFound - } - - w.Header().Set("X-FIC-Time", fmt.Sprintf("%f", float64(time.Now().UnixNano()/1000)/1000000)) - - if str, found := ret.(string); found { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(resStatus) - io.WriteString(w, str) - } else if bts, found := ret.([]byte); found { - w.Header().Set("Content-Type", "application/octet-stream") - w.Header().Set("Content-Disposition", "attachment") - w.Header().Set("Content-Transfer-Encoding", "binary") - w.WriteHeader(resStatus) - w.Write(bts) - } else if j, err := json.Marshal(ret); err != nil { - w.Header().Set("Content-Type", "application/json") - http.Error(w, fmt.Sprintf("{\"errmsg\":%q}", err), http.StatusInternalServerError) - } else { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(resStatus) - w.Write(j) - } - } -} - -func teamPublicHandler(f func(*fic.Team, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { - return func(ps httprouter.Params, body []byte) (interface{}, error) { - if tid, err := strconv.ParseInt(string(ps.ByName("tid")), 10, 64); err != nil { - return nil, err - } else if tid == 0 { - return f(nil, body) - } else if team, err := fic.GetTeam(tid); err != nil { - return nil, err - } else { - return f(team, body) - } - } -} - -func teamHandler(f func(*fic.Team, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { - return func(ps httprouter.Params, body []byte) (interface{}, error) { - if tid, err := strconv.ParseInt(string(ps.ByName("tid")), 10, 64); err != nil { - return nil, err - } else if team, err := fic.GetTeam(tid); err != nil { - return nil, err - } else { - return f(team, body) - } - } -} - -func teamAssocHandler(f func(*fic.Team, string, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { - return func(ps httprouter.Params, body []byte) (interface{}, error) { - var team *fic.Team - - teamHandler(func(tm *fic.Team, _ []byte) (interface{}, error) { - team = tm - return nil, nil - })(ps, body) - - return f(team, string(ps.ByName("assoc")), body) - } -} - -func themeHandler(f func(*fic.Theme, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { - return func(ps httprouter.Params, body []byte) (interface{}, error) { - if thid, err := strconv.ParseInt(string(ps.ByName("thid")), 10, 64); err != nil { - return nil, err - } else if theme, err := fic.GetTheme(thid); err != nil { - return nil, err - } else { - return f(theme, body) - } - } -} - -func exerciceHandler(f func(*fic.Exercice, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { - return func(ps httprouter.Params, body []byte) (interface{}, error) { - if eid, err := strconv.ParseInt(string(ps.ByName("eid")), 10, 64); err != nil { - return nil, err - } else if exercice, err := fic.GetExercice(eid); err != nil { - return nil, err - } else { - return f(exercice, body) - } - } -} - -func themedExerciceHandler(f func(*fic.Theme, *fic.Exercice, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { - return func(ps httprouter.Params, body []byte) (interface{}, error) { - var theme *fic.Theme - var exercice *fic.Exercice - - themeHandler(func(th *fic.Theme, _ []byte) (interface{}, error) { - theme = th - return nil, nil - })(ps, body) - - exerciceHandler(func(ex *fic.Exercice, _ []byte) (interface{}, error) { - exercice = ex - return nil, nil - })(ps, body) - - return f(theme, exercice, body) - } -} - -func hintHandler(f func(*fic.EHint, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { - return func(ps httprouter.Params, body []byte) (interface{}, error) { - if hid, err := strconv.ParseInt(string(ps.ByName("hid")), 10, 64); err != nil { - return nil, err - } else if hint, err := fic.GetHint(hid); err != nil { - return nil, err - } else { - return f(hint, body) - } - } -} - -func flagKeyHandler(f func(*fic.FlagKey, *fic.Exercice, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { - return func(ps httprouter.Params, body []byte) (interface{}, error) { - var exercice *fic.Exercice - exerciceHandler(func(ex *fic.Exercice, _ []byte) (interface{}, error) { - exercice = ex - return nil, nil - })(ps, body) - - if kid, err := strconv.ParseInt(string(ps.ByName("kid")), 10, 64); err != nil { - return nil, err - } else if flags, err := exercice.GetFlagKeys(); err != nil { - return nil, err - } else { - for _, flag := range flags { - if flag.Id == int(kid) { - return f(flag, exercice, body) - } - } - return nil, errors.New("Unable to find the requested key") - } - } -} - -func choiceHandler(f func(*fic.FlagChoice, *fic.Exercice, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { - return func(ps httprouter.Params, body []byte) (interface{}, error) { - var exercice *fic.Exercice - var flag *fic.FlagKey - flagKeyHandler(func(fl *fic.FlagKey, ex *fic.Exercice, _ []byte) (interface{}, error) { - exercice = ex - flag = fl - return nil, nil - })(ps, body) - - if cid, err := strconv.ParseInt(string(ps.ByName("cid")), 10, 32); err != nil { - return nil, err - } else if choice, err := flag.GetChoice(int(cid)); err != nil { - return nil, err - } else { - return f(choice, exercice, body) - } - } -} - -func quizHandler(f func(*fic.MCQ, *fic.Exercice, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { - return func(ps httprouter.Params, body []byte) (interface{}, error) { - var exercice *fic.Exercice - exerciceHandler(func(ex *fic.Exercice, _ []byte) (interface{}, error) { - exercice = ex - return nil, nil - })(ps, body) - - if qid, err := strconv.ParseInt(string(ps.ByName("qid")), 10, 64); err != nil { - return nil, err - } else if mcqs, err := exercice.GetMCQ(); err != nil { - return nil, err - } else { - for _, mcq := range mcqs { - if mcq.Id == int(qid) { - return f(mcq, exercice, body) - } - } - return nil, errors.New("Unable to find the requested key") - } - } -} - -func exerciceFileHandler(f func(*fic.EFile, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { - return func(ps httprouter.Params, body []byte) (interface{}, error) { - var exercice *fic.Exercice - exerciceHandler(func(ex *fic.Exercice, _ []byte) (interface{}, error) { - exercice = ex - return nil, nil - })(ps, body) - - if fid, err := strconv.ParseInt(string(ps.ByName("fid")), 10, 64); err != nil { - return nil, err - } else if files, err := exercice.GetFiles(); err != nil { - return nil, err - } else { - for _, file := range files { - if file.Id == fid { - return f(file, body) - } - } - return nil, errors.New("Unable to find the requested file") - } - } -} - -func eventHandler(f func(*fic.Event, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { - return func(ps httprouter.Params, body []byte) (interface{}, error) { - if evid, err := strconv.ParseInt(string(ps.ByName("evid")), 10, 64); err != nil { - return nil, err - } else if event, err := fic.GetEvent(evid); err != nil { - return nil, err - } else { - return f(event, body) - } - } -} - -func claimHandler(f func(*fic.Claim, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { - return func(ps httprouter.Params, body []byte) (interface{}, error) { - if cid, err := strconv.ParseInt(string(ps.ByName("cid")), 10, 64); err != nil { - return nil, fmt.Errorf("Invalid claim id: %w", err) - } else if claim, err := fic.GetClaim(cid); err != nil { - return nil, fmt.Errorf("Unable to find requested claim (id=%d): %w", cid, err) - } else { - return f(claim, body) - } - } -} - -func claimAssigneeHandler(f func(*fic.ClaimAssignee, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { - return func(ps httprouter.Params, body []byte) (interface{}, error) { - if aid, err := strconv.ParseInt(string(ps.ByName("aid")), 10, 64); err != nil { - return nil, err - } else if assignee, err := fic.GetAssignee(aid); err != nil { - return nil, err - } else { - return f(assignee, body) - } - } -} - -func fileHandler(f func(*fic.EFile, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { - return func(ps httprouter.Params, body []byte) (interface{}, error) { - if fileid, err := strconv.ParseInt(string(ps.ByName("fileid")), 10, 64); err != nil { - return nil, err - } else if file, err := fic.GetFile(fileid); err != nil { - return nil, err - } else { - return f(file, body) - } - } -} - -func fileDependancyHandler(f func(*fic.EFile, int, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { - return func(ps httprouter.Params, body []byte) (interface{}, error) { - if depid, err := strconv.ParseInt(string(ps.ByName("depid")), 10, 64); err != nil { - return nil, err - } else { - return fileHandler(func(file *fic.EFile, b []byte) (interface{}, error) { - return f(file, int(depid), b) - })(ps, body) - } - } -} - -func certificateHandler(f func(*fic.Certificate, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { - return func(ps httprouter.Params, body []byte) (interface{}, error) { - var cid uint64 - var err error - certid := strings.TrimSuffix(ps.ByName("certid"), ".p12") - if cid, err = strconv.ParseUint(certid, 10, 64); err != nil { - if cid, err = strconv.ParseUint(certid, 16, 64); err != nil { - return nil, err - } - } - - if cert, err := fic.GetCertificate(cid); err != nil { - return nil, err - } else { - return f(cert, body) - } - } -} - -func notFound(ps httprouter.Params, _ []byte) (interface{}, error) { - return nil, nil -} diff --git a/admin/api/health.go b/admin/api/health.go index f3cf2e27..133cf5af 100644 --- a/admin/api/health.go +++ b/admin/api/health.go @@ -3,6 +3,7 @@ package api import ( "fmt" "io/ioutil" + "net/http" "os" "path" "strings" @@ -10,26 +11,26 @@ import ( "srs.epita.fr/fic-server/admin/pki" - "github.com/julienschmidt/httprouter" + "github.com/gin-gonic/gin" ) var TimestampCheck = "submissions" -func init() { - router.GET("/api/timestamps.json", apiHandler( - func(httprouter.Params, []byte) (interface{}, error) { - if stat, err := os.Stat(TimestampCheck); err != nil { - return nil, err - } else { - now := time.Now().UTC() - return map[string]interface{}{ - "frontend": stat.ModTime().UTC(), - "backend": now, - "diffFB": now.Sub(stat.ModTime()), - }, nil - } - })) - router.GET("/api/health.json", apiHandler(GetHealth)) +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) } type healthFileReport struct { @@ -41,22 +42,22 @@ type healthFileReport struct { 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), - }) + 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 { + } else if !d.IsDir() && d.Mode()&os.ModeSymlink == 0 && time.Since(d.ModTime()) > 2*time.Second { 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", + Path: path.Join(teamDir, d.Name()), + Error: "existing untreated file", }) } } @@ -64,10 +65,11 @@ func getHealth(pathname string) (ret []healthFileReport) { } } -func GetHealth(httprouter.Params, []byte) (interface{}, error) { +func GetHealth(c *gin.Context) { if _, err := os.Stat(TimestampCheck); err != nil { - return nil, err - } else { - return getHealth(TimestampCheck), nil + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": fmt.Sprintf("health.json: %s", err.Error())}) + return } + + c.JSON(http.StatusOK, getHealth(TimestampCheck)) } diff --git a/admin/api/monitor.go b/admin/api/monitor.go index b8af548f..3d6a9114 100644 --- a/admin/api/monitor.go +++ b/admin/api/monitor.go @@ -3,20 +3,20 @@ package api import ( "bufio" "io/ioutil" + "net/http" "os" "strconv" "strings" - "github.com/julienschmidt/httprouter" + "github.com/gin-gonic/gin" ) -func init() { - router.GET("/api/monitor", apiHandler( - func(httprouter.Params, []byte) (interface{}, error) { - return map[string]interface{}{ - "localhost": genLocalConstants(), - }, nil - })) +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) { @@ -58,7 +58,7 @@ func readCPUStats(fd *os.File) (ret map[string]map[string]uint64) { 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 { + if v, err := strconv.ParseUint(f[i+1], 10, 64); err == nil { ret[f[0]][k] = v total += v } diff --git a/admin/api/password.go b/admin/api/password.go index 9a20c4a9..38d7896d 100644 --- a/admin/api/password.go +++ b/admin/api/password.go @@ -4,67 +4,97 @@ import ( "bytes" "fmt" "io/ioutil" + "log" + "net/http" "path" "text/template" "srs.epita.fr/fic-server/admin/pki" "srs.epita.fr/fic-server/libfic" - "github.com/julienschmidt/httprouter" + "github.com/gin-gonic/gin" ) var OidcSecret = "" -func init() { - router.POST("/api/password", apiHandler( - func(httprouter.Params, []byte) (interface{}, error) { - if passwd, err := fic.GeneratePassword(); err != nil { - return nil, err - } else { - return map[string]string{"password": passwd}, nil +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("/api/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("/api/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("/api/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("/api/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) + }) +} + +func declareTeamsPasswordRoutes(router *gin.RouterGroup) { + router.GET("/password", func(c *gin.Context) { + team := c.MustGet("team").(*fic.Team) + + c.String(http.StatusOK, *team.Password) + }) + 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 + + t, 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 } - })) - router.GET("/api/teams/:tid/password", apiHandler(teamHandler( - func(team *fic.Team, _ []byte) (interface{}, error) { - return team.Password, nil - }))) - router.POST("/api/teams/:tid/password", apiHandler(teamHandler( - func(team *fic.Team, _ []byte) (interface{}, error) { - if passwd, err := fic.GeneratePassword(); err != nil { - return nil, err - } else { - team.Password = &passwd - return team.Update() - } - }))) - router.GET("/api/dex.yaml", apiHandler( - func(httprouter.Params, []byte) (interface{}, error) { - return genDexConfig() - })) - router.POST("/api/dex.yaml", apiHandler( - func(httprouter.Params, []byte) (interface{}, error) { - if dexcfg, err := genDexConfig(); err != nil { - return nil, err - } else if err := ioutil.WriteFile(path.Join(pki.PKIDir, "shared", "dex-config.yaml"), []byte(dexcfg), 0644); err != nil { - return nil, err - } else { - return true, nil - } - })) - router.GET("/api/dex-password.tpl", apiHandler( - func(httprouter.Params, []byte) (interface{}, error) { - return genDexPasswordTpl() - })) - router.POST("/api/dex-password.tpl", apiHandler( - func(httprouter.Params, []byte) (interface{}, error) { - if dexcfg, err := genDexPasswordTpl(); err != nil { - return nil, err - } else if err := ioutil.WriteFile(path.Join(pki.PKIDir, "shared", "dex-password.tpl"), []byte(dexcfg), 0644); err != nil { - return nil, err - } else { - return true, nil - } - })) + + c.JSON(http.StatusOK, t) + } + }) } const dexcfgtpl = `issuer: https://fic.srs.epita.fr diff --git a/admin/api/public.go b/admin/api/public.go index 595cfba3..09319ac4 100644 --- a/admin/api/public.go +++ b/admin/api/public.go @@ -3,18 +3,20 @@ package api import ( "encoding/json" "fmt" + "log" + "net/http" "os" "path" - "github.com/julienschmidt/httprouter" + "github.com/gin-gonic/gin" ) var DashboardDir string -func init() { - router.GET("/api/public/:sid", apiHandler(getPublic)) - router.DELETE("/api/public/:sid", apiHandler(deletePublic)) - router.PUT("/api/public/:sid", apiHandler(savePublic)) +func declarePublicRoutes(router *gin.RouterGroup) { + router.GET("/public/:sid", getPublic) + router.DELETE("/public/:sid", deletePublic) + router.PUT("/public/:sid", savePublic) } type FICPublicScene struct { @@ -62,31 +64,44 @@ func savePublicTo(path string, s FICPublicDisplay) error { } } -func getPublic(ps httprouter.Params, body []byte) (interface{}, error) { - if _, err := os.Stat(path.Join(DashboardDir, fmt.Sprintf("public%s.json", ps.ByName("sid")))); !os.IsNotExist(err) { - return readPublic(path.Join(DashboardDir, fmt.Sprintf("public%s.json", ps.ByName("sid")))) - } else { - return FICPublicDisplay{Scenes: []FICPublicScene{}, Side: []FICPublicScene{}}, nil +func getPublic(c *gin.Context) { + if _, err := os.Stat(path.Join(DashboardDir, fmt.Sprintf("public%s.json", c.Params.ByName("sid")))); !os.IsNotExist(err) { + p, err := readPublic(path.Join(DashboardDir, fmt.Sprintf("public%s.json", c.Params.ByName("sid")))) + 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) } + + c.JSON(http.StatusOK, FICPublicDisplay{Scenes: []FICPublicScene{}, Side: []FICPublicScene{}}) } -func deletePublic(ps httprouter.Params, body []byte) (interface{}, error) { - if err := savePublicTo(path.Join(DashboardDir, fmt.Sprintf("public%s.json", ps.ByName("sid"))), FICPublicDisplay{}); err != nil { - return nil, err - } else { - return FICPublicDisplay{Scenes: []FICPublicScene{}, Side: []FICPublicScene{}}, nil +func deletePublic(c *gin.Context) { + if err := savePublicTo(path.Join(DashboardDir, fmt.Sprintf("public%s.json", c.Params.ByName("sid"))), FICPublicDisplay{}); 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(ps httprouter.Params, body []byte) (interface{}, error) { +func savePublic(c *gin.Context) { var scenes FICPublicDisplay - if err := json.Unmarshal(body, &scenes); err != nil { - return nil, err + err := c.ShouldBindJSON(&scenes) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } - if err := savePublicTo(path.Join(DashboardDir, fmt.Sprintf("public%s.json", ps.ByName("sid"))), scenes); err != nil { - return nil, err - } else { - return scenes, err + if err := savePublicTo(path.Join(DashboardDir, fmt.Sprintf("public%s.json", c.Params.ByName("sid"))), 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 index f24ae7d3..26c4cf63 100644 --- a/admin/api/qa.go +++ b/admin/api/qa.go @@ -1,84 +1,118 @@ package api import ( - "encoding/json" - "errors" + "log" + "net/http" "strconv" "srs.epita.fr/fic-server/libfic" - "github.com/julienschmidt/httprouter" + "github.com/gin-gonic/gin" ) -func init() { - router.POST("/api/qa/", apiHandler(importExerciceQA)) - router.POST("/api/qa/:qid/comments", apiHandler(qaHandler(importQAComment))) +func declareQARoutes(router *gin.RouterGroup) { + router.POST("/qa/", importExerciceQA) + + apiQARoutes := router.Group("/qa/:qid") + apiQARoutes.POST("/comments", importQAComment) } -func qaHandler(f func(*fic.QAQuery, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { - return func(ps httprouter.Params, body []byte) (interface{}, error) { - if qid, err := strconv.ParseInt(string(ps.ByName("qid")), 10, 64); err != nil { - return nil, err - } else if query, err := fic.GetQAQuery(qid); err != nil { - return nil, err - } else { - return f(query, body) - } +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(_ httprouter.Params, body []byte) (interface{}, error) { +func importExerciceQA(c *gin.Context) { // Create a new query var uq fic.QAQuery - if err := json.Unmarshal(body, &uq); err != nil { - return nil, err + err := c.ShouldBindJSON(&uq) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } var exercice *fic.Exercice - var err error if uq.IdExercice == 0 { - return nil, errors.New("id_exercice not filled") + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "id_exercice not filled"}) + return } else if exercice, err = fic.GetExercice(uq.IdExercice); err != nil { - return nil, err + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Unable to find requested exercice"}) + return } if len(uq.State) == 0 { - return nil, errors.New("State not filled") + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "State not filled"}) + return } if len(uq.Subject) == 0 { - return nil, errors.New("Subject not filled") + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Subject not filled"}) + return } if qa, err := exercice.NewQAQuery(uq.Subject, uq.IdTeam, uq.User, uq.State); err != nil { - return nil, err + 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 = qa.Closed _, err = qa.Update() - return qa, err + 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(query *fic.QAQuery, body []byte) (interface{}, error) { +func importQAComment(c *gin.Context) { + query := c.MustGet("qa-query").(*fic.QAQuery) + // Create a new query var uc fic.QAComment - if err := json.Unmarshal(body, &uc); err != nil { - return nil, err + err := c.ShouldBindJSON(&uc) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } if len(uc.Content) == 0 { - return nil, errors.New("Empty comment") + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Empty comment"}) + return } if qac, err := query.AddComment(uc.Content, uc.IdTeam, uc.User); err != nil { - return nil, err + 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() - return qac, err + 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/router.go b/admin/api/router.go index a6bd873b..991a58f1 100644 --- a/admin/api/router.go +++ b/admin/api/router.go @@ -1,11 +1,26 @@ package api import ( - "github.com/julienschmidt/httprouter" + "github.com/gin-gonic/gin" ) -var router = httprouter.New() +func DeclareRoutes(router *gin.RouterGroup) { + apiRoutes := router.Group("/api") -func Router() *httprouter.Router { - return router + declareCertificateRoutes(apiRoutes) + declareClaimsRoutes(apiRoutes) + declareEventsRoutes(apiRoutes) + declareExercicesRoutes(apiRoutes) + declareFilesRoutes(apiRoutes) + declareGlobalExercicesRoutes(apiRoutes) + declareHealthRoutes(apiRoutes) + declareMonitorRoutes(apiRoutes) + declarePasswordRoutes(apiRoutes) + declarePublicRoutes(apiRoutes) + declareQARoutes(apiRoutes) + declareTeamsRoutes(apiRoutes) + declareThemesRoutes(apiRoutes) + declareSettingsRoutes(apiRoutes) + declareSyncRoutes(apiRoutes) + DeclareVersionRoutes(apiRoutes) } diff --git a/admin/api/settings.go b/admin/api/settings.go index d3adb39f..2fed7856 100644 --- a/admin/api/settings.go +++ b/admin/api/settings.go @@ -1,35 +1,44 @@ package api import ( - "encoding/json" - "errors" + "fmt" + "log" + "net/http" "path" "reflect" + "time" "srs.epita.fr/fic-server/admin/sync" "srs.epita.fr/fic-server/libfic" "srs.epita.fr/fic-server/settings" - "github.com/julienschmidt/httprouter" + "github.com/gin-gonic/gin" ) var IsProductionEnv = false -func init() { - router.GET("/api/challenge.json", apiHandler(getChallengeInfo)) - router.PUT("/api/challenge.json", apiHandler(saveChallengeInfo)) +func declareSettingsRoutes(router *gin.RouterGroup) { + router.GET("/challenge.json", getChallengeInfo) + router.PUT("/challenge.json", saveChallengeInfo) - router.GET("/api/settings-ro.json", apiHandler(getROSettings)) - router.GET("/api/settings.json", apiHandler(getSettings)) - router.PUT("/api/settings.json", apiHandler(saveSettings)) - router.DELETE("/api/settings.json", apiHandler(func(_ httprouter.Params, _ []byte) (interface{}, error) { - return true, ResetSettings() - })) + router.GET("/settings-ro.json", getROSettings) + 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 + } - router.POST("/api/reset", apiHandler(reset)) + c.JSON(http.StatusOK, true) + }) + + router.POST("/reset", reset) } -func getROSettings(_ httprouter.Params, body []byte) (interface{}, error) { +func getROSettings(c *gin.Context) { syncMtd := "Disabled" if sync.GlobalImporter != nil { syncMtd = sync.GlobalImporter.Kind() @@ -40,51 +49,70 @@ func getROSettings(_ httprouter.Params, body []byte) (interface{}, error) { syncId = sync.GlobalImporter.Id() } - return map[string]interface{}{ + c.JSON(http.StatusOK, gin.H{ "sync-type": reflect.TypeOf(sync.GlobalImporter).Name(), "sync-id": syncId, "sync": syncMtd, - }, nil + }) } -func getChallengeInfo(_ httprouter.Params, body []byte) (interface{}, error) { - return settings.ReadChallengeInfo(path.Join(settings.SettingsDir, settings.ChallengeFile)) +func getChallengeInfo(c *gin.Context) { + s, err := settings.ReadChallengeInfo(path.Join(settings.SettingsDir, settings.ChallengeFile)) + if err != nil { + log.Println("Unable to ReadChallengeInfo:", err.Error()) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to read challenge info: %s", err.Error())}) + return + } + + c.JSON(http.StatusOK, s) } -func saveChallengeInfo(_ httprouter.Params, body []byte) (interface{}, error) { +func saveChallengeInfo(c *gin.Context) { var info *settings.ChallengeInfo - if err := json.Unmarshal(body, &info); err != nil { - return nil, err + err := c.ShouldBindJSON(&info) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } if err := settings.SaveChallengeInfo(path.Join(settings.SettingsDir, settings.ChallengeFile), info); err != nil { - return nil, err - } else { - return info, err + log.Println("Unable to SaveChallengeInfo:", err.Error()) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to save challenge info: %s", err.Error())}) + return } + + c.JSON(http.StatusOK, info) } -func getSettings(_ httprouter.Params, body []byte) (interface{}, error) { - if s, err := settings.ReadSettings(path.Join(settings.SettingsDir, settings.SettingsFile)); err != nil { - return nil, err - } else { - s.WorkInProgress = !IsProductionEnv - return s, nil +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(_ httprouter.Params, body []byte) (interface{}, error) { +func saveSettings(c *gin.Context) { var config *settings.Settings - if err := json.Unmarshal(body, &config); err != nil { - return nil, err + err := c.ShouldBindJSON(&config) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } if err := settings.SaveSettings(path.Join(settings.SettingsDir, settings.SettingsFile), config); err != nil { - return nil, err - } else { - ApplySettings(config) - return config, err + 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 ApplySettings(config *settings.Settings) { @@ -160,25 +188,40 @@ func ResetChallengeInfo() error { }) } -func reset(_ httprouter.Params, body []byte) (interface{}, error) { +func reset(c *gin.Context) { var m map[string]string - if err := json.Unmarshal(body, &m); err != nil { - return nil, err + err := c.ShouldBindJSON(&m) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } - if t, ok := m["type"]; !ok { - return nil, errors.New("Field type not found") - } else if t == "teams" { - return true, fic.ResetTeams() - } else if t == "challenges" { - return true, fic.ResetExercices() - } else if t == "game" { - return true, fic.ResetGame() - } else if t == "settings" { - return true, ResetSettings() - } else if t == "challengeInfo" { - return true, ResetChallengeInfo() - } else { - return nil, errors.New("Unknown reset type") + 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 "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 new file mode 100644 index 00000000..d54bbf46 --- /dev/null +++ b/admin/api/sync.go @@ -0,0 +1,191 @@ +package api + +import ( + "fmt" + "log" + "net/http" + "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 declareSyncRoutes(router *gin.RouterGroup) { + apiSyncRoutes := router.Group("/sync") + + // 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 { + c.JSON(http.StatusExpectationFailed, gin.H{"errmsg": err.Error()}) + } else { + 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.GET("/deep", func(c *gin.Context) { + if sync.DeepSyncProgress == 0 { + c.AbortWithStatusJSON(http.StatusTooEarly, gin.H{"errmsg": "Pas de synchronisation en cours"}) + return + } + c.JSON(http.StatusOK, gin.H{"progress": sync.DeepSyncProgress}) + }) + apiSyncRoutes.POST("/deep", func(c *gin.Context) { + c.JSON(http.StatusOK, sync.SyncDeep(sync.GlobalImporter)) + }) + + apiSyncDeepRoutes := apiSyncRoutes.Group("/deep/:thid") + apiSyncDeepRoutes.Use(ThemeHandler) + apiSyncDeepRoutes.POST("", func(c *gin.Context) { + theme := c.MustGet("theme").(*fic.Theme) + + st := sync.SyncThemeDeep(sync.GlobalImporter, theme, 0, 250) + sync.EditDeepReport(map[string][]string{theme.Name: st}, false) + sync.DeepSyncProgress = 255 + 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) { + c.JSON(http.StatusOK, sync.SyncThemes(sync.GlobalImporter)) + }) + + 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) +} + +func declareSyncExercicesRoutes(router *gin.RouterGroup) { + router.POST("/exercices", func(c *gin.Context) { + theme := c.MustGet("theme").(*fic.Theme) + c.JSON(http.StatusOK, sync.SyncExercices(sync.GlobalImporter, theme)) + }) + apiSyncExercicesRoutes := router.Group("/exercices/:eid") + apiSyncExercicesRoutes.Use(ExerciceHandler) + apiSyncExercicesRoutes.POST("", func(c *gin.Context) { + theme, exists := c.Get("theme") + if !exists { + c.AbortWithStatusJSON(http.StatusNotImplemented, gin.H{"errmsg": "You should sync exercice only through a theme."}) + return + } + + exercice := c.MustGet("exercice").(*fic.Exercice) + + _, _, errs := sync.SyncExercice(sync.GlobalImporter, theme.(*fic.Theme), exercice.Path, nil) + c.JSON(http.StatusOK, errs) + }) + apiSyncExercicesRoutes.POST("/files", func(c *gin.Context) { + exercice := c.MustGet("exercice").(*fic.Exercice) + c.JSON(http.StatusOK, sync.SyncExerciceFiles(sync.GlobalImporter, exercice)) + }) + 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) + _, errs := sync.SyncExerciceHints(sync.GlobalImporter, exercice, sync.ExerciceFlagsMap(sync.GlobalImporter, exercice)) + c.JSON(http.StatusOK, errs) + }) + apiSyncExercicesRoutes.POST("/flags", func(c *gin.Context) { + exercice := c.MustGet("exercice").(*fic.Exercice) + _, errs := sync.SyncExerciceFlags(sync.GlobalImporter, exercice) + _, herrs := sync.SyncExerciceHints(sync.GlobalImporter, exercice, sync.ExerciceFlagsMap(sync.GlobalImporter, exercice)) + c.JSON(http.StatusOK, 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.TrimPrefix(c.Params.ByName("p"), "/") + + 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 + } + + if p == "" { + if !IsProductionEnv { + for _, theme := range themes { + theme.DeleteDeep() + } + } + + st := sync.SyncDeep(sync.GlobalImporter) + c.JSON(http.StatusOK, st) + return + } + + for _, theme := range themes { + if theme.Path == p { + if !IsProductionEnv { + exercices, err := theme.GetExercices() + if err == nil { + for _, exercice := range exercices { + exercice.DeleteDeep() + } + } + } + + st := sync.SyncThemeDeep(sync.GlobalImporter, theme, 0, 250) + sync.EditDeepReport(map[string][]string{theme.Name: st}, false) + sync.DeepSyncProgress = 255 + + settings.ForceRegeneration() + + c.JSON(http.StatusOK, st) + return + } + } + + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": fmt.Sprintf("Theme not found %q", p)}) +} diff --git a/admin/api/team.go b/admin/api/team.go index c6151d3c..b58d8a91 100644 --- a/admin/api/team.go +++ b/admin/api/team.go @@ -3,167 +3,312 @@ package api import ( "encoding/json" "fmt" + "log" "math/rand" + "net/http" + "strconv" "strings" "time" "srs.epita.fr/fic-server/admin/pki" "srs.epita.fr/fic-server/libfic" - "github.com/julienschmidt/httprouter" + "github.com/gin-gonic/gin" ) -func init() { - router.GET("/api/teams.json", apiHandler( - func(httprouter.Params, []byte) (interface{}, error) { - return fic.ExportTeams(false) - })) - router.GET("/api/teams-members.json", apiHandler( - func(httprouter.Params, []byte) (interface{}, error) { - return fic.ExportTeams(true) - })) - router.GET("/api/teams-binding", apiHandler( - func(httprouter.Params, []byte) (interface{}, error) { - return bindingTeams() - })) - router.GET("/api/teams-nginx", apiHandler( - func(httprouter.Params, []byte) (interface{}, error) { - return nginxGenTeams() - })) - router.POST("/api/disableinactiveteams", apiHandler(disableInactiveTeams)) - router.POST("/api/enableallteams", apiHandler(enableAllTeams)) - router.GET("/api/teams-members-nginx", apiHandler( - func(httprouter.Params, []byte) (interface{}, error) { - return nginxGenMember() - })) - router.GET("/api/teams-tries.json", apiHandler( - func(httprouter.Params, []byte) (interface{}, error) { - return fic.GetTries(nil, nil) - })) - - router.GET("/api/teams/", apiHandler( - func(httprouter.Params, []byte) (interface{}, error) { - return fic.GetTeams() - })) - router.POST("/api/teams/", apiHandler(createTeam)) - - router.GET("/api/teams/:tid/", apiHandler(teamHandler( - func(team *fic.Team, _ []byte) (interface{}, error) { - return team, nil - }))) - router.PUT("/api/teams/:tid/", apiHandler(teamHandler(updateTeam))) - router.POST("/api/teams/:tid/", apiHandler(teamHandler(addTeamMember))) - router.DELETE("/api/teams/:tid/", apiHandler(teamHandler(deleteTeam))) - router.GET("/api/teams/:tid/score-grid.json", apiHandler(teamHandler( - func(team *fic.Team, _ []byte) (interface{}, error) { - return team.ScoreGrid() - }))) - router.GET("/api/teams/:tid/my.json", apiHandler(teamPublicHandler( - func(team *fic.Team, _ []byte) (interface{}, error) { - return fic.MyJSONTeam(team, true) - }))) - router.GET("/api/teams/:tid/wait.json", apiHandler(teamPublicHandler( - func(team *fic.Team, _ []byte) (interface{}, error) { - return fic.MyJSONTeam(team, false) - }))) - router.GET("/api/teams/:tid/stats.json", apiHandler(teamPublicHandler( - func(team *fic.Team, _ []byte) (interface{}, error) { - if team != nil { - return team.GetStats() - } else { - return fic.GetTeamsStats(nil) - } - }))) - router.GET("/api/teams/:tid/history.json", apiHandler(teamPublicHandler( - func(team *fic.Team, _ []byte) (interface{}, error) { - if team != nil { - return team.GetHistory() - } else { - return fic.GetTeamsStats(nil) - } - }))) - router.DELETE("/api/teams/:tid/history.json", apiHandler(teamPublicHandler(delHistory))) - router.GET("/api/teams/:tid/tries", apiHandler(teamPublicHandler( - func(team *fic.Team, _ []byte) (interface{}, error) { - return fic.GetTries(team, nil) - }))) - router.GET("/api/teams/:tid/members", apiHandler(teamHandler( - func(team *fic.Team, _ []byte) (interface{}, error) { - return team.GetMembers() - }))) - router.POST("/api/teams/:tid/members", apiHandler(teamHandler(addTeamMember))) - router.PUT("/api/teams/:tid/members", apiHandler(teamHandler(setTeamMember))) -} - -func nginxGenTeams() (string, error) { - if teams, err := fic.GetTeams(); err != nil { - return "", err - } else { - ret := "" - for _, team := range teams { - ret += fmt.Sprintf(" if ($remote_user = \"%s\") { set $team \"%d\"; }\n", strings.ToLower(team.Name), team.Id) +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 } - return ret, nil - } -} - -func nginxGenMember() (string, error) { - if teams, err := fic.GetTeams(); err != nil { - return "", err - } else { - 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 { - return "", err - } + 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 } - return ret, nil - } -} - -func bindingTeams() (string, error) { - if teams, err := fic.GetTeams(); err != nil { - return "", err - } else { - ret := "" - for _, team := range teams { - if members, err := team.GetMembers(); err != nil { - return "", err - } 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.JSON(http.StatusOK, teams) + }) + router.GET("/teams-binding", bindingTeams) + router.GET("/teams-nginx", nginxGenTeams) + 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 } - return ret, nil - } + + 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) { + tfile, err := fic.MyJSONTeam(c.MustGet("team").(*fic.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) { + tfile, err := fic.MyJSONTeam(c.MustGet("team").(*fic.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) { + team := c.MustGet("team").(*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.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) } -func createTeam(_ httprouter.Params, body []byte) (interface{}, error) { +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) +} + +func createTeam(c *gin.Context) { var ut fic.Team - if err := json.Unmarshal(body, &ut); err != nil { - return nil, err + err := c.ShouldBindJSON(&ut) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } if ut.Color == 0 { ut.Color = fic.HSL{rand.Float64(), 1, 0.5}.ToRGB() } - return fic.CreateTeam(strings.TrimSpace(ut.Name), ut.Color, ut.ExternalId) + 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(team *fic.Team, body []byte) (interface{}, error) { +func updateTeam(c *gin.Context) { + team := c.MustGet("team").(*fic.Team) + var ut fic.Team - if err := json.Unmarshal(body, &ut); err != nil { - return nil, err + err := c.ShouldBindJSON(&ut) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } ut.Id = team.Id @@ -172,102 +317,132 @@ func updateTeam(team *fic.Team, body []byte) (interface{}, error) { ut.Password = nil } - if _, err := ut.Update(); err != nil { - return nil, err + _, 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 } - return ut, nil + c.JSON(http.StatusOK, team) } -func disableInactiveTeams(_ httprouter.Params, _ []byte) (interface{}, error) { - if teams, err := fic.GetTeams(); err != nil { - return nil, err - } else { - for _, team := range teams { - var serials []uint64 - serials, err = pki.GetTeamSerials(TeamsDir, team.Id) - if err != nil { - return nil, err - } +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 + } - var assocs []string - assocs, err = pki.GetTeamAssociations(TeamsDir, team.Id) - if err != nil { - return nil, err - } - - if len(serials) == 0 && len(assocs) == 0 { - if team.Active { - team.Active = false - team.Update() - } - } else if !team.Active { - team.Active = true - team.Update() - } + for _, team := range teams { + var serials []uint64 + serials, err = pki.GetTeamSerials(TeamsDir, team.Id) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return } - return true, nil - } -} - -func enableAllTeams(_ httprouter.Params, _ []byte) (interface{}, error) { - if teams, err := fic.GetTeams(); err != nil { - return nil, err - } else { - for _, team := range teams { - if !team.Active { - team.Active = true - team.Update() - } + var assocs []string + assocs, err = pki.GetTeamAssociations(TeamsDir, team.Id) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return } - return true, nil + 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 deleteTeam(team *fic.Team, _ []byte) (interface{}, error) { +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 { - return nil, err + log.Printf("Unable to GetTeamAssociations(tid=%s): %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 { - return nil, err + log.Println("Unable to deleteTeam:", err.Error()) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during team deletion."}) + return } - return team.Delete() + c.JSON(http.StatusOK, true) } -func addTeamMember(team *fic.Team, body []byte) (interface{}, error) { +func addTeamMember(c *gin.Context) { + team := c.MustGet("team").(*fic.Team) + var members []fic.Member - if err := json.Unmarshal(body, &members); err != nil { - return nil, err + err := c.ShouldBindJSON(&members) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } for _, member := range members { - team.AddMember(strings.TrimSpace(member.Firstname), strings.TrimSpace(member.Lastname), strings.TrimSpace(member.Nickname), strings.TrimSpace(member.Company)) + _, 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 + } } - return team.GetMembers() + 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(team *fic.Team, body []byte) (interface{}, error) { - var members []fic.Member - if err := json.Unmarshal(body, &members); err != nil { - return nil, err - } - +func setTeamMember(c *gin.Context) { + team := c.MustGet("team").(*fic.Team) team.ClearMembers() - for _, member := range members { - team.AddMember(strings.TrimSpace(member.Firstname), strings.TrimSpace(member.Lastname), strings.TrimSpace(member.Nickname), strings.TrimSpace(member.Company)) - } - - return team.GetMembers() + addTeamMember(c) } type uploadedHistory struct { @@ -294,11 +469,21 @@ func updateHistory(team *fic.Team, body []byte) (interface{}, error) { return team.UpdateHistoryCoeff(uh.Kind, uh.Time, givenId, uh.Coefficient) } -func delHistory(team *fic.Team, body []byte) (interface{}, error) { +func delHistory(c *gin.Context) { + team := c.MustGet("team").(*fic.Team) + var uh uploadedHistory - if err := json.Unmarshal(body, &uh); err != nil { - return nil, err + err := c.ShouldBindJSON(&uh) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } - return team.DelHistoryItem(uh.Kind, uh.Time, uh.Primary, uh.Secondary) + _, 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 index 42af449b..71fe0211 100644 --- a/admin/api/theme.go +++ b/admin/api/theme.go @@ -1,171 +1,75 @@ package api import ( - "encoding/json" - "errors" "fmt" + "log" + "net/http" "path" "strconv" - "strings" "srs.epita.fr/fic-server/admin/sync" "srs.epita.fr/fic-server/libfic" "srs.epita.fr/fic-server/settings" - "github.com/julienschmidt/httprouter" + "github.com/gin-gonic/gin" ) -func init() { - router.GET("/api/themes", apiHandler(listThemes)) - router.POST("/api/themes", apiHandler(createTheme)) - router.GET("/api/themes.json", apiHandler(exportThemes)) - router.GET("/api/session-forensic.yaml", apiHandler(func(_ httprouter.Params, _ []byte) (interface{}, error) { +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 { - return nil, err - } else if c, err := settings.ReadChallengeInfo(path.Join(settings.SettingsDir, settings.ChallengeFile)); err != nil { - return nil, err + log.Printf("Unable to ReadSettings: %s", err.Error()) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during settings reading."}) + return + } else if ch, err := settings.ReadChallengeInfo(path.Join(settings.SettingsDir, settings.ChallengeFile)); 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 { - return fic.GenZQDSSessionFile(c, s) + c.JSON(http.StatusOK, sf) } - })) - router.GET("/api/files-bindings", apiHandler(bindingFiles)) + }) + router.GET("/files-bindings", bindingFiles) - router.GET("/api/themes/:thid", apiHandler(themeHandler(showTheme))) - router.PUT("/api/themes/:thid", apiHandler(themeHandler(updateTheme))) - router.DELETE("/api/themes/:thid", apiHandler(themeHandler(deleteTheme))) + apiThemesRoutes := router.Group("/themes/:thid") + apiThemesRoutes.Use(ThemeHandler) + apiThemesRoutes.GET("", showTheme) + apiThemesRoutes.PUT("", updateTheme) + apiThemesRoutes.DELETE("", deleteTheme) - router.GET("/api/themes/:thid/exercices", apiHandler(themeHandler(listThemedExercices))) - router.POST("/api/themes/:thid/exercices", apiHandler(themeHandler(createExercice))) - - router.GET("/api/themes/:thid/exercices_stats.json", apiHandler(themeHandler(getThemedExercicesStats))) - - router.GET("/api/themes/:thid/exercices/:eid", apiHandler(exerciceHandler(showExercice))) - router.PUT("/api/themes/:thid/exercices/:eid", apiHandler(exerciceHandler(updateExercice))) - router.DELETE("/api/themes/:thid/exercices/:eid", apiHandler(exerciceHandler(deleteExercice))) - - router.GET("/api/themes/:thid/exercices/:eid/files", apiHandler(exerciceHandler(listExerciceFiles))) - router.POST("/api/themes/:thid/exercices/:eid/files", apiHandler(exerciceHandler(createExerciceFile))) - - router.GET("/api/themes/:thid/exercices/:eid/hints", apiHandler(exerciceHandler(listExerciceHints))) - router.POST("/api/themes/:thid/exercices/:eid/hints", apiHandler(exerciceHandler(createExerciceHint))) - - router.GET("/api/themes/:thid/exercices/:eid/keys", apiHandler(exerciceHandler(listExerciceFlags))) - router.POST("/api/themes/:thid/exercices/:eid/keys", apiHandler(exerciceHandler(createExerciceFlag))) + declareExercicesRoutes(apiThemesRoutes) // Remote - router.GET("/api/remote/themes", apiHandler(sync.ApiListRemoteThemes)) - router.GET("/api/remote/themes/:thid", apiHandler(sync.ApiGetRemoteTheme)) - router.GET("/api/remote/themes/:thid/exercices", apiHandler(sync.ApiListRemoteExercices)) - - // Synchronize - router.GET("/api/sync/deep", apiHandler( - func(_ httprouter.Params, _ []byte) (interface{}, error) { - if sync.DeepSyncProgress == 0 { - return nil, errors.New("Pas de synchronisation en cours") - } else { - return map[string]interface{}{"progress": sync.DeepSyncProgress}, nil - } - })) - router.POST("/api/sync/base", apiHandler( - func(_ httprouter.Params, _ []byte) (interface{}, error) { - err := sync.GlobalImporter.Sync() - return true, err - })) - router.POST("/api/sync/speed", apiHandler( - func(_ httprouter.Params, _ []byte) (interface{}, error) { - st := sync.SpeedySyncDeep(sync.GlobalImporter) - sync.EditDeepReport(st, false) - return st, nil - })) - router.POST("/api/sync/deep", apiHandler( - func(_ httprouter.Params, _ []byte) (interface{}, error) { - return sync.SyncDeep(sync.GlobalImporter), nil - })) - router.POST("/api/sync/deep/:thid", apiHandler(themeHandler( - func(theme *fic.Theme, _ []byte) (interface{}, error) { - st := sync.SyncThemeDeep(sync.GlobalImporter, theme, 0, 250) - sync.EditDeepReport(map[string][]string{theme.Name: st}, false) - sync.DeepSyncProgress = 255 - return st, nil - }))) - router.POST("/api/sync/auto/*p", apiHandler( - func(ps httprouter.Params, _ []byte) (interface{}, error) { - p := strings.TrimPrefix(ps.ByName("p"), "/") - - themes, err := fic.GetThemes() - if err != nil { - return nil, err - } - - if p == "" { - if !IsProductionEnv { - for _, theme := range themes { - theme.DeleteDeep() - } - } - - st := sync.SyncDeep(sync.GlobalImporter) - return st, nil - } - - for _, theme := range themes { - if theme.Path == p { - if !IsProductionEnv { - exercices, err := theme.GetExercices() - if err == nil { - for _, exercice := range exercices { - exercice.DeleteDeep() - } - } - } - - st := sync.SyncThemeDeep(sync.GlobalImporter, theme, 0, 250) - sync.EditDeepReport(map[string][]string{theme.Name: st}, false) - sync.DeepSyncProgress = 255 - - settings.ForceRegeneration() - - return st, nil - } - } - - return nil, fmt.Errorf("Theme not found %q", p) - })) - router.POST("/api/sync/themes", apiHandler( - func(_ httprouter.Params, _ []byte) (interface{}, error) { - return sync.SyncThemes(sync.GlobalImporter), nil - })) - router.POST("/api/sync/themes/:thid/exercices", apiHandler(themeHandler( - func(theme *fic.Theme, _ []byte) (interface{}, error) { - return sync.SyncExercices(sync.GlobalImporter, theme), nil - }))) - router.POST("/api/sync/themes/:thid/exercices/:eid/files", apiHandler(exerciceHandler( - func(exercice *fic.Exercice, _ []byte) (interface{}, error) { - return sync.SyncExerciceFiles(sync.GlobalImporter, exercice), nil - }))) - router.POST("/api/sync/themes/:thid/exercices/:eid/hints", apiHandler(exerciceHandler( - func(exercice *fic.Exercice, _ []byte) (interface{}, error) { - _, errs := sync.SyncExerciceHints(sync.GlobalImporter, exercice, sync.ExerciceFlagsMap(sync.GlobalImporter, exercice)) - return errs, nil - }))) - router.POST("/api/sync/themes/:thid/exercices/:eid/keys", apiHandler(exerciceHandler( - func(exercice *fic.Exercice, _ []byte) (interface{}, error) { - _, errs := sync.SyncExerciceFlags(sync.GlobalImporter, exercice) - _, herrs := sync.SyncExerciceHints(sync.GlobalImporter, exercice, sync.ExerciceFlagsMap(sync.GlobalImporter, exercice)) - return append(errs, herrs...), nil - }))) - - router.POST("/api/sync/themes/:thid/fixurlid", apiHandler(themeHandler( - func(theme *fic.Theme, _ []byte) (interface{}, error) { - if theme.FixURLId() { - return theme.Update() - } - return 0, nil - }))) - router.POST("/api/sync/fixurlids", apiHandler(fixAllURLIds)) + router.GET("/remote/themes", sync.ApiListRemoteThemes) + router.GET("/remote/themes/:thid", sync.ApiGetRemoteTheme) + router.GET("/remote/themes/:thid/exercices", sync.ApiListRemoteExercices) } -func fixAllURLIds(_ httprouter.Params, _ []byte) (interface{}, error) { +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 + } + + 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 { @@ -185,19 +89,22 @@ func fixAllURLIds(_ httprouter.Params, _ []byte) (interface{}, error) { } } - return nbFix, nil + c.JSON(http.StatusOK, nbFix) } -func bindingFiles(_ httprouter.Params, body []byte) (interface{}, error) { - if files, err := fic.GetFiles(); err != nil { - return "", err - } else { - ret := "" - for _, file := range files { - ret += fmt.Sprintf("%s;%s\n", file.GetOrigin(), file.Path) - } - return ret, nil +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 getExercice(args []string) (*fic.Exercice, error) { @@ -212,60 +119,92 @@ func getExercice(args []string) (*fic.Exercice, error) { } } -func listThemes(_ httprouter.Params, _ []byte) (interface{}, error) { - return fic.GetThemes() +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 + } + + c.JSON(http.StatusOK, themes) } -func exportThemes(_ httprouter.Params, _ []byte) (interface{}, error) { - return fic.ExportThemes() +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(theme *fic.Theme, _ []byte) (interface{}, error) { - return theme, nil +func showTheme(c *gin.Context) { + c.JSON(http.StatusOK, c.MustGet("theme").(*fic.Theme)) } -func listThemedExercices(theme *fic.Theme, _ []byte) (interface{}, error) { - return theme.GetExercices() -} - -func showThemedExercice(theme *fic.Theme, exercice fic.Exercice, body []byte) (interface{}, error) { - return exercice, nil -} - -func createTheme(_ httprouter.Params, body []byte) (interface{}, error) { +func createTheme(c *gin.Context) { var ut fic.Theme - if err := json.Unmarshal(body, &ut); err != nil { - return nil, err + err := c.ShouldBindJSON(&ut) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return } if len(ut.Name) == 0 { - return nil, errors.New("Theme's name not filled") + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Theme's name not filled"}) + return } - return fic.CreateTheme(&ut) + 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(theme *fic.Theme, body []byte) (interface{}, error) { +func updateTheme(c *gin.Context) { + theme := c.MustGet("theme").(*fic.Theme) + var ut fic.Theme - if err := json.Unmarshal(body, &ut); err != nil { - return nil, err + 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 { - return nil, errors.New("Theme's name not filled") + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Theme's name not filled"}) + return } if _, err := ut.Update(); err != nil { - return nil, err - } else { - return ut, nil + log.Println("Unable to updateTheme:", err.Error()) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "An error occurs during theme update."}) + return } + + c.JSON(http.StatusOK, ut) } -func deleteTheme(theme *fic.Theme, _ []byte) (interface{}, error) { - return theme.Delete() +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(theme *fic.Theme, body []byte) (interface{}, error) { diff --git a/admin/api/version.go b/admin/api/version.go index c31f4343..52cb0726 100644 --- a/admin/api/version.go +++ b/admin/api/version.go @@ -1,13 +1,15 @@ package api import ( - "github.com/julienschmidt/httprouter" + "net/http" + + "github.com/gin-gonic/gin" ) -func init() { - router.GET("/api/version", apiHandler(showVersion)) +func DeclareVersionRoutes(router *gin.RouterGroup) { + router.GET("/version", showVersion) } -func showVersion(_ httprouter.Params, body []byte) (interface{}, error) { - return map[string]interface{}{"version": 1.0}, nil +func showVersion(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{"version": 1.0}) } diff --git a/admin/app.go b/admin/app.go new file mode 100644 index 00000000..0352cf2b --- /dev/null +++ b/admin/app.go @@ -0,0 +1,71 @@ +package main + +import ( + "context" + "log" + "net/http" + "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) + }) + + 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, + } + + 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/main.go b/admin/main.go index 7e3765d8..cacd1961 100644 --- a/admin/main.go +++ b/admin/main.go @@ -1,9 +1,7 @@ package main import ( - "context" "flag" - "fmt" "io/fs" "log" "net/http" @@ -198,12 +196,12 @@ func main() { os.MkdirAll(settings.SettingsDir, 0777) // Initialize settings and load them + var config *settings.Settings if !settings.ExistsSettings(path.Join(settings.SettingsDir, settings.SettingsFile)) { if err = api.ResetSettings(); err != nil { log.Fatal("Unable to initialize settings.json:", err) } } else { - 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 { @@ -231,21 +229,13 @@ func main() { interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM) - srv := &http.Server{ - Addr: *bind, - Handler: StripPrefix(baseURL, api.Router()), - } - - // Serve content - go func() { - log.Fatal(srv.ListenAndServe()) - }() - log.Println(fmt.Sprintf("Ready, listening on %s", *bind)) + app := NewApp(config, baseURL, *bind) + go app.Start() // Wait shutdown signal <-interrupt log.Print("The service is shutting down...") - srv.Shutdown(context.Background()) + app.Stop() log.Println("done") } diff --git a/admin/static.go b/admin/static.go index 2b548fa5..8a0f837f 100644 --- a/admin/static.go +++ b/admin/static.go @@ -3,6 +3,7 @@ package main import ( "bytes" "embed" + "errors" "log" "net/http" "path" @@ -12,8 +13,9 @@ import ( "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/julienschmidt/httprouter" + "github.com/gin-gonic/gin" ) //go:embed static @@ -33,83 +35,83 @@ func genIndex(baseURL string) { } } -func serveIndex(w http.ResponseWriter, r *http.Request) { - w.Write(indexPage) +func serveIndex(c *gin.Context) { + c.Writer.Write(indexPage) } var staticFS http.FileSystem -func serveFile(w http.ResponseWriter, r *http.Request, url string) { - r.URL.Path = url - http.FileServer(staticFS).ServeHTTP(w, r) +func serveFile(c *gin.Context, url string) { + c.Request.URL.Path = url + http.FileServer(staticFS).ServeHTTP(c.Writer, c.Request) } -func init() { - api.Router().GET("/", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - serveIndex(w, r) +func declareStaticRoutes(router *gin.RouterGroup, cfg *settings.Settings, baseURL string) { + router.GET("/", func(c *gin.Context) { + serveIndex(c) }) - api.Router().GET("/claims/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - serveIndex(w, r) + router.GET("/claims/*_", func(c *gin.Context) { + serveIndex(c) }) - api.Router().GET("/exercices/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - serveIndex(w, r) + router.GET("/exercices/*_", func(c *gin.Context) { + serveIndex(c) }) - api.Router().GET("/events/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - serveIndex(w, r) + router.GET("/events/*_", func(c *gin.Context) { + serveIndex(c) }) - api.Router().GET("/files", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - serveIndex(w, r) + router.GET("/files", func(c *gin.Context) { + serveIndex(c) }) - api.Router().GET("/public/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - serveIndex(w, r) + router.GET("/public/*_", func(c *gin.Context) { + serveIndex(c) }) - api.Router().GET("/pki/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - serveIndex(w, r) + router.GET("/pki/*_", func(c *gin.Context) { + serveIndex(c) }) - api.Router().GET("/settings/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - serveIndex(w, r) + router.GET("/settings/*_", func(c *gin.Context) { + serveIndex(c) }) - api.Router().GET("/teams/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - serveIndex(w, r) + router.GET("/teams/*_", func(c *gin.Context) { + serveIndex(c) }) - api.Router().GET("/themes/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - serveIndex(w, r) + router.GET("/themes/*_", func(c *gin.Context) { + serveIndex(c) }) - api.Router().GET("/css/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - serveFile(w, r, r.URL.Path) + router.GET("/css/*_", func(c *gin.Context) { + serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL)) }) - api.Router().GET("/fonts/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - serveFile(w, r, r.URL.Path) + router.GET("/fonts/*_", func(c *gin.Context) { + serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL)) }) - api.Router().GET("/img/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - serveFile(w, r, r.URL.Path) + router.GET("/img/*_", func(c *gin.Context) { + serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL)) }) - api.Router().GET("/js/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - serveFile(w, r, r.URL.Path) + router.GET("/js/*_", func(c *gin.Context) { + serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL)) }) - api.Router().GET("/views/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - serveFile(w, r, r.URL.Path) + router.GET("/views/*_", func(c *gin.Context) { + serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL)) }) - api.Router().GET("/files/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(fic.FilesDir, strings.TrimPrefix(r.URL.Path, "/files"))) + router.GET("/files/*_", func(c *gin.Context) { + http.ServeFile(c.Writer, c.Request, path.Join(fic.FilesDir, strings.TrimPrefix(c.Request.URL.Path, "/files"))) }) - api.Router().GET("/submissions/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(api.TimestampCheck, strings.TrimPrefix(r.URL.Path, "/submissions"))) + router.GET("/submissions/*_", func(c *gin.Context) { + http.ServeFile(c.Writer, c.Request, path.Join(api.TimestampCheck, strings.TrimPrefix(c.Request.URL.Path, "/submissions"))) }) - api.Router().GET("/vids/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + router.GET("/vids/*_", func(c *gin.Context) { if importer, ok := sync.GlobalImporter.(sync.LocalImporter); ok { - http.ServeFile(w, r, path.Join(importer.Base, strings.TrimPrefix(r.URL.Path, "/vids"))) + http.ServeFile(c.Writer, c.Request, path.Join(importer.Base, strings.TrimPrefix(c.Request.URL.Path, "/vids"))) } else { - http.Error(w, "Only available with local importer.", 400) + c.AbortWithError(http.StatusBadRequest, errors.New("Only available with local importer.")) } }) - api.Router().GET("/check_import.html", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - serveFile(w, r, "check_import.html") + router.GET("/check_import.html", func(c *gin.Context) { + serveFile(c, "check_import.html") }) - api.Router().GET("/full_import_report.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, sync.DeepReportPath) + router.GET("/full_import_report.json", func(c *gin.Context) { + http.ServeFile(c.Writer, c.Request, sync.DeepReportPath) }) } diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 47ac1810..f1f0f610 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -300,7 +300,7 @@ angular.module("FICApp") return $resource("api/exercices/:exerciceId/history.json", { exerciceId: '@id' }) }) .factory("ExercicesStats", function($resource) { - return $resource("api/themes/:themeId/exercices_stats.json", { themeId: '@id' }) + return $resource("api/exercices_stats.json", { themeId: '@id' }) }) .factory("ExerciceStats", function($resource) { return $resource("api/exercices/:exerciceId/stats.json", { exerciceId: '@id' }) @@ -1653,7 +1653,7 @@ angular.module("FICApp") }) .controller("ExercicesStatsController", function($scope, ExercicesStats) { - $scope.exercices = ExercicesStats.query({ themeId: $scope.theme.id }); + $scope.exercices = ExercicesStats.query(); }) .controller("ExerciceStatsController", function($scope, ExerciceStats, $routeParams) { diff --git a/admin/sync/exercice_files.go b/admin/sync/exercice_files.go index 3f189904..3d852bdd 100644 --- a/admin/sync/exercice_files.go +++ b/admin/sync/exercice_files.go @@ -4,11 +4,12 @@ import ( "bufio" "encoding/hex" "fmt" + "net/http" "path" "strings" "unicode" - "github.com/julienschmidt/httprouter" + "github.com/gin-gonic/gin" "srs.epita.fr/fic-server/libfic" ) @@ -146,10 +147,10 @@ func SyncExerciceFiles(i Importer, exercice *fic.Exercice) (errs []string) { } // ApiGetRemoteExerciceFiles is an accessor to remote exercice files list. -func ApiGetRemoteExerciceFiles(ps httprouter.Params, _ []byte) (interface{}, error) { - theme, errs := BuildTheme(GlobalImporter, ps.ByName("thid")) +func ApiGetRemoteExerciceFiles(c *gin.Context) { + theme, errs := BuildTheme(GlobalImporter, c.Params.ByName("thid")) if theme != nil { - exercice, _, _, _, errs := BuildExercice(GlobalImporter, theme, path.Join(theme.Path, ps.ByName("exid")), nil) + exercice, _, _, _, errs := BuildExercice(GlobalImporter, theme, path.Join(theme.Path, c.Params.ByName("exid")), nil) if exercice != nil { files, digests, errs := BuildFilesListInto(GlobalImporter, exercice, "files") if files != nil { @@ -164,14 +165,17 @@ func ApiGetRemoteExerciceFiles(ps httprouter.Params, _ []byte) (interface{}, err Size: fSize, }) } - return ret, nil + c.JSON(http.StatusOK, ret) } else { - return nil, fmt.Errorf("%q", errs) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Errorf("%q", errs)}) + return } } else { - return nil, fmt.Errorf("%q", errs) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Errorf("%q", errs)}) + return } } else { - return nil, fmt.Errorf("%q", errs) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Errorf("%q", errs)}) + return } } diff --git a/admin/sync/exercice_hints.go b/admin/sync/exercice_hints.go index c0a03a8e..a0f4edc8 100644 --- a/admin/sync/exercice_hints.go +++ b/admin/sync/exercice_hints.go @@ -6,11 +6,12 @@ import ( "encoding/hex" "fmt" "io" + "net/http" "os" "path" "strings" - "github.com/julienschmidt/httprouter" + "github.com/gin-gonic/gin" _ "golang.org/x/crypto/blake2b" "srs.epita.fr/fic-server/libfic" @@ -140,21 +141,24 @@ func SyncExerciceHints(i Importer, exercice *fic.Exercice, flagsBindings map[int } // ApiListRemoteExerciceHints is an accessor letting foreign packages to access remote exercice hints. -func ApiGetRemoteExerciceHints(ps httprouter.Params, _ []byte) (interface{}, error) { - theme, errs := BuildTheme(GlobalImporter, ps.ByName("thid")) +func ApiGetRemoteExerciceHints(c *gin.Context) { + theme, errs := BuildTheme(GlobalImporter, c.Params.ByName("thid")) if theme != nil { - exercice, _, _, _, errs := BuildExercice(GlobalImporter, theme, path.Join(theme.Path, ps.ByName("exid")), nil) + exercice, _, _, _, errs := BuildExercice(GlobalImporter, theme, path.Join(theme.Path, c.Params.ByName("exid")), nil) if exercice != nil { hints, errs := CheckExerciceHints(GlobalImporter, exercice) if hints != nil { - return hints, nil - } else { - return hints, fmt.Errorf("%q", errs) + c.JSON(http.StatusOK, hints) + return } - } else { - return exercice, fmt.Errorf("%q", errs) + + c.AbortWithStatusJSON(http.StatusInternalServerError, fmt.Errorf("%q", errs)) + return } - } else { - return nil, fmt.Errorf("%q", errs) + + c.AbortWithStatusJSON(http.StatusInternalServerError, fmt.Errorf("%q", errs)) + return } + + c.AbortWithStatusJSON(http.StatusInternalServerError, fmt.Errorf("%q", errs)) } diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index c7ffc972..d8b9a4e6 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -3,13 +3,14 @@ package sync import ( "fmt" "math/rand" + "net/http" "path" "sort" "strconv" "strings" "unicode" - "github.com/julienschmidt/httprouter" + "github.com/gin-gonic/gin" "srs.epita.fr/fic-server/libfic" ) @@ -554,21 +555,25 @@ func SyncExerciceFlags(i Importer, exercice *fic.Exercice) (kmap map[int64]fic.F } // ApiListRemoteExerciceFlags is an accessor letting foreign packages to access remote exercice flags. -func ApiGetRemoteExerciceFlags(ps httprouter.Params, _ []byte) (interface{}, error) { - theme, errs := BuildTheme(GlobalImporter, ps.ByName("thid")) +func ApiGetRemoteExerciceFlags(c *gin.Context) { + theme, errs := BuildTheme(GlobalImporter, c.Params.ByName("thid")) if theme != nil { - exercice, _, _, _, errs := BuildExercice(GlobalImporter, theme, path.Join(theme.Path, ps.ByName("exid")), nil) + exercice, _, _, _, errs := BuildExercice(GlobalImporter, theme, path.Join(theme.Path, c.Params.ByName("exid")), nil) if exercice != nil { flags, errs := CheckExerciceFlags(GlobalImporter, exercice, []string{}) if flags != nil { - return flags, nil - } else { - return flags, fmt.Errorf("%q", errs) + c.JSON(http.StatusOK, flags) + return } - } else { - return exercice, fmt.Errorf("%q", errs) + + c.AbortWithStatusJSON(http.StatusInternalServerError, fmt.Errorf("%q", errs)) + return } - } else { - return nil, fmt.Errorf("%q", errs) + + c.AbortWithStatusJSON(http.StatusInternalServerError, fmt.Errorf("%q", errs)) + return } + + c.AbortWithStatusJSON(http.StatusInternalServerError, fmt.Errorf("%q", errs)) + return } diff --git a/admin/sync/exercices.go b/admin/sync/exercices.go index f337f128..0563664c 100644 --- a/admin/sync/exercices.go +++ b/admin/sync/exercices.go @@ -3,12 +3,13 @@ package sync import ( "fmt" "log" + "net/http" "path" "strconv" "strings" "github.com/BurntSushi/toml" - "github.com/julienschmidt/httprouter" + "github.com/gin-gonic/gin" "github.com/russross/blackfriday/v2" "srs.epita.fr/fic-server/libfic" @@ -308,26 +309,36 @@ func SyncExercices(i Importer, theme *fic.Theme) (errs []string) { } // ApiListRemoteExercices is an accessor letting foreign packages to access remote exercices list. -func ApiListRemoteExercices(ps httprouter.Params, _ []byte) (interface{}, error) { - theme, errs := BuildTheme(GlobalImporter, ps.ByName("thid")) +func ApiListRemoteExercices(c *gin.Context) { + theme, errs := BuildTheme(GlobalImporter, c.Params.ByName("thid")) if theme != nil { - return GetExercices(GlobalImporter, theme) + exercices, err := GetExercices(GlobalImporter, theme) + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) + return + } + + c.JSON(http.StatusOK, exercices) } else { - return nil, fmt.Errorf("%q", errs) + c.AbortWithStatusJSON(http.StatusInternalServerError, fmt.Errorf("%q", errs)) + return } } // ApiListRemoteExercice is an accessor letting foreign packages to access remote exercice attributes. -func ApiGetRemoteExercice(ps httprouter.Params, _ []byte) (interface{}, error) { - theme, errs := BuildTheme(GlobalImporter, ps.ByName("thid")) +func ApiGetRemoteExercice(c *gin.Context) { + theme, errs := BuildTheme(GlobalImporter, c.Params.ByName("thid")) if theme != nil { - exercice, _, _, _, errs := BuildExercice(GlobalImporter, theme, path.Join(theme.Path, ps.ByName("exid")), nil) + exercice, _, _, _, errs := BuildExercice(GlobalImporter, theme, path.Join(theme.Path, c.Params.ByName("exid")), nil) if exercice != nil { - return exercice, nil + c.JSON(http.StatusOK, exercice) + return } else { - return exercice, fmt.Errorf("%q", errs) + c.JSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Errorf("%q", errs)}) + return } } else { - return nil, fmt.Errorf("%q", errs) + c.JSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Errorf("%q", errs)}) + return } } diff --git a/admin/sync/themes.go b/admin/sync/themes.go index d6ee3421..bbadaa4e 100644 --- a/admin/sync/themes.go +++ b/admin/sync/themes.go @@ -5,13 +5,14 @@ import ( "image" "image/jpeg" "math/rand" + "net/http" "os" "path" "regexp" "strings" "unicode" - "github.com/julienschmidt/httprouter" + "github.com/gin-gonic/gin" "github.com/russross/blackfriday/v2" "golang.org/x/image/draw" @@ -235,16 +236,23 @@ func SyncThemes(i Importer) (errs []string) { } // ApiListRemoteThemes is an accessor letting foreign packages to access remote themes list. -func ApiListRemoteThemes(_ httprouter.Params, _ []byte) (interface{}, error) { - return GetThemes(GlobalImporter) +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) } // ApiListRemoteTheme is an accessor letting foreign packages to access remote main theme attributes. -func ApiGetRemoteTheme(ps httprouter.Params, _ []byte) (interface{}, error) { - r, errs := BuildTheme(GlobalImporter, ps.ByName("thid")) +func ApiGetRemoteTheme(c *gin.Context) { + r, errs := BuildTheme(GlobalImporter, c.Params.ByName("thid")) if r == nil { - return r, fmt.Errorf("%q", errs) - } else { - return r, nil + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Errorf("%q", errs)}) + return } + + c.JSON(http.StatusOK, r) } diff --git a/go.mod b/go.mod index 568f246a..f6846f7b 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.16 require ( github.com/BurntSushi/toml v1.1.0 github.com/fsnotify/fsnotify v1.4.9 // indirect + github.com/gin-gonic/gin v1.7.7 // indirect github.com/go-git/go-git/v5 v5.4.2 github.com/go-sql-driver/mysql v1.6.0 github.com/julienschmidt/httprouter v1.3.0 diff --git a/go.sum b/go.sum index 3a792777..e13a032d 100644 --- a/go.sum +++ b/go.sum @@ -66,6 +66,10 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +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.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= +github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= @@ -78,6 +82,13 @@ github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= 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/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -114,6 +125,7 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -134,6 +146,8 @@ github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH 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/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= @@ -147,9 +161,17 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn 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/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -164,6 +186,7 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB 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/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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/studio-b12/gowebdav v0.0.0-20210630100626-7ff61aa87be8 h1:ipNUBPHSUmHhhcLhvqC2vGZsJPzVuJap8rJx3uGAEco= @@ -172,6 +195,10 @@ github.com/studio-b12/gowebdav v0.0.0-20210917133250-a3a86976a1df h1:C+J/LwTqP8g github.com/studio-b12/gowebdav v0.0.0-20210917133250-a3a86976a1df/go.mod h1:gCcfDlA1Y7GqOaeEKw5l9dOGx1VLdc/HuQSlQAaZ30s= github.com/studio-b12/gowebdav v0.0.0-20220128162035-c7b1ff8a5e62 h1:b2nJXyPCa9HY7giGM+kYcnQ71m14JnGdQabMPmyt++8= github.com/studio-b12/gowebdav v0.0.0-20220128162035-c7b1ff8a5e62/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 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/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -345,6 +372,7 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -509,6 +537,8 @@ 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.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/libfic/file.go b/libfic/file.go index ff9c83de..d60813ed 100644 --- a/libfic/file.go +++ b/libfic/file.go @@ -74,6 +74,12 @@ func GetFile(id int64) (f *EFile, err error) { return } +func (e *Exercice) GetFile(id int64) (f *EFile, err error) { + f = &EFile{} + err = DBQueryRow("SELECT id_file, origin, path, name, cksum, size FROM exercice_files WHERE id_file = ? AND id_exercice = ?", id, e.Id).Scan(&f.Id, &f.origin, &f.Path, &f.Name, &f.Checksum, &f.Size) + return +} + // GetFileByPath retrieves the file that should be found at the given location. func GetFileByPath(path string) (*EFile, error) { path = strings.TrimPrefix(path, FilesDir) diff --git a/libfic/flag_key.go b/libfic/flag_key.go index 1ba98246..6e225526 100644 --- a/libfic/flag_key.go +++ b/libfic/flag_key.go @@ -78,6 +78,13 @@ func GetFlagKey(id int) (k *FlagKey, err error) { return } +// GetFlagKey returns a flag. +func (e *Exercice) GetFlagKey(id int) (k *FlagKey, err error) { + k = &FlagKey{} + err = DBQueryRow("SELECT id_flag, id_exercice, ordre, label, type, placeholder, help, unit, ignorecase, notrim, multiline, validator_regexp, sort_re_grps, cksum, choices_cost FROM exercice_flags WHERE id_flag = ? AND id_exercice = ?", id, e.Id).Scan(&k.Id, &k.IdExercice, &k.Order, &k.Label, &k.Type, &k.Placeholder, &k.Help, &k.Unit, &k.IgnoreCase, &k.NoTrim, &k.Multiline, &k.ValidatorRegexp, &k.SortReGroups, &k.Checksum, &k.ChoicesCost) + return +} + // GetFlagKeyByLabel returns a flag matching the given label. func (e *Exercice) GetFlagKeyByLabel(label string) (k *FlagKey, err error) { k = &FlagKey{} diff --git a/libfic/hint.go b/libfic/hint.go index a36132af..db754e07 100644 --- a/libfic/hint.go +++ b/libfic/hint.go @@ -46,6 +46,17 @@ func GetHint(id int64) (*EHint, error) { return h, nil } +// GetHint retrieves the hint with the given id. +func (e *Exercice) GetHint(id int64) (*EHint, error) { + h := &EHint{} + if err := DBQueryRow("SELECT id_hint, id_exercice, title, content, cost FROM exercice_hints WHERE id_hint = ? AND id_exercice = ?", id, e.Id).Scan(&h.Id, &h.IdExercice, &h.Title, &h.Content, &h.Cost); err != nil { + return nil, err + } + treatHintContent(h) + + return h, nil +} + // GetHintByTitle retrieves the hint with the given id. func (e *Exercice) GetHintByTitle(id int64) (*EHint, error) { h := &EHint{} diff --git a/libfic/mcq.go b/libfic/mcq.go index d1ea52f3..d9770616 100644 --- a/libfic/mcq.go +++ b/libfic/mcq.go @@ -31,7 +31,7 @@ type MCQ_entry struct { // GetMCQ returns a list of flags comming with the challenge. func GetMCQ(id int) (m *MCQ, err error) { m = &MCQ{} - err = DBQueryRow("SELECT id_mcq, id_exercice, order, title FROM exercice_mcq WHERE id_mcq = ?", id).Scan(&m.Id, &m.IdExercice, &m.Order, &m.Title) + err = DBQueryRow("SELECT id_mcq, id_exercice, ordre, title FROM exercice_mcq WHERE id_mcq = ?", id).Scan(&m.Id, &m.IdExercice, &m.Order, &m.Title) m.fillEntries() return } @@ -84,6 +84,14 @@ func (e *Exercice) GetMCQ() ([]*MCQ, error) { } } +// GetMCQById returns a MCQs. +func (e *Exercice) GetMCQById(id int) (m *MCQ, err error) { + m = &MCQ{} + err = DBQueryRow("SELECT id_mcq, id_exercice, ordre, title FROM exercice_mcq WHERE id_mcq = ? AND id_exercice = ?", id, e.Id).Scan(&m.Id, &m.IdExercice, &m.Order, &m.Title) + m.fillEntries() + return +} + // GetMCQbyChoice returns the MCQ corresponding to a choice ID. func GetMCQbyChoice(cid int) (m *MCQ, c *MCQ_entry, err error) { m = &MCQ{} From 72add55723c6b44573b59439cdf56c2674ddd1b4 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 19 May 2022 12:14:33 +0200 Subject: [PATCH 0482/1637] ui: Open PDF, JPG, PNG, TXT in another tab --- frontend/ui/src/components/ExerciceDownloads.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/ui/src/components/ExerciceDownloads.svelte b/frontend/ui/src/components/ExerciceDownloads.svelte index e4999a94..e02e6047 100644 --- a/frontend/ui/src/components/ExerciceDownloads.svelte +++ b/frontend/ui/src/components/ExerciceDownloads.svelte @@ -27,7 +27,7 @@ {#each files as file, index} - +

From f690a4e1c8e35bc800bc370e0a30e35df2b505c7 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 19 May 2022 12:45:32 +0200 Subject: [PATCH 0483/1637] sync: Use goldmark instead of blackfriday --- admin/sync/exercices.go | 12 ++++- admin/sync/markdown.go | 108 ++++++++++++++++++++++++++-------------- admin/sync/themes.go | 11 +++- go.mod | 4 +- go.sum | 87 +++++++------------------------- 5 files changed, 111 insertions(+), 111 deletions(-) diff --git a/admin/sync/exercices.go b/admin/sync/exercices.go index 0563664c..b9c34373 100644 --- a/admin/sync/exercices.go +++ b/admin/sync/exercices.go @@ -1,6 +1,7 @@ package sync import ( + "bytes" "fmt" "log" "net/http" @@ -10,7 +11,7 @@ import ( "github.com/BurntSushi/toml" "github.com/gin-gonic/gin" - "github.com/russross/blackfriday/v2" + "github.com/yuin/goldmark" "srs.epita.fr/fic-server/libfic" ) @@ -123,10 +124,17 @@ func BuildExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]* errs = append(errs, fmt.Sprintf("%q: overview.txt: %s", edir, err)) } else { e.Overview = fixnbsp(e.Overview) - e.Headline = string(blackfriday.Run([]byte(strings.Split(e.Overview, "\n")[0]))) if e.Overview, err = ProcessMarkdown(i, e.Overview, epath); err != nil { errs = append(errs, fmt.Sprintf("%q: overview.txt: an error occurs during markdown formating: %s", edir, err)) } + + var buf bytes.Buffer + err := goldmark.Convert([]byte(strings.Split(e.Overview, "\n")[0]), &buf) + if err != nil { + errs = append(errs, fmt.Sprintf("%q: overview.txt: an error occurs during markdown formating of the headline: %s", edir, err)) + } else { + e.Headline = string(buf.Bytes()) + } } e.Statement, err = getFileContent(i, path.Join(epath, "statement.txt")) diff --git a/admin/sync/markdown.go b/admin/sync/markdown.go index 3fb1881e..9dc89d26 100644 --- a/admin/sync/markdown.go +++ b/admin/sync/markdown.go @@ -2,65 +2,99 @@ package sync import ( "bufio" + "bytes" "encoding/base32" "os" "path" - "regexp" "strings" "srs.epita.fr/fic-server/libfic" - "github.com/russross/blackfriday/v2" + "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)) - absPath := "$FILES$/" + strings.ToLower(base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(hash[:])) // Process md - output = string(blackfriday.Run( - []byte(input), - blackfriday.WithRenderer(blackfriday.NewHTMLRenderer( - blackfriday.HTMLRendererParameters{ - AbsolutePrefix: absPath, - Flags: blackfriday.CommonHTMLFlags, - }, - )), - )) + 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(NewImageImporterTransformer(i, rootDir, hash), 200), + ), + ), + goldmark.WithRendererOptions( + html.WithHardWraps(), + ), + ) - // Import files - var re *regexp.Regexp - re, err = regexp.Compile(strings.Replace(absPath, "$", "\\$", -1) + "/[^\"]+") - if err != nil { + var buf bytes.Buffer + context := parser.NewContext() + if err = markdown.Convert([]byte(input), &buf, parser.WithContext(context)); err != nil { return } - files := re.FindAllString(output, -1) - for _, filePath := range files { - iPath := strings.TrimPrefix(filePath, absPath) - dPath := path.Join(fic.FilesDir, strings.ToLower(base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(hash[:])), iPath) - - if err = os.MkdirAll(path.Dir(dPath), 0755); err != nil { - return - } - - var fdto *os.File - if fdto, err = os.Create(dPath); err != nil { - return - } else { - defer fdto.Close() - writer := bufio.NewWriter(fdto) - if err = getFile(i, rootDir+iPath, writer); err != nil { - os.Remove(dPath) - return - } - } - } + output = string(buf.Bytes()) // Trim output output = strings.TrimSpace(output) return } + +type imageImporterTransformer struct { + importer Importer + rootDir string + hash [blake2b.Size]byte + absPath string +} + +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} +} + +func (t *imageImporterTransformer) Transform(doc *ast.Document, reader text.Reader, pc parser.Context) { + 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) + 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))) + + if err := os.MkdirAll(path.Dir(dPath), 0755); err != nil { + return ast.WalkStop, err + } + + if fdto, err := os.Create(dPath); err != nil { + return ast.WalkStop, err + } else { + defer fdto.Close() + writer := bufio.NewWriter(fdto) + if err := getFile(t.importer, path.Join(t.rootDir, iPath), writer); err != nil { + os.Remove(dPath) + return ast.WalkStop, err + } + } + } + + return ast.WalkContinue, nil + }) +} diff --git a/admin/sync/themes.go b/admin/sync/themes.go index bbadaa4e..284b0535 100644 --- a/admin/sync/themes.go +++ b/admin/sync/themes.go @@ -1,6 +1,7 @@ package sync import ( + "bytes" "fmt" "image" "image/jpeg" @@ -13,7 +14,7 @@ import ( "unicode" "github.com/gin-gonic/gin" - "github.com/russross/blackfriday/v2" + "github.com/yuin/goldmark" "golang.org/x/image/draw" "srs.epita.fr/fic-server/libfic" @@ -143,7 +144,13 @@ func BuildTheme(i Importer, tdir string) (th *fic.Theme, errs []string) { if err != nil { errs = append(errs, fmt.Sprintf("%q: overview.txt: an error occurs during markdown formating: %s", tdir, err)) } - th.Headline = string(blackfriday.Run([]byte(th.Headline))) + var buf bytes.Buffer + err := goldmark.Convert([]byte(th.Headline), &buf) + if err != nil { + errs = append(errs, fmt.Sprintf("%q: overview.txt: an error occurs during markdown formating of the headline: %s", tdir, err)) + } else { + th.Headline = string(buf.Bytes()) + } } if i.exists(path.Join(tdir, "heading.jpg")) { diff --git a/go.mod b/go.mod index f6846f7b..4473b592 100644 --- a/go.mod +++ b/go.mod @@ -5,12 +5,12 @@ go 1.16 require ( github.com/BurntSushi/toml v1.1.0 github.com/fsnotify/fsnotify v1.4.9 // indirect - github.com/gin-gonic/gin v1.7.7 // indirect + github.com/gin-gonic/gin v1.7.7 github.com/go-git/go-git/v5 v5.4.2 github.com/go-sql-driver/mysql v1.6.0 github.com/julienschmidt/httprouter v1.3.0 - github.com/russross/blackfriday/v2 v2.1.0 github.com/studio-b12/gowebdav v0.0.0-20220128162035-c7b1ff8a5e62 + github.com/yuin/goldmark v1.1.32 golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8 golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9 golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 diff --git a/go.sum b/go.sum index e13a032d..846a30d5 100644 --- a/go.sum +++ b/go.sum @@ -32,10 +32,6 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= -github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU= -github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -46,7 +42,9 @@ github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C6 github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= 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/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/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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -56,6 +54,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= @@ -70,18 +69,21 @@ 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.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= +github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= 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/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-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/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-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +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.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= @@ -124,6 +126,7 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -157,12 +160,15 @@ github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 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 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 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.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +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.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= @@ -174,7 +180,9 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLD github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -188,11 +196,8 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ 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.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/studio-b12/gowebdav v0.0.0-20210630100626-7ff61aa87be8 h1:ipNUBPHSUmHhhcLhvqC2vGZsJPzVuJap8rJx3uGAEco= -github.com/studio-b12/gowebdav v0.0.0-20210630100626-7ff61aa87be8/go.mod h1:gCcfDlA1Y7GqOaeEKw5l9dOGx1VLdc/HuQSlQAaZ30s= -github.com/studio-b12/gowebdav v0.0.0-20210917133250-a3a86976a1df h1:C+J/LwTqP8gRPt1MdSzBNZP0OYuDm5wsmDKgwpLjYzo= -github.com/studio-b12/gowebdav v0.0.0-20210917133250-a3a86976a1df/go.mod h1:gCcfDlA1Y7GqOaeEKw5l9dOGx1VLdc/HuQSlQAaZ30s= github.com/studio-b12/gowebdav v0.0.0-20220128162035-c7b1ff8a5e62 h1:b2nJXyPCa9HY7giGM+kYcnQ71m14JnGdQabMPmyt++8= github.com/studio-b12/gowebdav v0.0.0-20220128162035-c7b1ff8a5e62/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= @@ -203,6 +208,7 @@ 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/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32 h1:5tjfNdR2ki3yYQ842+eX2sQHeiwpKJ0RnHO4IYOc4V8= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -217,50 +223,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U 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-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce h1:Roh6XWxHFKrPgC/EQhVubSAGQ6Ozk6IdxHSzt1mR0EI= -golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 h1:71vQrMauZZhcTVK6KdYM+rklehEEwb3E+ZhaE5jrPrE= -golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab h1:lnZ4LoV0UMdibeCUfIB2a4uFwRu491WX/VB2reB8xNc= -golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220208233918-bba287dce954 h1:BkypuErRT9A9I/iljuaG3/zdMjd/J6m8tKKJQtGfSdA= -golang.org/x/crypto v0.0.0-20220208233918-bba287dce954/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220209155544-dad33157f4bf h1:gdgmgieTI2lLaGI2N+xEiaCMUgo2XFmAS0rlF8HZoso= -golang.org/x/crypto v0.0.0-20220209155544-dad33157f4bf/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220209195652-db638375bc3a h1:atOEWVSedO4ksXBe/UrlbSLVxQQ9RxM/tT2Jy10IaHo= -golang.org/x/crypto v0.0.0-20220209195652-db638375bc3a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220210151621-f4118a5b28e2 h1:XdAboW3BNMv9ocSCOk/u1MFioZGzCNkiJZ19v9Oe3Ig= -golang.org/x/crypto v0.0.0-20220210151621-f4118a5b28e2/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220213190939-1e6e3497d506 h1:EuGTJDfeg/PGZJp3gq1K+14eSLFTsrj1eg8KQuiUyKg= -golang.org/x/crypto v0.0.0-20220213190939-1e6e3497d506/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70 h1:syTAU9FwmvzEoIYMqcPHOcVm4H3U5u90WsvuYgwpETU= -golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220312131142-6068a2e6cfdc h1:i6Z9eOQAdM7lvsbkT3fwFNtSAAC+A59TYilFj53HW+E= -golang.org/x/crypto v0.0.0-20220312131142-6068a2e6cfdc/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220313003712-b769efc7c000 h1:SL+8VVnkqyshUSz5iNnXtrBQzvFF2SkROm6t5RczFAE= -golang.org/x/crypto v0.0.0-20220313003712-b769efc7c000/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220314234724-5d542ad81a58 h1:L8CkJyVoa0/NslN3RUMLgasK5+KatNvyRGQ9QyCYAfc= -golang.org/x/crypto v0.0.0-20220314234724-5d542ad81a58/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 h1:S25/rfnfsMVgORT4/J61MJ7rdyseOZOyvLIrZEZ7s6s= -golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNyAMPVWllTNOf/qKDze4p9o= -golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f h1:OeJjE6G4dgCY4PIXvIRQbE8+RX+uXZyGhUy/ksMGJoc= -golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 h1:NvGWuYG8dkDHFSKksI1P9faiVJ9rayE6l0+ouWVIDs8= -golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88 h1:Tgea0cVUD0ivh5ADBX4WwuI12DUd2to3nCYe2eayMIw= -golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9 h1:NUzdAbFtCJSXU20AOXgeqaUwg8Ypg4MPYmL+d+rsB5c= -golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8 h1:y+mHpWoQJNAHt26Nhh6JP7hvM71IRZureyvZhoVALIs= golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -275,12 +237,6 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs= -golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20220302094943-723b81ca9867 h1:TcHcE0vrmgzNH1v3ppjcMGbhG5+9fMuvOmUYwNEF4q4= -golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20220321031419-a8550c1d254a h1:LnH9RNcpPv5Kzi15lXg42lYMPUf0x8CuPv1YnvBWZAg= -golang.org/x/image v0.0.0-20220321031419-a8550c1d254a/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9 h1:LRtI4W37N+KFebI/qV0OFiLUv4GLOWeEW5hn/KEJvxE= golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -327,11 +283,8 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210326060303-6b1517762897 h1:KrsHThm5nFk34YtATK1LsThyGhGbGe1olrte/HInHvs= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= @@ -340,12 +293,6 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a h1:qfl7ob3DIEs3Ml9oLuPwY2N04gymzAW04WsUQHIClgM= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE= golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -390,11 +337,11 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w 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 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -402,6 +349,7 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 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 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -449,6 +397,7 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc 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= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -529,6 +478,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 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= @@ -540,6 +490,7 @@ gopkg.in/yaml.v2 v2.2.4/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 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From 7cdca440e60032d9ea8c203a2451311c21849679 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 19 May 2022 13:43:09 +0200 Subject: [PATCH 0484/1637] ui: Ensure images in statement fit container --- frontend/ui/src/fic.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/ui/src/fic.scss b/frontend/ui/src/fic.scss index ca720b6f..104589bd 100644 --- a/frontend/ui/src/fic.scss +++ b/frontend/ui/src/fic.scss @@ -22,3 +22,8 @@ $enable-print-styles: false; @import "bootswatch/dist/slate/_variables"; @import "bootstrap/scss/bootstrap"; @import "bootswatch/dist/slate/_bootswatch"; + +p img { + margin: auto; + max-width: 100%; +} From 630c06582502167078424825c44dbbfeba901de1 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 19 May 2022 14:14:30 +0200 Subject: [PATCH 0485/1637] ui: When enter is pressed on vector flag, add an item --- frontend/ui/src/components/FlagKey.svelte | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/ui/src/components/FlagKey.svelte b/frontend/ui/src/components/FlagKey.svelte index b4287f84..cad9f925 100644 --- a/frontend/ui/src/components/FlagKey.svelte +++ b/frontend/ui/src/components/FlagKey.svelte @@ -4,6 +4,7 @@ Icon, Spinner, } from 'sveltestrap'; + import { tick } from 'svelte'; import { settings } from '../stores/settings.js'; @@ -35,6 +36,10 @@ let wcsubmitted = false; async function wantchoices() { + if (!confirm("Êtes-vous sûr de vouloir utiliser " + (flag.choices_cost * $settings.wchoiceCurrentCoefficient) + " points pour avoir une liste de propositions à la place de ce champ de texte à compléter ?")) { + return; + } + wcsubmitted = true; const response = await fetch( @@ -125,6 +130,7 @@ bind:value={values[index]} placeholder={flag.placeholder} title={flag.placeholder} + on:keydown={(e) => {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} -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
-
-
-
-

Options

-
-
+
-
-
-
-
- Synchronisation et suppressions de masse +
+
+ + +

Infos challenge

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

Changements anticipés

-
-
-
-
-
-
-
Synchronisation
-
-
ID
-
{{ configro['sync-id'] }}
-
+
+
+
+ Synchronisation et suppressions de masse +
+
+
+
+ +
+
+
-
- {{ syncProgress }} -
-
- Dernier import : {{ syncReport._date[1] }} -
-
- -
- - - -
- - Voir le rapport +
+
+ Paramètres de synchronisation + + + +
- -
- - - - -
diff --git a/admin/static/views/sync.html b/admin/static/views/sync.html new file mode 100644 index 00000000..32723c5c --- /dev/null +++ b/admin/static/views/sync.html @@ -0,0 +1,47 @@ +
+
+

+ Synchronisation +

+
+
+
+ +
+
+
+ +
+
+
+
Synchronisation
+
+
ID
+
{{ configro['sync-id'] }}
+
+ +
+ {{ syncProgress }} +
+
+ Dernier import : {{ syncReport._date[1] }} +
+
+ +
+ + + +
+ + Voir le rapport +
+
+
From 8eb2bda539b7948dc07d2ebbf0da8a00632789c8 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 20 May 2022 10:00:46 +0200 Subject: [PATCH 0488/1637] admin/ui: Improve sync page --- admin/static/js/app.js | 8 +++-- admin/static/views/sync.html | 68 +++++++++++++++++++++++++++--------- 2 files changed, 57 insertions(+), 19 deletions(-) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 3dce0387..9467e719 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -617,11 +617,15 @@ angular.module("FICApp") needRefreshSyncReportWhenReady = true; else if (needRefreshSyncReportWhenReady) refreshSyncReport(); - if (response.data && response.data.progress) + if (response.data && response.data.progress) { + $scope.syncPercent = Math.floor(response.data.progress * 100 / 255); $scope.syncProgress = Math.floor(response.data.progress * 100 / 255) + " %"; - else + } else { $scope.syncProgress = response.data; + $scope.syncPercent = 0; + } }, function(response) { + $scope.syncPercent = 0; if (response.data && response.data.errmsg) $scope.syncProgress = response.data.errmsg; else diff --git a/admin/static/views/sync.html b/admin/static/views/sync.html index 32723c5c..a457e6ed 100644 --- a/admin/static/views/sync.html +++ b/admin/static/views/sync.html @@ -14,34 +14,68 @@
-
-
-
-
Synchronisation
-
-
ID
-
{{ configro['sync-id'] }}
-
- -
- {{ syncProgress }} -
-
+
+
+

+ Import des thèmes +

+
Dernier import : {{ syncReport._date[1] }}
-
+
+
+
+
+
+
+
Type
+
+
Synchronisation
+
+
ID
+
{{ configro['sync-id'] }}
+
Statut
+
{{ syncProgress }}
+
+ +
-
+
- - Voir le rapport
+ +
+
+ + + Lien public + + +

+ Dernier rapport de synchronisation +

+
+
+

+ {{ th }} + + + +

+
    +
  • {{ item }}
  • +
+
+
From b92381f007c205821212c9120a1a4e6f3acf3ad6 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 22 May 2022 19:10:41 +0200 Subject: [PATCH 0489/1637] admin/ui: Improve home page --- admin/static/views/home.html | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/admin/static/views/home.html b/admin/static/views/home.html index eea45d14..08b9a737 100644 --- a/admin/static/views/home.html +++ b/admin/static/views/home.html @@ -25,22 +25,24 @@
-
+
+ - +
+

Classement

@@ -78,4 +82,5 @@
+
From 4a190f51c536e9af7de2b8d85027830d3dcfd6b5 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 23 May 2022 01:39:35 +0200 Subject: [PATCH 0490/1637] admin: Fix video route --- admin/static.go | 8 ++++---- admin/static/js/common.js | 5 +++++ admin/static/views/exercice-resolution.html | 2 +- admin/sync/exercices.go | 1 + admin/sync/file.go | 5 +++++ admin/sync/importer_git_common.go | 4 ++++ admin/sync/importer_localfs.go | 6 ++++++ frontend/ui/src/components/ExerciceVideo.svelte | 4 ++-- 8 files changed, 28 insertions(+), 7 deletions(-) diff --git a/admin/static.go b/admin/static.go index bdf5e0a6..df54ea5d 100644 --- a/admin/static.go +++ b/admin/static.go @@ -98,14 +98,14 @@ func declareStaticRoutes(router *gin.RouterGroup, cfg *settings.Settings, baseUR }) router.GET("/files/*_", func(c *gin.Context) { - http.ServeFile(c.Writer, c.Request, path.Join(fic.FilesDir, strings.TrimPrefix(c.Request.URL.Path, "/files"))) + http.ServeFile(c.Writer, c.Request, path.Join(fic.FilesDir, strings.TrimPrefix(c.Request.URL.Path, path.Join(baseURL, "files")))) }) router.GET("/submissions/*_", func(c *gin.Context) { - http.ServeFile(c.Writer, c.Request, path.Join(api.TimestampCheck, strings.TrimPrefix(c.Request.URL.Path, "/submissions"))) + 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.LocalImporter); ok { - http.ServeFile(c.Writer, c.Request, path.Join(importer.Base, strings.TrimPrefix(c.Request.URL.Path, "/vids"))) + 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.")) } diff --git a/admin/static/js/common.js b/admin/static/js/common.js index ec0cc409..29eba62c 100644 --- a/admin/static/js/common.js +++ b/admin/static/js/common.js @@ -84,6 +84,11 @@ angular.module("FICApp") }]); angular.module("FICApp") + .filter("escapeURL", function() { + return function(input) { + return encodeURIComponent(input); + } + }) .filter("stripHTML", function() { return function(input) { if (!input) diff --git a/admin/static/views/exercice-resolution.html b/admin/static/views/exercice-resolution.html index 2a58267c..43736b1f 100644 --- a/admin/static/views/exercice-resolution.html +++ b/admin/static/views/exercice-resolution.html @@ -7,5 +7,5 @@
- +
diff --git a/admin/sync/exercices.go b/admin/sync/exercices.go index d2e07c47..30830a31 100644 --- a/admin/sync/exercices.go +++ b/admin/sync/exercices.go @@ -235,6 +235,7 @@ func BuildExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]* errs = append(errs, fmt.Sprintf("%q: resolution.mp4: The file is empty!", edir)) e.VideoURI = "" } else { + e.VideoURI = path.Join("$FILES$", e.VideoURI) resolutionFound = true } diff --git a/admin/sync/file.go b/admin/sync/file.go index eb6a3bef..057603ce 100644 --- a/admin/sync/file.go +++ b/admin/sync/file.go @@ -41,6 +41,11 @@ type Importer interface { 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 +} + // GlobalImporter stores the main importer instance to use for global imports. var GlobalImporter Importer diff --git a/admin/sync/importer_git_common.go b/admin/sync/importer_git_common.go index faf7b25c..179c3c5a 100644 --- a/admin/sync/importer_git_common.go +++ b/admin/sync/importer_git_common.go @@ -22,6 +22,10 @@ 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) } diff --git a/admin/sync/importer_localfs.go b/admin/sync/importer_localfs.go index 63f82df2..9c234294 100644 --- a/admin/sync/importer_localfs.go +++ b/admin/sync/importer_localfs.go @@ -4,6 +4,7 @@ import ( "bufio" "fmt" "io/ioutil" + "log" "os" "path" ) @@ -53,9 +54,14 @@ func (i LocalImporter) exists(filename string) bool { } func (i LocalImporter) toURL(filename string) string { + log.Println(i.Base, filename, path.Join(i.Base, filename)) 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) diff --git a/frontend/ui/src/components/ExerciceVideo.svelte b/frontend/ui/src/components/ExerciceVideo.svelte index a13c20c8..9c889963 100644 --- a/frontend/ui/src/components/ExerciceVideo.svelte +++ b/frontend/ui/src/components/ExerciceVideo.svelte @@ -18,8 +18,8 @@ Solution du défi - From 3bf0fc69ee1a46d89264ba8232eb4a2d8facfc1b Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 24 May 2022 13:32:02 +0200 Subject: [PATCH 0491/1637] admin: Handle resolution.md display --- admin/index.go | 3 +++ admin/static/js/app.js | 2 +- admin/static/views/exercice-resolution.html | 10 +++++++--- admin/static/views/exercice.html | 4 ++-- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/admin/index.go b/admin/index.go index 74b36661..00dc37eb 100644 --- a/admin/index.go +++ b/admin/index.go @@ -61,6 +61,9 @@ const indextpl = ` text-align: left; text-overflow: ellipsis; } + .col img { + max-width: 100%; + } diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 9467e719..7e06c71f 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -1619,7 +1619,7 @@ angular.module("FICApp") }); }); $scope.exercices = Exercice.query(); - $scope.fields = ["title", "urlid", "statement", "headline", "overview", "finished", "depend", "gain", "coefficient", "videoURI", "issue", "issuekind"]; + $scope.fields = ["title", "urlid", "statement", "headline", "overview", "finished", "depend", "gain", "coefficient", "videoURI", "resolution", "issue", "issuekind"]; $scope.inSync = false; $scope.syncExo = function() { diff --git a/admin/static/views/exercice-resolution.html b/admin/static/views/exercice-resolution.html index 43736b1f..c6aa19c8 100644 --- a/admin/static/views/exercice-resolution.html +++ b/admin/static/views/exercice-resolution.html @@ -1,11 +1,15 @@

- {{exercice.title}} Vidéo de résolution + {{exercice.title}} Résolution

-
- +
+
+ +
+
+
diff --git a/admin/static/views/exercice.html b/admin/static/views/exercice.html index 3b598d32..e3d5003c 100644 --- a/admin/static/views/exercice.html +++ b/admin/static/views/exercice.html @@ -16,10 +16,10 @@
- + - + From 45a9240834ea5df8577d118a77412c3312fdddc0 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 24 May 2022 17:36:33 +0200 Subject: [PATCH 0492/1637] Handle special chars in exercice path --- admin/sync/exercices.go | 3 ++- frontend/resolution.go | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/admin/sync/exercices.go b/admin/sync/exercices.go index 30830a31..dec97460 100644 --- a/admin/sync/exercices.go +++ b/admin/sync/exercices.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "net/http" + "net/url" "path" "strconv" "strings" @@ -235,7 +236,7 @@ func BuildExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]* errs = append(errs, fmt.Sprintf("%q: resolution.mp4: The file is empty!", edir)) e.VideoURI = "" } else { - e.VideoURI = path.Join("$FILES$", e.VideoURI) + e.VideoURI = strings.Replace(url.PathEscape(path.Join("$FILES$", e.VideoURI)), "%2F", "/", -1) resolutionFound = true } diff --git a/frontend/resolution.go b/frontend/resolution.go index b75ec796..3d553724 100644 --- a/frontend/resolution.go +++ b/frontend/resolution.go @@ -3,13 +3,15 @@ package main import ( "log" "net/http" + "net/url" "path" + "strings" "text/template" ) var enableResolutionRoute bool = false -type ResolutionHandler struct {} +type ResolutionHandler struct{} const resolutiontpl = ` @@ -40,7 +42,7 @@ func (s ResolutionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if resolutionTmpl, err := template.New("resolution").Parse(resolutiontpl); err != nil { log.Println("Cannot create template: ", err) - } else if err = resolutionTmpl.Execute(w, path.Join("/vids/", r.URL.Path)); err != nil { + } else if err = resolutionTmpl.Execute(w, path.Join("/vids/", strings.Replace(url.PathEscape(r.URL.Path), "%2F", "/", -1))); err != nil { log.Println("An error occurs during template execution: ", err) } } From a6adc1ac8c0db638724c01ce26d1cc28246e1eed Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 24 May 2022 17:53:44 +0200 Subject: [PATCH 0493/1637] ui: Display writeup in interface --- .../ui/src/components/ExerciceVideo.svelte | 21 +++++-------------- .../ui/src/routes/[theme]/[exercice].svelte | 20 ++++++++++++++++-- libfic/team_my.go | 2 ++ 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/frontend/ui/src/components/ExerciceVideo.svelte b/frontend/ui/src/components/ExerciceVideo.svelte index 9c889963..b8deeaa9 100644 --- a/frontend/ui/src/components/ExerciceVideo.svelte +++ b/frontend/ui/src/components/ExerciceVideo.svelte @@ -1,25 +1,14 @@ - - - - Solution du défi - - - - - + + + diff --git a/frontend/ui/src/routes/[theme]/[exercice].svelte b/frontend/ui/src/routes/[theme]/[exercice].svelte index 0e5fd189..ba818e6c 100644 --- a/frontend/ui/src/routes/[theme]/[exercice].svelte +++ b/frontend/ui/src/routes/[theme]/[exercice].svelte @@ -30,6 +30,9 @@ Alert, Badge, Card, + CardBody, + CardHeader, + CardText, Col, Icon, Row, @@ -172,8 +175,21 @@ exercice={$my.exercices[exercice.id]} /> {/if} - {#if $my.exercices[exercice.id].video_uri} - + {#if $my.exercices[exercice.id].resolution || $my.exercices[exercice.id].video_uri} + + + + Solution du défi + + {#if $my.exercices[exercice.id].resolution} + + {@html $my.exercices[exercice.id].resolution} + + {/if} + {#if $my.exercices[exercice.id].video_uri} + + {/if} + {/if} diff --git a/libfic/team_my.go b/libfic/team_my.go index 150027dc..0a163f2b 100644 --- a/libfic/team_my.go +++ b/libfic/team_my.go @@ -76,6 +76,7 @@ type myTeamExercice struct { Tries int64 `json:"tries,omitempty"` TotalTries int64 `json:"total_tries,omitempty"` VideoURI string `json:"video_uri,omitempty"` + Resolution string `json:"resolution,omitempty"` Issue string `json:"issue,omitempty"` IssueKind string `json:"issuekind,omitempty"` } @@ -130,6 +131,7 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { exercice.Overview = strings.Replace(e.Overview, "$FILES$", FilesDir, -1) exercice.Finished = strings.Replace(e.Finished, "$FILES$", FilesDir, -1) exercice.VideoURI = e.VideoURI + exercice.Resolution = strings.Replace(e.Resolution, "$FILES$", FilesDir, -1) exercice.TotalTries = e.TriedCount() exercice.Gain = int(float64(e.Gain) * e.Coefficient * GlobalScoreCoefficient) } else { From 80917ae436ea909f5b57b93c4f42c7506116cb58 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 24 May 2022 21:25:27 +0200 Subject: [PATCH 0494/1637] admin: New page to list tags --- admin/api/exercice.go | 27 +++++++++++++++++++++ admin/static.go | 3 +++ admin/static/js/app.js | 14 +++++++++++ admin/static/views/exercice-list.html | 6 +++++ admin/static/views/tags.html | 34 +++++++++++++++++++++++++++ 5 files changed, 84 insertions(+) create mode 100644 admin/static/views/tags.html diff --git a/admin/api/exercice.go b/admin/api/exercice.go index e7b98c43..7998b63d 100644 --- a/admin/api/exercice.go +++ b/admin/api/exercice.go @@ -17,6 +17,7 @@ import ( func declareGlobalExercicesRoutes(router *gin.RouterGroup) { router.GET("/resolutions.json", exportResolutionMovies) router.GET("/exercices_stats.json", getExercicesStats) + router.GET("/tags", listTags) } func declareExercicesRoutes(router *gin.RouterGroup) { @@ -225,6 +226,32 @@ func listExercices(c *gin.Context) { } } +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,.title, @uri \"https://fic.srs.epita.fr/resolution/\\(.videoURI)\" ] | join(\";\")" func exportResolutionMovies(c *gin.Context) { diff --git a/admin/static.go b/admin/static.go index df54ea5d..c91e17b2 100644 --- a/admin/static.go +++ b/admin/static.go @@ -74,6 +74,9 @@ func declareStaticRoutes(router *gin.RouterGroup, cfg *settings.Settings, baseUR 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) }) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 7e06c71f..295a25d8 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -41,6 +41,10 @@ angular.module("FICApp", ["ngRoute", "ngResource", "ngSanitize"]) controller: "ExerciceController", templateUrl: "views/exercice-resolution.html" }) + .when("/tags", { + controller: "TagsListController", + templateUrl: "views/tags.html" + }) .when("/teams", { controller: "TeamsListController", templateUrl: "views/team-list.html" @@ -1478,6 +1482,16 @@ angular.module("FICApp") } }) + .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", diff --git a/admin/static/views/exercice-list.html b/admin/static/views/exercice-list.html index 998f8e21..050bf57c 100644 --- a/admin/static/views/exercice-list.html +++ b/admin/static/views/exercice-list.html @@ -1,6 +1,12 @@

Exercices

+ + + + + + + + + + + + + + + + +
+ Tag + + Nb + + Exercices +
+ {{ tag }} + + {{ exercices.length }} + + + {{ e.title }} + +
From 2c76b5c7a34fc951cbc2daff3263e5ec7ee5ccc2 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 24 May 2022 21:25:51 +0200 Subject: [PATCH 0495/1637] admin: Add link to forge --- admin/api/exercice.go | 16 ++++++- admin/api/theme.go | 16 ++++++- admin/static/views/exercice.html | 1 + admin/static/views/theme.html | 5 ++- admin/sync/file.go | 7 +++ admin/sync/importer_git_common.go | 12 +++++ admin/sync/importer_gitbin.go | 75 +++++++++++++++++++++++++++++++ 7 files changed, 129 insertions(+), 3 deletions(-) diff --git a/admin/api/exercice.go b/admin/api/exercice.go index 7998b63d..f82f562c 100644 --- a/admin/api/exercice.go +++ b/admin/api/exercice.go @@ -85,6 +85,11 @@ func declareExercicesRoutes(router *gin.RouterGroup) { router.GET("/remote/themes/:thid/exercices/:exid/flags", sync.ApiGetRemoteExerciceFlags) } +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 { @@ -359,7 +364,16 @@ func listExerciceQuiz(c *gin.Context) { } func showExercice(c *gin.Context) { - c.JSON(http.StatusOK, c.MustGet("exercice").(*fic.Exercice)) + 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) { diff --git a/admin/api/theme.go b/admin/api/theme.go index 71fe0211..bd0c4565 100644 --- a/admin/api/theme.go +++ b/admin/api/theme.go @@ -51,6 +51,11 @@ func declareThemesRoutes(router *gin.RouterGroup) { 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 { @@ -142,7 +147,16 @@ func exportThemes(c *gin.Context) { } func showTheme(c *gin.Context) { - c.JSON(http.StatusOK, c.MustGet("theme").(*fic.Theme)) + 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) { diff --git a/admin/static/views/exercice.html b/admin/static/views/exercice.html index e3d5003c..ccf9cd2f 100644 --- a/admin/static/views/exercice.html +++ b/admin/static/views/exercice.html @@ -8,6 +8,7 @@
Vidéo Flags + Voir sur la forge
diff --git a/admin/static/views/theme.html b/admin/static/views/theme.html index 04ad8f74..64477a39 100644 --- a/admin/static/views/theme.html +++ b/admin/static/views/theme.html @@ -1,4 +1,7 @@ -

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

+

+ {{theme.name}} {{theme.authors | stripHTML}} + Voir sur la forge +

diff --git a/admin/sync/file.go b/admin/sync/file.go index 057603ce..087514e4 100644 --- a/admin/sync/file.go +++ b/admin/sync/file.go @@ -6,6 +6,7 @@ import ( "encoding/base32" "fmt" "io" + "net/url" "os" "path" "strings" @@ -46,6 +47,12 @@ 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) +} + // GlobalImporter stores the main importer instance to use for global imports. var GlobalImporter Importer diff --git a/admin/sync/importer_git_common.go b/admin/sync/importer_git_common.go index 179c3c5a..ee4c03a1 100644 --- a/admin/sync/importer_git_common.go +++ b/admin/sync/importer_git_common.go @@ -2,9 +2,13 @@ package sync import ( "bufio" + "net/url" "os" + "regexp" ) +var gitRemoteRe = regexp.MustCompile(`^(?:(?:git@|https://)([\w.@]+)(?:/|:))((?:[\w-_]+)/(?:[\w-_/]+))(?:.git){0,1}(?:(?:/){0,1})$`) + func countFileInDir(dirname string) (int, error) { files, err := os.ReadDir(dirname) if err != nil { @@ -45,3 +49,11 @@ func (i GitImporter) stat(filename string) (os.FileInfo, error) { func (i GitImporter) Kind() string { return "git originated from " + i.Remote + " on " + i.li.Kind() } + +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 +} diff --git a/admin/sync/importer_gitbin.go b/admin/sync/importer_gitbin.go index f7b0b779..fa41125b 100644 --- a/admin/sync/importer_gitbin.go +++ b/admin/sync/importer_gitbin.go @@ -8,10 +8,13 @@ import ( "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 @@ -133,3 +136,75 @@ func (i GitImporter) Sync() error { 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", "master", 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", "master", strings.TrimPrefix(e.Path, prefix)) + + return +} From d24b1c5d4d184d227ef295b07d96f4f4594f6180 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 24 May 2022 21:30:11 +0200 Subject: [PATCH 0496/1637] libfic: Use MEDIUMTEXT to store resolution.md --- libfic/db.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfic/db.go b/libfic/db.go index 96ff40ff..bd5df77b 100644 --- a/libfic/db.go +++ b/libfic/db.go @@ -145,7 +145,7 @@ CREATE TABLE IF NOT EXISTS exercices( coefficient_cur FLOAT NOT NULL DEFAULT 1.0, finished TEXT NOT NULL, video_uri VARCHAR(255) NOT NULL, - resolution TEXT NOT NULL, + resolution MEDIUMTEXT NOT NULL, seealso TEXT NOT NULL, FOREIGN KEY(id_theme) REFERENCES themes(id_theme), FOREIGN KEY(depend) REFERENCES exercices(id_exercice) From 560110ba5e2eb4d18823d2fba855a5b1d8bc83c6 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 24 May 2022 21:52:58 +0200 Subject: [PATCH 0497/1637] sync: Expose GetFile and GetFileContent functions --- admin/sync/exercice_defines.go | 2 +- admin/sync/exercice_files.go | 4 ++-- admin/sync/exercices.go | 14 +++++++------- admin/sync/file.go | 12 ++++++------ admin/sync/markdown.go | 2 +- admin/sync/themes.go | 8 ++++---- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/admin/sync/exercice_defines.go b/admin/sync/exercice_defines.go index 0fabe9e7..7dc3e2b4 100644 --- a/admin/sync/exercice_defines.go +++ b/admin/sync/exercice_defines.go @@ -75,7 +75,7 @@ type ExerciceParams struct { // 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 - defs, err = getFileContent(i, path.Join(exPath, "challenge.txt")) + defs, err = GetFileContent(i, path.Join(exPath, "challenge.txt")) if err != nil { return } diff --git a/admin/sync/exercice_files.go b/admin/sync/exercice_files.go index 3d852bdd..bc196bf3 100644 --- a/admin/sync/exercice_files.go +++ b/admin/sync/exercice_files.go @@ -21,7 +21,7 @@ func BuildFilesListInto(i Importer, exercice *fic.Exercice, into string) (files } // Parse DIGESTS.txt - if digs, err := getFileContent(i, path.Join(exercice.Path, into, "DIGESTS.txt")); err != nil { + if digs, err := GetFileContent(i, path.Join(exercice.Path, into, "DIGESTS.txt")); err != nil { errs = append(errs, fmt.Sprintf("%q: unable to read DIGESTS.txt: %s", path.Base(exercice.Path), err)) } else { digests = map[string][]byte{} @@ -104,7 +104,7 @@ func CheckExerciceFiles(i Importer, exercice *fic.Exercice) (files []string, err for _, fname := range flist { w, hash160, hash512 := fic.CreateHashBuffers() - if err := getFile(i, path.Join(exercice.Path, "files", fname), bufio.NewWriter(w)); err != nil { + if err := GetFile(i, path.Join(exercice.Path, "files", fname), bufio.NewWriter(w)); err != nil { errs = append(errs, fmt.Sprintf("%q: unable to read file %q: %s", path.Base(exercice.Path), fname, err)) continue } else if _, err := fic.CheckBufferHash(hash160, hash512, digests[fname]); err != nil { diff --git a/admin/sync/exercices.go b/admin/sync/exercices.go index dec97460..ffbcf6a9 100644 --- a/admin/sync/exercices.go +++ b/admin/sync/exercices.go @@ -107,7 +107,7 @@ func BuildExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]* } // Overwrite title if title.txt exists - if myTitle, err := getFileContent(i, path.Join(epath, "title.txt")); err == nil { + if myTitle, err := GetFileContent(i, path.Join(epath, "title.txt")); err == nil { myTitle = strings.TrimSpace(myTitle) if strings.Contains(myTitle, "\n") { errs = append(errs, fmt.Sprintf("%q: title.txt: Title can't contain new lines", edir)) @@ -121,9 +121,9 @@ func BuildExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]* // Texts to format using Markdown if i.exists(path.Join(epath, "overview.txt")) { - e.Overview, err = getFileContent(i, 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")) + e.Overview, err = GetFileContent(i, path.Join(epath, "overview.md")) } else { err = fmt.Errorf("Unable to find overview.txt nor overview.md") } @@ -146,9 +146,9 @@ func BuildExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]* } if i.exists(path.Join(epath, "statement.txt")) { - e.Statement, err = getFileContent(i, 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")) + e.Statement, err = GetFileContent(i, path.Join(epath, "statement.md")) } else { err = fmt.Errorf("Unable to find statement.txt nor statement.md") } @@ -161,7 +161,7 @@ func BuildExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]* } if i.exists(path.Join(epath, "finished.txt")) { - e.Finished, err = getFileContent(i, path.Join(epath, "finished.txt")) + e.Finished, err = GetFileContent(i, path.Join(epath, "finished.txt")) if err != nil { errs = append(errs, fmt.Sprintf("%q: finished.txt: %s", edir, err)) } else { @@ -250,7 +250,7 @@ func BuildExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]* errs = append(errs, fmt.Sprintf("%q: resolution.md: %s", edir, err.Error())) } else if size == 0 { errs = append(errs, fmt.Sprintf("%q: resolution.md: The file is empty!", edir)) - } else if e.Resolution, err = getFileContent(i, writeup); err != nil { + } else if e.Resolution, err = GetFileContent(i, writeup); err != nil { errs = append(errs, fmt.Sprintf("%q: resolution.md: %s", edir, err.Error())) } else if e.Resolution, err = ProcessMarkdown(i, e.Resolution, epath); err != nil { errs = append(errs, fmt.Sprintf("%q: resolution.md: error during markdown processing: %s", edir, err.Error())) diff --git a/admin/sync/file.go b/admin/sync/file.go index 087514e4..77279f1a 100644 --- a/admin/sync/file.go +++ b/admin/sync/file.go @@ -95,8 +95,8 @@ func getFileSize(i Importer, URI string) (size int64, err error) { 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, writer *bufio.Writer) error { +// GetFile helps to manage huge file transfert by concatenating splitted (with split(1)) files. +func GetFile(i Importer, URI string, writer *bufio.Writer) error { // Import file if it exists if i.exists(URI) { return i.getFile(URI, writer) @@ -130,11 +130,11 @@ func getFile(i Importer, URI string, writer *bufio.Writer) error { return 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) { +// GetFileContent retrieves the content of the given text file. +func GetFileContent(i Importer, URI string) (string, error) { cnt := bytes.Buffer{} - if err := getFile(i, URI, bufio.NewWriter(io.Writer(&cnt))); err != nil { + if err := GetFile(i, URI, bufio.NewWriter(io.Writer(&cnt))); err != nil { return "", err } else { // Ensure we read UTF-8 content. @@ -181,7 +181,7 @@ func ImportFile(i Importer, URI string, next func(string, string) (interface{}, } else { defer fdto.Close() writer := bufio.NewWriter(fdto) - if err := getFile(i, URI, writer); err != nil { + if err := GetFile(i, URI, writer); err != nil { os.Remove(dest) return nil, err } diff --git a/admin/sync/markdown.go b/admin/sync/markdown.go index 9dc89d26..58e687e2 100644 --- a/admin/sync/markdown.go +++ b/admin/sync/markdown.go @@ -88,7 +88,7 @@ func (t *imageImporterTransformer) Transform(doc *ast.Document, reader text.Read } else { defer fdto.Close() writer := bufio.NewWriter(fdto) - if err := getFile(t.importer, path.Join(t.rootDir, iPath), writer); err != nil { + if err := GetFile(t.importer, path.Join(t.rootDir, iPath), writer); err != nil { os.Remove(dPath) return ast.WalkStop, err } diff --git a/admin/sync/themes.go b/admin/sync/themes.go index 284b0535..cd64eaf2 100644 --- a/admin/sync/themes.go +++ b/admin/sync/themes.go @@ -87,7 +87,7 @@ func resizePicture(importedPath string, rect image.Rectangle) error { // 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 { + if authors, err := GetFileContent(i, path.Join(tname, "AUTHORS.txt")); err != nil { return nil, err } else { var ret []string @@ -112,7 +112,7 @@ func BuildTheme(i Importer, tdir string) (th *fic.Theme, errs []string) { th.Path = tdir // Extract theme's label - if tname, err := getFileContent(i, path.Join(tdir, "title.txt")); err == nil { + 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:]) @@ -129,7 +129,7 @@ func BuildTheme(i Importer, tdir string) (th *fic.Theme, errs []string) { th.Authors = strings.Join(authors, ", ") } - if intro, err := getFileContent(i, path.Join(tdir, "overview.txt")); err != nil { + if intro, err := GetFileContent(i, path.Join(tdir, "overview.txt")); err != nil { errs = append(errs, fmt.Sprintf("%q: unable to get theme's overview: %s", th.Name, err)) } else { // Split headline from intro @@ -168,7 +168,7 @@ func BuildTheme(i Importer, tdir string) (th *fic.Theme, errs []string) { } if i.exists(path.Join(tdir, "partner.txt")) { - if txt, err := getFileContent(i, path.Join(tdir, "partner.txt")); err != nil { + if txt, err := GetFileContent(i, path.Join(tdir, "partner.txt")); err != nil { errs = append(errs, fmt.Sprintf("%q: unable to get partner's text: %s", th.Name, err)) } else { th.PartnerText, err = ProcessMarkdown(i, txt, tdir) From 8ed9415c68d02a27a984549011c0f917220fba6b Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 24 May 2022 21:54:45 +0200 Subject: [PATCH 0498/1637] admin: Read challenge.json from imported directory --- admin/api/settings.go | 9 ++++++++- admin/api/theme.go | 7 ++++++- settings/challenge.go | 18 +++++++----------- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/admin/api/settings.go b/admin/api/settings.go index 2fed7856..a9e76b13 100644 --- a/admin/api/settings.go +++ b/admin/api/settings.go @@ -57,7 +57,14 @@ func getROSettings(c *gin.Context) { } func getChallengeInfo(c *gin.Context) { - s, err := settings.ReadChallengeInfo(path.Join(settings.SettingsDir, settings.ChallengeFile)) + challengeinfo, err := sync.GetFileContent(sync.GlobalImporter, "challenge.json") + if 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 + } + + s, err := settings.ReadChallengeInfo(challengeinfo) if err != nil { log.Println("Unable to ReadChallengeInfo:", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to read challenge info: %s", err.Error())}) diff --git a/admin/api/theme.go b/admin/api/theme.go index bd0c4565..468e51a9 100644 --- a/admin/api/theme.go +++ b/admin/api/theme.go @@ -23,7 +23,12 @@ func declareThemesRoutes(router *gin.RouterGroup) { log.Printf("Unable to ReadSettings: %s", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during settings reading."}) return - } else if ch, err := settings.ReadChallengeInfo(path.Join(settings.SettingsDir, settings.ChallengeFile)); err != nil { + + } 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 diff --git a/settings/challenge.go b/settings/challenge.go index 456a85a3..a6b38dee 100644 --- a/settings/challenge.go +++ b/settings/challenge.go @@ -3,6 +3,7 @@ package settings import ( "encoding/json" "os" + "strings" ) // ChallengeFile is the expected name of the file containing the challenge infos. @@ -28,20 +29,15 @@ type ChallengeInfo struct { } // ReadChallenge parses the file at the given location. -func ReadChallengeInfo(path string) (*ChallengeInfo, error) { +func ReadChallengeInfo(content string) (*ChallengeInfo, error) { var s ChallengeInfo - if fd, err := os.Open(path); err != nil { - return nil, err - } else { - defer fd.Close() - jdec := json.NewDecoder(fd) + jdec := json.NewDecoder(strings.NewReader(content)) - if err := jdec.Decode(&s); err != nil { - return &s, err - } - - return &s, nil + if err := jdec.Decode(&s); err != nil { + return &s, err } + + return &s, nil } // SaveChallenge saves challenge at the given location. From aab66bf6124d240826e0110a9327d1a1e4ed1f57 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 24 May 2022 22:13:38 +0200 Subject: [PATCH 0499/1637] sync: Implement writable importer --- admin/sync/file.go | 15 +++++++++++++++ admin/sync/importer_cloud.go | 24 ++++++++++++++++++++++++ admin/sync/importer_git_common.go | 5 +++++ admin/sync/importer_localfs.go | 11 +++++++++++ 4 files changed, 55 insertions(+) diff --git a/admin/sync/file.go b/admin/sync/file.go index 77279f1a..b8c60d1c 100644 --- a/admin/sync/file.go +++ b/admin/sync/file.go @@ -53,6 +53,12 @@ type ForgeLinkedImporter interface { 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 @@ -189,3 +195,12 @@ func ImportFile(i Importer, URI string, next func(string, string) (interface{}, 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) + } +} diff --git a/admin/sync/importer_cloud.go b/admin/sync/importer_cloud.go index 718f3905..8ca51dbe 100644 --- a/admin/sync/importer_cloud.go +++ b/admin/sync/importer_cloud.go @@ -3,6 +3,7 @@ package sync import ( "bufio" "errors" + "io" "net/http" "net/url" "os" @@ -103,6 +104,29 @@ func (i CloudImporter) getFile(filename string, writer *bufio.Writer) error { return 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) diff --git a/admin/sync/importer_git_common.go b/admin/sync/importer_git_common.go index ee4c03a1..2bd24147 100644 --- a/admin/sync/importer_git_common.go +++ b/admin/sync/importer_git_common.go @@ -2,6 +2,7 @@ package sync import ( "bufio" + "io" "net/url" "os" "regexp" @@ -38,6 +39,10 @@ func (i GitImporter) getFile(filename string, writer *bufio.Writer) error { return i.li.getFile(filename, writer) } +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) } diff --git a/admin/sync/importer_localfs.go b/admin/sync/importer_localfs.go index 9c234294..a960bce3 100644 --- a/admin/sync/importer_localfs.go +++ b/admin/sync/importer_localfs.go @@ -3,6 +3,7 @@ package sync import ( "bufio" "fmt" + "io" "io/ioutil" "log" "os" @@ -94,6 +95,16 @@ func (i LocalImporter) getFile(filename string, writer *bufio.Writer) error { } } +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 From 58217d1d8a24ebbcdd72c0cdaca88e10fe27d2fa Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 24 May 2022 22:19:07 +0200 Subject: [PATCH 0500/1637] admin: Save challenge info over importer --- admin/api/settings.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/admin/api/settings.go b/admin/api/settings.go index a9e76b13..9bd4a24b 100644 --- a/admin/api/settings.go +++ b/admin/api/settings.go @@ -1,6 +1,7 @@ package api import ( + "encoding/json" "fmt" "log" "net/http" @@ -82,7 +83,14 @@ func saveChallengeInfo(c *gin.Context) { return } - if err := settings.SaveChallengeInfo(path.Join(settings.SettingsDir, settings.ChallengeFile), info); err != 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()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to save challenge info: %s", err.Error())}) return From 123467f3eb7a5e55d72cd11e72bdf479633b60ba Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 24 May 2022 23:00:49 +0200 Subject: [PATCH 0501/1637] settings: Save duration in challenge.json --- admin/static/js/app.js | 7 ++++++- admin/static/views/settings.html | 2 +- settings/challenge.go | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 295a25d8..87bb4c6c 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -518,7 +518,11 @@ angular.module("FICApp") $rootScope.settings.activateTime = new Date(response.activateTime); }) $scope.challenge = SettingsChallenge.get(); - $scope.duration = 360; + $scope.duration = 360; + $scope.challenge.$promise.then(function(c) { + if (c.duration) + $scope.duration = c.duration; + }); $scope.exerciceDependChange = function() { if ($scope.config.enableExerciceDepend) @@ -528,6 +532,7 @@ angular.module("FICApp") }; $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) { diff --git a/admin/static/views/settings.html b/admin/static/views/settings.html index 7512d4b3..5c6bdfe3 100644 --- a/admin/static/views/settings.html +++ b/admin/static/views/settings.html @@ -169,7 +169,7 @@
diff --git a/settings/challenge.go b/settings/challenge.go index a6b38dee..ed6c2af1 100644 --- a/settings/challenge.go +++ b/settings/challenge.go @@ -17,6 +17,8 @@ type ChallengeInfo struct { SubTitle string `json:"subtitle"` // Authors is the group name of people making the challenge. Authors string `json:"authors"` + // ExpectedDuration is the duration (in minutes) suggested when stating the challenge. + ExpectedDuration uint `json:"duration"` // VideoLink is the link to explaination videos when the challenge is over. VideosLink string `json:"videoslink"` From 4b2625c47d457bc646408f7fb71898aa4e1eae25 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 26 May 2022 11:37:43 +0200 Subject: [PATCH 0502/1637] admin: Fix toast that wasn't hidden on button click --- admin/static/js/app.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 87bb4c6c..6c0cbe11 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -156,13 +156,16 @@ angular.module("FICApp") 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('dispose'); + $element.children(0).toast('hide'); if (this.onyes) this.onyes(); } this.noFunc = function() { - $element.children(0).toast('dispose'); + $element.children(0).toast('hide'); if (this.onno) this.onno(); } From eb07eadae0b4d6200f8552406b1e75fb06744917 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 26 May 2022 12:26:53 +0200 Subject: [PATCH 0503/1637] admin: Copy challenge.json from sync to distsettings --- admin/api/settings.go | 24 +++++++++++++++++++++++- admin/main.go | 14 ++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/admin/api/settings.go b/admin/api/settings.go index 9bd4a24b..8aa49d83 100644 --- a/admin/api/settings.go +++ b/admin/api/settings.go @@ -3,8 +3,10 @@ package api import ( "encoding/json" "fmt" + "io" "log" "net/http" + "os" "path" "reflect" "time" @@ -58,7 +60,21 @@ func getROSettings(c *gin.Context) { } func getChallengeInfo(c *gin.Context) { - challengeinfo, err := sync.GetFileContent(sync.GlobalImporter, "challenge.json") + 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()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to retrive challenge.json: %s", err.Error())}) @@ -96,6 +112,12 @@ func saveChallengeInfo(c *gin.Context) { 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) } diff --git a/admin/main.go b/admin/main.go index cacd1961..8008d8b3 100644 --- a/admin/main.go +++ b/admin/main.go @@ -142,6 +142,20 @@ func main() { log.Fatal("Unable to initialize the importer:", err) } log.Println("Using", sync.GlobalImporter.Kind()) + + // Update distributed challenge.json + challengeinfo, err := sync.GetFileContent(sync.GlobalImporter, settings.ChallengeFile) + if err == nil { + 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) + } + } + } } // Sanitize options From 465a48c1c09c20ba833ca2f5a5ba5c287828e603 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 26 May 2022 12:53:47 +0200 Subject: [PATCH 0504/1637] admin: Show diff on settings form --- admin/static/js/app.js | 5 +- admin/static/views/settings.html | 112 +++++++++++++++---------------- 2 files changed, 59 insertions(+), 58 deletions(-) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 6c0cbe11..fc06d574 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -512,8 +512,10 @@ angular.module("FICApp") .controller("SettingsController", function($scope, $rootScope, Settings, SettingsChallenge, $location, $http, $interval) { $scope.displayDangerousActions = false; - $scope.config = Settings.get(); + $scope.config = Settings.get(); + $scope.dist_config = {}; $scope.config.$promise.then(function(response) { + $scope.dist_config = Object.assign({}, response); response.enableExerciceDepend = response.unlockedChallengeDepth >= 0; $rootScope.settings.start = new Date(response.start); $rootScope.settings.end = new Date(response.end); @@ -551,6 +553,7 @@ angular.module("FICApp") var state = this.config.enableExerciceDepend; this.config.unlockedChallengeDepth = (this.config.enableExerciceDepend?this.config.unlockedChallengeDepth:-1) this.config.$update(function(response) { + $scope.dist_config = Object.assign({}, response); $scope.addToast('success', msg); response.enableExerciceDepend = response.unlockedChallengeDepth >= 0; $rootScope.settings.start = new Date(nStart); diff --git a/admin/static/views/settings.html b/admin/static/views/settings.html index 5c6bdfe3..7fda4224 100644 --- a/admin/static/views/settings.html +++ b/admin/static/views/settings.html @@ -3,6 +3,29 @@
+
+ + + Propagation dans : {{ activateTimeCountDown | timer }} + Il restera : {{ timeRemaining - activateTimeCountDown | timer }} + +

Paramètres

@@ -11,10 +34,10 @@
- +
- +
@@ -23,9 +46,9 @@
- +
- +
@@ -43,168 +66,143 @@
- +
- +
- +
- +
- +
- +
- +
- +
- +
- +
- +
- +
-
-
- - - Propagation dans : {{ activateTimeCountDown | timer }} - Il restera : {{ timeRemaining - activateTimeCountDown | timer }} - -
-
From 3c237819c3df346286ec4c13450daf27036a480c Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 26 May 2022 22:54:46 +0200 Subject: [PATCH 0505/1637] settings: Save future changes in a dedicated file --- admin/api/settings.go | 124 +++++++++++++++++++++++++++++-- admin/static/js/app.js | 41 ++++++++-- admin/static/views/settings.html | 26 ++++++- settings/diff.go | 117 +++++++++++++++++++++++++++++ settings/settings.go | 2 +- 5 files changed, 295 insertions(+), 15 deletions(-) create mode 100644 settings/diff.go diff --git a/admin/api/settings.go b/admin/api/settings.go index 8aa49d83..8c0c5636 100644 --- a/admin/api/settings.go +++ b/admin/api/settings.go @@ -9,6 +9,7 @@ import ( "os" "path" "reflect" + "strconv" "time" "srs.epita.fr/fic-server/admin/sync" @@ -38,9 +39,34 @@ func declareSettingsRoutes(router *gin.RouterGroup) { 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) } +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 getROSettings(c *gin.Context) { syncMtd := "Disabled" if sync.GlobalImporter != nil { @@ -142,14 +168,102 @@ func saveSettings(c *gin.Context) { return } - 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())}) + // 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 } - ApplySettings(config) - c.JSON(http.StatusOK, config) + 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) { diff --git a/admin/static/js/app.js b/admin/static/js/app.js index fc06d574..06f94728 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -234,6 +234,11 @@ angular.module("FICApp") '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'}, @@ -510,7 +515,23 @@ angular.module("FICApp") $scope.monitor = Monitor.get(); }) - .controller("SettingsController", function($scope, $rootScope, Settings, SettingsChallenge, $location, $http, $interval) { + .controller("SettingsController", function($scope, $rootScope, NextSettings, Settings, SettingsChallenge, $location, $http, $interval) { + $scope.nextsettings = NextSettings.query(); + $scope.erase = false; + $scope.editNextSettings = function(ns) { + $scope.activateTime = ns.date; + $scope.erase = true; + Object.keys(ns.values).forEach(function(k) { + $scope.config[k] = ns.values[k]; + }); + $scope.config.enableExerciceDepend = $scope.config.unlockedChallengeDepth >= 0; + } + $scope.deleteNextSettings = function(ns) { + ns.$delete().then(function() { + $scope.nextsettings = NextSettings.query(); + }) + } + $scope.displayDangerousActions = false; $scope.config = Settings.get(); $scope.dist_config = {}; @@ -524,6 +545,7 @@ angular.module("FICApp") }) $scope.challenge = SettingsChallenge.get(); $scope.duration = 360; + $scope.activateTime = "0001-01-01T00:00:00Z"; $scope.challenge.$promise.then(function(c) { if (c.duration) $scope.duration = c.duration; @@ -549,17 +571,24 @@ angular.module("FICApp") var nStart = this.config.start; var nEnd = this.config.end; var nGen = this.config.generation; - var aTime = this.config.activateTime; var state = this.config.enableExerciceDepend; this.config.unlockedChallengeDepth = (this.config.enableExerciceDepend?this.config.unlockedChallengeDepth:-1) - this.config.$update(function(response) { + var updateQuery = {}; + if (this.activateTime && this.activateTime != '0001-01-01T00:00:00Z') { + updateQuery['t'] = this.activateTime; + } + 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; $rootScope.settings.start = new Date(nStart); $rootScope.settings.end = new Date(nEnd); $rootScope.settings.generation = new Date(nGen); - $rootScope.settings.activateTime = new Date(aTime); }, function(response) { $scope.addToast('danger', 'An error occurs when saving settings:', response.data.errmsg); }); @@ -582,12 +611,12 @@ angular.module("FICApp") }); } $scope.updateActivateTime = function() { - $rootScope.settings.activateTime = this.config.activateTime; + $rootScope.settings.activateTime = this.activateTime; } $scope.updActivateTime = function(modulo) { var ts = Math.floor((new Date(this.config.end) - Date.now() - (60000 * modulo / 2)) / (60000 * modulo)) * (60000 * modulo); var d = new Date(this.config.end) - ts; - this.config.activateTime = new Date(d).toISOString(); + this.activateTime = new Date(d).toISOString(); this.updateActivateTime(); } $scope.reset = function(type) { diff --git a/admin/static/views/settings.html b/admin/static/views/settings.html index 7fda4224..5b961969 100644 --- a/admin/static/views/settings.html +++ b/admin/static/views/settings.html @@ -6,7 +6,7 @@
Propagation dans : {{ activateTimeCountDown | timer }} @@ -279,6 +279,26 @@

Changements anticipés

+
+
+

+ {{ ns.date }} +

+
+ + +
+
+
    +
  • + {{ k }} → {{ v }} +
  • +
+
diff --git a/settings/diff.go b/settings/diff.go new file mode 100644 index 00000000..34a2f855 --- /dev/null +++ b/settings/diff.go @@ -0,0 +1,117 @@ +package settings + +import ( + "encoding/json" + "fmt" + "log" + "os" + "path" + "reflect" + "strconv" + "strings" + "time" +) + +// DiffSettings returns only the fields that differs between the two objects. +func DiffSettings(old, new *Settings) (ret map[string]interface{}) { + ret = map[string]interface{}{} + for _, field := range reflect.VisibleFields(reflect.TypeOf(*old)) { + if !reflect.DeepEqual(reflect.ValueOf(*old).FieldByName(field.Name).Interface(), reflect.ValueOf(*new).FieldByName(field.Name).Interface()) { + name := field.Name + + if tag, ok := field.Tag.Lookup("json"); ok { + name = strings.Split(tag, ",")[0] + } + + ret[name] = reflect.ValueOf(*new).FieldByName(field.Name).Interface() + } + } + + return +} + +type NextSettingsFile struct { + Id int64 `json:"id"` + Date time.Time `json:"date"` + Values map[string]interface{} `json:"values"` +} + +func ReadNextSettingsFile(filename string, ts int64) (*NextSettingsFile, error) { + fd, err := os.Open(filename) + if err != nil { + return nil, fmt.Errorf("unable to open(%s): %w", filename, err) + } else { + defer fd.Close() + + var s map[string]interface{} + + jdec := json.NewDecoder(fd) + + if err := jdec.Decode(&s); err != nil { + return nil, fmt.Errorf("an error occurs during JSON decoding of %s: %w", filename, err) + } + + return &NextSettingsFile{ + Id: ts, + Date: time.Unix(ts, 0), + Values: s, + }, nil + } +} + +func ListNextSettingsFiles() ([]*NextSettingsFile, error) { + files, err := os.ReadDir(SettingsDir) + if err != nil { + return nil, err + } + + var ret []*NextSettingsFile + for _, file := range files { + ts, err := strconv.ParseInt(file.Name()[:10], 10, 64) + if err == nil { + nsf, err := ReadNextSettingsFile(path.Join(SettingsDir, file.Name()), ts) + if err != nil { + return nil, err + } + + ret = append(ret, nsf) + } + } + + return ret, nil +} + +func MergeNextSettingsUntil(until *time.Time) (map[string]interface{}, error) { + nsfs, err := ListNextSettingsFiles() + if err != nil { + return nil, err + } + + ret := map[string]interface{}{} + for _, nsf := range nsfs { + if until == nil || time.Unix(nsf.Id, 0).Before(*until) { + for k, v := range nsf.Values { + ret[k] = v + } + } + } + + return ret, nil +} + +func MergeSettings(current Settings, new map[string]interface{}) *Settings { + for _, field := range reflect.VisibleFields(reflect.TypeOf(current)) { + name := field.Name + + if tag, ok := field.Tag.Lookup("json"); ok { + name = strings.Split(tag, ",")[0] + } + + if v, ok := new[name]; ok { + log.Println(name, field.Name, v) + reflect.ValueOf(¤t).Elem().FieldByName(field.Name).Set(reflect.ValueOf(v)) + } + } + + return ¤t +} diff --git a/settings/settings.go b/settings/settings.go index a16f5207..37971c4e 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -103,7 +103,7 @@ func ReadSettings(path string) (*Settings, error) { } // SaveSettings saves settings at the given location. -func SaveSettings(path string, s *Settings) error { +func SaveSettings(path string, s interface{}) error { if fd, err := os.Create(path); err != nil { return err } else { From bd35705f58f666c974b55633fac5fa4f87ee94a8 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 26 May 2022 23:51:34 +0200 Subject: [PATCH 0506/1637] admin: Make menu items active on rights pages --- admin/index.go | 18 +++++++++--------- admin/static/js/app.js | 3 ++- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/admin/index.go b/admin/index.go index 00dc37eb..13cca410 100644 --- a/admin/index.go +++ b/admin/index.go @@ -79,23 +79,23 @@ const indextpl = ` -
+
diff --git a/admin/static/views/exercice.html b/admin/static/views/exercice.html index ccf9cd2f..a0977e02 100644 --- a/admin/static/views/exercice.html +++ b/admin/static/views/exercice.html @@ -1,15 +1,19 @@ -

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

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

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

+
+ Vidéo + Flags + + Voir sur la forge +
+
diff --git a/admin/static/views/sync.html b/admin/static/views/sync.html index a457e6ed..cef9e825 100644 --- a/admin/static/views/sync.html +++ b/admin/static/views/sync.html @@ -75,7 +75,7 @@
    -
  • {{ item }}
  • +
  • {{ item }}
diff --git a/admin/static/views/theme.html b/admin/static/views/theme.html index 64477a39..bc03a3cc 100644 --- a/admin/static/views/theme.html +++ b/admin/static/views/theme.html @@ -1,7 +1,11 @@ -

- {{theme.name}} {{theme.authors | stripHTML}} - Voir sur la forge -

+
+

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

+ +
From 4c84038b286ef7c808a8bf874aa840c8ac664341 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 31 May 2022 16:42:17 +0200 Subject: [PATCH 0518/1637] password_paper: Update --- password_paper/Makefile | 2 +- password_paper/comcyber.png | Bin 70404 -> 0 bytes password_paper/dnred.png | Bin 0 -> 139405 bytes password_paper/fic.png | Bin 145164 -> 70064 bytes password_paper/pass.sh | 2 +- 5 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 password_paper/comcyber.png create mode 100644 password_paper/dnred.png diff --git a/password_paper/Makefile b/password_paper/Makefile index 74f1fe2a..77cabe11 100644 --- a/password_paper/Makefile +++ b/password_paper/Makefile @@ -1,7 +1,7 @@ all: pass.pdf .sh.tex: - sort teams.pass | sh $< > $@ + cat teams.pass | sh $< > $@ .tex.pdf: latexmk --xelatex ${.IMPSRC} diff --git a/password_paper/comcyber.png b/password_paper/comcyber.png deleted file mode 100644 index 2876b4b93276460e3e7c1a94d043ec2a6d63375f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 70404 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

<@o5@ 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¨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 literal 0 HcmV?d00001 diff --git a/password_paper/fic.png b/password_paper/fic.png index e8a86e219c294f35c1da9cf05a88adfc887c5c7f..4beb17c9c2b4a8ab9a887e27862eb582f411e8e2 100644 GIT binary patch literal 70064 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

<@lLwAS#WQRQ4S<-jatS(Pvoz@Bcpi0*EpK)1G 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

E!<_?n~UE{KEe2Yj%<)vXq@N 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 VR^}nEmt;14pK&vU|z&vf3-KIX#m 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 z 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

4e(% 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 F{{n6gE06#H diff --git a/password_paper/pass.sh b/password_paper/pass.sh index bddcb288..2791abcc 100755 --- a/password_paper/pass.sh +++ b/password_paper/pass.sh @@ -63,7 +63,7 @@ cat < Date: Tue, 31 May 2022 18:18:08 +0200 Subject: [PATCH 0519/1637] admin: Handle more info in challenge.json --- admin/static/views/settings.html | 43 ++++++++++++++++++++++++++++++++ settings/challenge.go | 23 +++++++++++++---- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/admin/static/views/settings.html b/admin/static/views/settings.html index 5b961969..f288ee1e 100644 --- a/admin/static/views/settings.html +++ b/admin/static/views/settings.html @@ -269,6 +269,49 @@

+
+ +
+
+ +
+ + +
+
+
+
+ +
+ +
+ +
+
+ +
+ +
+
+ + + +
+ + +
+
+
+
+
diff --git a/settings/challenge.go b/settings/challenge.go index ed6c2af1..150ff1aa 100644 --- a/settings/challenge.go +++ b/settings/challenge.go @@ -9,25 +9,38 @@ import ( // ChallengeFile is the expected name of the file containing the challenge infos. const ChallengeFile = "challenge.json" +type ChallengePartner struct { + Src string `json:"img"` + Alt string `json:"alt,omitempty"` + Href string `json:"href,omitempty"` +} + // ChallengeInfo stores common descriptions and informations about the challenge. type ChallengeInfo struct { // Title is the displayed name of the challenge. Title string `json:"title"` // SubTitle is appended to the title. - SubTitle string `json:"subtitle"` + SubTitle string `json:"subtitle,omitempty"` // Authors is the group name of people making the challenge. Authors string `json:"authors"` // ExpectedDuration is the duration (in minutes) suggested when stating the challenge. ExpectedDuration uint `json:"duration"` // VideoLink is the link to explaination videos when the challenge is over. - VideosLink string `json:"videoslink"` + VideosLink string `json:"videoslink,omitempty"` // Description gives an overview of the challenge. - Description string `json:"description"` + Description string `json:"description,omitempty"` // Rules tell the player some help. - Rules string `json:"rules"` + Rules string `json:"rules,omitempty"` // YourMission is a small introduction to understand the goals. - YourMission string `json:"your_mission"` + YourMission string `json:"your_mission,omitempty"` + + // MainLogo stores path to logos displayed in the header. + MainLogo []string `json:"main_logo,omitempty"` + // MainLink stores link to the parent website. + MainLink string `json:"main_link,omitempty"` + // Partners holds the challenge partners list. + Partners []ChallengePartner `json:"partners,omitempty"` } // ReadChallenge parses the file at the given location. From c0260da035194fa5ebed202802221ef70a54ff3d Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 31 May 2022 18:41:42 +0200 Subject: [PATCH 0520/1637] admin: Add resync button on theme --- admin/static/views/sync.html | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/admin/static/views/sync.html b/admin/static/views/sync.html index cef9e825..46d9cf58 100644 --- a/admin/static/views/sync.html +++ b/admin/static/views/sync.html @@ -68,12 +68,19 @@
-

- {{ th }} - - - -

+
+

+ {{ th }} +

+
+ + + + +
+
  • {{ item }}
From cd03b99f9ba0209b6b42fc199fda52153c767c68 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 31 May 2022 18:42:41 +0200 Subject: [PATCH 0521/1637] dashboard: Take information from challenge.json --- dashboard/static/index.html | 22 +++++++++++----------- dashboard/static/js/dashboard.js | 8 ++++++++ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/dashboard/static/index.html b/dashboard/static/index.html index 23e023bd..392537cc 100644 --- a/dashboard/static/index.html +++ b/dashboard/static/index.html @@ -533,16 +533,18 @@
From 58af047a2603c0d6e1ce3a57d7d4a93cf1986479 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 4 Jun 2022 18:21:41 +0200 Subject: [PATCH 0541/1637] admin: Pick challenge title from challenge.json --- admin/api/password.go | 6 ++++-- admin/api/settings.go | 19 +++++++++++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/admin/api/password.go b/admin/api/password.go index d9b9ecff..c6222766 100644 --- a/admin/api/password.go +++ b/admin/api/password.go @@ -191,13 +191,15 @@ func genDexConfig() ([]byte, error) { } else { b := bytes.NewBufferString("") - if dexTmpl, err := template.New("dexcfg").Parse(dexcfgtpl); err != nil { + if challengeInfo, err := GetChallengeInfo(); err != nil { + return nil, fmt.Errorf("Cannot create template: %w", err) + } else if dexTmpl, err := template.New("dexcfg").Parse(dexcfgtpl); err != nil { return nil, fmt.Errorf("Cannot create template: %w", err) } else if err = dexTmpl.Execute(b, dexConfig{ Clients: []dexConfigClient{ dexConfigClient{ Id: "epita-challenge", - Name: "Challenge Forensic", + Name: challengeInfo.Title, RedirectURIs: []string{"https://fic.srs.epita.fr/challenge_access/auth"}, Secret: OidcSecret, }, diff --git a/admin/api/settings.go b/admin/api/settings.go index 8c0c5636..ce2ab503 100644 --- a/admin/api/settings.go +++ b/admin/api/settings.go @@ -85,7 +85,7 @@ func getROSettings(c *gin.Context) { }) } -func getChallengeInfo(c *gin.Context) { +func GetChallengeInfo() (*settings.ChallengeInfo, error) { var challengeinfo string var err error if sync.GlobalImporter == nil { @@ -103,18 +103,25 @@ func getChallengeInfo(c *gin.Context) { if 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 + 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()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to read challenge info: %s", err.Error())}) - return + return nil, fmt.Errorf("Unable to read challenge info: %w", err) } - c.JSON(http.StatusOK, s) + 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) { From 046b38cc32fdd3eb9f55ac8bdaee5fa9d8e33322 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sat, 4 Jun 2022 18:47:13 +0200 Subject: [PATCH 0542/1637] frontend: Use latest node version --- Dockerfile-frontend-ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile-frontend-ui b/Dockerfile-frontend-ui index 6eb88135..e6c8d011 100644 --- a/Dockerfile-frontend-ui +++ b/Dockerfile-frontend-ui @@ -1,4 +1,4 @@ -FROM node:lts-alpine as nodebuild +FROM node:alpine as nodebuild WORKDIR /ui From 8cb7bf8b96700f9a232c21068ab7754c48301a53 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 31 May 2022 00:00:23 +0000 Subject: [PATCH 0543/1637] chore(deps): update dependency sass-loader to v13 --- frontend/ui/package-lock.json | 6 +++--- frontend/ui/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/ui/package-lock.json b/frontend/ui/package-lock.json index bb84adef..3abc48c3 100644 --- a/frontend/ui/package-lock.json +++ b/frontend/ui/package-lock.json @@ -4293,9 +4293,9 @@ } }, "sass-loader": { - "version": "12.6.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", - "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.0.tgz", + "integrity": "sha512-IHCFecI+rbPvXE2zO/mqdVFe8MU7ElGrwga9hh2H65Ru4iaBJAMRteum1c4Gsxi9Cq1FOtTEDd6+/AEYuQDM4Q==", "dev": true, "requires": { "klona": "^2.0.4", diff --git a/frontend/ui/package.json b/frontend/ui/package.json index dd624444..e83710a0 100644 --- a/frontend/ui/package.json +++ b/frontend/ui/package.json @@ -16,7 +16,7 @@ "prettier": "^2.6.2", "prettier-plugin-svelte": "^2.7.0", "sass": "^1.51.0", - "sass-loader": "^12.6.0", + "sass-loader": "^13.0.0", "svelte": "^3.48.0", "sveltestrap": "^5.9.0" }, From 635e67c224c5785a8ae42b1d17bfab00952361dd Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 6 Jun 2022 10:47:38 +0200 Subject: [PATCH 0544/1637] dashboard: Use gin-gonic instead of httprouter directly --- dashboard/api/handler.go | 81 --------------- dashboard/api/router.go | 11 --- dashboard/app.go | 60 +++++++++++ dashboard/main.go | 48 ++++----- dashboard/static.go | 208 ++++++++++++++++++++------------------- 5 files changed, 193 insertions(+), 215 deletions(-) delete mode 100644 dashboard/api/handler.go delete mode 100644 dashboard/api/router.go create mode 100644 dashboard/app.go diff --git a/dashboard/api/handler.go b/dashboard/api/handler.go deleted file mode 100644 index e1443268..00000000 --- a/dashboard/api/handler.go +++ /dev/null @@ -1,81 +0,0 @@ -package api - -import ( - "encoding/json" - "fmt" - "io" - "log" - "net/http" - "time" - - "github.com/julienschmidt/httprouter" -) - -type DispatchFunction func(httprouter.Params, []byte) (interface{}, error) - -func apiHandler(f DispatchFunction) func(http.ResponseWriter, *http.Request, httprouter.Params) { - return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - if addr := r.Header.Get("X-Forwarded-For"); addr != "" { - r.RemoteAddr = addr - } - log.Printf("%s \"%s %s\" [%s]\n", r.RemoteAddr, r.Method, r.URL.Path, r.UserAgent()) - - // Read the body - if r.ContentLength < 0 || r.ContentLength > 6553600 { - http.Error(w, fmt.Sprintf("{errmsg:\"Request too large or request size unknown\"}"), http.StatusRequestEntityTooLarge) - return - } - var body []byte - if r.ContentLength > 0 { - tmp := make([]byte, 1024) - for { - n, err := r.Body.Read(tmp) - for j := 0; j < n; j++ { - body = append(body, tmp[j]) - } - if err != nil || n <= 0 { - break - } - } - } - - var ret interface{} - var err error = nil - - ret, err = f(ps, body) - - // Format response - resStatus := http.StatusOK - if err != nil { - ret = map[string]string{"errmsg": err.Error()} - resStatus = http.StatusBadRequest - log.Println(r.RemoteAddr, resStatus, err.Error()) - } - - if ret == nil { - ret = map[string]string{"errmsg": "Page not found"} - resStatus = http.StatusNotFound - } - - w.Header().Set("X-FIC-Time", fmt.Sprintf("%f", float64(time.Now().UnixNano()/1000)/1000000)) - - if str, found := ret.(string); found { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(resStatus) - io.WriteString(w, str) - } else if bts, found := ret.([]byte); found { - w.Header().Set("Content-Type", "application/octet-stream") - w.Header().Set("Content-Disposition", "attachment") - w.Header().Set("Content-Transfer-Encoding", "binary") - w.WriteHeader(resStatus) - w.Write(bts) - } else if j, err := json.Marshal(ret); err != nil { - w.Header().Set("Content-Type", "application/json") - http.Error(w, fmt.Sprintf("{\"errmsg\":%q}", err), http.StatusInternalServerError) - } else { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(resStatus) - w.Write(j) - } - } -} diff --git a/dashboard/api/router.go b/dashboard/api/router.go deleted file mode 100644 index a6bd873b..00000000 --- a/dashboard/api/router.go +++ /dev/null @@ -1,11 +0,0 @@ -package api - -import ( - "github.com/julienschmidt/httprouter" -) - -var router = httprouter.New() - -func Router() *httprouter.Router { - return router -} diff --git a/dashboard/app.go b/dashboard/app.go new file mode 100644 index 00000000..b3cf5171 --- /dev/null +++ b/dashboard/app.go @@ -0,0 +1,60 @@ +package main + +import ( + "context" + "log" + "net/http" + "time" + + "github.com/gin-gonic/gin" +) + +type App struct { + router *gin.Engine + srv *http.Server + bind string +} + +func NewApp(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("") + } + declareStaticRoutes(baserouter, baseURL) + + app := App{ + router: router, + bind: bind, + } + + return app +} + +func (app *App) Start() { + app.srv = &http.Server{ + Addr: app.bind, + Handler: app.router, + } + + 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/main.go b/dashboard/main.go index d4fca6ca..a9e2b17e 100644 --- a/dashboard/main.go +++ b/dashboard/main.go @@ -1,9 +1,8 @@ package main import ( - "context" "flag" - "fmt" + "io/fs" "log" "net/http" "net/url" @@ -14,12 +13,10 @@ import ( "strings" "syscall" - "srs.epita.fr/fic-server/dashboard/api" "srs.epita.fr/fic-server/libfic" "srs.epita.fr/fic-server/settings" ) -var StaticDir string var DashboardDir string var TeamsDir string @@ -64,15 +61,16 @@ func StripPrefix(prefix string, h http.Handler) http.Handler { } func main() { + var baseURL string // Read paremeters from environment if v, exists := os.LookupEnv("FIC_BASEURL"); exists { - BaseURL = v + baseURL = v } // Read parameters from command line var bind = flag.String("bind", "127.0.0.1:8082", "Bind port/socket") - flag.StringVar(&BaseURL, "baseurl", BaseURL, "URL prepended to each URL") - flag.StringVar(&StaticDir, "static", "./htdocs-dashboard/", "Directory containing static files") + 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") @@ -86,8 +84,20 @@ func main() { // Sanitize options var err error log.Println("Checking paths...") - if StaticDir, err = filepath.Abs(StaticDir); err != nil { - log.Fatal(err) + 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) @@ -95,10 +105,10 @@ func main() { if settings.SettingsDir, err = filepath.Abs(settings.SettingsDir); err != nil { log.Fatal(err) } - if BaseURL != "/" { - BaseURL = path.Clean(BaseURL) + if baseURL != "/" { + baseURL = path.Clean(baseURL) } else { - BaseURL = "" + baseURL = "" } if fwdr != nil && len(*fwdr) > 0 { forwarder = fwdr @@ -108,21 +118,13 @@ func main() { interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM) - srv := &http.Server{ - Addr: *bind, - Handler: StripPrefix(BaseURL, api.Router()), - } - - // Serve content - go func() { - log.Fatal(srv.ListenAndServe()) - }() - log.Println(fmt.Sprintf("Ready, listening on %s", *bind)) + app := NewApp(baseURL, *bind) + go app.Start() // Wait shutdown signal <-interrupt log.Print("The service is shutting down...") - srv.Shutdown(context.Background()) + app.Stop() log.Println("done") } diff --git a/dashboard/static.go b/dashboard/static.go index ce1ac4e7..b12bcf00 100644 --- a/dashboard/static.go +++ b/dashboard/static.go @@ -2,32 +2,35 @@ package main import ( "bytes" + "embed" "fmt" "io" "io/ioutil" "log" "net/http" - "os" "path" "strings" "time" - "srs.epita.fr/fic-server/dashboard/api" "srs.epita.fr/fic-server/libfic" "srs.epita.fr/fic-server/settings" - "github.com/julienschmidt/httprouter" + "github.com/gin-gonic/gin" ) -var BaseURL = "/" +//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) { +func getIndexHtml(w io.Writer, baseURL string) { if len(indexTmpl) == 0 { - if file, err := os.Open(path.Join(StaticDir, "index.html")); err != nil { + if file, err := staticFS.Open("index.html"); err != nil { log.Println("Unable to open index.html: ", err) } else { defer file.Close() @@ -35,7 +38,7 @@ func getIndexHtml(w io.Writer) { 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) + indexTmpl = bytes.Replace(indexTmpl, []byte("{{.urlbase}}"), []byte(path.Clean(path.Join(baseURL+"/", "nuke"))[:len(path.Clean(path.Join(baseURL+"/", "nuke")))-4]), -1) } } } @@ -43,190 +46,195 @@ func getIndexHtml(w io.Writer) { w.Write(indexTmpl) } -func init() { - api.Router().GET("/", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.Redirect(w, r, "public0.html", http.StatusFound) +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++ { - api.Router().GET(fmt.Sprintf("/public%d.html", i), func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - getIndexHtml(w) + router.GET(fmt.Sprintf("/public%d.html", i), func(c *gin.Context) { + getIndexHtml(c.Writer, baseURL) }) } - api.Router().GET("/css/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, r.URL.Path)) + router.GET("/css/*_", func(c *gin.Context) { + serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL)) }) - api.Router().GET("/fonts/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, r.URL.Path)) + router.GET("/fonts/*_", func(c *gin.Context) { + serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL)) }) - api.Router().GET("/img/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, r.URL.Path)) + router.GET("/img/*_", func(c *gin.Context) { + serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL)) }) - api.Router().GET("/js/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, r.URL.Path)) + router.GET("/js/*_", func(c *gin.Context) { + serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL)) }) - api.Router().GET("/views/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - http.ServeFile(w, r, path.Join(StaticDir, r.URL.Path)) + router.GET("/views/*_", func(c *gin.Context) { + serveFile(c, strings.TrimPrefix(c.Request.URL.Path, baseURL)) }) - api.Router().GET("/files/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + router.GET("/files/*_", func(c *gin.Context) { if forwarder != nil { - fwd_request(w, r, *forwarder) + fwd_request(c.Writer, c.Request, *forwarder) } else { - http.ServeFile(w, r, path.Join(fic.FilesDir, strings.TrimPrefix(r.URL.Path, "/files"))) + http.ServeFile(c.Writer, c.Request, path.Join(fic.FilesDir, strings.TrimPrefix(c.Request.URL.Path, path.Join(baseURL, "files")))) } }) - api.Router().GET("/events.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - w.Header().Set("Cache-Control", "no-cache") + router.GET("/events.json", func(c *gin.Context) { + c.Writer.Header().Set("Cache-Control", "no-cache") if forwarder != nil { - fwd_request(w, r, *forwarder) + fwd_request(c.Writer, c.Request, *forwarder) } else { - http.ServeFile(w, r, path.Join(TeamsDir, "events.json")) + http.ServeFile(c.Writer, c.Request, path.Join(TeamsDir, "events.json")) } }) - api.Router().GET("/my.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - w.Header().Set("Cache-Control", "no-cache") + router.GET("/my.json", func(c *gin.Context) { + c.Writer.Header().Set("Cache-Control", "no-cache") if forwarder != nil { - fwd_request(w, r, *forwarder) + fwd_request(c.Writer, c.Request, *forwarder) } else { - http.ServeFile(w, r, path.Join(TeamsDir, "public", "my.json")) + http.ServeFile(c.Writer, c.Request, path.Join(TeamsDir, "public", "my.json")) } }) - api.Router().GET("/api/teams/:tid/score-grid.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + router.GET("/api/teams/:tid/score-grid.json", func(c *gin.Context) { if forwarder != nil { - fwd_request(w, r, *forwarder) + fwd_request(c.Writer, c.Request, *forwarder) } else { - fwd_request(w, r, "http://127.0.0.1:8081/") + fwd_request(c.Writer, c.Request, "http://127.0.0.1:8081/") } }) - api.Router().GET("/api/teams/:tid/stats.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + router.GET("/api/teams/:tid/stats.json", func(c *gin.Context) { if forwarder != nil { - fwd_request(w, r, *forwarder) + fwd_request(c.Writer, c.Request, *forwarder) } else { - fwd_request(w, r, "http://127.0.0.1:8081/") + fwd_request(c.Writer, c.Request, "http://127.0.0.1:8081/") } }) - api.Router().GET("/challenge.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - w.Header().Set("Cache-Control", "no-cache") + router.GET("/challenge.json", func(c *gin.Context) { + c.Writer.Header().Set("Cache-Control", "no-cache") if forwarder != nil { - fwd_request(w, r, *forwarder) + fwd_request(c.Writer, c.Request, *forwarder) } else { - http.ServeFile(w, r, path.Join(settings.SettingsDir, settings.ChallengeFile)) + http.ServeFile(c.Writer, c.Request, path.Join(settings.SettingsDir, settings.ChallengeFile)) } }) - api.Router().GET("/settings.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - w.Header().Set("Cache-Control", "no-cache") + router.GET("/settings.json", func(c *gin.Context) { + c.Writer.Header().Set("Cache-Control", "no-cache") if forwarder != nil { - fwd_request(w, r, *forwarder) + fwd_request(c.Writer, c.Request, *forwarder) } else { - w.Header().Set("X-FIC-Time", fmt.Sprintf("%f", float64(time.Now().UnixNano()/1000)/1000000)) - http.ServeFile(w, r, path.Join(settings.SettingsDir, settings.SettingsFile)) + 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)) } }) - api.Router().GET("/teams.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - w.Header().Set("Cache-Control", "no-cache") + router.GET("/teams.json", func(c *gin.Context) { + c.Writer.Header().Set("Cache-Control", "no-cache") if forwarder != nil { - fwd_request(w, r, *forwarder) + fwd_request(c.Writer, c.Request, *forwarder) } else { - http.ServeFile(w, r, path.Join(TeamsDir, "public", "teams.json")) + http.ServeFile(c.Writer, c.Request, path.Join(TeamsDir, "public", "teams.json")) } }) - api.Router().GET("/themes.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - w.Header().Set("Cache-Control", "no-cache") + router.GET("/themes.json", func(c *gin.Context) { + c.Writer.Header().Set("Cache-Control", "no-cache") if forwarder != nil { - fwd_request(w, r, *forwarder) + fwd_request(c.Writer, c.Request, *forwarder) } else { - http.ServeFile(w, r, path.Join(TeamsDir, "themes.json")) + http.ServeFile(c.Writer, c.Request, path.Join(TeamsDir, "themes.json")) } }) - api.Router().GET("/public.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - w.Header().Set("Cache-Control", "no-cache") + router.GET("/public.json", func(c *gin.Context) { + c.Writer.Header().Set("Cache-Control", "no-cache") if forwarder != nil && fwdPublicJson { - fwd_request(w, r, *forwarder) + fwd_request(c.Writer, c.Request, *forwarder) } else { - http.ServeFile(w, r, path.Join(DashboardDir, "public.json")) + http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public.json")) } }) - api.Router().GET("/public0.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - w.Header().Set("Cache-Control", "no-cache") + router.GET("/public0.json", func(c *gin.Context) { + c.Writer.Header().Set("Cache-Control", "no-cache") if forwarder != nil && fwdPublicJson { - fwd_request(w, r, *forwarder) + fwd_request(c.Writer, c.Request, *forwarder) } else { - http.ServeFile(w, r, path.Join(DashboardDir, "public0.json")) + http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public0.json")) } }) - api.Router().GET("/public1.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - w.Header().Set("Cache-Control", "no-cache") + router.GET("/public1.json", func(c *gin.Context) { + c.Writer.Header().Set("Cache-Control", "no-cache") if forwarder != nil && fwdPublicJson { - fwd_request(w, r, *forwarder) + fwd_request(c.Writer, c.Request, *forwarder) } else { - http.ServeFile(w, r, path.Join(DashboardDir, "public1.json")) + http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public1.json")) } }) - api.Router().GET("/public2.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - w.Header().Set("Cache-Control", "no-cache") + router.GET("/public2.json", func(c *gin.Context) { + c.Writer.Header().Set("Cache-Control", "no-cache") if forwarder != nil && fwdPublicJson { - fwd_request(w, r, *forwarder) + fwd_request(c.Writer, c.Request, *forwarder) } else { - http.ServeFile(w, r, path.Join(DashboardDir, "public2.json")) + http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public2.json")) } }) - api.Router().GET("/public3.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - w.Header().Set("Cache-Control", "no-cache") + router.GET("/public3.json", func(c *gin.Context) { + c.Writer.Header().Set("Cache-Control", "no-cache") if forwarder != nil && fwdPublicJson { - fwd_request(w, r, *forwarder) + fwd_request(c.Writer, c.Request, *forwarder) } else { - http.ServeFile(w, r, path.Join(DashboardDir, "public3.json")) + http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public3.json")) } }) - api.Router().GET("/public4.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - w.Header().Set("Cache-Control", "no-cache") + router.GET("/public4.json", func(c *gin.Context) { + c.Writer.Header().Set("Cache-Control", "no-cache") if forwarder != nil && fwdPublicJson { - fwd_request(w, r, *forwarder) + fwd_request(c.Writer, c.Request, *forwarder) } else { - http.ServeFile(w, r, path.Join(DashboardDir, "public4.json")) + http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public4.json")) } }) - api.Router().GET("/public5.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - w.Header().Set("Cache-Control", "no-cache") + router.GET("/public5.json", func(c *gin.Context) { + c.Writer.Header().Set("Cache-Control", "no-cache") if forwarder != nil && fwdPublicJson { - fwd_request(w, r, *forwarder) + fwd_request(c.Writer, c.Request, *forwarder) } else { - http.ServeFile(w, r, path.Join(DashboardDir, "public5.json")) + http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public5.json")) } }) - api.Router().GET("/public6.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - w.Header().Set("Cache-Control", "no-cache") + router.GET("/public6.json", func(c *gin.Context) { + c.Writer.Header().Set("Cache-Control", "no-cache") if forwarder != nil && fwdPublicJson { - fwd_request(w, r, *forwarder) + fwd_request(c.Writer, c.Request, *forwarder) } else { - http.ServeFile(w, r, path.Join(DashboardDir, "public6.json")) + http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public6.json")) } }) - api.Router().GET("/public7.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - w.Header().Set("Cache-Control", "no-cache") + router.GET("/public7.json", func(c *gin.Context) { + c.Writer.Header().Set("Cache-Control", "no-cache") if forwarder != nil && fwdPublicJson { - fwd_request(w, r, *forwarder) + fwd_request(c.Writer, c.Request, *forwarder) } else { - http.ServeFile(w, r, path.Join(DashboardDir, "public7.json")) + http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public7.json")) } }) - api.Router().GET("/public8.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - w.Header().Set("Cache-Control", "no-cache") + router.GET("/public8.json", func(c *gin.Context) { + c.Writer.Header().Set("Cache-Control", "no-cache") if forwarder != nil && fwdPublicJson { - fwd_request(w, r, *forwarder) + fwd_request(c.Writer, c.Request, *forwarder) } else { - http.ServeFile(w, r, path.Join(DashboardDir, "public8.json")) + http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public8.json")) } }) - api.Router().GET("/public9.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - w.Header().Set("Cache-Control", "no-cache") + router.GET("/public9.json", func(c *gin.Context) { + c.Writer.Header().Set("Cache-Control", "no-cache") if forwarder != nil && fwdPublicJson { - fwd_request(w, r, *forwarder) + fwd_request(c.Writer, c.Request, *forwarder) } else { - http.ServeFile(w, r, path.Join(DashboardDir, "public9.json")) + http.ServeFile(c.Writer, c.Request, path.Join(DashboardDir, "public9.json")) } }) } From 437ed4d393ff2a60a25fe1d1cd552759c73d3ea0 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 6 Jun 2022 10:48:06 +0200 Subject: [PATCH 0545/1637] dashboard: Can restrict access through htpasswd --- dashboard/app.go | 7 +++- dashboard/htpasswd.go | 78 +++++++++++++++++++++++++++++++++++++++++++ dashboard/main.go | 3 +- go.mod | 1 + go.sum | 2 ++ 5 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 dashboard/htpasswd.go diff --git a/dashboard/app.go b/dashboard/app.go index b3cf5171..7dda3e8b 100644 --- a/dashboard/app.go +++ b/dashboard/app.go @@ -15,7 +15,7 @@ type App struct { bind string } -func NewApp(baseURL string, bind string) App { +func NewApp(htpasswd_file *string, baseURL string, bind string) App { gin.ForceConsoleColor() router := gin.Default() @@ -29,6 +29,11 @@ func NewApp(baseURL string, bind string) App { } else { baserouter = router.Group("") } + + if htpasswd_file != nil && len(*htpasswd_file) > 0 { + baserouter.Use(Htpassword(*htpasswd_file)) + } + declareStaticRoutes(baserouter, baseURL) app := App{ diff --git a/dashboard/htpasswd.go b/dashboard/htpasswd.go new file mode 100644 index 00000000..1f62bf9a --- /dev/null +++ b/dashboard/htpasswd.go @@ -0,0 +1,78 @@ +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 index a9e2b17e..9a245ca5 100644 --- a/dashboard/main.go +++ b/dashboard/main.go @@ -69,6 +69,7 @@ func main() { // 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") 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") @@ -118,7 +119,7 @@ func main() { interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM) - app := NewApp(baseURL, *bind) + app := NewApp(htpasswd_file, baseURL, *bind) go app.Start() // Wait shutdown signal diff --git a/go.mod b/go.mod index 02e5d18f..871ad780 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/julienschmidt/httprouter v1.3.0 github.com/studio-b12/gowebdav v0.0.0-20220128162035-c7b1ff8a5e62 github.com/yuin/goldmark v1.4.12 + gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b // indirect golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9 golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401 diff --git a/go.sum b/go.sum index 6e95ccb2..a7c3cd35 100644 --- a/go.sum +++ b/go.sum @@ -209,6 +209,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.12 h1:6hffw6vALvEDqJ19dOJvJKOoAOKe4NDaTqvd2sktGN0= github.com/yuin/goldmark v1.4.12/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +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.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= From 0e6a6893f1e6a41d3ba7d2c0986757dec938d703 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 6 Jun 2022 11:02:35 +0200 Subject: [PATCH 0546/1637] dashboard: Can restrict access by IP --- dashboard/app.go | 53 ++++++++++++++++++++++++++++++++++++++++++++--- dashboard/main.go | 3 ++- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/dashboard/app.go b/dashboard/app.go index 7dda3e8b..fa55b098 100644 --- a/dashboard/app.go +++ b/dashboard/app.go @@ -2,8 +2,12 @@ package main import ( "context" + "encoding/json" + "fmt" "log" "net/http" + "os" + "strings" "time" "github.com/gin-gonic/gin" @@ -13,9 +17,10 @@ type App struct { router *gin.Engine srv *http.Server bind string + ips []string } -func NewApp(htpasswd_file *string, baseURL string, bind string) App { +func NewApp(htpasswd_file *string, restrict_to_ips *string, baseURL string, bind string) App { gin.ForceConsoleColor() router := gin.Default() @@ -34,16 +39,58 @@ func NewApp(htpasswd_file *string, baseURL string, bind string) App { baserouter.Use(Htpassword(*htpasswd_file)) } - declareStaticRoutes(baserouter, baseURL) - app := App{ router: router, bind: bind, } + if restrict_to_ips != nil && len(*restrict_to_ips) > 0 { + var err error + app.ips, err = loadIPs(*restrict_to_ips) + if err != nil { + log.Fatal("Unable to parse ip restricting file: ", err) + } + baserouter.Use(app.Restrict2IPs) + } + + declareStaticRoutes(baserouter, baseURL) + return app } +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)) + } +} + func (app *App) Start() { app.srv = &http.Server{ Addr: app.bind, diff --git a/dashboard/main.go b/dashboard/main.go index 9a245ca5..f33272cb 100644 --- a/dashboard/main.go +++ b/dashboard/main.go @@ -70,6 +70,7 @@ func main() { // 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") @@ -119,7 +120,7 @@ func main() { interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM) - app := NewApp(htpasswd_file, baseURL, *bind) + app := NewApp(htpasswd_file, restrict_ip, baseURL, *bind) go app.Start() // Wait shutdown signal From f2e7b30acea62a91df78481580763a0900a68abd Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 6 Jun 2022 11:26:23 +0200 Subject: [PATCH 0547/1637] dashboard: Autoreload ip restricted list --- dashboard/app.go | 43 +---------------- dashboard/restrict_ip.go | 99 ++++++++++++++++++++++++++++++++++++++++ fickit-backend.yml | 3 +- 3 files changed, 102 insertions(+), 43 deletions(-) create mode 100644 dashboard/restrict_ip.go diff --git a/dashboard/app.go b/dashboard/app.go index fa55b098..af41a65c 100644 --- a/dashboard/app.go +++ b/dashboard/app.go @@ -2,12 +2,8 @@ package main import ( "context" - "encoding/json" - "fmt" "log" "net/http" - "os" - "strings" "time" "github.com/gin-gonic/gin" @@ -45,11 +41,7 @@ func NewApp(htpasswd_file *string, restrict_to_ips *string, baseURL string, bind } if restrict_to_ips != nil && len(*restrict_to_ips) > 0 { - var err error - app.ips, err = loadIPs(*restrict_to_ips) - if err != nil { - log.Fatal("Unable to parse ip restricting file: ", err) - } + app.loadAndWatchIPs(*restrict_to_ips) baserouter.Use(app.Restrict2IPs) } @@ -58,39 +50,6 @@ func NewApp(htpasswd_file *string, restrict_to_ips *string, baseURL string, bind return app } -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)) - } -} - func (app *App) Start() { app.srv = &http.Server{ Addr: app.bind, diff --git a/dashboard/restrict_ip.go b/dashboard/restrict_ip.go new file mode 100644 index 00000000..e2ec172c --- /dev/null +++ b/dashboard/restrict_ip.go @@ -0,0 +1,99 @@ +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/fickit-backend.yml b/fickit-backend.yml index 5aa07b7b..e74b875f 100644 --- a/fickit-backend.yml +++ b/fickit-backend.yml @@ -208,9 +208,10 @@ services: - /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 + - /var/lib/fic/dashboard:/srv/DASHBOARD:ro - /var/lib/fic/files:/srv/FILES:ro - /var/lib/fic/teams:/srv/TEAMS:ro - /var/lib/fic/settings:/srv/SETTINGS:ro From cf7482a14a04a3c0e1fda798df8ece52afd72912 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 6 Jun 2022 11:26:39 +0200 Subject: [PATCH 0548/1637] configs: Update SSH keys --- configs/authorized_keys | 97 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 4 deletions(-) diff --git a/configs/authorized_keys b/configs/authorized_keys index 3f41ee8f..cc7b64ed 100644 --- a/configs/authorized_keys +++ b/configs/authorized_keys @@ -1,5 +1,94 @@ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDO/3qKhSUbGYZBVraFo68oScJahRDNQfG+uwDQlLv7g nemunaire@khonsou -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC19G9HGGZevJKeoDeew2VO6hbpWI71SBqemQUvYnD+WLureahuyCZYeJCalyU9QimsUTyQp6WGqwhh8I9x+K6gss79ITOUVt6yFbmj2Hdm00qJhPElduk+Jg0dTydgeGj83FJExz37dfWTq0Sp2hb4zfxoIH/BT81M+gZpQAQ3f9DIsgHlIE+/x6wLSWOcRVCOeX4YreIVXG75HaKmpY3p2gGuHtmrmuYdgLinihARKih4IvBFOMl0oC5y2AOwZP4AXvU0amc2Pkcn+Q/vzvu0Wf8CVH2ZdywC6HUIJpF41qIDM582ddwgxK1Qj0e7CDfzYRy0ChejfKIIksTDtYB4AkGRWTQ57onxwvDrAVXFq4OP76a9nD+oz7Fowc7VbqP9llJDU8wHWavsAsvbWCcLZ86kOvbbveHgBs23/L4cPwcyA7euta3j8wcW6In+eyMVhy5vs0eQFtzh9tbApFDhCUDdZAdBN5avEpjltizMy0wpEBZynafUO+goholH3t96fdJzeF+RWyzH8po0sNEXQeFJi8gGLgkqdJwWnKnJy1gb4zkemgs8T1B0Uy6wUVFaXNKEAy7gCtIF+dlFxy2pyKar9cITNds2V8cSuw5NM4z66aNB9ONdkU3ZkFQi8nyiJAvVmVhSt9JOhjGHNbGtHeN3Gaz0qW1EtM0GAy8mIw== segharish@gmail.com -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC1TdooqihPGLSGp9LZxU044d+ULhDb0sjoif5DsajyXH7qpVfR8K3HNs3dVcvenyZwA9+tJomEFMFTDDg+03peQ4KYLjpdtRR2wofGCjyNcRcmzcvpi428c4JBOdNxiPkBN/XaiZUJzXZL4fEqm396ZLnyfQ6ns9flKV0zJJt9C98+TXqG01H35tcQamwZMDl1KMrIGCRvywdu+wqB4Z2AkBoI+VH8t2AqXcyKi7ltGZTzbEEItFx4HqlemxgcaM86jho/WlsCvF3AHr9sDJlO93+SuX6gX4sZmRo00KJU9ss/w1FpYjX8crOlr1Ze7YUU/wmTpzzhje50ZNhSRR9R thibaut.passilly@MacBook-Pro.local -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCWrRHAMkZolWFm3jTo0wcBUUJVNKlfO66S2z731nsiQb1r0pQjZDsZjs1YKPp92F2XncQXFUFujOPd4dNT8vkYOKLm6FeDCU8DR9omRwYQq6ZkJ65ySYCLyaKqrH9nKWb2nessAmo1lWzj+K9UmsZeV5tjguxdlpTCod7EHByrRSNTOEURzW96CDsr2IGl2h7XmUDOnMhiTn4y2Qq4sAz0BisP1Y9wBFkLgqYOAVmc+3r41W1w8oCcy2jyGaGp4Bo/mNZdHyozGrEvYXl/uJvOgdTXYufqb4oosoMLrakkEvINybyqspKKX2RNx6HitYdTYNzsZIBHKh70KIDmVvLL ron@CyberWeasley -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCkLmYDbAHx+jCPS4XLQDwlgr1WbZ6oyHBYNOJCm8ektdHPrXUuXwjOAns+eieUdH+AS8/Jrgds31qne5wOHesA8vxilHRsJdJPZgndKntAky7xrfr9W7rYGNTBSMn7tiUBOWEg5uz5px/NTmtIdIhkDabG760yyDUNOpvm62MFNIjqixGpVWSjJvf/buKbCZVTq8Foqs40v8HwMaR4Z76Wvtl982B7BBgg1sf34XiN62Q2ej/Heqp+qxlMolTENQ+ezTZcDu39cBO/AV9FLhQ5Faz0SKPXWLz6nksvUMffWEajPnvJMnw/RjXxpW09ZAJuWoi4Xn1LDTTgToVmUu9B mike@IMiykyRo + +# mahe.charpy +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDHhsVckvOKXdlfeKc1OpqMn/hE/7DoMF5MQagd8UAif9gX1HW/zyIavL5EN/ttspcF620Rn2jUcjNk072wraaGyR3/0DIFFP6MCLM21KHtkCGXKWtgK6qiJuCKuJ2x02Hp78CcxqXKaIn5HrbaxS2AasiOIhRyR5Iy3eObnuEuetYdP6HP4fNDn3KFZkzGse3b0JdsI7n4jVIvb7MHoS0MirNGng7P9s81TwGj6AekEa6nbX/ERY/x0nFmpyNZls6NWolU0Ts4/KZl3qp7Gs/C/ZZxA6j6GDEmvoYXbQWVrmLquBaSIjBRMWgoi/yY0QxfeetWiNgX5jUF8k1xMPn7y0JmZvhW2KjYuDSQBHnQ6DEgFj6GYZDutqKaMXjU1VIEjSG+CavWWfkXWr3PTmHRYGhLA9GLVtTSvL+LoFOvTm0YipgnxfEM3UFlKl9tz2DB/PzyxaxmjABTZ4PXFn+AgtpqFcUlGsI1csoMrxYju9nlt35vjVJBVFxqIbTu3B0= mahe.charpy +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCzeHEVx5IuJ7qFW9XCofj3xGB8LDN9ji56EFmyn9oRnmXgqjELq/0nRuacDJkJ/aqmhbscPuXkJGPKpnpQog3gfwDSDgUxQ2gCWir4soCks75cQ8yxxLyS77e3jE/IrZ3rJ6rsOVboiqV+WD/0gcFCxxiYbtHNnx1gp4x217atRigck25FT2MmJm7xoF/c9fiJV8HsebvJ7nIODuOBEAuY+LwdF80dQCRTLR6razSFqkYyUvggLartHaGg1FT7DQpiJoHaErFbfOYgbR53EJVS/r+pR2JVMyNItLisdpRmTsfBgqLecpwGL1Dz/jPfCbEOnwdRDjEfEY5fYKjNTrMtPIYgw/YjNXaVvwpMzmQfjFEDJzlfahvIYGUFeV5PyI9TNiECxQrsnFYDYz6xZIRvfI8o734DDyCfjKA7G1my0EvIDfSLZFgVEFVkmYiX7ZUylu+W8pEBu1+KKjGjF7YTblqjmc36ryxcfBfQvgNK2E7Q1Mg84LJcHrlnTAAmUMeW438X/Y3KoNsBLlHLfwRrFq5F7VeTqTPqTZbWV82A2sVY7QIxDw30DK9E91CcQtlfmdKfRrSMM9lruLFKRk65o3uBa88dYVtKuXNYzdl2bQbIHEPaWoKqHFN/l3763aAOAYkBAvDK0XazxKwYqJtn8/Q1qRXyO/YPpzb/jFzmQQ== mahe.charpy +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDDMotQvkr7kt4iWCk9T8tBrETzYjcLotZJDkm4EhN4oSMXU3F3cjeb2gJalVzJRfR1B0Sdk8lpNNbQ6w2asTIBDuFuwQTurSyFsLX7mWqvlYukcNZ8vrCAuuIea0Ks4eYZif0N+0QZ0FS+UY+fz8XLdGKabHThyhCEXaN/oTJ3z4Ov9LFs5Sgu5TSEmD2dFWFHQ8kJiz0ZIV/OVVgqR8/4ZKGHvF9OeFVzylkTL1us6C0cRLU27+sZHzbD0dbGG5g45an8YctLDW9+bo8Hl6Het+H2AvRn9ySlU8936IdwmXzJp54Batheh3BaPtcmBYU0RGswHdQabMzEs3kXuCIk6nC6eXl7MhHXpYBOOj6yTHFdWiZ7Q3nkalVXEGG6uZFTT15PcNe4aYtRdBrnFruTow9c0roQ+p61blPvdCf/P4w/UcnGd2kDZT9572aF/PuBXfL1c1Ts+A2/HHGlZ8hgEqaK6fsfSPF71csnt08dSn7W6DU8XXoUfXHUiWlKRE0= mahe.charpy +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC3DaXvd//y5zmiVUMGuITzl9Qt4l1/lEuhmEcSs4zKbGj7y//OZrfcWlVIWmTBkVaIAohuiqA+Y45i3+Fntt7PQKlMt6qTWO5Fsr3VT6qVTC5obL6X8/zXb5SNyPihpmss1Krnp0YmnxzNnN2O+jiMz+mokKYz/IoF2P42MFqrKUGNx9IHnVsRrW9mDlESqxLn41lWCXPAhF3ADwVC4lvj7AYaVnrz+VKDGGQHtUM0rq8N2WMpAxkBibRfhofRv3P2oqdtdWwA9RbRv0/KXdn5wA6hDPZ99MFFTm0qYMRNfH2AQpELPe9u2ESjMTbtFZLUXKQjcSDpi5NMAeRSyPUeukQIOzg+UgX75g+byc9ZjRe47ysInQkTI/CyFzGguRVwGv3bbJectGoAA2mciHSsIqFZ2vwR8KwYjGjFMaUvnR1BXhZaYuL3/YG9i4YPhTF3uAgxDbpNSKNyo15xdVL07YwtWZBXpE1o+sVQ+xjCS1aBa5Ih+k1B1iPXK1isxfU= mahe.charpy +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDCqU1xI9ru+K9pes/su2MjiF7pO/RZDHKgskvpXM6Zy2Utq1ugcfal1pYXYAKTgKxs598JpHYmVSQqMslgIow6uUdSOKVJ0RqrTK9vVXcOVEXT0G8+fJU3iZf/m4uq9WwiAlIk++fpfnSMATxkGw5ETp9V+CmbpI5A8udRYotn1GCYqW+PXBSETncf926nUp0fIpJuI0cE8vHzB8rGjLK6g1AzsIY6pt+FmboocC6qhltMclrKqDzlrWSut+nd5qCxK+HbCZ6gTr9hBUCG6Pt+Woq82yYsZSLlIUbKXoyOgtGh33/5gIwMoT55p1fKp9febJ328sdrPCOQoFQXBLtIzauB/Ley+tqiZNKOr0pWXrsaolQ986MgtfwFq7aZpXd/u1Q7y3je17blqzi9Xti2a0Kkyq6fgMoJXPWycNoOOHme7r4raZ+TptRPZqiSYdoNS66U8ClrED7wmfmKNjWR2kJswF8vo6OPUr3OotJvA5PHrywUFLulPO0828LfLCWTk4KnXgf8nq+WwRfL0gyww+hVS8sQ074o+H8KW5AgruSVB76J8x2H5Fe1yG9KgyP9eBSDdiLHyDDkHejKqcrzWLQOxLkFlvuNuDTP6rvXLMJaZo6by+6Ctt1rysBKzqEmfPnE4oesEeLpNPxiKbR0od4nJUu18oJRtBePXMTcvw== mahe.charpy + +# justin.puchelle +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCfFjH/wKNccA9LSlvRG62oIozgy6ncHa9Z6prMcvoWB0tbrBTIvaxku9IwVcto/EHkOYepb39NG/aAKbz+AAWFbm91rBIksoH+Yy6/nli27DN+4qfYdYjn+e16PnEZsH26ep2T6jgpQ45Lt35YMT+WMehGMejVb8Hy/6xfdFjBwj0BYxU4adtolfL/olVz6X3OxnkyFEY3/YZzgvjj+umfOTMbr0X4H5hxqL9esYuMvHHw00Ma4Fo0vwSN2b13KjzxAv5+v1zLyRVxHXSYs3QvqpCsAWErOLBwUzI2ccXEYw0v2uav0v4t7xbpC/1c8UeyQQ44s0/5oVAAiDlBe6bLngWvrm02WlWDRNK5U4oeXsVeYXjcuRhaaaYT6dLqGCkRni73pvkzI3E1H7KYrkc1wFgfLyuDyiQkcutEky9FRYT2Wo4xT/VPpWaPijj+ibsCByTUGLU/gXfs4e+hF30YnerxmL5XMz0u+h+8fCraDxZ7ZYgzY5hntkHbhEUHCPI8r5YORLQlnzDxGyxHH25cioE0HyKCpJ8Mc+kO0gi5nnHJEt6mQJHoPrg//3R+alRjCzlBWtqmsov3k73mu8cxOMnlvGSlOgT5Mh3OeMMg2wX+O6A/413wd+V7xRviv9tlCu6dx9edushQaRuN8v4fTkzt26SoO0eKOkkJWRMc0Q== justin.puchelle +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDO64lN5B/w5NQdbR/BGO6nm1W46MxmSTfw/PoXs4ppWHHcPz5zuu2Al6nlcSP0oDx9/avsJTVvh8XfNBGyGnqbFkALD2rRoNxSeVFASYv+LXWCkyvL1fWelrbxIezFZvgXNS5VB6h3W873QSPm0D0bDkvGKtUD629V00CJ4ijHvIQ+lQCr6dxsKLUYkpW5L5H6zWvNtDMPB429VCBXsWEmZnCcMXa87nu9KqtjAs0bZWOehCmQahRQA+cqEw2iDL1mgYXv5U5apNHICCqnlCdvg+bGgjFY8LUJIxtmHN3eRbu9N6OAMUJi4wzzQTu2Ll+qzBLX2rJUekZyAbrYQnBH2h8OH+Lw5PVXNSgWhzIzJwuEIgEg/LcSe1r7mvyRbssBchp3/cm/PFUcfX2tbFg8Uu+7n9LFrsGkmX9xsNvy8VIppWOPqfQfm/N4V5s8OX2RJ46UwGACaRaPdCRq+rUH00g8ESxXHlsrMFbRVLz80wihn0pYxaDaN6Nbxw85roqIMUPgGcJX07DKKbW08XTxRFWvtprOSH6efxIjNzlPd4u4a5S0yJvvtGVM6QWv3XK8VDzue2ecEvYSLHvJDiJQyAI2od/G6NiLBi3qq71Kx7ppxBGKOjpK7c+SXFY77KsMnIPNCkur6qgleX8noVqSKrc14U44IjEgztgHM21Gjw== justin.puchelle +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC9eByzqa7E9ccQccEDRMfHLFmDVfMowZkiDo6WYLMpsW7KRblE2ErbwWwF10KPJjyiA6ItZVMudIXlgY7QsJJeqIqi7NHc/67BC2pxZQrIEiTX2hxFZDLbf4R7cJD3+q7J0DHVlM2JolGxnQIolTC1sboXgM/M+cy3kPOASfvcmR9SnTTiLe+csfq/pO0fQVKoo6qPtymeTv7s3UEH1xzZJS05WwHTMMpifcJohMRKCFDMUx+ei2Fr7AXf1ki0t5DZRHmwvmSvAG5zgulnB89AQxnHpfMfW/4KTpKyrPm9YuQxkczFeN9PMqjCmr4KbMErybgKkt8UHfdypHyK+H1t1uPB4VyInPVGsIvzuIIw6SQEaa1PMHb4Tz3sAA6HsyJeVtPviR89BV4IuMU1XTosjeY8/NN5gFJ3mleUuDbxOATwyNbiUsj5qcdS0BA4chlccQ0/kYmi9D2PlZ0lovEQ1rAEFtgs+gR+aPh12P3FCjX9O7jQswIGugybk7RhDIrWvbJ7lPplKTuOVIFwndoKtjMEP4M/3tusv32qjC8qptIjTss3KO7GUAUrPyUTRzYpXGhfNuFrCOdsIdfdhdFNSWkhPYskP+xO++pF0nHajKGl6cWzm5dr4d8cwjav2hQFK+7bbLMX494a+leA2RqAPe2Z194VYJZdC48F2uL2Gw== justin.puchelle +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDWtoILKvpDJwlhB4UrjbG/1WYOGIiOMBJkNe3+VthLqXmoAOTqJTn48BwRGQo+Dy/7ZzEszMbVi7AMCJZ2/CaXdbpxFmBDmBgGmiy1I4I7dmPUhpP1kdTjlzfl5dUm3QVZkv6r+gDfK4rtRyWXGHOyED37+nrROdnWzWoT8mqWtHYkZDPoAeieTcHpq9nNna3EZ7MltnlxXsHBuky/l4n5X6JiC546D2JIPenXwAF4gF0piJ3QaB4v5hRv8rpu3DHvXxUIm7SUIKpQ7kEpJi0Sj7Ifbz2J0ks+ptwSSa3dm963GCxn7OgiT9yBZzunL3aWz8cIs7eJAvynUcYUZ8vylJVZwKrLO+4tBZyWQYv1Pvd83n2zM6o6vkVZa32FS+TmwppK2VYdBhxDc/qx9oUvkA28XgJlUjS/56l8dgzlmpioVfduYU/AEjIgp3LZ99QqXH1rjiko2pHvPcAMet5vBsHyVHqBQ1yjsdVoKM86Y0y522iUbTAkJFGpjwVXjH/JNO94rGLViwrvakdLk/vufCbrz/Cz0As0chZIFYw8O7ckz3/sAMct6kqzboVQdJZnd+y+75kNfjcqGOMQeC3Gapw3F7L4FWciIMkuZcJqyYcgOND8RkWLkacNSE488sktY6JiodtmaPasT7kLjnMGTg9W3V/fBAP9wncz891vfw== justin.puchelle +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDRgs4vfuis8xT0GjBz0Mi69dfYZWp7oq5iAYROWF5RPd5H5gy1V2du8/WBVKqIv85kqGAENR13Q43XS/FJEXhVPP7IMsdTCigjPOFu12ACsHEd06v0VsPzNlF/oyq5m2lC6cLMv9TYKvgy7kYHwpsmjJ9eybcTKnlb4rQoNzvYbk09ERUnlXLayZIeaASJmWW+h6nknw11Rla5ma9LzOdzEl2bxiYJYJNK+esWPB/xxlh9g0Jy6GSR469XdZk3qbLoPlZTxCt9mvRZRYMasDCMbtYHzokq7dhUDltE2w1YNwrdYd1VL7tdJr68q/LCc/9r+tvxb3TiXMsP19FxrB7aBb5roPSVD0TUV99nReLY1vtPDxxlPdbWwKZIGPk5cmjYsJOBRqY2ckOjSfO7mbiE4moviBse5uebTWOq+W67dNX9YhPKhHQ+lEn5drLrYwOTZg2Dqhum1uZ+fH9M17XljXYTZ3WE0rfrsb9wViiT0sqc1w93XFPza72RjEuHw+aNz/qhY/NrBJJZVBujg/sEg00dkbGT+KMaA13/ihlyYma6URi/IAdIQHkuqLk+tY0qvdcR4A9FOBNLNikRNeHgeNQwCovIGKKpIPZWTGxEoHK+UXFDXFdoAxVsJU6jeLd6Oc/NC/IXcgSEwOiD9sN0L82KvjOOgrZ+644bTRz1SQ== justin.puchelle +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDIX0Sq0zcWgTJQRQQkIid320FY/dll/GpkRdjl6iWZOM3E+rwfj6jga81xlcZXNzUOWZzo6Z/lQnJk2HCNb1+iBuvNDf3yGiqsZnglEwj4spTPtWm0MLxxbMLcNR6FW3ckKtdJNuBPzDHMDQSnuGAz84Beos1P9YTRk7W89rZ1dPFkxPDuZ72O8tKRwJt70FeAG0JWSpQBvHMjFce9OuWqFZQneR+z11H4wkxTRrkB7vpmDTU+0pj0r3YR6lp95AJ/NT8B3czBG08gaQJLXYDOx3nM75oQl9/m8eQQ0PAAnGb5RU4i23NJ+WEpxJ4D5L6gVs+uJzMZXRN+8vIAfZgJ justin.puchelle +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDArJs2UNbQyx5N61takqBRgrIBeLzsVHKzHkXXd8+6udAWzw8fo98OAqImTuddfrAH3qpoWNVCQBP9HDvRi8kWaOU/I0VD0VKTEzPBPLQIcMXDWQbfG3YxmEXb2s+XCdLhKSQPeb18Nxza+JAosgbZpytLt0hKNMlHAseNr9g2HuHC9+PQOPgH/j8nDr5qDXx8+Arsvg7E51SJ0BBJirb4MYyzFEEsa40W3MJ36ILib/v1fG9NuJwptjiWKnLYnZG0I8XVRHY60BGo4xxt7kQlfu/dPeKdyh5vBR9w/jsBhabRE7HqST/WUPlt05lK+4929q0t4HwueprjGftCF92hCO45C1DyhP/EFp2MtT5EyyZ4X7zuTX64jlNpKAcXZHjkkxt1hmbNLQyNynKCJHYA8cfowcZ3hS5s+tH0qhKbgkqMdRPu6Er/Y/TnR42Aa96dtskHUwi5tlZjKTPVXn86hGJ8YckTV26TUiJ0Amy1rCxScjCkIcjX3AMNneUKtGs4gjln0f0FIVUs39lrGpIAHKLLNYTbFbcIXtUibI9+Ov1Sm8tdLL4S4Ti2K83MAn01ikCa0yOIenPBDql1FPPegKOpwQEsqhEU40n8Xz1yVCAQexuHgYaAcg18CRL17jP1P15C643MOdb+tpBYPyUA+9iXIDGuw6EJpBuRk7GneQ== justin.puchelle +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDCKQWklZHMqyG1eHkliXUQ4JeU+Qyn+DIXoPV5mn4slLA6yu8HkkoPMSkiDHoF1CLVGlp/HivN7ROLKGYLwjaRTdX9BHCZzhLg9/Xox5jwzRysPSgR80PdMZUFj5k53W6jGqGBK6jbLDUJeMa3yDWt9BTspVxiRhXigH2vEW+C696r5ITIbiEKvNeHnJjcoI1WeFWz0AIMwM7CfvlHvobl6ehLC2Rnwx2qYGArBd46oIofmOyQhG5CspBwWKzUNjwP16QIMuQ81EMVEWhttHn9CmJ3+l/R7KIrWVZL6SRdspFhUY4TUJ4Z4bz887YL6J+PkHqUifNTp1NDWFjdbY/FvLLHc4Sac2uyRA9TCXmJL9/o4zERmKncrN5oQxJqGp6KQO68/hkDf/T9lDNyO1+m4ZFLazgdKo5n+CzZGneIvv6yxvdKi4WTte0tSxgGsTBq1teXmSvl905IAcwRmGYXeMyTDL2OllHg3GnUYGKk0jT1ekSDddlyyD1IUOwZFoLB69+AZQGAuvYzELprVsJtYQbK6SlggeHnOyhrGEQPN9fuCYUw+IKvhLNc+DdyMYKdmVmFitEiArF3yDQmL4NZw8p2flmAwMiQYG/QT/scCWGvbcCzm/t3RSOKUcmK8IF1ytYRmH7Y15+tye3NQtjKP6aQwVgAOmo76HVoCZ4GvQ== justin.puchelle +ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAgEAgDIkGIv8TyOpCt/yWqDBICwE3e4Myiiaa5jrrPK//pQde7AJ18E90zkPrY6DWedIyMlFdmvWLFHw97968qSjWuH95P1b0O5aCssshP7TJyFwPpgrnitYPTxmTxkZ7O5i7vp6jWxXPZJEWH9B1dz+BDSDnLdIoqqjjgabq8NNPXosKfBeD/DWqoxuyBVXCLDeQXc8PyOyTmJNpicWKLfSvCTxGvh0CAhknQYk9O9BLk/I+0AIbP3J2tc4ncfOcYUdG7vxqJyusH1FGlGpNEaYjo2RGSWTv3glBqvE8bhSBeExY4V8IDf5OG5HVN8MaLx27IqmhduFs3fvi0qYO4SQ1bKBHKUdnwhGWU2oVwz0M9wR6LXFgsX7yuZHs6rPwmvcU4BU0sU9B7cKWKf5Gk/Bnf075Fm6pDUsDr2w+Y5dGMbqgGU9soczrJfKn88unLtKLRPFWrzqX3I8jDR62lVMaknImpi4zCx6Whl4NUV04qLkv1dCv6TmfQ/BF3mkQxPwEu5ypRY+jVepGw70Z2gDrwM32gB1ZE6IQ6wq/S6nq1YPoRitowJ4cC3GS3Qllst8Z9j/CIqlSNf20ckucegC0VgMOvr5wMetTWNVRQlTnA6gmfcUzBIzdzO5j4KMYJ8FBty+/5U34YCa30Ek6YbojXW5060WYUNHmYhBAanyyyM= justin.puchelle +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDelsALVLz1DBMGDrRmk1zBtPj8aAw3VcmV+uDnRNww4IF2vT11ZjA+3J25Jejx+0zBAfD+O0KbQNeqUGftx6CinAlxbv/nr7BvFzJmvd3wH3XPKOL9AgUlFpynxg2nlmVtMbl0g8e7CCvuv9kjhk3dwxsoiGxn0WRGqC5artlGw4FAPRKY+eB5V0NIb3ZsB0ewblfSOK7SshoXbzbNk9Wq4PLXNzWECY5ORVh+FhXbrLMQakiwwDQoM2EMsIqCljfKSk8MBp7Hlq+K2NdMVNIZRkdl504awDmIcDoG29LTCn7v0wlMBgPvvLx4NRCS+bD4ql+CCxxAry1ATNEpHO04JIj+J5q6m8+GVRVGVX1v6fnXPPXOcUA8kSdkISK3jSr+i9YAhI5qrAt5OVMRGiI/NWLid6fC5hfCyAoCtp4lB5Q74TDH7L1ADiOCgshkg40oweh7QHLTDzT6qu172OIsE5PpOtMCIHNeXpjLVEzVLfX3GJLerwIvvpgvIqmVEslk6YzxJ0vaqEWPAhIsRpi201uLALq3vlRtM9kNXP12fjT05gGxWC6SxijHLO2mnQnaLk72NwPmCoPKAaYkzauOflCMEW4LA76JBo/IhPiZojySCskD7IfqrcPvlgRewgHo7hQqxczc8auEbNCEErULb8rc0KJtcnfPX9oIfJncbQ== justin.puchelle +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDBTBrLKHs7AfxOXptkEdKGH3ZDpCSWX3AVIgHH46fgHXqFjGeHzcF78iQXxlH4BEXRfX3/5SmLGASo77HmiQneSTjz08qcNwD7t2ENzYIfd5t0ET4ZyfwXekk9L0b8kn9xUK+jCgUxY7W8dJlubJlt1ZAWGgqeJzTMS1UzlRIKf62j18C8T6r/JNsFuzGhTzPJwJ4YGZfHCCicNxx8Vfa5agKSOErSVuxBewUXsotTyJ/kWNriC3iUlRSy5GOtnioeJ4lyHWyIwqMojnFQJ8uo4HYAmN6RqlTInOvEQZD4e6hIvZMGpQTGaEuAVf3TfN5NGukuX+q7mGGr8fRzTL+wGEj2f1YD6EuoBhFmJWhyrRkhxVGq9SVlv2Qef0RwNCOOLFWnKumUVf/NWYQt7Vc85IKDjLGvhMqZ5ZV2S8DLOPfQcxAjR9kAd8f+PRQFmmT0CVF97xFIAdbLtxMobzEX0WZCYPkHfJLD8TsXCF6gKTp4kXElh7RPBi00vGHdLbHM9U1cEEThOQyjqUdh4pkInGBKvTSlLaxzdNrf4OoMYlQadxg8yU0sWL22RjVd+fjJrXt+vg9zz8ST1OwnlVKtLnTZ+h7/Tk2XVTLWYFNF0Q50CtG8pBUIAsf4gfsLlSs27VRXGyc3DaurkiXZnh6SLYs7NE4s2tWfHJiuY7YLdw== justin.puchelle +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDt+2M0slOtHGo9WtuXe71qQSVP/lxZvZsYFtrV2KnzqAE1DAeXcihWW5rBs0pAckMl6+K1xNBWxCp9kTeTHscJW7IjfiAarb9MTV0a1ID8GBl9/MJ5REhAOMU3n9bLqCJw83ZDyH8zaoyFUYp2A1M2BdPM5989/i/wIkgt/OP/1Bf4NnVVLS1ny1o2qFKHaWq8vtfAioS34sbRVGI1Tfq6koCsaPl0C9LjELZ3FMLknYq9ngx+z0z0pOVF5E33hc3MlRhEQRoQrvdY9Q4VsUf/XSFqGU1I/bOywmW3nK3VWI8R8QD3un9JB+VW/4NBbk2VQXDTmMAzQSwdGDQYHzRx4FrwuDcopGdgWES2w7UH9VBhx27OjGLuxBj8WgaLm98umYoq9zgmMia3+ZIIO6141DB5JLYZmMdwA2ZfS7DYbQkKua8htL/+obAfLJi2tJ09xlcjfsOcrflcHS7+hvz+7BPL96ZyEBswX0Gb/pNLncaQICw/gBqHZvO7VqZMSHWH8S4EQ6dNtkovVjaHRLWTySWb4CZxY/bMQNvJeM9sWK0D6gxgn3e/qRpADpeahmgta1kmxTkImjFuGlmBYDpqKNyn50orcdeycMzUt8MzFXH+WiMo/W3CU5xrUZ5Yy7DHbV8kt2/XcgdAXL7kH3cbEm6DLPNO+P229CdBsmgYAw== justin.puchelle +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDOxGQgZMHlBoRFDXUfVhOMUPbPRhN8dD+jW8JkRG2IT/8nqT8jSxqeOqSm99RStIDtnCMB5DehdnyGTwD0uYAUsm1f1VLonbx1em2K6C4B4sQtojGwD0JX/E6CDmMRJI0d9f8xe9jWePwvy4nQePGnvZ71PgIBnbyunwB9wCZo2MexQlhVp/+ftxuCYbwPsrA6uegsC6vCvNq6yQx0Gobft4s8HGLRw2YyJfs4puEaeSq8MAM84NSIf0VyV5rpzDVAHVoXLQ9ui/TamBCqSZ0c6tn+oUhBLG0a1sIaL54esiBwgbcPjUjex/AMqK4Jk0vpGCdwge5YvW0somwwhlOwdO7jHmISQkA+h7iAL0UgjH9GX1mTMpL6prK8p9ubJ5y3puyFMEnShtjxc+lHv0EB6kvMsPVXsAaYDLRBJ+8EXyrWa/O/XuppOASx2EtVYuVclfKhD2++yGeIBImv29b/nsv1ol0pqe06regQoOSqLnr7uOY+kXL7p5SIs5MGRZbj9y9ztmM3DR2mw9LLKL9R0NF5HDLfMmaZ1yR1VYsXlWh+VyydRCoY4jJHQ2OAfaUMfrzUd5l0G76iMEhvS+B9ygihQTsva2iM1vg4ttxv/Mc0C4hxz7V4YdBmcfyQE4BMZ+3hbKVGI+cv8LI2ahXHqcqPHq7PFK8gv4bC9aTA7w== justin.puchelle + +# theodore.decazes +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDPhJVhmf5bDd0XNc2P8sU3dYMHUyUJSSwY0F++U9Ru7Sc7M6iUIc9OSqRn+SWVJZc8t8IB/j6L0pnai4Htm7zvWFBjF49/gf76E9QP7BY1DZPIzyid3CAMtBjR4xqL92ztkbGtBP2cvBlOmcZZdvVfMkG2OiBdtD4DnuMHeBmZQeBYMxG3Op2S/LYCCd+1ICaIZd3BoboOhHb1AQdpF6+lJueBEmjDvVaO2X9RjC2JsLY3ajy6f5Ic2ZAQqc6lQ2Qc5u5MKtwTnRYCdORbiKFxRHlwTBr4xMNV9wAt8ZDmXphAXrRMKCXGgNGob7AJxnVV6WeMiZmXqnT9apfyJPHUEUEoVt/EHbSQRRG1L1MuQfPI8z33QA7FZs6V3E6xKZX3ARgGN65E4Os7ADdRChtB2Bf4Cx63RETrog7mWXer9GJN8fQmSvytozeXlQoj6EgoUIG/x0wO87M7CL1w5S8FJJsP1PPMrCfd8Dzigsl3vJV4PkCGZvQKb2CFzbTsEFPCY+nBXhL3bA6HLJfFBsc/QIBAGkRbig1VAeihgh3oZsIY25hm3VtQsNKWVC+MroY/OKnAetfPlUcSkqqyfJbOuzWkmnhBmK9YHQjBT0cF3sHWPFwkddJOQwFepqIF9p8DiK24RpEwBcc0QMYF+sMFiwjm0pRvZdNA3MgJ7VgnLw== theodore.decazes +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDEFkbXzYBalMeRKC7mCKppdVBJP25Zp1MsqrMMs3glODdT3/N1sijBWLDP9GamoTe+zWmgtuSB3GTgFuQCvxd88goeBgtn70ZsQrk7dgXRs8gHhu92JneE63E4Wmt1tP5+0Ht7npC4y64oaU5r60wx9F9IMUA4aJXNxWr/1tBsxEFHbsDW3h3lTwdlY5xPlCQhMd8yMijrkvcbpwGuUoqFVF49RpDhom7mQ66nF8yIrTzR3zxqLI9uZ6EJl3aSOf/Dr/ASj/+L+d9MdDxjDvQmffXW7utpt1Hl81iZftFEATr9lrFbv6uamoz/P/vPLeXakNzKSIBnehWQ1YRjE80kbZ83XI6bPoF14uZjDUmolrjN2haEcitfANdVeE7Y7GjaIvKY818EWWVOFb9JJtu/h57WGrwyL/ywuGfmfLvCVbfW2RZKobVmxn+KUgvFMauXGu4FOrT58XgT+H1gx6GQxsQatWFJLLUkuUjlXdhXl5RJPQx04WvcDwOGy0C7vyQoNws04kqunOqWhqW47hgYGMApy1I/XyWeTwV7ltsCeIzH0Eg2TbO7b/a2wvWuKQFUPeczP3icQ84RXlO1R8TdXKuE4MU9Ff3tfuYUJbDrIXpGlaa79AA5A7Jg+FAsxvSKMx9w8JJDOEpsE9yWt0WoiLbCdfVNzdlCe9KmtLBNaw== theodore.decazes +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCgE3vbJDdpmHqWL2nZC2QqjsWCBTE0owOAll3qxcdrIJLQvB08jYpoaFM/WxULuGd5OHNyM97jxYrSNuBzUxpKGZuP8Nq66BXuBKDaREx/MFO4BgdQodhw5/RSigBK7YTDtonGbEpXu7Lum9kcdjbkRQGDDDEMVpq26X1I2o/QcNzXi9CE9hAD2hSiKOkvoh3FEyU7mWnV0nnXcSkyiFPSWubhgvKt1MRCUPhTn2173zfOPHL0IEECg2LQhDdGqr0e/KctHNUHMNM1gLYde2DWDB6uQQjOwvjwyVbQbR5uOCC0n3SLOcETWgWCgiTrZMeaFaDnsZIsSZzeXAEv7jWIgtuhbumKs2KEhpmC6TsxaUrB67hURvPNwwp44v6pgXgrrDbqnzRo88/nfF2iuPj9CoWss1Afp5V37FXs47AQp0FfgZRBq+180eYJOaWfqUSLFjnz7ZH0NyCQsP3SKoNZfIdrv2LsSn/41FMhN+6Y1k/IWmfdp/YZv9zodlCJ9mngMUXQZmzzQV3ZFxZikCLwCY2R2i1MUZ7wjVu//rsdQ4zgoMQpC5Ngr0GZkguW18jT87Hdi/nMflV+IICY7Pwrvxd7/llE0t6ii/Wfkb14D3NdGw9utjyfhmK+wdUkC7QTckjMnM0iDIIaKGx/VL62Q8odXwQE0y/DWtPauV3/9w== theodore.decazes +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC2Sc63GCAjbfdLVkDciSKvdsKffSIZI6Svl27XNgIoWsC59M3yh/x7uy6bqv8Krs7dvi+LWYTj4dKvFvL2+iDKHX8APEjWlQbJPghWScD0nIqB1IM4INX8WjcQ2AEiJOAXj58DnFAugwm4O1OTZj82wbmzyuoB6qvR0Psrrz5fWK0xOqgESx/d7+AhPelME/iVNaBWwjj6YAXPNYw0XTATl5w3+ge2QiHpyPADunC1W5u/cxHurs3rxWCYgkcxmFud2yMc6KOGeJyClte+2pOv6YG2BVs1sCsxphshVL6aQjltxAHnZRmOR1AwlhtA3TjBSmJAXKniA4pkIPVf3wgHYKJhqVkxppi/5uQ9SLSd9BfqswXH5/Lv3TUutYoehwNsCw6ML45rI/6JPqo5C0RzEGxj1g3dkbh0hJhi7+/x6qEq9Z98p5vx92Srj3/ibTxh8GBgBWJA3wMPJZtPRv5AA9fs4D4ws4aUEnxHhpUpmjOBbIziAS6bAp/QjFONzEA0buhgA8/XHKGvyFPW18NLqR0U+ByCWv+ZMr1J2mE2jZaHpASYJWQTiMPDU7WKyrvFYzrdVJpCxLbBNksD+RJ/NGTVlpEPZ/KSQyfjJVoZr2mFRP5nWyQq9StJYEP+1HrNJ6iaKKkJ5fQ6+VfUkz9XnyqArNV946q1nyjxECP1OQ== theodore.decazes + +# louis.chevalier +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJV+jL/i6BWsP7WSQ2a2vEl1EKGN40x1293DUTpJpu3t louis.chevalier + +# maxime.chardon +sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIBQsCDcvnzlkzf9YU/hS1l226O7tKZ/Q1DuArsqBCH4PAAAABHNzaDo= maxime.chardon +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDSi0hiioisUm3KPsN9792ertxbE09SBY2qqQm8Hu7VfDbRnDa2IUUMYjg68wh9QBQEJscJg77v8tVeJL9pbWfO/Y5WXNx3an6VnhqnvdV/Nf6p4GOWvbJ6YAHAy7xSP62t1UbWpBTNKHERGRYWn9uZuZGvwm4TG9shG8UbNx0dN8I6i+V0io/uPiGvf6AbABLhtslrjxAZDBTldvTktoUpijSJ4vhWua38M6bITFgpwj5l4EcyF1QRk1+J4wa3yOeavNpT5XNWfy27xUdfOgaNWYrayt37JX+Jk/9IllU2pKtg2o0Tqm2tNApz7RMeVw5G9BFaOc4ZJIhV661cNs/rwi5Ppp2bFcRNo/eRuCzekSfx7XyTkaHSWkPx2a9ex9+uZyNt+vB6RwTMC1dIHx52ascthUo2r7h6cEYwfh8CM4SYmkSRtITaj9hY+VbH37cZpMiB7mgTxjlUqJS0yHFThXpCUZC+AIhly5mPVlyz9P3h6/E3VRUP28ol2FOmlpAJMF37ZjMnnNex8/CzLC0lE0Bfozjchj5gWKHTX36Sdwr30xwcSuwcPdhL5J+NLj2LUHaMUTFHUHuwH5xjobw0J+YTNNwejqELTjlctlQkSghRWFeSTjuMNnA26tYTZ1dv2X9SgC/h56FLHB6Y08NDzpPJHyDrmQIx2QbSfULsMw== maxime.chardon +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICdfLs4x4aU1cPmUeBTzMwjXMiV1WVFaSbzjej5HMRyf maxime.chardon + +# antoine.thouvenin +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJm/IT0Y+veFOu8XpwviWwv7bYxlBPaMqDbiVrhXVyOz antoine.thouvenin +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCrG+mRjeeKSaa0vvUSMOav439SFArJrpQHOFemr7BZaaxwlHhAQrJkErJSFnfExBAwgtI5AhBUyrdJsjaReFQaNIOLmdCGnrW7OS5p5r+t67G158Djrke/XI2dWHw7KphSsMCKa9vvi/k7qW/OteIAgOBexeklW269kd1p/77l0fg2zY+7zfMOy60N6wyGUWwEOIoBCzynIq5wL51WpTqOBzE9iXNy3nyolNcCSBB0l3LJ67IJ5AOWiuKqp8uLUTpXo8mAiFAF3v/uYDiKFQzBMrw6bmuFsngXKuON2Ch1D/G+c8Tiz8u1teZuqZlLN6UEFw5cVFmUyYXVrVoNsKcIMjs/eE4ZJRnXpOEOXd05u4CpyHp4mrO1mcTaOthl2UHz0KpZmLoZWLINPj9SU11+AXqYHKuB2v7+24mHAQaaQX5pttlilAp80J96vkF0rGSissscZdoVcEJ5inIq6P03a+IST9oZtWaITVxPXQF2S3dRF04aM5xeuwBZczWBtxk= antoine.thouvenin +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFQCODahUdZZ4e4f6C3WVJY2bJRQ/Oq4WSwDccT4Gdft antoine.thouvenin +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO8BA6gC2aIHob68DGmcTM6aMYURwuqkoRyHrQ6fJy6Y antoine.thouvenin +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILhLHTjnWWsT8dqUow8NBxRFQSXVk6w57HVGF/1DY6Zp antoine.thouvenin +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB28uLQ2LKQWCan4g4KYtFGdQU8rk3qxxJtFJ+myP3DS antoine.thouvenin +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIACBMfx/8DfOGB+sl81RsR2IP9ky+44JV+kIBDR6HjxF antoine.thouvenin +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE4k5xJ/HKCDBji/B2dM3rVD+H4UzUp9x+yuAtZQMLeF antoine.thouvenin +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJm/IT0Y+veFOu8XpwviWwv7bYxlBPaMqDbiVrhXVyOz antoine.thouvenin +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDd+Iy05scGzL666Nhp5VXUQTMcsEqKURM+F1HSiHu/V6SR91Oyb9VifNVvOuVq37OuANPfkvQYosd6I4u8ovGtR8D0Nnad+JF5nxp62E9QmkgmwGSi7sroNKoKHJvJ0g/BYD8pb5ejEco4TamBOLg8IRdu1RaisXpBSXpBqqW2gGRyyXV2k4ClL3WnTSfIhNB1vHNQJwZRqU3csj1tLotEOSDn8H+i/H7163nWT8Upu3SM/iFbwiWjO7kvw+I2dXfB3WBZcKwFF8nclEgs7NwOiAEHPi3dLmV1qBPZsVs/5FAIM6BT9NFm5spkES9WBPrhd/Vxcg0nBC/bixfcXRZBQ2j/GpMQUvuG8JwpuEuRe+tUcnDXs7C5L9AttXWKfTftS1NCuPWYkoUkYgvV10lkHxQ15pt5/Igcf0QaLO7oowQHOHbjr6Z8KPaU5PiInx4WP4LzlqzVfXoXUEs3us9LGpL0kpa6Frjn+2fEz+tSjQ0Uvc3TaZ1Q25ytK/jXpvE= antoine.thouvenin +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDGwusmjd4MmO3ktPOU1raatCHIXJ+qM+KjbMm9VlkdlP/LJ6XcvqYj/he1qVU1RsK+tcNT6sv5Xhiz4cDV52PKpAm7hr08rHytk7UqQxoQWup3qLvfoL5/sBad/Jb0b7pAvL4YiN/vZgXgmVXPuV+mgNck2kAzU8JdUyJ7P3/NRCPUBK4mXqX3Rx9Q2V4JkU+fuSmyO69yni2YsZfU8GAf8K7GqJE+APDE9BJZqpMooFS9QzTym34AT6WdWEh7Nv8QqnxU2rl14lqysm8aX/0XFoxKnHVOZ4nUbkhma3LyUocvTnxHvATr+t5gPnb0H2aEtWvvoog/D08TAxCVJO35Z1B2kjHluVBkPWAkut4fWwrWvl0K+mu9BuwuR+8UFmrOyAXAKQjIWtP2kBjPV/P8QqMJA6o7wXOdVWoqT8a128WNlzAQpDufMRXI5Ku8FS9Lwf3GeuYCp9dXXdzhlkaBdA3l37FFeFlIvP7As2bj+eY4FcwqybQea8vQvaSF308= antoine.thouvenin +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDSP5A3ksPw+cBeZfSUtcwsLr1AwDcdsYwo6itdvnp4g1pWJrlNvbUt+Pijnw7+e7xikAcAcPIGPLOJH5QplxYmgAnwLtgyc5+IGbiIFVRXB9RNODe/Nw1V7vKBi8DJIme2Mhs3wg7yFuOjUrv8il/IPs6aoi970ruxM50X0c9gE1/fjFXfzGw3Y37GPEKdcXnpWPilKEUg2paCFdXtSyPlzwgUaBk6Pi9S6AcuyphksLji3T8dcMVhUHakvsSJ0i+BzIXgXr+i0BoJGPo0yuLqP9+P72Q97hi9x+yjxVbWMCluu/uB5JJVrKpmdMX/0w3eeN5CiUI3Y9W45OreLDYBQSvWlhZm9/t0+MZrCZeB9jyiFBYNmEObU6e4nm5upMOiVCbI4HBl0hGpogZyWTywUZdtJwM/TsPiq0oxD1AwLSA0KiMj7Dx3qy0wUO2bcM2NdzTRQcJd63vZkoObCn4DQKiIDVm2Iyf4DgMCqANwxF1KIevJxQzgB0BdwMs6fRs= antoine.thouvenin +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDAc0fPXw/arJ5YH1adxBAWhQanFhtTq5E9UX5CIV2d6kBFor+6ugQ1JTmBoxuWMwlDiUk+lrCM5fjg9d16QQYdFH/tD/t2aL9GTqPSk+8Y+Wn4/mARZsOEWEJzqsmZyw3wO9ws7C8pFTRCxJh0o2jaTm6CxHL0YHYuM8UJi7yejhOV5u9KWlvv5DIewulyzYGCr2B6Gj1GQXLQD345ujBp85QsMizOQ/dHv8AzBg+X3mbMIEyuLXB/HUEp10b3HVvLcxSb923Xvnvq1sV5mYhwbEvViXfUlRnVl/OCOsNwoe/UNbndtjsYiKbULJXNkHXGrwOrZ8FV59UO8s18WYtdvWAYUDc1YNPl1pkncNhN3qocI96PDnyOccvGT79JRb+lasWiuYQW//neopt72HPrWNuLhBs8I8IhlN5C3xF8J0nv5bPg4jAYy1ZznmsdZ/vMWFRDc+nN5hozVf7M2zjYDvB4INycJQ20zfrCMWOg1/hRyKNX6lkdyoif76bFQj8= antoine.thouvenin +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILq8xRzCcRLuxtQBAyZCtGgfQoClvf7Cq6XypvfkKkj9 antoine.thouvenin +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDI1qEi1EwGmgrB00oM0C4AqH55hJC1gzFoXXdCv8EVSE0dR5mDqQ42PegmGcBoElwBfI7m47DheXxA34pt1wA7ynEMfxYUBEYTwE1EtfSDv//HfhAeC+P7zh4StucZurHrZShXGcubta81LA46mylmZ2l/xOi4AXXNjsuOoMk7w8/3ZfdsoCYOXrjuHCVw3chP685cYZ4GK/K6hjIubMF7NxIjvnyQIAf0tZsQhgEy+QTylRHGLWWMJdRLt88LWT/Bvc/2YG2cJuWguyoR2+Vo0GMU5MbXPhlnA8kNmkEzve1f15w4/n8/pBfgdngW48feNHgzJxw0V53Ye5P1TQO2qHmwORyG0cVvTqfdpg29Zwp6fMDUUw2ghCeHs1J/FZWALky8muNZEBJVA67dPmIdj/5R2OIzJCqrGzDP1Gkuk66WAWuQOJrffSz8vP+oilaRl/T/BR3W3BTYAHcma4a+bUHA+NKEwRjg9TyXF/Sv3T7oPdFY0d4azqv+1SIDNT0= antoine.thouvenin +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDMGBgDPHC0IExStgkXEOiBRLK0RWt7ydDL9tU0XofLFebtC1ZaRr457iGxrUuedz8NAZPAwAF0TxMgiXTcZPs7xfoUYzNYX7vMdf6dSEYUcmhP1AqMlIuuOjjJLIpG9sNu/BeGLeD93MG2LOPAuH15jcdqqRvoDgILvhROoOVO4hmyJdTyyYvTlHaxtskX4lzC9wrUNePeknPL1KLjd8ZDBO+7qO45OiM/W8vRqBcueEai2yQBAfekg/51VUE22kmnPf1UzB9NRP8No2UQpKPStINOGEzew6v7YXK+f7zwEFqfQFgPlOph4F7rvkIW/m9VUy7NLN77PCoTRj8PZyb9SRHgt6R+Qh9Wch/+UKisvoSngoRgmcKHNpU2ZZUmikIJnmu9Duz/YytCVagQ76F9dJqf4E47X+Ya3RqzY1Eup/Q4iDNZJWM6ul7aC54b1je6cDQSyaZAm82fCG0TIROB176AN8Wb9vf/VHTuQEeargC7eojAHmziBdJ4PJH68/U= antoine.thouvenin +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILLZp1O55CUi0/sRXVMImSHKFv00DQUs7ssd15B4lS0X antoine.thouvenin + +# elie.brami +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC2GgXL8Amob1osINUStN0vLJu9pvfkOo7+hjp5kFAEyjD6OInPD5oLAIylHbxCsH+b9Rma8nJ9LKBOegXsHCmgcZdPo87NU8U6gVeylKbZiq8LLH1QVf4fZDJA3ZBClSucu3r2wOA5vi9xmnIQPV1Mfmh01pLKDm2DYzEa73WzqbtNXcR4Wuh8HRvB30FBHYRg+Oi7A+neWJiacnmc2PjGb/CRzBYhH3MzFZDwPXjwcC/R3y6XUs0GrK7rzXP3H70sMGpWtbCaobHCJZCy68d2dlFJ4DtulDz+UPpDpLjc8pB5e4Q27c4ERsFt8kRJusKgEXFl2NH4ztZm7/R2WvMceUWT95MSAQQk2T/I2eD4QxzClGoWSaJ4J0d8OoDIW+CUdO78ZssP/0maAs3w1ECYRuE4aXgIXGBmvpyVfjX99+sm+5aDlW/r4rVbfmskvA0483UM8Nt79xsL4Aryn/D8kp1dlg6vWpNUrY1L08cEMjMqW1erPDf5eSM64zQAk6e8Uzm79FNzN8uyfLCe0aiuTGimryU87VSP6vFT9qzOIpkFwDmLxBaXBrwVJH7XVbtZ6eC2blNNLLfRCrLwAisi0XF8av92HTtupULfsB6KrqRPcVqQEkLp0f+4VGRVeR/pSJTGN3Y4BVKRdamiHsGg6s/LQWNsuMHF/seBojlMSQ== elie.brami +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDeNhm9Dd6NWJ9nbOictdcta9zTZSWNv2QuJpciFdzpSVfbYDk2ir8GWUe2AEeFRsD6f5lg5/JzUZx1ocuhI4YjRmXCwY/i8Ma8cNHqKTno9QqTnXNCw5RIbrK8lB6Vu8pylS8GeZ78mI2hBFSHMeYM6CwAdUE4H/1ZTQ0U1rYWzoxFfFOAxwVNS4z+mTMqJkWpOJitZo24MVst9or7syGQS+AUsqdyZwgH2RLmOg61A1V1+GQsT6xeMNpD+dRHBFiLw28dWoEY29dhegYNo/0mBzMO4/0YwOcWZYFXogeLOMaJPETFQShTpKKedQ9BdUR/yydgzJLOcHSufEFX3vnXa+D3FJ/o7cvzn7I+WOYT3U0bteaiLQybehf0hbM3tEu6KYHMlFRRJE3YFVRQz9Nb/i/Al2YEhO546Jy+9N//87i2Yotq/RWpoXU9OeoXaRlxje1ZjKlbaKBOW5u42hYgr9vU7k8BT9kg8+b49fFiFLQHx6hW46P89tJWIOC/Ekj/p18nHnFlWRh+8fKUpT4FNJGGMUMMlEDJoYa/9QjPKU9ijSPG8gtUhD4H/oR9qSw1EXZnL8mLvK/ur4mu6VtBbJR7L15IclXlRaXVvCEYXcIU7htJiOqkiOFNEFZ86ymXqirldv9yhoP0qjW1xZZ2SA7nqrFvn5wXVOzNHkizFQ== elie.brami +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDDX1+F8GFoK3TnvUnXdVlg0E5FleFfnBYsUsmVOFmSW2kUqDaal+Mr1YhaBDeRw0QaZLE8ncWaOE1qnBxhdBEzlCNhYfom91M1yomKZwvW06G3I5pzFxXUYSUHYSnr8beNV9pbKnSlx/M0cgeW88DcfR+W2ANU44qIcFiL21WZ1F+4FJqnq2Rqonhe82sIlAwrUJhXH/XmEUpq0pG68DkcmhqE4N0VyqqkZrtosLfZjGiRYrc3h/MGaaV/6QYolsnjxtWrsEM8kSIoJFU/iNm8mQTuaRi7QCJrHiyXpW6dXLVlgGm8Rrjw/SHgxb14CeU4B1jSqlFgLjpkiZOTJGxGfBvyoU/75QJBsoBMwuB0JQdbWnSx8Y2xuDXqd1BTpHV3l+FffjGzwsfLIDX2F08FVraCw3dqjEuIRspOJMAJ+M+A0/8i7xURBM5+UgPRKDKgdKQ7Yf6JUUJlYq9N7Nk1ADRjUK+yNv6devpMHyl2SNHIekhV3Ofk7gBX3pRT3+Iv73ZQbZKhcMV2e6tEhnE52xB6vn8DGxqXpm6xkyX1jYDTnr5z1Hbmi/7NbiIZHcv7p1fsivE9tVgv/uHAneVyDlRSomund3sDJD/5yknlcWTtAMuWSjsPtQ+dxFK5RIra38s+D9tnpuob8hKd/JVaOgETP/AP5Duxf+8RDCd57Q== elie.brami +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPy6vFWLpghtArqSzR4U1cSa0WRGvfJ0ypdeTzPpB5je elie.brami +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAEAQDOJfAjS57t7Rrhq4aNVnSWYrWkMg4rqTyBkZ3MSxPW29rbNR49gTKtdcsaDc+WDpkzPb5bOyL4HnxzpOE+ZqP0AT2DsGJWsvSJSA3EdRa5nJi32ChsXhq1hg174CZlom5ggelKgN2cx6jnoD3bxiEMSjHO72bN6E3wNlWWmXwRwLZVtrzHmG68tUq8p+d45juyc9p1eSFfS9PsEBcuqeMyfVYX6//t4NBEETWX3uQU0wT8gKrglEIPlp8FEj0akIAm8MBX3aC2kD3hZsvdaDfS5RN41+fhnEZwL07zP+MyD3bVGlpQDy7n1vT6Ue0oa9spzjmdImMG3S8kNv3T3M77ATU6XqN9azoiTddnIngcUuZCvlrnNZOye1D+1QJH68bAS4DO1IWyCtfberJJMjDRGROEQEW/Pk4rZEbRSJzg3oKpDB6xEIRRmc3PK/rlZ2N40lG/FF3uQqj8/5f1KzbC/45guSyEqmKEDNzqZsbGH/aPx4mBqt7uo+i9/F78agD/Wq6RTCOqE72qN5MbtX4MR/yHEkOC8acw+Exb7I3tk7m1aolcDLJNwKmThQPPgFDWCKnc0XROOOZyi5CAfOlk5d0JdD+MkqLENTEPkhQga9fXRM68TSLXWwxj6x/nYydIpRn8DuOG59yBAval8LubM9gXibEtPNlB+sfnY3Gza6YXWiac9BhJr1mA7FLbxZPIfzXOK1jRtQg25ErwajG4ogRge/y+lM+FcytVeCfINnziqxP+5v+AFIKyZBU9w1LghyYp+ovZXKBkkRGdvJO0B0A38L7BMped4XFIgOwSEHclBWjSRsoCtYj65JKDdTnSRz+VkmzbehVVmMRmfRe6iZBFt5lwTXim31V8JfGMtq0wbRqBFIp+ZLuR4JPXjTd4F83oAMGVxtmjm3YSnhqwtpARZ4TDGRObkZLYnu81jZRa5Qm1Be9hA0lFb2RpC21OFzcKNPLxcGCA3ig1PfMdm+zvLV8hxDtbGbqM1ILNyIJqdg0niK24UIebJGVSowj5oQO+1q/OwtSuQjCti/FGdcF3iKYFiqjEN1nj71Kdtierhjx0jJtJ5ulfGlkLssSAYj97OzUnpbJBy0VmOvrPwudusCL0oaodD9zY8CuYKf0tN1wswJfs7p3ucduMKEJ9w2tyct7Be98RkzhI3WglgPBWRqKV3yxgHbbaIocYRL+GsvEMI/8qZyteO9Fhgiu/a/YGqxeSJABYdDTl68dgRXoBWnXQxJTlYZvKdz9jbe6+OiFeRZ64uyBfqcqktsWweu5Lnr/PbjpucjXH+DKLinx5TxmqGRZvrNpr7H1jccYpoSQ18dh85dUGxyGvex9SjeF9iiBstxE6EVT8ITAv elie.brami +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMN9Jp263kie6AMQAVzcvu3cLB9Q9nUkiMCnQZYjxAQD elie.brami +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCslb/1NGD8d6eig4m8KGbFgMCfir5YP7Fg9MlIq066WrWsQ6nQ5G/AD6/on+PqKONH7BjWK4Zo7pd6VvEyzFO7PmC82jOd16upLR50yNK+52gI8+LFUuiEomtHodIB6lxeZv3tkqVE1FvtxSYotpFXpWEvdMeK5M6AlkklJ6CzO+hrUW+2I9orCPchM/MqVtT4ddz3cLaHPZ+YmARoJ/ST0+X9jNchEtoG68Bw+JdLheGr2zuGlE9q5nMzwpHl5mBhqFJnga02v+D4/JTEKgu9ReCpBV+gI1l+Zk1depEhnopKvRhL5DHV8jA8u02kNvJQEY8sQqstp1KDc8gkHJGh2HZJIlN1CnmEy/BK4vonLKzaq6TRN34Y6K0eUQsKegx4MMwYKAlYVRGrc+tFIyHT2aJ/+d1qvwNSJ3cKA2i0z8LavwOIPhS9NjqsDwYPtHhKtna59SUdtCw5Ke7EcJ2u0ker79B+zut98Cf8zHlsjOiCwSzrw5kLyfRt4rS6nqFOjH0TLZTBr87IdKMLtZ84UvbXM1tlWYJXOA9361foSCXYZGSMcw2R1jNeD/aiYPvBIj03L1MaZqukN6VmMaVp0Tvkf7Mj3g4jpD/c+acDAUyUIpOYloZLAOO/jrOmQDKW80165tGH+OhapmjYNnOrLKzmsNAAER+OUXsRBJvmzQ== elie.brami +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDgKM5N/yvQh5fALmHHXMKlnyF4LbWxgYlh4HTFPuZ9flEYK9+0XjZjZk1sPB6zrphQn/EKr4conEHnB9P38/+qEkVGQVJsPipAzRU4cvn5/V0M7zt+47AMDWRlp4ULaAZ8NFQqIoC3EIuRR9y60n0/qmUwZT7KXAgtRJeU7RcGid6bVbFseJKFQTrtdwTbhACcc9tIBuZLT97P9ByzbmS8cFAJVx3JwpCWqCpkl2eA4prBQBv+Qsp8CAagwfCRv4wrGs+ihHoqn1YJ/mbVf4M+Tn9KqDAHePfX06vwn3kVhGqR0V3na5+wAFDsktaP0hyyUHphKBMIe4hBqR44kBXFHkhs+y3bGCwq9D2GWzV4qvYGEy9DJhoBRYOCtO6/m8GsHsaqJIIiWfQgORKgEQDCFH9gUAf4c6mkBW210Yde1f7QPri1t8ux5QKXDhMDBwK8dbhHDT4495brd8Q+2TV71Z3S+tQSVYhJihRKObDlyigOeMuIANdp1LA9ZsBNV2E= elie.brami +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHYI+OZIwDiY8dXgjlwB6x91/UcIXTg43s/WxV+2uIUl elie.brami +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC2wMItGI7x/B0bOq5kTbgQhDe45Zl7GCE/2r7cBiUkISDPnRIhc11js+FtAUfigJeF/bagBxcFMJg2NCBIeXjxWpFVRaINxyHrmnlpMLBaurNLjL9CwGXuq9v6a0Y+vduT5y7jMxdlUolN0cGYIpRwL4sdhA+sC7hUNAGGtGWLLBPuVG0c5DAvcJYsJCwxgbrUYfm5m72JS9/zLcO4wJ5kOpCAnek0j0XudZwX3a/vH91bUwq9a9cFmg4M+R8wA3+fsrVL0ai3cJz87sBzSeC9bDoqmP2+Qph1iOeoUZV6pFRbg0vg2tc5eoVieGlvUgXVdBS6xPXaRKbMJRhjxQ1STgbJVQ+ncvKft2SfVeg/K7ATbdKKydyyX3ZgBdcU4xC0Iuejd8GVjML7e9QQJRnMaZcFpGLM9EO+tVNr3rc8M7qGIsSFLhBfc/rV60NwnejJT/juJAGgmo7l8YlUIhTp0Lpp805swzxi5Hg9KYrLlZ3Szt8Q4/Yj4nAyZZ0ZVE8= elie.brami +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9Q5YYEPhBoJ62TOgNi7sK6740Srp3o2zWAsGexzEY+yMxfhhwNA8HTf3iv9iikbxfP2Q9jJMNdsZqdemTjE7STQo/zp3HphCD34cThsfK1DC/lFnEWGQGxdM2mNXPfk5CL4S9xpJImEqMnM2WpXNQqzm5PlypKclaXLIk011o7PHyrMlKFyJ1DfauiiWsL5GrgCXwqWiNCyq8n0GhfCIkWwTpXC2NnTfHbNb0pRGsbNtMi2dCIReN4RWvrrH60fL3FospzMtasSzdO3GVCAF1/gdBEFKdlKDRvTRmZkJzP2hSTP29IDxVu5WbkZrEdSQYtb9c0ecbob5vUBX0BiaT elie.brami +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJaeO6pxXcsKumOSXLTsQpli+VDsx/tpMitQgEZ6zXZ3 elie.brami + +# jean-baptiste.lapeyre +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDK3vYiD1RytllWwBg9R8RsHuTD529i0ndWHuxvVOk28AyRg7dkbApYFA5RTCv8uapDkXTH4HVHmfrfJwaqI2QBnCDegG9yTKB1TUsXNupsTOidB9e1XwZdDxyKh0Cl/LYwRiYhwygLu/Nevk0S+yjKdk24yElJRabKCK2WIdB7TPEU5T10mjcxcO4UFD6fjVJr5n6tJ3U7seoU5zH7RC/uoIWJLLdQLqljPN/DxJaoVXGU2brCfJnscsT2d/rOFTz69XL4n2T7cWReCvwbD3ljupzahBLmAWlIhrlz8qI4qp+BBj3slp3j2d7wbE9jffjNSIIL9jNTAICIj5RZl9R9 jean-baptiste.lapeyre +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAu0kXJKJCtW7OaMlHy8zuly83SevNBGgScWZgBBGfgg1fh9f0HJeu4DnKJ3RQzZ+9ullcudXEO8kZjjLHcjw00jt+k0ZkKO0NPQF6is3l4k4Piu/Z+oiZX5kwi9OGI1j5oxPege33gOLcbx2gpXK86smidZ9sIpm+iZ2x9HCbv8yuuhD+SJIwG6BVjBsdE8MXJTpnGpjKyvac91BcVP5dKWmuta2EskfXs93OeK93Ge5xLRvvbb1Pz5Vn0dONyhXrSYkoCmNPflojeHTfnZhz2+fMejkEPbHAo74tgfcMU7rOwNhIsyph18aSX8w4mzOr1y59rvfOb87n6dgNaebJ jean-baptiste.lapeyre +ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAqF9cHU6O9DAV9OqFKcflNArysGBfTRpZTFwvIh3lzUP2dvjJF1TE/poz242A/JRnxpI0D4Z6BFnSwGWQ7QNG7+J02qA8h4xq/45nYK0Cfknc5+gQiayodbxp1Sr29ZpX9YrdbrEMxs9W0xeEZgiZ2i+Ms0/MIXlguFdb7FwYeWTlizcqZLvfulnhxj/PE3miooYPuze/rgo5UvFqzhskR36FYoA634FUIrRdbSQ1ydyKJ4bftkTcOckEYg+s1rLyW6gn6Ifp85NojvugkeyEAn6kW+M2u4Dw/ejymgm5dkroUwQSZm2pgGRiFkAf4smnaBgKwdzQfYxbZDvW2Bl5Nw== jean-baptiste.lapeyre + +# loic.perry +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAFNdYNu3Jsw+m+p8qQiNIdGDPfx3DVootRSxx9QsmVN loic.perry +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKzCpVs8DmsFx4SkOd9AZMwgYCWZAmEPCuPLH4L1UwCU loic.perry +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFVTElWKzz4iv/bRzZqm32e0y1m9apheWmdHB7d2j1eA loic.perry +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDE0Hr8x8mT/WNw9850H/noXhntfL7aT0dgaINX/S9PSicoYeLRh/94g/Nhq0vIvseHsxcf+JD8OLljA28Y3qjRVw3s8gf/+qnfrH657gvVpe1IgBfS7n/9EzpxwZQhH7By0QwEsQ1M73gReCPJCCHF7eTuUsU/oE5jLTDfUc6BYhWhG2REsxQem5OXoRmgokp3DJHTXriSat21fMapCUdKXfv66D53Su5oKlbRei6E0TT9T5BgCcIVr5Pnm1aekCqjTY4fIuSriiM2zIbtT3hT/yPRPczM2fVfHJw+QNCLIBj11Qv6LqPVSxSKMR/zSAZ12GWiXXXOfmIzp2p8X8TX loic.perry + +# sacha.athias +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDS8gnM7iDSKcoilTSvyBz/LRQOjwZ+8N8/i23CH+A2YPTDWZvTVZoqXHVmg3q/ADpXV0r+uXdwyIb4RPQfpnZT2nAE4J62m/ExFTUdNDyAhh1pprqKtzRzeFTZTfakOs53an7xjeo3D/JdhQbisvX2bEpC0bCu8Vxq5fOfM3rb27BmjHp+Hlu2c0HrW7d0nP8O6ImbDMZVxN+QB9J3/pt2RTqLIxlBge2hQcFXyI/F+3Nv2pOUENNSuznc+I5PJN9OZ6W7oXXHWYHFaS8PFXTxFgEJOye+CfPoByCq1zQZH6uG66ltZt2T2gqp3eWj8uIeDb3u2Pi7GjXKY2Z3PrZs+VQbbgvqp+cOiPJtNdcX74ntPgrNsPAMJO01Hx3xdFknuWm9VMNCJBoq3vQp1aGOGKLusOnNoCpLYRohO89sHvrI8SrNfLlZns2HG3owknx6p7hfaUVDkjbGl1ota2F1zS3nI+rnHKR1zOKcSQ47rAZezGI1lZgBZA1izuA7VwsrfqjWhY1t+xximj/Uox+flXfMrwalrqkiUPiRnAbQ4I1HpRTP6+Nuq4yJ+ljC7qjjBKUdet2njxDGnuLiMdnKvKunkyu3Wk7RwR3fW4mhrB2eQ3UK9RfMJT5J/1tkZanzOdC8aTemPiDL9VfezClzyJhqKIMBQfdn5dETWDrSTw== sacha.athias +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDUW1dKqbUAqxaWW1Ac1LJx4Q2m0xE1rYy+TR9AhitCfDEzbqauFvbLRMb+CCJtktETVakUMO1wfnVD5DM+UI2ojuucGmIAQXRLS777CsZJzYWGTe/vIYxNW8iQRy5yzarKAUjRXEGDHX4qppTAH3/kiRgfzKV9pfe8nTjf29WW5e35D9fbAkp0zrF17yUeBoIVVZtR+B0GJccB0K1T38ufJ1qE2KkmBZXiGjbLRTEM+bsbDrk6IG4DIi4DbUJgIT9NxmgJgGsosqsQij84UFWEsBxM2w/xpbcvHqrNFF3o3Yqs71xtf7VHUC9tXzkdunqrQJUpt5Sl7wq1QvcUA8lp0v7oiSW8NqlJtuuG5XSGiYy7hSk4yu8BiApauk97AGHnoG3bxspU2W227IGyEndAey/oJiIu8iVX6M2HTnvn3klcZci5AizMx1GqBpcyUAIgEdCnFOmtNoZxgo6fdgolbmXQVeUcpMkIze3nNEzxW0UvlWyGKz2YP1j9QsR1ggIFsDqbTHnDTj+Rb7FGwmpa9zNfmeQbt99nF1LybAHHJHPaQc3VW/vikHjLwczRzp3cGRzrrfur9g/31rHsSEMJQhNNkMkOqljhIfm6kGvO/zd99l5Q9K1p0TODeuyH5VXLAb7SnuVuZG0TJRyhqkbtI7KppeP7k2uYA9HoT1YAkw== sacha.athias +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBnCcYuKlRrDCfgFztujj4gevWOPhKX/Ae60RaqRoQgP sacha.athias +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFlMYXGCEq2fPZQGAkqHpsxV3T8x8b5UcGLrTtj9YelG sacha.athias +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEOePEqER04JBpXpqLIratJndZUe6m/7swbgip7cSzEm sacha.athias +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILU4KuumZO5Zp0pcGa/pkOXD3sFkJoc+AquJd7rADrGO sacha.athias +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPXxlSLAZo4PBSVLGsn3VRTPNRhOf2bqhDsE8mq4W3Fo sacha.athias +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDSE959b9EaYTWxPBHpoOfi8iUfX6Zf+w/bR1fznuu3D6JVP6q5PGpszmYBiuK54JYwhei9biuf7BYGnghizpQta4PzdWu26RFfLZBfLZUxago7crNn3S3tHgxsf0xitt18nJ+O2qZUWK6e7MxgXQEhAQ84vG1RATaPSLkS2BqyJDF2DFtNiDduonOX9XqMlpJ2taP67gpAgccaoFJekZdflx06ho5U551h0dMaSDprqNcl9JSeON4TECIQWmgMfMJuN6xjzbrzftidrzyHRC1V4xPvnC0njZPl/5CNmV1AA+PZoZP2JLz4xcX4yq4cvFR73dV2t5spwgVwgBTAoZ3o5IoLP1Hz8XfiIHxxunOXXtQDTOztiuh/9CoVZCEmHH8LFehvctY1K4GXIl5zmoQgQbnJwn3obPYEFbxM42xH1EmKwvwAxDIrClzXO0VSkr7UVt0oY2yfEVICVWDNFwJ//EH2qAkSGfZfW6R8TZhsnen9WXuhiNRsotrQ690QqDe9IRNgZiQhsmxBSGTHe/0hm9U2UvRvKIgNK0TAU5X0i+UrrqJI5U1smUR3t9WoRQ048PSJ7rNIrCgafshnOIsCPGYLVr4Xj5vpTZit1Up8gQXsvtRymput6ayQk+sKU6zT7WddU18jmyppYMc2CbnctNEYv7DpLYZ++EooQbLQhw== sacha.athias +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCk12t90HxLrvgPJ15umRa8nkdooiwp0YN8iyYpkhqnBJoWOiLLrFZzPTxHfM+L5PlZMj13HvPn1bEOBNAaH1DRKkICCTgMo3T5qJhO/JL/CRL0PJYA8jG1OSqN3EJWAlbdvR4gwxHjrLO59797YZJk9rv04/0zGduCfMy3cV9kQEDgDbrtOwv4EDQWj1mMUeB6wd67DMN66gjkQ3D9gl1Nmi0jMijkwToPEO8dxIhy4pUtmnFaAZGhVgZXsbQpiz5e32FbDjUNkOs7YlV8gDhuEefQGAwAyP3IZjDXG32PY/ruaYuKd6JtwGp90SMNL+e087J2S8ayBgKRPD3XmB2p sacha.athias +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBVOaQsx53Qzry2gfVu8i/BVHJymUgYuyzBs7U23eZdzj19W1HE93+TaO46SAka0vfSa/RBRfhylqS3EL0gxlMNzqVX9X8DVFnDDoleT3O2eiEPplIqQpccmyY3piJGt7Y7SmSxOxS3NY/M01nI9zI/ODu+V8HKmdakDOd6df7c8FCLFduMt/+qOBPRJBYWXulZ/vJwdwklhUnoIzzTLkkCS9Pq5L/ZpUKSlLvjcLnxwJ1tsr+eXr0Dh3NoRXKDI/7Xgu+zia/sjqTELCGYLhBWEQzaKxRa/7bGiBCz9w5CtUB6cDrsCt10UCkJvsrqXnjmUlgZIuiERrYOEkic7B9 sacha.athias +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPyqNQ/gXb7M6KbONUtybTLd5IiwNwVa9Nu+KrEvASrA sacha.athias From cf502bd9d5523e676584130667c4ed5e4801c4cd Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 6 Jun 2022 11:27:24 +0200 Subject: [PATCH 0549/1637] fickit: Allow connections to admin only from local (through ssh) --- fickit-backend.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fickit-backend.yml b/fickit-backend.yml index e74b875f..d33d5111 100644 --- a/fickit-backend.yml +++ b/fickit-backend.yml @@ -162,7 +162,7 @@ services: - /var/lib/fic/backups - name: fic-admin image: nemunaire/fic-admin:latest - command: ["/srv/admin", "-4real", "-bind=:8081", "-baseurl=/admin/", "-localimport=/mnt/fic", "-timestampCheck=/srv/submissions"] + command: ["/srv/admin", "-4real", "-bind=127.0.0.1:8081", "-baseurl=/admin/", "-localimport=/mnt/fic", "-timestampCheck=/srv/submissions"] env: - MYSQL_HOST=db - FICCA_PASS=jee8AhloAith1aesCeQu5ahgIegaeM4K @@ -360,7 +360,6 @@ files: [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 8081 -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 From bfdb1c2bf74caf31148de25b6c6c382bc2f21bb6 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 6 Jun 2022 12:55:39 +0200 Subject: [PATCH 0550/1637] Introduce remote-challenge-sync-airbus --- .drone.yml | 30 ++--- Dockerfile-remote-challenge-sync-airbus | 24 ++++ fickit-frontend.yml | 11 +- remote/challenge-sync-airbus/.gitignore | 1 + remote/challenge-sync-airbus/api.go | 166 ++++++++++++++++++++++++ remote/challenge-sync-airbus/main.go | 98 ++++++++++++++ 6 files changed, 309 insertions(+), 21 deletions(-) create mode 100644 Dockerfile-remote-challenge-sync-airbus create mode 100644 remote/challenge-sync-airbus/.gitignore create mode 100644 remote/challenge-sync-airbus/api.go create mode 100644 remote/challenge-sync-airbus/main.go diff --git a/.drone.yml b/.drone.yml index 66191483..d1c64991 100644 --- a/.drone.yml +++ b/.drone.yml @@ -256,6 +256,21 @@ steps: 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 + trigger: event: - cron @@ -557,21 +572,6 @@ steps: password: from_secret: docker_password - - 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 - trigger: event: - push diff --git a/Dockerfile-remote-challenge-sync-airbus b/Dockerfile-remote-challenge-sync-airbus new file mode 100644 index 00000000..aef39743 --- /dev/null +++ b/Dockerfile-remote-challenge-sync-airbus @@ -0,0 +1,24 @@ +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.16 + +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/fickit-frontend.yml b/fickit-frontend.yml index f30d3359..efc394b1 100644 --- a/fickit-frontend.yml +++ b/fickit-frontend.yml @@ -192,13 +192,12 @@ services: - /var/lib/fic/startingblock - /var/lib/fic/submissions - /var/lib/fic/teams - - name: fic-remote-scores-sync-zqds - image: nemunaire/fic-remote-scores-sync-zqds:latest + - name: fic-remote-challenge-sync-airbus + image: nemunaire/fic-remote-challenge-sync-airbus:latest env: - - ZQDS_EVENTID=6109ae5acbb7b36b789c9330 - - ZQDS_ROUNDID=612d3a5179fe4f747ea89274 - - ZQDS_CLIENTID= - - ZQDS_CLIENTSECRET= + - AIRBUS_BASEURL=https://.... + - AIRBUS_TOKEN=abcdef0123456789abcdef0123456789 + - AIRBUS_SESSIONID=42 binds: - /etc/hosts:/etc/hosts:ro - /var/lib/fic/teams:/srv/TEAMS:ro diff --git a/remote/challenge-sync-airbus/.gitignore b/remote/challenge-sync-airbus/.gitignore new file mode 100644 index 00000000..d46d7700 --- /dev/null +++ b/remote/challenge-sync-airbus/.gitignore @@ -0,0 +1 @@ +challenge-sync-airbus \ No newline at end of file diff --git a/remote/challenge-sync-airbus/api.go b/remote/challenge-sync-airbus/api.go new file mode 100644 index 00000000..9b01cfe5 --- /dev/null +++ b/remote/challenge-sync-airbus/api.go @@ -0,0 +1,166 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "strconv" +) + +type AirbusAPI struct { + BaseURL string + Token string + SessionID uint64 +} + +func (a *AirbusAPI) request(method, endpoint string, data []byte, out interface{}) error { + var reader *bytes.Reader + if data != nil { + reader = bytes.NewReader(data) + } + req, err := http.NewRequest(method, a.BaseURL+endpoint, reader) + if err != nil { + return fmt.Errorf("unable to prepare request to %q: %w", endpoint, err) + } + + req.Header.Add("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return fmt.Errorf("error during request execution to %q: %w", endpoint, err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + if out != nil { + jdec := json.NewDecoder(resp.Body) + + if err := jdec.Decode(out); err != nil { + return fmt.Errorf("an error occurs when trying to decode response: %w", err) + } + } + } else if all, err := io.ReadAll(resp.Body); err != nil { + return fmt.Errorf("error returned by the API + error on decoding: %d // %w", resp.StatusCode, err) + } else { + return fmt.Errorf("error returned by the API: %d -> %s", resp.StatusCode, all) + } + + return nil +} + +type AirbusUserId int64 + +func (aui AirbusUserId) String() string { + return strconv.FormatInt(int64(aui), 10) +} + +type AirbusUser struct { + Id AirbusUserId `json:"id"` + Name string `json:"name"` +} + +func (a *AirbusAPI) GetUsers() (users []AirbusUser, err error) { + err = a.request("GET", fmt.Sprintf("/sessions/%d/users", a.SessionID), nil, &users) + return +} + +func (a *AirbusAPI) GetUserFromName(name string) (*AirbusUser, error) { + users, err := a.GetUsers() + if err != nil { + return nil, fmt.Errorf("unable to retrieve users list: %w", err) + } + + for _, u := range users { + if u.Name == name { + return &u, nil + } + } + + return nil, fmt.Errorf("unable to find user %q", name) +} + +type AirbusChallengeId int64 + +func (aci AirbusChallengeId) String() string { + return strconv.FormatInt(int64(aci), 10) +} + +type AirbusChallenge struct { + Id AirbusChallengeId `json:"id"` + Name string `json:"name"` +} + +func (a *AirbusAPI) GetChallenges() (challenges []AirbusChallenge, err error) { + err = a.request("GET", fmt.Sprintf("/sessions/%d/challenges", a.SessionID), nil, &challenges) + return +} + +func (a *AirbusAPI) GetChallengeFromName(name string) (*AirbusChallenge, error) { + challenges, err := a.GetChallenges() + if err != nil { + return nil, fmt.Errorf("unable to retrieve challenges list: %w", err) + } + + for _, c := range challenges { + if c.Name == name { + return &c, nil + } + } + + return nil, fmt.Errorf("unable to find challenge %q", name) +} + +func (a *AirbusAPI) ValidateChallengeFromUser(user *AirbusUser, challenge *AirbusChallenge) (err error) { + err = a.request("GET", fmt.Sprintf("/sessions/%d/%s/%s/validate", a.SessionID, challenge.Id.String(), user.Id.String()), nil, nil) + return +} + +type AirbusUserAwards struct { + UserId AirbusUserId `json:"gaming_user_id"` + Message string `json:"name"` + Value int64 `json:"value"` +} + +func (a *AirbusAPI) AwardUser(user *AirbusUser, value int64, message string) (err error) { + awards := AirbusUserAwards{ + UserId: user.Id, + Message: message, + Value: value, + } + + j, err := json.Marshal(awards) + if err != nil { + return fmt.Errorf("unable to marshall JSON from awards struct: %w", err) + } + + err = a.request("POST", fmt.Sprintf("/sessions/%d/awards", a.SessionID), j, nil) + return +} + +type AirbusStats struct { + Data AirbusStatsData `json:"data"` +} + +type AirbusStatsData struct { + BySessions []AirbusStatsSession `json:"by_sessions"` +} + +type AirbusStatsSession struct { + UUID AirbusUUID `json:"uuid"` + Name string `json:"name"` + Duration string `json:"duration"` + TeamStats []AirbusTeamStats `json:"team_stats"` +} + +type AirbusTeamStats struct { + UUID AirbusUUID `json:"uuid"` + Name string `json:"name"` + Score int64 `json:"score"` +} + +func (a *AirbusAPI) GetCurrentStats() (stats AirbusStats, err error) { + err = a.request("GET", "/stats", nil, &stats) + return +} diff --git a/remote/challenge-sync-airbus/main.go b/remote/challenge-sync-airbus/main.go new file mode 100644 index 00000000..3c873c10 --- /dev/null +++ b/remote/challenge-sync-airbus/main.go @@ -0,0 +1,98 @@ +package main + +import ( + "flag" + "log" + "os" + "os/signal" + "path" + "strconv" + "syscall" + + "gopkg.in/fsnotify.v1" +) + +var ( + TeamsDir string + skipInitialSync bool +) + +func main() { + flag.StringVar(&TeamsDir, "teams", "./TEAMS", "Base directory where save teams JSON files") + var debugINotify = flag.Bool("debuginotify", false, "Show skipped inotofy events") + flag.BoolVar(&skipInitialSync, "skipinitialsync", skipInitialSync, "Skip the initial synchronization") + flag.Parse() + + api := AirbusAPI{ + BaseURL: "https://portal.european-cybercup.lan/api/v1", + } + + if v, exists := os.LookupEnv("AIRBUS_BASEURL"); exists { + api.BaseURL = v + } + if v, exists := os.LookupEnv("AIRBUS_TOKEN"); exists { + api.Token = v + } + if v, exists := os.LookupEnv("AIRBUS_SESSIONID"); exists { + var err error + api.SessionID, err = strconv.ParseUint(v, 10, 64) + if err != nil { + log.Fatal("AIRBUS_SESSIONID is invalid: ", err.Error()) + } + } + + log.SetPrefix("[challenge-sync-airbus] ") + + TeamsDir = path.Clean(TeamsDir) + + log.Println("Registering directory events...") + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Fatal(err) + } + defer watcher.Close() + + if err := watcher.Add(TeamsDir); err != nil { + log.Fatal(err) + } + + if !skipInitialSync { + if _, err := os.Stat(path.Join(TeamsDir, "teams.json")); err == nil { + treatAll(path.Join(TeamsDir, "teams.json")) + } + } + + // Register SIGUSR1, SIGUSR2 + interrupt := make(chan os.Signal, 1) + signal.Notify(interrupt, syscall.SIGHUP) + + watchedNotify := fsnotify.Create + + for { + select { + case <-interrupt: + log.Println("SIGHUP received, resyncing all teams' score...") + treatAll(path.Join(TeamsDir, "teams.json")) + log.Println("SIGHUP treated.") + case ev := <-watcher.Events: + if path.Base(ev.Name) == "teams.json" { + if ev.Op&watchedNotify == watchedNotify { + if *debugINotify { + log.Println("Treating event:", ev, "for", ev.Name) + } + go treatDiff(ev.Name) + } else if 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 treatDiff(ev.Name) + } else if *debugINotify { + log.Println("Skipped teams.json event:", ev) + } + } else if *debugINotify { + log.Println("Skipped NON teams.json event:", ev, "for", ev.Name) + } + case err := <-watcher.Errors: + log.Println("error:", err) + } + } +} From 46d1bb21f71de7446aadb5fcf25543e7c7e9b49a Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 6 Jun 2022 13:01:09 +0200 Subject: [PATCH 0551/1637] backend: Also generate scores.json for each team --- backend/generation.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/backend/generation.go b/backend/generation.go index da20019a..22b3eca6 100644 --- a/backend/generation.go +++ b/backend/generation.go @@ -116,7 +116,7 @@ func genTeamIssuesFile(team *fic.Team) error { return nil } -// Generate my.json and wait.json for a given team +// Generate my.json, wait.json and scores.json for a given team func genTeamMyFile(team *fic.Team) error { dirPath := path.Join(TeamsDir, fmt.Sprintf("%d", team.Id)) @@ -143,6 +143,14 @@ func genTeamMyFile(team *fic.Team) error { } 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 From 39acdee6b25a5b95ca93fec2151577e52c4651c3 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 6 Jun 2022 14:40:18 +0200 Subject: [PATCH 0552/1637] ui: Display score grid in team page --- configs/nginx/base/demo.conf | 7 ++ configs/nginx/base/docker.conf | 7 ++ configs/nginx/base/prod.conf | 7 ++ frontend/ui/src/components/ScoreGrid.svelte | 84 +++++++++++++++++++++ frontend/ui/src/routes/edit.svelte | 9 +++ 5 files changed, 114 insertions(+) create mode 100644 frontend/ui/src/components/ScoreGrid.svelte diff --git a/configs/nginx/base/demo.conf b/configs/nginx/base/demo.conf index d2234498..448af78e 100644 --- a/configs/nginx/base/demo.conf +++ b/configs/nginx/base/demo.conf @@ -150,6 +150,13 @@ server { 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; diff --git a/configs/nginx/base/docker.conf b/configs/nginx/base/docker.conf index 416147cb..a9f004bd 100644 --- a/configs/nginx/base/docker.conf +++ b/configs/nginx/base/docker.conf @@ -136,6 +136,13 @@ server { 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; diff --git a/configs/nginx/base/prod.conf b/configs/nginx/base/prod.conf index 59bd79e6..bfecd55e 100644 --- a/configs/nginx/base/prod.conf +++ b/configs/nginx/base/prod.conf @@ -140,6 +140,13 @@ server { 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; diff --git a/frontend/ui/src/components/ScoreGrid.svelte b/frontend/ui/src/components/ScoreGrid.svelte new file mode 100644 index 00000000..828173f1 --- /dev/null +++ b/frontend/ui/src/components/ScoreGrid.svelte @@ -0,0 +1,84 @@ + + +{#await req} + + Veuillez patienter &hellips; + +{:then scores} + {#if scores} + + + + + + {#if row.reason == "Validation"} + + Étape validée + {: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} + + {row.reason} + {/if} + {#if row.id_exercice && $my.exercices[row.id_exercice]} + sur + {$themes[$my.exercices[row.id_exercice].theme_id].exercices[row.id_exercice].title} + + {/if} + + + {Math.trunc(10*row.points)/10} × {row.coeff} + + + {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/ui/src/routes/edit.svelte b/frontend/ui/src/routes/edit.svelte index 0deccc73..71d9ca83 100644 --- a/frontend/ui/src/routes/edit.svelte +++ b/frontend/ui/src/routes/edit.svelte @@ -13,12 +13,14 @@ Alert, Badge, Card, + CardHeader, Col, Container, Icon, Row, } from 'sveltestrap'; + import ScoreGrid from '../components/ScoreGrid.svelte'; import TeamChangeName from '../components/TeamChangeName.svelte'; import TeamMembers from '../components/TeamMembers.svelte'; @@ -45,6 +47,13 @@ {/if} + + + + Détail du score + + + From f4188ec289956e2786763cfce6e1d7c8c687c79b Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 6 Jun 2022 15:13:33 +0200 Subject: [PATCH 0553/1637] fix typo Thanks-to: Elie Brami --- entrypoint-frontend.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/entrypoint-frontend.sh b/entrypoint-frontend.sh index 37aaba48..9de9f175 100755 --- a/entrypoint-frontend.sh +++ b/entrypoint-frontend.sh @@ -22,7 +22,7 @@ run() { fi } -[ "${CURRENT_BASE}" != "${BASEURL}"] && { +[ "${CURRENT_BASE}" != "${BASEURL}" ] && { run "${BASEURL}" /srv/htdocs-frontend echo "${BASEURL}" > /chbase-done } From 11a12e1d44ebb7c0e87edef8e9a14636a942bba1 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 6 Jun 2022 20:42:46 +0200 Subject: [PATCH 0554/1637] Import logos from challenge.json --- admin/api/settings.go | 30 +++++++++------ admin/sync/challengeinfo.go | 38 +++++++++++++++++++ admin/sync/file.go | 34 ++++++++++------- frontend/ui/src/components/Header.svelte | 2 +- .../ui/src/components/HeaderPartners.svelte | 4 +- 5 files changed, 81 insertions(+), 27 deletions(-) create mode 100644 admin/sync/challengeinfo.go diff --git a/admin/api/settings.go b/admin/api/settings.go index ce2ab503..0a182c73 100644 --- a/admin/api/settings.go +++ b/admin/api/settings.go @@ -118,7 +118,6 @@ func GetChallengeInfo() (*settings.ChallengeInfo, error) { 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) } @@ -132,17 +131,26 @@ func saveChallengeInfo(c *gin.Context) { return } - jenc, err := json.Marshal(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()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to save challenge info: %s", err.Error())}) - return + err = sync.WriteFileContent(sync.GlobalImporter, "challenge.json", jenc) + if err != nil { + log.Println("Unable to SaveChallengeInfo:", err.Error()) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to save challenge info: %s", err.Error())}) + return + } + + err = sync.ImportChallengeInfo(info) + 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 { diff --git a/admin/sync/challengeinfo.go b/admin/sync/challengeinfo.go new file mode 100644 index 00000000..436756e8 --- /dev/null +++ b/admin/sync/challengeinfo.go @@ -0,0 +1,38 @@ +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) (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.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/file.go b/admin/sync/file.go index b8c60d1c..6f067170 100644 --- a/admin/sync/file.go +++ b/admin/sync/file.go @@ -165,6 +165,26 @@ func getDestinationFilePath(URI string) string { return path.Join(fic.FilesDir, strings.ToLower(base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(hash[:])), path.Base(URI)) } +func importFile(i Importer, URI string, dest string) error { + if err := os.MkdirAll(path.Dir(dest), 0755); err != nil { + return err + } + + // Write file + if fdto, err := os.Create(dest); err != nil { + return err + } else { + defer fdto.Close() + writer := bufio.NewWriter(fdto) + if err := GetFile(i, URI, writer); err != nil { + os.Remove(dest) + return err + } + } + + return nil +} + // 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) { @@ -177,22 +197,10 @@ func ImportFile(i Importer, URI string, next func(string, string) (interface{}, } } - if err := os.MkdirAll(path.Dir(dest), 0755); err != nil { + if err := importFile(i, URI, dest); err != nil { return nil, err } - // Write file - if fdto, err := os.Create(dest); err != nil { - return nil, err - } else { - defer fdto.Close() - writer := bufio.NewWriter(fdto) - if err := GetFile(i, URI, writer); err != nil { - os.Remove(dest) - return nil, err - } - } - return next(dest, URI) } diff --git a/frontend/ui/src/components/Header.svelte b/frontend/ui/src/components/Header.svelte index 081bc70f..16056708 100644 --- a/frontend/ui/src/components/Header.svelte +++ b/frontend/ui/src/components/Header.svelte @@ -45,7 +45,7 @@ {#if $challengeInfo && $challengeInfo.main_logo} {#each $challengeInfo.main_logo as logo, i} - {'Logo 0?' d-none d-md-inline ms-2':'')}> + {'Logo 0?' d-none d-md-inline ms-2':'')}> {/each} {/if} diff --git a/frontend/ui/src/components/HeaderPartners.svelte b/frontend/ui/src/components/HeaderPartners.svelte index b1c6749f..ab4bfd7f 100644 --- a/frontend/ui/src/components/HeaderPartners.svelte +++ b/frontend/ui/src/components/HeaderPartners.svelte @@ -16,10 +16,10 @@ {#if partner.href} - {partner.alt} + {partner.alt} {:else} - {partner.alt} + {partner.alt} {/if} {/each} From 1591ec4376cdaf0aa9711e74a1b040b70591078e Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 6 Jun 2022 21:25:40 +0200 Subject: [PATCH 0555/1637] CI: Remove remote-challenge-sync-airbus temporaly --- .drone.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.drone.yml b/.drone.yml index d1c64991..83007dc6 100644 --- a/.drone.yml +++ b/.drone.yml @@ -256,20 +256,20 @@ steps: 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 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 trigger: event: From 83a579fbd20531fe5c78c5aacb6c07a2488b388e Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 6 Jun 2022 23:00:03 +0200 Subject: [PATCH 0556/1637] admin: Don't fail if importer is not writable --- admin/api/settings.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/admin/api/settings.go b/admin/api/settings.go index 0a182c73..ae561157 100644 --- a/admin/api/settings.go +++ b/admin/api/settings.go @@ -141,8 +141,7 @@ func saveChallengeInfo(c *gin.Context) { err = sync.WriteFileContent(sync.GlobalImporter, "challenge.json", jenc) if err != nil { log.Println("Unable to SaveChallengeInfo:", err.Error()) - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to save challenge info: %s", err.Error())}) - return + // Ignore the error, try to continue } err = sync.ImportChallengeInfo(info) From 9a2fd85d573977ec1b92c99a27c0ba68203a5319 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 7 Jun 2022 00:54:20 +0200 Subject: [PATCH 0557/1637] sync: Unneeded log --- admin/sync/importer_localfs.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/admin/sync/importer_localfs.go b/admin/sync/importer_localfs.go index a960bce3..41753d67 100644 --- a/admin/sync/importer_localfs.go +++ b/admin/sync/importer_localfs.go @@ -5,7 +5,6 @@ import ( "fmt" "io" "io/ioutil" - "log" "os" "path" ) @@ -55,7 +54,6 @@ func (i LocalImporter) exists(filename string) bool { } func (i LocalImporter) toURL(filename string) string { - log.Println(i.Base, filename, path.Join(i.Base, filename)) return path.Join(i.Base, filename) } From ba096c0af10e75d3522b7d2f723b638c422349d1 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 7 Jun 2022 12:37:35 +0200 Subject: [PATCH 0558/1637] admin: Able to reset issues, QA and events --- admin/api/settings.go | 2 ++ admin/static/views/settings.html | 1 + libfic/reset.go | 24 ++++++++++++++++++++---- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/admin/api/settings.go b/admin/api/settings.go index ae561157..7bb907ee 100644 --- a/admin/api/settings.go +++ b/admin/api/settings.go @@ -373,6 +373,8 @@ func reset(c *gin.Context) { err = fic.ResetExercices() case "game": err = fic.ResetGame() + case "annexes": + err = fic.ResetAnnexes() case "settings": err = ResetSettings() case "challengeInfo": diff --git a/admin/static/views/settings.html b/admin/static/views/settings.html index f288ee1e..060a8ca3 100644 --- a/admin/static/views/settings.html +++ b/admin/static/views/settings.html @@ -367,6 +367,7 @@ +
diff --git a/libfic/reset.go b/libfic/reset.go index 0b7b2d6f..74e3f996 100644 --- a/libfic/reset.go +++ b/libfic/reset.go @@ -5,7 +5,7 @@ import ( ) // truncateTable performs an insecure wipe on the given tables. -func truncateTable(tables ...string) (error) { +func truncateTable(tables ...string) error { if tx, err := db.BeginTx(context.TODO(), nil); err != nil { return err } else { @@ -27,8 +27,20 @@ func truncateTable(tables ...string) (error) { return nil } +// ResetAnnexes resets all tables containing annexe info like events, claims and qa. +func ResetAnnexes() error { + return truncateTable( + "claim_descriptions", + "claims", + "events", + "qa_comments", + "teams_qa_todo", + "teams_qa_view", + ) +} + // ResetGame resets all tables containing team attempts and solves. -func ResetGame() (error) { +func ResetGame() error { return truncateTable( "team_wchoices", "team_hints", @@ -40,7 +52,7 @@ func ResetGame() (error) { } // ResetExercices wipes out all challenges (both attempts and statements). -func ResetExercices() (error) { +func ResetExercices() error { return truncateTable( "team_wchoices", "team_hints", @@ -55,6 +67,10 @@ func ResetExercices() (error) { "exercice_hints_okey_deps", "exercice_hints_omcq_deps", "flag_choices", + "exercice_flag_deps", + "exercice_flag_labels_omcq_deps", + "exercice_flag_labels_deps", + "exercice_flag_labels", "exercice_flags", "exercice_solved", "exercice_tries", @@ -69,7 +85,7 @@ func ResetExercices() (error) { } // ResetTeams wipes out all teams, incluings members and attempts. -func ResetTeams() (error) { +func ResetTeams() error { return truncateTable( "team_wchoices", "team_hints", From 6aa0f4da9555c9e49f3c711c6ba7bb47f8ffbb54 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 7 Jun 2022 12:40:31 +0200 Subject: [PATCH 0559/1637] ui: Use a PNG favicon --- frontend/ui/src/app.html | 1 - frontend/ui/src/routes/__layout.svelte | 3 +++ frontend/ui/static/favicon.ico | Bin 2238 -> 0 bytes 3 files changed, 3 insertions(+), 1 deletion(-) delete mode 100644 frontend/ui/static/favicon.ico diff --git a/frontend/ui/src/app.html b/frontend/ui/src/app.html index 39af4b93..f23bf00a 100644 --- a/frontend/ui/src/app.html +++ b/frontend/ui/src/app.html @@ -4,7 +4,6 @@ - %sveltekit.head% diff --git a/frontend/ui/src/routes/__layout.svelte b/frontend/ui/src/routes/__layout.svelte index 2b31cbac..d3b305b8 100644 --- a/frontend/ui/src/routes/__layout.svelte +++ b/frontend/ui/src/routes/__layout.svelte @@ -148,6 +148,9 @@ {#if $challengeInfo} {$challengeInfo.title} + {#if $challengeInfo.main_logo.length} + + {/if} {/if} diff --git a/frontend/ui/static/favicon.ico b/frontend/ui/static/favicon.ico deleted file mode 100644 index 3da960762392e9c97016697e56d1aabd11690e7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2238 zcmc)McTkj96b0~u3Llo(5Nsfw6_6sb^tRO9WrbZ!6uYsD1!M0hHn1S}1`2|pCLWDYc zB2?&wP$zHVgHUG?Lfiup;vq(ecX!AF`#>fRBmE$g^oLA30J7kLWDsPs!3Yi+3Tfyt z1j~m*su&4rk5Q2H91Ur&v5@u-hosL0Nc&ENq;CWyVG)pqO@gG~WQYe$g=F9~hzHGp zgop>vgm}m-1Pz;ypy81S9I*gFN(&JRf* z!<(A~?|Dh^nx71>$Q1Yx&jq{Sy)YGCQM=)}Xb(w;=i+pDEXjb!(oDE5%Y-{|i_U_3 zOg7wN4!~{sLAb8Sfg5pIc?hn=Wo0g0WAoq=n-AyJhvBli0M2m*5XK#W(;8kOoYoeR zVhGojK)9Y)3dapcAta6)kE7eB6L8pk65WWymQ%3bdKz7M@n>Kke-;kJKH)5E6VAbo zxBVP!wx5UXjtj6!ya?OGOR!G53>)6gE3n>q6#*Ea1}kEjdIO!) zZbFb&PFW7Y?px$G1bc47BK;1`((l5YSnQ?Tdk^OO?!#=~1DIqyfN{n{%7-xJGn0}S zXFj6&5e)Y~h7mE$dIDo&l=T#yvY){u`zZ`5^$$FU0qLal0v!*&fIcx$W(8#hI_6ZM zeNH9X9eRoOqyuS}`wDGyt0=3Wmsf?hL^rRRvKo5%)zHmb!i_h?%59!-iqP<}u&!WI7xZPJ)%mHYv1qFM404N5L&*{}9z1>?&UEue)YT zMlcpPc+4DS-GA*VtuE6psXs~dV{A%{7Ukg=p?Q_Ilq@)w-%Z$7xwc zF}8r4%%WRyXsN+lDLpn!9R>)%&p<^*hXy^-g$dvT$45>UmgtMHZxXsot@zv z@3+$}Q1**B(U-k1cUd{l;AXGj!d11K*4f$zb2z8f59ngo_~IZgQ`Hs@ORn9&2y4jc z_WY$-(~}M0@@iZ4ES+SoeRU!i#l~{0)O)r081rsk#f6zB-Z|fP~Me=_h{11n| IFMl-t21&Mmb^rhX From 6d9fd1ff12e6bfe0230d362ccecf96feab3498f4 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 7 Jun 2022 16:05:41 +0200 Subject: [PATCH 0560/1637] libfic: Update ScoreGridFormat format and expose stats --- libfic/stats.go | 35 +++++++++++++++++++++-------------- libfic/team_my.go | 13 +++++++++++-- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/libfic/stats.go b/libfic/stats.go index 2eb78c51..f6f2dd81 100644 --- a/libfic/stats.go +++ b/libfic/stats.go @@ -2,7 +2,9 @@ package fic import ( "database/sql" + "encoding/json" "fmt" + "os" "time" ) @@ -48,7 +50,15 @@ func rankQuery(whereTeam string) string { ) A ` + whereTeam + ` GROUP BY A.id_team ORDER BY score DESC, time ASC` } -func (t *Team) ScoreGrid() (grid []map[string]interface{}, err error) { +type ScoreGridRow struct { + Reason string `json:"reason"` + IdExercice int64 `json:"id_exercice"` + Time time.Time `json:"time"` + Points float64 `json:"points"` + Coeff float64 `json:"coeff"` +} + +func (t *Team) ScoreGrid() (grid []ScoreGridRow, err error) { q := "SELECT G.reason, G.id_exercice, G.time, G.points, G.coeff FROM (" + teamptsQuery() + ") AS G WHERE G.id_team = ? AND G.points != 0" if rows, err := DBQuery(q, t.Id); err != nil { return nil, err @@ -56,28 +66,25 @@ func (t *Team) ScoreGrid() (grid []map[string]interface{}, err error) { defer rows.Close() for rows.Next() { - var reason string - var exercice int64 - var time time.Time - var points float64 - var coeff float64 + var sgr ScoreGridRow - if err := rows.Scan(&reason, &exercice, &time, &points, &coeff); err != nil { + if err := rows.Scan(&sgr.Reason, &sgr.IdExercice, &sgr.Time, &sgr.Points, &sgr.Coeff); err != nil { return nil, err } - grid = append(grid, map[string]interface{}{ - "reason": reason, - "id_exercice": exercice, - "time": time, - "points": points, - "coeff": coeff, - }) + grid = append(grid, sgr) } } return } +func ReadScoreGrid(fd *os.File) (grid []ScoreGridRow, err error) { + jdec := json.NewDecoder(fd) + + err = jdec.Decode(&grid) + return +} + // Points // EstimateGain calculates the amount of point the Team has (or could have, if not already solved) won. diff --git a/libfic/team_my.go b/libfic/team_my.go index edb42598..f1b0ac64 100644 --- a/libfic/team_my.go +++ b/libfic/team_my.go @@ -2,8 +2,10 @@ package fic import ( "encoding/hex" + "encoding/json" "fmt" "log" + "os" "path" "sort" "strconv" @@ -81,7 +83,7 @@ type myTeamExercice struct { Issue string `json:"issue,omitempty"` IssueKind string `json:"issuekind,omitempty"` } -type myTeam struct { +type MyTeam struct { Id int64 `json:"team_id"` Name string `json:"name"` Points int64 `json:"score"` @@ -96,7 +98,7 @@ func (a ByOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a ByOrder) Less(i, j int) bool { return a[i].order < a[j].order } func MyJSONTeam(t *Team, started bool) (interface{}, error) { - ret := myTeam{} + ret := MyTeam{} // Fill information about the team if t == nil { @@ -401,3 +403,10 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { return ret, nil } + +func ReadMyJSON(fd *os.File) (my MyTeam, err error) { + jdec := json.NewDecoder(fd) + + err = jdec.Decode(&my) + return +} From 367e686e8a1972f661575e2e414bf5224c85c450 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 7 Jun 2022 16:06:36 +0200 Subject: [PATCH 0561/1637] remote-challenge-sync-airbus: WIP --- remote/challenge-sync-airbus/api.go | 37 ++++++- remote/challenge-sync-airbus/bindings.go | 39 ++++++++ remote/challenge-sync-airbus/main.go | 87 ++++++++--------- remote/challenge-sync-airbus/timestamp.go | 50 ++++++++++ remote/challenge-sync-airbus/treat.go | 112 ++++++++++++++++++++++ 5 files changed, 272 insertions(+), 53 deletions(-) create mode 100644 remote/challenge-sync-airbus/bindings.go create mode 100644 remote/challenge-sync-airbus/timestamp.go create mode 100644 remote/challenge-sync-airbus/treat.go diff --git a/remote/challenge-sync-airbus/api.go b/remote/challenge-sync-airbus/api.go index 9b01cfe5..d2bdd0cf 100644 --- a/remote/challenge-sync-airbus/api.go +++ b/remote/challenge-sync-airbus/api.go @@ -12,7 +12,7 @@ import ( type AirbusAPI struct { BaseURL string Token string - SessionID uint64 + SessionID int64 } func (a *AirbusAPI) request(method, endpoint string, data []byte, out interface{}) error { @@ -52,6 +52,11 @@ func (a *AirbusAPI) request(method, endpoint string, data []byte, out interface{ type AirbusUserId int64 +func NewAirbusUserId(externalid string) AirbusUserId { + v, _ := strconv.ParseInt(externalid, 10, 64) + return AirbusUserId(v) +} + func (aui AirbusUserId) String() string { return strconv.FormatInt(int64(aui), 10) } @@ -112,8 +117,8 @@ func (a *AirbusAPI) GetChallengeFromName(name string) (*AirbusChallenge, error) return nil, fmt.Errorf("unable to find challenge %q", name) } -func (a *AirbusAPI) ValidateChallengeFromUser(user *AirbusUser, challenge *AirbusChallenge) (err error) { - err = a.request("GET", fmt.Sprintf("/sessions/%d/%s/%s/validate", a.SessionID, challenge.Id.String(), user.Id.String()), nil, nil) +func (a *AirbusAPI) ValidateChallengeFromUser(userId AirbusUserId, challengeId AirbusChallengeId) (err error) { + err = a.request("GET", fmt.Sprintf("/sessions/%d/%s/%s/validate", a.SessionID, challengeId.String(), userId.String()), nil, nil) return } @@ -123,9 +128,9 @@ type AirbusUserAwards struct { Value int64 `json:"value"` } -func (a *AirbusAPI) AwardUser(user *AirbusUser, value int64, message string) (err error) { +func (a *AirbusAPI) AwardUser(userId AirbusUserId, value int64, message string) (err error) { awards := AirbusUserAwards{ - UserId: user.Id, + UserId: userId, Message: message, Value: value, } @@ -147,6 +152,18 @@ type AirbusStatsData struct { BySessions []AirbusStatsSession `json:"by_sessions"` } +func (s AirbusStatsData) GetSession(sessionId AirbusUUID) *AirbusStatsSession { + for _, session := range s.BySessions { + if session.UUID == sessionId { + return &session + } + } + + return nil +} + +type AirbusUUID string + type AirbusStatsSession struct { UUID AirbusUUID `json:"uuid"` Name string `json:"name"` @@ -154,6 +171,16 @@ type AirbusStatsSession struct { TeamStats []AirbusTeamStats `json:"team_stats"` } +func (s AirbusStatsSession) GetTeam(teamId AirbusUUID) *AirbusTeamStats { + for _, team := range s.TeamStats { + if team.UUID == teamId { + return &team + } + } + + return nil +} + type AirbusTeamStats struct { UUID AirbusUUID `json:"uuid"` Name string `json:"name"` diff --git a/remote/challenge-sync-airbus/bindings.go b/remote/challenge-sync-airbus/bindings.go new file mode 100644 index 00000000..f7af582b --- /dev/null +++ b/remote/challenge-sync-airbus/bindings.go @@ -0,0 +1,39 @@ +package main + +import ( + "encoding/json" + "io/ioutil" + "os" + + "srs.epita.fr/fic-server/libfic" +) + +type AirbusExercicesBindings map[int64]AirbusChallengeId + +func ReadExercicesBindings(ebpath string) (AirbusExercicesBindings, error) { + fd, err := os.Open(ebpath) + if err != nil { + return nil, err + } + defer fd.Close() + + jdec := json.NewDecoder(fd) + + var aeb AirbusExercicesBindings + err = jdec.Decode(&aeb) + + return aeb, err +} + +func getTeams(pathname string) (teams map[string]fic.ExportedTeam, err error) { + var cnt_raw []byte + if cnt_raw, err = ioutil.ReadFile(pathname); err != nil { + return + } + + if err = json.Unmarshal(cnt_raw, &teams); err != nil { + return + } + + return +} diff --git a/remote/challenge-sync-airbus/main.go b/remote/challenge-sync-airbus/main.go index 3c873c10..75078360 100644 --- a/remote/challenge-sync-airbus/main.go +++ b/remote/challenge-sync-airbus/main.go @@ -4,12 +4,10 @@ import ( "flag" "log" "os" - "os/signal" "path" + "path/filepath" "strconv" - "syscall" - - "gopkg.in/fsnotify.v1" + "time" ) var ( @@ -19,8 +17,12 @@ var ( func main() { flag.StringVar(&TeamsDir, "teams", "./TEAMS", "Base directory where save teams JSON files") - var debugINotify = flag.Bool("debuginotify", false, "Show skipped inotofy events") + //var debugINotify = flag.Bool("debuginotify", false, "Show skipped inotofy events") flag.BoolVar(&skipInitialSync, "skipinitialsync", skipInitialSync, "Skip the initial synchronization") + //watcher := flag.Bool("watch", false, "Enable daemon mode by watching the directory") + tspath := flag.String("timestamp-file", "./REMOTE/timestamp", "Path to the file storing the last timestamp") + exercicespath := flag.String("exercices-file", "./REMOTE/exercices-bindings.json", "Path to the file containing the ID bindings") + coeff := flag.Float64("global-coeff", 10.0, "Coefficient to use to multiply all scores before passing them to the other platform") flag.Parse() api := AirbusAPI{ @@ -35,7 +37,7 @@ func main() { } if v, exists := os.LookupEnv("AIRBUS_SESSIONID"); exists { var err error - api.SessionID, err = strconv.ParseUint(v, 10, 64) + api.SessionID, err = strconv.ParseInt(v, 10, 64) if err != nil { log.Fatal("AIRBUS_SESSIONID is invalid: ", err.Error()) } @@ -45,54 +47,43 @@ func main() { TeamsDir = path.Clean(TeamsDir) - log.Println("Registering directory events...") - watcher, err := fsnotify.NewWatcher() + // Load the timestamp + ts, err := loadTS(*tspath) if err != nil { - log.Fatal(err) - } - defer watcher.Close() - - if err := watcher.Add(TeamsDir); err != nil { - log.Fatal(err) + log.Fatal("Unable to open timestamp file: ", err.Error()) } - if !skipInitialSync { - if _, err := os.Stat(path.Join(TeamsDir, "teams.json")); err == nil { - treatAll(path.Join(TeamsDir, "teams.json")) - } + // Load exercices bindings + exbindings, err := ReadExercicesBindings(*exercicespath) + if err != nil { + log.Fatal("Unable to open exercices bindings file: ", err.Error()) } - // Register SIGUSR1, SIGUSR2 - interrupt := make(chan os.Signal, 1) - signal.Notify(interrupt, syscall.SIGHUP) + // Load teams.json + teamsbindings, err := getTeams(filepath.Join(TeamsDir, "teams.json")) + if err != nil { + log.Fatal("Unable to open teams bindings file: ", err.Error()) + } - watchedNotify := fsnotify.Create + w := Walker{ + LastSync: *ts, + Exercices: exbindings, + Teams: teamsbindings, + API: api, + Coeff: *coeff, + } - for { - select { - case <-interrupt: - log.Println("SIGHUP received, resyncing all teams' score...") - treatAll(path.Join(TeamsDir, "teams.json")) - log.Println("SIGHUP treated.") - case ev := <-watcher.Events: - if path.Base(ev.Name) == "teams.json" { - if ev.Op&watchedNotify == watchedNotify { - if *debugINotify { - log.Println("Treating event:", ev, "for", ev.Name) - } - go treatDiff(ev.Name) - } else if 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 treatDiff(ev.Name) - } else if *debugINotify { - log.Println("Skipped teams.json event:", ev) - } - } else if *debugINotify { - log.Println("Skipped NON teams.json event:", ev, "for", ev.Name) - } - case err := <-watcher.Errors: - log.Println("error:", err) - } + // Iterate over teams scores + err = filepath.WalkDir(TeamsDir, w.WalkScore) + if err != nil { + log.Printf("Something goes wrong during walking") + } + + // Update timestamp for the next time + w.LastSync = time.Now() + + err = saveTS(*tspath, &w.LastSync) + if err != nil { + log.Fatal("Unable to save timestamp file: ", err.Error()) } } diff --git a/remote/challenge-sync-airbus/timestamp.go b/remote/challenge-sync-airbus/timestamp.go new file mode 100644 index 00000000..1b31d963 --- /dev/null +++ b/remote/challenge-sync-airbus/timestamp.go @@ -0,0 +1,50 @@ +package main + +import ( + "fmt" + "os" + "time" +) + +func loadTS(tspath string) (timestamp *time.Time, err error) { + if _, err = os.Stat(tspath); os.IsNotExist(err) { + init := time.Unix(0, 0) + timestamp = &init + + err = saveTS(tspath, timestamp) + return + } + + var fd *os.File + fd, err = os.Open(tspath) + if err != nil { + return + } + defer fd.Close() + + var init int64 + _, err = fmt.Fscanf(fd, "%d", &init) + if err != nil { + return + } + + tmp := time.Unix(init, 0) + timestamp = &tmp + + return +} + +func saveTS(tspath string, ts *time.Time) error { + fd, err := os.Create(tspath) + if err != nil { + return err + } + defer fd.Close() + + _, err = fmt.Fprintf(fd, "%d", ts.Unix()) + if err != nil { + return err + } + + return nil +} diff --git a/remote/challenge-sync-airbus/treat.go b/remote/challenge-sync-airbus/treat.go new file mode 100644 index 00000000..769c5ba1 --- /dev/null +++ b/remote/challenge-sync-airbus/treat.go @@ -0,0 +1,112 @@ +package main + +import ( + "fmt" + "os" + "path/filepath" + "time" + + "srs.epita.fr/fic-server/libfic" +) + +type Walker struct { + LastSync time.Time + Exercices AirbusExercicesBindings + Teams map[string]fic.ExportedTeam + API AirbusAPI + Coeff float64 +} + +func (w *Walker) WalkScore(path string, d os.DirEntry, err error) error { + if filepath.Base(path) == "scores.json" { + mypath := filepath.Join(filepath.Dir(path), "my.json") + if _, err := os.Stat(mypath); !os.IsNotExist(err) { + // Read team ID + fdmy, err := os.Open(mypath) + if err != nil { + return err + } + defer fdmy.Close() + + teammy, err := fic.ReadMyJSON(fdmy) + if err != nil { + return err + } + + airbusTeamId := NewAirbusUserId(w.Teams[fmt.Sprintf("%d", teammy.Id)].ExternalId) + + // Treat score grid + err = w.TreatScoreGrid(path, airbusTeamId) + if err != nil { + return err + } + + // Balance scores + err = w.BalanceScore(int64(float64(teammy.Points)*w.Coeff), airbusTeamId) + if err != nil { + return err + } + } + } + + return nil +} + +func (w *Walker) TreatScoreGrid(path string, airbusTeamId AirbusUserId) error { + // Read score grid + fdscores, err := os.Open(path) + if err != nil { + return err + } + defer fdscores.Close() + + teamscores, err := fic.ReadScoreGrid(fdscores) + if err != nil { + return err + } + + // Found all new entries + for _, row := range teamscores { + if row.Time.After(w.LastSync) { + if row.Reason == "Validation" { + err = w.API.ValidateChallengeFromUser(airbusTeamId, w.Exercices[row.IdExercice]) + } else { + err = w.API.AwardUser(airbusTeamId, int64(row.Points*row.Coeff*w.Coeff), row.Reason) + } + + if err != nil { + return err + } + } + } + + return nil +} + +func (w *Walker) BalanceScore(score int64, airbusTeamId AirbusUserId) error { + // Read current score on other platform + stats, err := w.API.GetCurrentStats() + if err != nil { + fmt.Errorf("unable to retrieve current stats: %w", err) + } + + my_session := stats.Data.GetSession(AirbusUUID(w.API.SessionID)) + if my_session == nil { + return fmt.Errorf("session not found") + } + + other_team := my_session.GetTeam(AirbusUUID(airbusTeamId)) + if other_team == nil { + return fmt.Errorf("team %q not found", airbusTeamId) + } + + other_score := other_team.Score + + // Send diff to the platform + if other_score != score { + diff := score - other_score + return w.API.AwardUser(airbusTeamId, diff, "Équilibrage") + } + + return nil +} From cc1b212cca46568f1d1278f9c9332928c1605d6c Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 7 Jun 2022 17:04:07 +0200 Subject: [PATCH 0562/1637] remote-challenge-sync-airbus: Add inotify watcher --- remote/challenge-sync-airbus/main.go | 98 ++++++++++++++++++++--- remote/challenge-sync-airbus/timestamp.go | 54 ++++++------- remote/challenge-sync-airbus/treat.go | 68 +++++++++------- 3 files changed, 149 insertions(+), 71 deletions(-) diff --git a/remote/challenge-sync-airbus/main.go b/remote/challenge-sync-airbus/main.go index 75078360..33641b92 100644 --- a/remote/challenge-sync-airbus/main.go +++ b/remote/challenge-sync-airbus/main.go @@ -2,12 +2,14 @@ package main import ( "flag" + "io/ioutil" "log" "os" "path" "path/filepath" "strconv" - "time" + + "gopkg.in/fsnotify.v1" ) var ( @@ -17,9 +19,9 @@ var ( func main() { flag.StringVar(&TeamsDir, "teams", "./TEAMS", "Base directory where save teams JSON files") - //var debugINotify = flag.Bool("debuginotify", false, "Show skipped inotofy events") + var debugINotify = flag.Bool("debuginotify", false, "Show skipped inotofy events") flag.BoolVar(&skipInitialSync, "skipinitialsync", skipInitialSync, "Skip the initial synchronization") - //watcher := flag.Bool("watch", false, "Enable daemon mode by watching the directory") + daemon := flag.Bool("watch", false, "Enable daemon mode by watching the directory") tspath := flag.String("timestamp-file", "./REMOTE/timestamp", "Path to the file storing the last timestamp") exercicespath := flag.String("exercices-file", "./REMOTE/exercices-bindings.json", "Path to the file containing the ID bindings") coeff := flag.Float64("global-coeff", 10.0, "Coefficient to use to multiply all scores before passing them to the other platform") @@ -66,24 +68,94 @@ func main() { } w := Walker{ - LastSync: *ts, + LastSync: ts, Exercices: exbindings, Teams: teamsbindings, API: api, Coeff: *coeff, } - // Iterate over teams scores - err = filepath.WalkDir(TeamsDir, w.WalkScore) - if err != nil { - log.Printf("Something goes wrong during walking") + if !skipInitialSync { + // Iterate over teams scores + err = filepath.WalkDir(TeamsDir, w.WalkScore) + if err != nil { + log.Printf("Something goes wrong during walking") + } + + // save current timestamp for teams + err = saveTS(*tspath, w.LastSync) + if err != nil { + log.Fatal("Unable to save timestamp file: ", err.Error()) + } } - // Update timestamp for the next time - w.LastSync = time.Now() + if daemon != nil && *daemon { + // Watch teams.json and scores.json + log.Println("Registering directory events...") + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Fatal(err) + } + defer watcher.Close() - err = saveTS(*tspath, &w.LastSync) - if err != nil { - log.Fatal("Unable to save timestamp file: ", err.Error()) + if err := watchsubdir(watcher, TeamsDir); err != nil { + log.Fatal(err) + } + + watchedNotify := fsnotify.Create + + for { + select { + 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 ev.Op&watchedNotify == watchedNotify && d.Mode().IsRegular() { + if *debugINotify { + log.Println("Treating event:", ev, "for", ev.Name) + } + if filepath.Base(ev.Name) == "scores.json" { + go w.treat(ev.Name) + } else if filepath.Base(ev.Name) == "teams.json" { + teamsbindings, err := getTeams(filepath.Join(TeamsDir, "teams.json")) + if err != nil { + log.Println("Unable to open teams bindings file: ", err.Error()) + return + } + w.Teams = teamsbindings + } + } else if 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 + } else if *debugINotify { + log.Println("Skipped event:", ev, "for", ev.Name) + } + case err := <-watcher.Errors: + log.Println("error:", err) + } + } + } +} + +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 + } + } + } + return nil } } diff --git a/remote/challenge-sync-airbus/timestamp.go b/remote/challenge-sync-airbus/timestamp.go index 1b31d963..413f76e2 100644 --- a/remote/challenge-sync-airbus/timestamp.go +++ b/remote/challenge-sync-airbus/timestamp.go @@ -1,50 +1,42 @@ package main import ( - "fmt" + "encoding/json" "os" "time" ) -func loadTS(tspath string) (timestamp *time.Time, err error) { +func loadTS(tspath string) (timestamp map[AirbusUserId]time.Time, err error) { + var fd *os.File if _, err = os.Stat(tspath); os.IsNotExist(err) { - init := time.Unix(0, 0) - timestamp = &init - + timestamp = map[AirbusUserId]time.Time{} err = saveTS(tspath, timestamp) return - } + } else if fd, err = os.Open(tspath); err != nil { + return nil, err + } else { + defer fd.Close() + jdec := json.NewDecoder(fd) + + if err = jdec.Decode(×tamp); err != nil { + return + } - var fd *os.File - fd, err = os.Open(tspath) - if err != nil { return } - defer fd.Close() - - var init int64 - _, err = fmt.Fscanf(fd, "%d", &init) - if err != nil { - return - } - - tmp := time.Unix(init, 0) - timestamp = &tmp - - return } -func saveTS(tspath string, ts *time.Time) error { - fd, err := os.Create(tspath) - if err != nil { +func saveTS(tspath string, ts map[AirbusUserId]time.Time) error { + if fd, err := os.Create(tspath); err != nil { return err - } - defer fd.Close() + } else { + defer fd.Close() + jenc := json.NewEncoder(fd) - _, err = fmt.Fprintf(fd, "%d", ts.Unix()) - if err != nil { - return err - } + if err := jenc.Encode(ts); err != nil { + return err + } - return nil + return nil + } } diff --git a/remote/challenge-sync-airbus/treat.go b/remote/challenge-sync-airbus/treat.go index 769c5ba1..df01b87e 100644 --- a/remote/challenge-sync-airbus/treat.go +++ b/remote/challenge-sync-airbus/treat.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "log" "os" "path/filepath" "time" @@ -10,45 +11,52 @@ import ( ) type Walker struct { - LastSync time.Time + LastSync map[AirbusUserId]time.Time Exercices AirbusExercicesBindings Teams map[string]fic.ExportedTeam API AirbusAPI Coeff float64 } -func (w *Walker) WalkScore(path string, d os.DirEntry, err error) error { - if filepath.Base(path) == "scores.json" { - mypath := filepath.Join(filepath.Dir(path), "my.json") - if _, err := os.Stat(mypath); !os.IsNotExist(err) { - // Read team ID - fdmy, err := os.Open(mypath) - if err != nil { - return err - } - defer fdmy.Close() +func (w *Walker) treat(path string) { + mypath := filepath.Join(filepath.Dir(path), "my.json") + if _, err := os.Stat(mypath); !os.IsNotExist(err) { + // Read team ID + fdmy, err := os.Open(mypath) + if err != nil { + log.Println("Unable to open my.json:", err) + return + } + defer fdmy.Close() - teammy, err := fic.ReadMyJSON(fdmy) - if err != nil { - return err - } + teammy, err := fic.ReadMyJSON(fdmy) + if err != nil { + log.Println("Unable to parse my.json:", err) + return + } - airbusTeamId := NewAirbusUserId(w.Teams[fmt.Sprintf("%d", teammy.Id)].ExternalId) + airbusTeamId := NewAirbusUserId(w.Teams[fmt.Sprintf("%d", teammy.Id)].ExternalId) - // Treat score grid - err = w.TreatScoreGrid(path, airbusTeamId) - if err != nil { - return err - } + // Treat score grid + err = w.TreatScoreGrid(path, airbusTeamId) + if err != nil { + log.Println("Unable to treat score grid:", err) + return + } - // Balance scores - err = w.BalanceScore(int64(float64(teammy.Points)*w.Coeff), airbusTeamId) - if err != nil { - return err - } + // Balance scores + err = w.BalanceScore(int64(float64(teammy.Points)*w.Coeff), airbusTeamId) + if err != nil { + log.Println("Unable to balance score:", err) + return } } +} +func (w *Walker) WalkScore(path string, d os.DirEntry, err error) error { + if filepath.Base(path) == "scores.json" { + go w.treat(path) + } return nil } @@ -66,8 +74,12 @@ func (w *Walker) TreatScoreGrid(path string, airbusTeamId AirbusUserId) error { } // Found all new entries + maxts := w.LastSync[airbusTeamId] for _, row := range teamscores { - if row.Time.After(w.LastSync) { + if row.Time.After(maxts) { + maxts = row.Time + } + if row.Time.After(w.LastSync[airbusTeamId]) { if row.Reason == "Validation" { err = w.API.ValidateChallengeFromUser(airbusTeamId, w.Exercices[row.IdExercice]) } else { @@ -80,6 +92,8 @@ func (w *Walker) TreatScoreGrid(path string, airbusTeamId AirbusUserId) error { } } + w.LastSync[airbusTeamId] = maxts + return nil } From d2d7b35623222974e109295f4c55f3c74096333f Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 7 Jun 2022 17:13:30 +0200 Subject: [PATCH 0563/1637] remote-challenge-sync-airbus: Handle interrupts --- remote/challenge-sync-airbus/main.go | 65 ++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/remote/challenge-sync-airbus/main.go b/remote/challenge-sync-airbus/main.go index 33641b92..f65c4fe8 100644 --- a/remote/challenge-sync-airbus/main.go +++ b/remote/challenge-sync-airbus/main.go @@ -5,9 +5,11 @@ import ( "io/ioutil" "log" "os" + "os/signal" "path" "path/filepath" "strconv" + "syscall" "gopkg.in/fsnotify.v1" ) @@ -102,10 +104,61 @@ func main() { log.Fatal(err) } + // Register SIGUSR1, SIGUSR2 + interrupt1 := make(chan os.Signal, 1) + signal.Notify(interrupt1, syscall.SIGHUP) + interrupt2 := make(chan os.Signal, 1) + signal.Notify(interrupt2, syscall.SIGUSR1) + interrupt3 := make(chan os.Signal, 1) + signal.Notify(interrupt3, syscall.SIGUSR2) + + c := time.NewTicker(5 * time.Second) + watchedNotify := fsnotify.Create for { select { + case <-interrupt1: + log.Println("SIGHUP received, reloading files...") + teamsbindings, err := getTeams(filepath.Join(TeamsDir, "teams.json")) + if err != nil { + log.Println("Unable to open teams bindings file: ", err.Error()) + return + } + w.Teams = teamsbindings + + // save current timestamp for teams + err = saveTS(*tspath, w.LastSync) + if err != nil { + log.Fatal("Unable to save timestamp file: ", err.Error()) + } + log.Println("SIGHUP treated.") + case <-interrupt2: + log.Println("SIGUSR1 received, resynching all teams") + // Iterate over teams scores + err = filepath.WalkDir(TeamsDir, w.WalkScore) + if err != nil { + log.Printf("Something goes wrong during walking") + } + + // save current timestamp for teams + err = saveTS(*tspath, w.LastSync) + if err != nil { + log.Fatal("Unable to save timestamp file: ", err.Error()) + } + log.Println("SIGUSR1 treated.") + case <-interrupt3: + log.Println("SIGUSR2 received, resynching all teams from teams.json") + teamsbindings, err := getTeams(filepath.Join(TeamsDir, "teams.json")) + if err != nil { + log.Println("Unable to open teams bindings file: ", err.Error()) + return + } + w.Teams = teamsbindings + + // FIXME + + log.Println("SIGUSR2 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 @@ -134,9 +187,21 @@ func main() { } case err := <-watcher.Errors: log.Println("error:", err) + case <-ticker.C: + // save current timestamp for teams + err = saveTS(*tspath, w.LastSync) + if err != nil { + log.Fatal("Unable to save timestamp file: ", err.Error()) + } } } } + + // save current timestamp for teams + err = saveTS(*tspath, w.LastSync) + if err != nil { + log.Fatal("Unable to save timestamp file: ", err.Error()) + } } func watchsubdir(watcher *fsnotify.Watcher, pathname string) error { From cdc342bea3f67811bf5d8e0f2b05649c863b75b8 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 8 Jun 2022 00:06:03 +0200 Subject: [PATCH 0564/1637] fickit: Missing SYNC dir --- fickit-backend.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fickit-backend.yml b/fickit-backend.yml index d33d5111..3c60ce31 100644 --- a/fickit-backend.yml +++ b/fickit-backend.yml @@ -174,6 +174,7 @@ services: - /var/lib/fic/pki:/srv/PKI - /var/lib/fic/teams:/srv/TEAMS - /var/lib/fic/settings:/srv/SETTINGS + - /var/lib/fic/sync:/srv/SYNC - /var/lib/fic/submissions:/srv/submissions:ro net: /run/netns/fic-admin pid: new @@ -186,6 +187,7 @@ services: - /var/lib/fic/raw_files - /var/lib/fic/pki - /var/lib/fic/settings + - /var/lib/fic/sync - /var/lib/fic/submissions - /var/lib/fic/teams - name: fic-backend From a7dda3a99907e966c4b6f2b4c9247fca3d06d885 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 8 Jun 2022 00:06:39 +0200 Subject: [PATCH 0565/1637] fickit: Reenable dhcpd on admin --- fickit-backend.yml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/fickit-backend.yml b/fickit-backend.yml index 3c60ce31..c8ba0f70 100644 --- a/fickit-backend.yml +++ b/fickit-backend.yml @@ -269,18 +269,18 @@ services: 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 + - 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 @@ -338,9 +338,9 @@ files: default-lease-time 600; max-lease-time 7200; option subnet-mask 255.255.255.0; - option broadcast-address 192.168.23.255; - subnet 192.168.23.0 netmask 255.255.255.0 { - range 192.168.23.10 192.168.23.250; + option broadcast-address 192.168.49.255; + subnet 192.168.49.0 netmask 255.255.255.0 { + range 192.168.49.11 192.168.49.250; } mode: "0440" - path: etc/iptables/rules.v6 From a82defe2a7cafb61ad6befb8f2a3b303e860c101 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 8 Jun 2022 00:07:31 +0200 Subject: [PATCH 0566/1637] fickit: Add missing gateway --- fickit-frontend.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fickit-frontend.yml b/fickit-frontend.yml index efc394b1..f3af83a2 100644 --- a/fickit-frontend.yml +++ b/fickit-frontend.yml @@ -48,7 +48,7 @@ onboot: net: /run/netns/nginx - name: frontal-ip-setup # without bonding image: linuxkit/ip:6cc44dd4e18ddb02de01bc4b34b5799971b6a7bf - command: ["/bin/sh", "-c", "ip link set name bond-frontal eth3; ip link set bond-frontal up; ip a add 192.168.50.10/24 dev bond-frontal; ip a add 91.243.117.240/32 dev bond-frontal; ip link add link bond-frontal name internet type vlan id 4; ip link set internet up;" ] + command: ["/bin/sh", "-c", "ip link set name bond-frontal eth3; ip link set bond-frontal up; ip a add 192.168.50.10/24 dev bond-frontal; ip a add 91.243.117.240/32 dev bond-frontal; ip r add default via 192.168.50.1; ip link add link bond-frontal name internet type vlan id 4; ip link set internet up;" ] net: /run/netns/nginx runtime: interfaces: From 0831ea6088ad1116e37acaecb58870f73da0103d Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 8 Jun 2022 00:09:49 +0200 Subject: [PATCH 0567/1637] remote-challenge-sync-airbus: WIP --- remote/challenge-sync-airbus/api.go | 64 ++++++++++++++++++++------- remote/challenge-sync-airbus/main.go | 12 +++-- remote/challenge-sync-airbus/treat.go | 13 ++++-- 3 files changed, 66 insertions(+), 23 deletions(-) diff --git a/remote/challenge-sync-airbus/api.go b/remote/challenge-sync-airbus/api.go index d2bdd0cf..dd0dbd2e 100644 --- a/remote/challenge-sync-airbus/api.go +++ b/remote/challenge-sync-airbus/api.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "crypto/tls" "encoding/json" "fmt" "io" @@ -10,17 +11,23 @@ import ( ) type AirbusAPI struct { - BaseURL string - Token string - SessionID int64 + BaseURL string + Token string + SessionID int64 + SessionUUID string } func (a *AirbusAPI) request(method, endpoint string, data []byte, out interface{}) error { - var reader *bytes.Reader + var req *http.Request + var err error + + http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + if data != nil { - reader = bytes.NewReader(data) + req, err = http.NewRequest(method, a.BaseURL+endpoint, bytes.NewReader(data)) + } else { + req, err = http.NewRequest(method, a.BaseURL+endpoint, nil) } - req, err := http.NewRequest(method, a.BaseURL+endpoint, reader) if err != nil { return fmt.Errorf("unable to prepare request to %q: %w", endpoint, err) } @@ -61,12 +68,18 @@ func (aui AirbusUserId) String() string { return strconv.FormatInt(int64(aui), 10) } -type AirbusUser struct { - Id AirbusUserId `json:"id"` - Name string `json:"name"` +type AirbusUserData struct { + Data []AirbusUser `json:"data"` } -func (a *AirbusAPI) GetUsers() (users []AirbusUser, err error) { +type AirbusUser struct { + Id AirbusUserId `json:"id"` + Email string `json:"email"` + Name string `json:"name"` + Nickname string `json:"nickname"` +} + +func (a *AirbusAPI) GetUsers() (users AirbusUserData, err error) { err = a.request("GET", fmt.Sprintf("/sessions/%d/users", a.SessionID), nil, &users) return } @@ -77,7 +90,7 @@ func (a *AirbusAPI) GetUserFromName(name string) (*AirbusUser, error) { return nil, fmt.Errorf("unable to retrieve users list: %w", err) } - for _, u := range users { + for _, u := range users.Data { if u.Name == name { return &u, nil } @@ -86,19 +99,38 @@ func (a *AirbusAPI) GetUserFromName(name string) (*AirbusUser, error) { return nil, fmt.Errorf("unable to find user %q", name) } +func (a *AirbusAPI) GetUserFromEmail(email string) (*AirbusUser, error) { + users, err := a.GetUsers() + if err != nil { + return nil, fmt.Errorf("unable to retrieve users list: %w", err) + } + + for _, u := range users.Data { + if u.Email == email { + return &u, nil + } + } + + return nil, fmt.Errorf("unable to find user with email %q", email) +} + type AirbusChallengeId int64 func (aci AirbusChallengeId) String() string { return strconv.FormatInt(int64(aci), 10) } +type AirbusChallengeData struct { + Data []AirbusChallenge `json:"data"` +} + type AirbusChallenge struct { Id AirbusChallengeId `json:"id"` Name string `json:"name"` } -func (a *AirbusAPI) GetChallenges() (challenges []AirbusChallenge, err error) { - err = a.request("GET", fmt.Sprintf("/sessions/%d/challenges", a.SessionID), nil, &challenges) +func (a *AirbusAPI) GetChallenges() (challenges AirbusChallengeData, err error) { + err = a.request("GET", fmt.Sprintf("/v1/sessions/%d/challenges", a.SessionID), nil, &challenges) return } @@ -108,7 +140,7 @@ func (a *AirbusAPI) GetChallengeFromName(name string) (*AirbusChallenge, error) return nil, fmt.Errorf("unable to retrieve challenges list: %w", err) } - for _, c := range challenges { + for _, c := range challenges.Data { if c.Name == name { return &c, nil } @@ -118,7 +150,7 @@ func (a *AirbusAPI) GetChallengeFromName(name string) (*AirbusChallenge, error) } func (a *AirbusAPI) ValidateChallengeFromUser(userId AirbusUserId, challengeId AirbusChallengeId) (err error) { - err = a.request("GET", fmt.Sprintf("/sessions/%d/%s/%s/validate", a.SessionID, challengeId.String(), userId.String()), nil, nil) + err = a.request("GET", fmt.Sprintf("/v1/sessions/%d/%s/%s/validate", a.SessionID, challengeId.String(), userId.String()), nil, nil) return } @@ -140,7 +172,7 @@ func (a *AirbusAPI) AwardUser(userId AirbusUserId, value int64, message string) return fmt.Errorf("unable to marshall JSON from awards struct: %w", err) } - err = a.request("POST", fmt.Sprintf("/sessions/%d/awards", a.SessionID), j, nil) + err = a.request("POST", fmt.Sprintf("/v1/sessions/%d/awards", a.SessionID), j, nil) return } diff --git a/remote/challenge-sync-airbus/main.go b/remote/challenge-sync-airbus/main.go index f65c4fe8..82948061 100644 --- a/remote/challenge-sync-airbus/main.go +++ b/remote/challenge-sync-airbus/main.go @@ -10,6 +10,7 @@ import ( "path/filepath" "strconv" "syscall" + "time" "gopkg.in/fsnotify.v1" ) @@ -30,7 +31,7 @@ func main() { flag.Parse() api := AirbusAPI{ - BaseURL: "https://portal.european-cybercup.lan/api/v1", + BaseURL: "https://portal.european-cybercup.lan/api", } if v, exists := os.LookupEnv("AIRBUS_BASEURL"); exists { @@ -46,6 +47,9 @@ func main() { log.Fatal("AIRBUS_SESSIONID is invalid: ", err.Error()) } } + if v, exists := os.LookupEnv("AIRBUS_SESSIONUUID"); exists { + api.SessionUUID = v + } log.SetPrefix("[challenge-sync-airbus] ") @@ -79,7 +83,7 @@ func main() { if !skipInitialSync { // Iterate over teams scores - err = filepath.WalkDir(TeamsDir, w.WalkScore) + err = filepath.WalkDir(TeamsDir, w.WalkScoreSync) if err != nil { log.Printf("Something goes wrong during walking") } @@ -112,7 +116,7 @@ func main() { interrupt3 := make(chan os.Signal, 1) signal.Notify(interrupt3, syscall.SIGUSR2) - c := time.NewTicker(5 * time.Second) + ticker := time.NewTicker(5 * time.Second) watchedNotify := fsnotify.Create @@ -136,7 +140,7 @@ func main() { case <-interrupt2: log.Println("SIGUSR1 received, resynching all teams") // Iterate over teams scores - err = filepath.WalkDir(TeamsDir, w.WalkScore) + err = filepath.WalkDir(TeamsDir, w.WalkScoreSync) if err != nil { log.Printf("Something goes wrong during walking") } diff --git a/remote/challenge-sync-airbus/treat.go b/remote/challenge-sync-airbus/treat.go index df01b87e..f248055c 100644 --- a/remote/challenge-sync-airbus/treat.go +++ b/remote/challenge-sync-airbus/treat.go @@ -38,11 +38,11 @@ func (w *Walker) treat(path string) { airbusTeamId := NewAirbusUserId(w.Teams[fmt.Sprintf("%d", teammy.Id)].ExternalId) // Treat score grid - err = w.TreatScoreGrid(path, airbusTeamId) + /*err = w.TreatScoreGrid(path, airbusTeamId) if err != nil { log.Println("Unable to treat score grid:", err) return - } + }*/ // Balance scores err = w.BalanceScore(int64(float64(teammy.Points)*w.Coeff), airbusTeamId) @@ -53,6 +53,13 @@ func (w *Walker) treat(path string) { } } +func (w *Walker) WalkScoreSync(path string, d os.DirEntry, err error) error { + if filepath.Base(path) == "scores.json" { + w.treat(path) + } + return nil +} + func (w *Walker) WalkScore(path string, d os.DirEntry, err error) error { if filepath.Base(path) == "scores.json" { go w.treat(path) @@ -104,7 +111,7 @@ func (w *Walker) BalanceScore(score int64, airbusTeamId AirbusUserId) error { fmt.Errorf("unable to retrieve current stats: %w", err) } - my_session := stats.Data.GetSession(AirbusUUID(w.API.SessionID)) + my_session := stats.Data.GetSession(AirbusUUID(w.API.SessionUUID)) if my_session == nil { return fmt.Errorf("session not found") } From af6e86d4ef83f19e4286194b5f04b7fbb7f5cb20 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 30 May 2022 17:25:07 +0200 Subject: [PATCH 0568/1637] evdist: New project to handle settings programming --- .drone-manifest-fic-evdist.yml | 22 +++++ .drone.yml | 58 +++++++++++ Dockerfile-evdist | 21 ++++ evdist/.gitignore | 1 + evdist/main.go | 169 +++++++++++++++++++++++++++++++++ evdist/settings.go | 110 +++++++++++++++++++++ frontend/main.go | 7 -- frontend/settings.go | 2 - settings/diff.go | 5 +- 9 files changed, 384 insertions(+), 11 deletions(-) create mode 100644 .drone-manifest-fic-evdist.yml create mode 100644 Dockerfile-evdist create mode 100644 evdist/.gitignore create mode 100644 evdist/main.go create mode 100644 evdist/settings.go diff --git a/.drone-manifest-fic-evdist.yml b/.drone-manifest-fic-evdist.yml new file mode 100644 index 00000000..70b1e5c5 --- /dev/null +++ b/.drone-manifest-fic-evdist.yml @@ -0,0 +1,22 @@ +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.yml b/.drone.yml index 83007dc6..a7fc62af 100644 --- a/.drone.yml +++ b/.drone.yml @@ -18,6 +18,7 @@ steps: - apk --no-cache add git - go get -v -d srs.epita.fr/fic-server/admin - go get -v -d srs.epita.fr/fic-server/backend + - go get -v -d srs.epita.fr/fic-server/evdist - go get -v -d srs.epita.fr/fic-server/frontend - go get -v -d srs.epita.fr/fic-server/dashboard - go get -v -d srs.epita.fr/fic-server/repochecker @@ -31,6 +32,7 @@ steps: - go vet -v -buildvcs=false -tags gitgo srs.epita.fr/fic-server/admin - go vet -v -buildvcs=false srs.epita.fr/fic-server/admin - go vet -v -buildvcs=false srs.epita.fr/fic-server/backend + - go vet -v -buildvcs=false srs.epita.fr/fic-server/evdist - go vet -v -buildvcs=false srs.epita.fr/fic-server/frontend - go vet -v -buildvcs=false srs.epita.fr/fic-server/dashboard - go vet -v -buildvcs=false srs.epita.fr/fic-server/repochecker @@ -52,6 +54,13 @@ steps: environment: CGO_ENABLED: 0 + - name: build evdist + image: golang:alpine + commands: + - go build -v -buildvcs=false -o deploy/evdist-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/evdist + environment: + CGO_ENABLED: 0 + - name: build frontend image: golang:alpine commands: @@ -151,6 +160,21 @@ steps: 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 frontend image: plugins/docker settings: @@ -297,6 +321,7 @@ steps: - apk --no-cache add git - go get -v -d srs.epita.fr/fic-server/admin - go get -v -d srs.epita.fr/fic-server/backend + - go get -v -d srs.epita.fr/fic-server/evdist - go get -v -d srs.epita.fr/fic-server/frontend - go get -v -d srs.epita.fr/fic-server/dashboard @@ -314,6 +339,13 @@ steps: environment: CGO_ENABLED: 0 + - name: build evdist + image: golang:alpine + commands: + - go build -v -buildvcs=false -o deploy/evdist-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} srs.epita.fr/fic-server/evdist + environment: + CGO_ENABLED: 0 + - name: build frontend image: golang:alpine commands: @@ -410,6 +442,21 @@ steps: 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 frontend image: plugins/docker settings: @@ -517,6 +564,17 @@ steps: 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 frontend image: plugins/manifest settings: diff --git a/Dockerfile-evdist b/Dockerfile-evdist new file mode 100644 index 00000000..d70f2c78 --- /dev/null +++ b/Dockerfile-evdist @@ -0,0 +1,21 @@ +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.16 + +WORKDIR /srv + +ENTRYPOINT ["/srv/evdist"] + +COPY --from=gobuild /go/src/srs.epita.fr/fic-server/evdist/evdist /srv/evdist diff --git a/evdist/.gitignore b/evdist/.gitignore new file mode 100644 index 00000000..f2b553d5 --- /dev/null +++ b/evdist/.gitignore @@ -0,0 +1 @@ +evdist \ No newline at end of file diff --git a/evdist/main.go b/evdist/main.go new file mode 100644 index 00000000..2aec1960 --- /dev/null +++ b/evdist/main.go @@ -0,0 +1,169 @@ +package main + +import ( + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "path" + "strconv" + "strings" + "time" + + "srs.epita.fr/fic-server/settings" + + "gopkg.in/fsnotify.v1" +) + +var SettingsDistDir = "./SETTINGSDIST/" +var TmpSettingsDirectory string +var TmpSettingsDistDirectory string + +func watchsubdir(l *distList, 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.Mode().IsRegular() && d.Name() != ".tmp" { + l.treat(p) + } + } + return nil + } +} + +func main() { + flag.StringVar(&settings.SettingsDir, "settings", settings.SettingsDir, "Base directory where read settings") + flag.StringVar(&SettingsDistDir, "settingsDist", SettingsDistDir, "Directory where place settings to distribute") + var debugINotify = flag.Bool("debuginotify", false, "Show skipped inotofy events") + flag.Parse() + + log.SetPrefix("[evdist] ") + + settings.SettingsDir = path.Clean(settings.SettingsDir) + + log.Println("Creating settingsDist directory...") + TmpSettingsDistDirectory = path.Join(SettingsDistDir, ".tmp") + if _, err := os.Stat(TmpSettingsDistDirectory); os.IsNotExist(err) { + if err = os.MkdirAll(TmpSettingsDistDirectory, 0755); err != nil { + log.Fatal("Unable to create settingsdist directory:", err) + } + } + + TmpSettingsDirectory = path.Join(settings.SettingsDir, ".tmp") + if _, err := os.Stat(TmpSettingsDirectory); os.IsNotExist(err) { + if err = os.MkdirAll(TmpSettingsDirectory, 0755); err != nil { + log.Fatal("Unable to create settings directory:", err) + } + } + + log.Println("Registering directory events...") + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Fatal(err) + } + defer watcher.Close() + + l := &distList{} + l.Timer = time.NewTimer(time.Minute) + + if err := watchsubdir(l, watcher, settings.SettingsDir); err != nil { + log.Fatal(err) + } + + watchedNotify := fsnotify.Create + + for { + select { + case <-l.Timer.C: + if v := l.Pop(); v != nil { + log.Printf("TREATING DIFF: %v", v) + + v, err = settings.ReadNextSettingsFile(path.Join(settings.SettingsDir, fmt.Sprintf("%d.json", v.Id)), v.Id) + if err != nil { + log.Printf("Unable to read %d.json: %s", v.Id, err.Error()) + } else if cur_settings, err := settings.ReadSettings(path.Join(settings.SettingsDir, settings.SettingsFile)); err != nil { + log.Printf("Unable to read settings.json: %s", err.Error()) + } else { + cur_settings = settings.MergeSettings(*cur_settings, v.Values) + + if err = settings.SaveSettings(path.Join(TmpSettingsDirectory, "settings.json"), cur_settings); err != nil { + log.Printf("Unable to save settings.json to tmp dir: %s", err.Error()) + } else if err = os.Rename(path.Join(TmpSettingsDirectory, "settings.json"), path.Join(settings.SettingsDir, "settings.json")); err != nil { + log.Printf("Unable to move settings.json to dest dir: %s", err.Error()) + } else if err = os.Remove(path.Join(settings.SettingsDir, fmt.Sprintf("%d.json", v.Id))); err != nil { + log.Printf("Unable to remove initial diff file (%d.json): %s", v.Id, err.Error()) + } + } + } + l.ResetTimer() + case ev := <-watcher.Events: + if d, err := os.Lstat(ev.Name); err == nil && ev.Op&watchedNotify == watchedNotify && d.Name() != ".tmp" && d.Mode().IsRegular() { + if *debugINotify { + log.Println("Treating event:", ev, "for", ev.Name) + } + go l.treat(ev.Name) + } else if 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 + } else if *debugINotify { + log.Println("Skipped event:", ev, "for", ev.Name) + } + case err := <-watcher.Errors: + log.Println("error:", err) + } + } +} + +func (l *distList) treat(raw_path string) { + bpath := path.Base(raw_path) + + if bpath == "challenge.json" || bpath == "settings.json" { + log.Printf("Copying %s to SETTINGDIST...", bpath) + // Copy content through tmp file + fd, err := os.Open(raw_path) + if err != nil { + log.Printf("ERROR: Unable to open %s: %s", raw_path, err.Error()) + return + } + defer fd.Close() + + tmpfile, err := ioutil.TempFile(TmpSettingsDistDirectory, "") + if err != nil { + log.Printf("ERROR: Unable to create temporary file for %s: %s", bpath, err.Error()) + return + } + + _, err = io.Copy(tmpfile, fd) + tmpfile.Close() + + if err != nil { + log.Printf("ERROR: Unable to copy content to temporary file (%s): %s", bpath, err.Error()) + return + } + + if err = os.Rename(tmpfile.Name(), path.Join(SettingsDistDir, bpath)); err != nil { + log.Println("ERROR: Unable to move file:", err) + return + } + } else if ts, err := strconv.ParseInt(strings.TrimSuffix(bpath, ".json"), 10, 64); err == nil { + activateTime := time.Unix(ts, 0) + + log.Printf("Preparing %s: activation time at %s", bpath, activateTime) + + l.AddEvent(&settings.NextSettingsFile{ + Id: ts, + Date: activateTime, + }) + } else { + log.Println("WARNING: Unknown file to treat: not a valid timestamp:", err.Error()) + } +} diff --git a/evdist/settings.go b/evdist/settings.go new file mode 100644 index 00000000..caba3e5d --- /dev/null +++ b/evdist/settings.go @@ -0,0 +1,110 @@ +package main + +import ( + "log" + "sync" + "time" + + "srs.epita.fr/fic-server/settings" +) + +// distList maintain a nextSettingsFile list up-to-date. +type distList struct { + List []*settings.NextSettingsFile + Lock sync.RWMutex + Timer *time.Timer +} + +// NewDistList creates a distList from the given src directory +func NewDistList(src string) (*distList, error) { + list, err := settings.ListNextSettingsFiles() + if err != nil { + return nil, err + } + return &distList{List: list, Timer: time.NewTimer(time.Minute)}, nil +} + +func (l *distList) TimerNextEvent() *time.Timer { + l.Lock.RLock() + defer l.Lock.RUnlock() + + var min *time.Time + + for _, f := range l.List { + if min == nil || f.Date.Before(*min) { + min = &f.Date + } + } + + if min == nil { + return nil + } + + if min == nil { + return nil + } + + return time.NewTimer(time.Until(*min)) +} + +func (l *distList) AddEvent(nsf *settings.NextSettingsFile) { + l.Lock.Lock() + + istop := len(l.List) + for i, n := range l.List { + if n.Id == nsf.Id { + return + } else if n.Date.After(nsf.Date) { + istop = i + break + } + } + + l.List = append(l.List, nsf) + copy(l.List[istop+1:], l.List[istop:]) + l.List[istop] = nsf + + l.Lock.Unlock() + + if istop == 0 { + l.ResetTimer() + } +} + +func (l *distList) ResetTimer() { + l.Lock.RLock() + defer l.Lock.RUnlock() + + if len(l.List) == 0 { + l.Timer.Reset(time.Minute) + } else { + l.Timer.Reset(time.Until(l.List[0].Date)) + } +} + +func (l *distList) Pop() *settings.NextSettingsFile { + l.Lock.Lock() + defer l.Lock.Unlock() + + if len(l.List) == 0 { + return nil + } + + if time.Now().Before(l.List[0].Date) { + return nil + } + + ret := l.List[0] + l.List = l.List[1:] + return ret +} + +func (l *distList) Print() { + l.Lock.RLock() + defer l.Lock.RUnlock() + + log.Println("Seeing distlist") + for n, i := range l.List { + log.Printf("#%d: %v", n, *i) + } +} diff --git a/frontend/main.go b/frontend/main.go index 8b307de9..75827103 100644 --- a/frontend/main.go +++ b/frontend/main.go @@ -41,13 +41,6 @@ func main() { } } - log.Println("Creating settingsDist directory...") - if _, err := os.Stat(SettingsDistDir); os.IsNotExist(err) { - if err = os.MkdirAll(SettingsDistDir, 0755); err != nil { - log.Fatal("Unable to create settingsdist directory:", err) - } - } - *prefix = strings.TrimRight(*prefix, "/") // Load configuration diff --git a/frontend/settings.go b/frontend/settings.go index 3455c81f..311b3edb 100644 --- a/frontend/settings.go +++ b/frontend/settings.go @@ -12,8 +12,6 @@ import ( var startedFile = "started" -var SettingsDistDir = "./SETTINGSDIST/" - var touchTimer *time.Timer = nil var challengeStart time.Time var challengeEnd time.Time diff --git a/settings/diff.go b/settings/diff.go index 34a2f855..f5535b58 100644 --- a/settings/diff.go +++ b/settings/diff.go @@ -3,7 +3,6 @@ package settings import ( "encoding/json" "fmt" - "log" "os" "path" "reflect" @@ -67,6 +66,9 @@ func ListNextSettingsFiles() ([]*NextSettingsFile, error) { var ret []*NextSettingsFile for _, file := range files { + if len(file.Name()) < 10 { + continue + } ts, err := strconv.ParseInt(file.Name()[:10], 10, 64) if err == nil { nsf, err := ReadNextSettingsFile(path.Join(SettingsDir, file.Name()), ts) @@ -108,7 +110,6 @@ func MergeSettings(current Settings, new map[string]interface{}) *Settings { } if v, ok := new[name]; ok { - log.Println(name, field.Name, v) reflect.ValueOf(¤t).Elem().FieldByName(field.Name).Set(reflect.ValueOf(v)) } } From cfde1689cc33597b2d1fc41c92f8a6fd525662c8 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 8 Jun 2022 02:51:17 +0200 Subject: [PATCH 0569/1637] Remove from frontend the settings distribution role --- backend/main.go | 2 +- configs/synchro.sh | 2 +- dashboard/main.go | 2 +- fickit-backend.yml | 26 ++++++++++++++++++++------ fickit-frontend.yml | 8 +++----- frontend/main.go | 5 ++--- frontend/settings.go | 20 -------------------- 7 files changed, 28 insertions(+), 37 deletions(-) diff --git a/backend/main.go b/backend/main.go index 2bae6bca..6097b7a1 100644 --- a/backend/main.go +++ b/backend/main.go @@ -110,7 +110,7 @@ func main() { } var dsn = flag.String("dsn", fic.DSNGenerator(), "DSN to connect to the MySQL server") - flag.StringVar(&settings.SettingsDir, "settings", settings.SettingsDir, "Base directory where load and save settings") + 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") flag.StringVar(&fic.FilesDir, "files", fic.FilesDir, "Request path prefix to reach files") diff --git a/configs/synchro.sh b/configs/synchro.sh index 77ab9f98..b8dbab9d 100644 --- a/configs/synchro.sh +++ b/configs/synchro.sh @@ -32,7 +32,7 @@ done & while ! [ -f SETTINGS/stop ] || [ /tmp/stop -nt SETTINGS/stop ] do # Synchronize static files pages - rsync -e "$SSH_OPTS" -av --delete PKI TEAMS SETTINGS "${FRONTEND_HOSTNAME}":"${BASEDIR}" + rsync -e "$SSH_OPTS" -av --delete 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/ diff --git a/dashboard/main.go b/dashboard/main.go index f33272cb..69ec447a 100644 --- a/dashboard/main.go +++ b/dashboard/main.go @@ -76,7 +76,7 @@ func main() { 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", settings.SettingsDir, "Base directory where load and save settings") + 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() diff --git a/fickit-backend.yml b/fickit-backend.yml index c8ba0f70..4ec8da66 100644 --- a/fickit-backend.yml +++ b/fickit-backend.yml @@ -190,6 +190,20 @@ services: - /var/lib/fic/sync - /var/lib/fic/submissions - /var/lib/fic/teams + - name: fic-evdist + image: nemunaire/fic-evdist:latest + binds: + - /etc/hosts:/etc/hosts:ro + - /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-backend image: nemunaire/fic-backend:latest env: @@ -197,7 +211,7 @@ services: binds: - /etc/hosts:/etc/hosts:ro - /var/lib/fic/teams:/srv/TEAMS - - /var/lib/fic/settings:/srv/SETTINGS:ro + - /var/lib/fic/settingsdist:/srv/SETTINGSDIST:ro - /var/lib/fic/submissions:/srv/submissions net: /run/netns/fic-backend pid: new @@ -205,7 +219,7 @@ services: uts: new runtime: mkdir: - - /var/lib/fic/settings + - /var/lib/fic/settingsdist - /var/lib/fic/submissions - /var/lib/fic/teams - name: fic-dashboard @@ -216,7 +230,7 @@ services: - /var/lib/fic/dashboard:/srv/DASHBOARD:ro - /var/lib/fic/files:/srv/FILES:ro - /var/lib/fic/teams:/srv/TEAMS:ro - - /var/lib/fic/settings:/srv/SETTINGS:ro + - /var/lib/fic/settingsdist:/srv/SETTINGSDIST:ro net: /run/netns/fic-admin pid: new ipc: new @@ -225,7 +239,7 @@ services: mkdir: - /var/lib/fic/dashboard - /var/lib/fic/teams - - /var/lib/fic/settings + - /var/lib/fic/settingsdist - name: fic-synchro image: nemunaire/rsync:5d1f678641de2197041f4bc4c745e7748bedab02 command: ["/bin/ash", "/root/synchro.sh"] @@ -237,7 +251,7 @@ services: - /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/settings:/srv/SETTINGS: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 @@ -246,7 +260,7 @@ services: mkdir: - /var/lib/fic/files - /var/lib/fic/pki/shared - - /var/lib/fic/settings + - /var/lib/fic/settingsdist - /var/lib/fic/ssh - /var/lib/fic/submissions - /var/lib/fic/teams diff --git a/fickit-frontend.yml b/fickit-frontend.yml index f3af83a2..b32a16b8 100644 --- a/fickit-frontend.yml +++ b/fickit-frontend.yml @@ -178,8 +178,7 @@ services: binds: - /etc/hosts:/etc/hosts:ro - /var/lib/fic/files:/srv/FILES:ro - - /var/lib/fic/settings:/srv/SETTINGS:ro - - /var/lib/fic/settingsdist:/srv/SETTINGSDIST + - /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 @@ -187,7 +186,6 @@ services: runtime: mkdir: - /var/lib/fic/files - - /var/lib/fic/settings - /var/lib/fic/settingsdist - /var/lib/fic/startingblock - /var/lib/fic/submissions @@ -212,7 +210,7 @@ services: - /root/.ssh/id_synchro.pub:/root/.ssh/authorized_keys:ro - /var/lib/fic/files:/srv/FILES - /var/lib/fic/pki:/srv/PKI - - /var/lib/fic/settings:/srv/SETTINGS + - /var/lib/fic/settingsdist:/srv/SETTINGSDIST - /var/lib/fic/submissions:/srv/submissions - /var/lib/fic/teams:/srv/TEAMS - /var/log:/var/log:ro @@ -221,7 +219,7 @@ services: mkdir: - /var/lib/fic/files - /var/lib/fic/pki - - /var/lib/fic/settings + - /var/lib/fic/settingsdist - /var/lib/fic/ssh - /var/lib/fic/submissions - /var/lib/fic/teams diff --git a/frontend/main.go b/frontend/main.go index 75827103..58b8696e 100644 --- a/frontend/main.go +++ b/frontend/main.go @@ -20,8 +20,7 @@ func main() { var bind = flag.String("bind", "127.0.0.1:8080", "Bind port/socket") var prefix = flag.String("prefix", "/", "Request path prefix to strip (from proxy)") var teamsDir = flag.String("teams", "./TEAMS/", "Base directory where find existing teams") - flag.StringVar(&settings.SettingsDir, "settings", settings.SettingsDir, "Base directory where read settings") - flag.StringVar(&SettingsDistDir, "settingsDist", SettingsDistDir, "Directory where place settings to distribute") + flag.StringVar(&settings.SettingsDir, "settings", "./SETTINGSDIST", "Base directory where read settings") flag.StringVar(&startedFile, "startedFile", startedFile, "Path to the file to create/remove whether or not the challenge is running") flag.StringVar(&SubmissionDir, "submission", "./submissions/", "Base directory where save submissions") var simulator = flag.String("simulator", "", "Team to simulate (for development only)") @@ -68,7 +67,7 @@ func main() { http.Handle(fmt.Sprintf("%s/teams.json", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir(*teamsDir)))) http.Handle(fmt.Sprintf("%s/themes.json", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir(*teamsDir)))) http.Handle(fmt.Sprintf("%s/stats.json", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir(*teamsDir)))) - http.Handle(fmt.Sprintf("%s/settings.json", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir(SettingsDistDir)))) + http.Handle(fmt.Sprintf("%s/settings.json", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir(settings.SettingsDir)))) // Serve static assets http.Handle(fmt.Sprintf("%s/css/", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir(staticDir)))) diff --git a/frontend/settings.go b/frontend/settings.go index 311b3edb..c237d22f 100644 --- a/frontend/settings.go +++ b/frontend/settings.go @@ -1,10 +1,8 @@ package main import ( - "io/ioutil" "log" "os" - "path" "time" "srs.epita.fr/fic-server/settings" @@ -26,24 +24,6 @@ func touchStartedFile() { } func reloadSettings(config *settings.Settings) { - // Copy the new settings file for distribution - if data, err := ioutil.ReadFile(path.Join(settings.SettingsDir, settings.SettingsFile)); err != nil { - log.Println("Unable to read settings file:", err) - } else if err = ioutil.WriteFile(path.Join(SettingsDistDir, settings.SettingsFile+".tmp"), data, 0644); err != nil { - log.Println("Unable to write tmp settings file:", err) - } else if err := os.Rename(path.Join(SettingsDistDir, settings.SettingsFile+".tmp"), path.Join(SettingsDistDir, settings.SettingsFile)); err != nil { - log.Println("Unable to move new settings file:", err) - } - - // Copy the challenge info file for distribution - if data, err := ioutil.ReadFile(path.Join(settings.SettingsDir, settings.ChallengeFile)); err != nil { - log.Println("Unable to read challenge info file:", err) - } else if err = ioutil.WriteFile(path.Join(SettingsDistDir, settings.ChallengeFile+".tmp"), data, 0644); err != nil { - log.Println("Unable to write tmp challenge info file:", err) - } else if err := os.Rename(path.Join(SettingsDistDir, settings.ChallengeFile+".tmp"), path.Join(SettingsDistDir, settings.ChallengeFile)); err != nil { - log.Println("Unable to move new challenge info file:", err) - } - if challengeStart != config.Start || challengeEnd != config.End { if touchTimer != nil { touchTimer.Stop() From 159672ec47ee4c2c181d402a91d144df1f495f37 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 8 Jun 2022 03:00:50 +0200 Subject: [PATCH 0570/1637] admin: Don't erase challenge.json if already exists --- admin/main.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/admin/main.go b/admin/main.go index 478f9761..7ffe46e9 100644 --- a/admin/main.go +++ b/admin/main.go @@ -144,15 +144,17 @@ func main() { log.Println("Using", sync.GlobalImporter.Kind()) // Update distributed challenge.json - challengeinfo, err := sync.GetFileContent(sync.GlobalImporter, settings.ChallengeFile) - if err == nil { - 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 _, err := os.Stat(path.Join(settings.SettingsDir, settings.ChallengeFile)); os.IsNotExist(err) { + challengeinfo, err := sync.GetFileContent(sync.GlobalImporter, settings.ChallengeFile) + if err == nil { + 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) + } } } } From 9ea415b857bf1f95f62b064b901646b607055ec6 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 8 Jun 2022 03:06:29 +0200 Subject: [PATCH 0571/1637] admin: Fix nil pointer when seeing public team --- admin/api/team.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/admin/api/team.go b/admin/api/team.go index b58d8a91..c598a160 100644 --- a/admin/api/team.go +++ b/admin/api/team.go @@ -109,7 +109,10 @@ func declareTeamsRoutes(router *gin.RouterGroup) { c.JSON(http.StatusOK, tfile) }) apiTeamsPublicRoutes.GET("/stats.json", func(c *gin.Context) { - team := c.MustGet("team").(*fic.Team) + 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 { From 116c061715c77845c76e6f299c24d806821c2c5f Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 8 Jun 2022 03:22:29 +0200 Subject: [PATCH 0572/1637] dashboard: Update --- dashboard/static.go | 2 +- dashboard/static/index.html | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dashboard/static.go b/dashboard/static.go index b12bcf00..a5a8cdaf 100644 --- a/dashboard/static.go +++ b/dashboard/static.go @@ -81,7 +81,7 @@ func declareStaticRoutes(router *gin.RouterGroup, baseURL string) { 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")))) + http.ServeFile(c.Writer, c.Request, path.Join(fic.FilesDir, strings.TrimPrefix(c.Request.URL.Path, "/"+path.Join(baseURL, "files")))) } }) diff --git a/dashboard/static/index.html b/dashboard/static/index.html index 392537cc..6d0b5a6a 100644 --- a/dashboard/static/index.html +++ b/dashboard/static/index.html @@ -537,7 +537,7 @@ @@ -558,7 +558,7 @@ From 329bd246c7ceaf3d839933bf745e3b8a480fb317 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 8 Jun 2022 03:40:03 +0200 Subject: [PATCH 0573/1637] admin: Add stats about submissions rate --- admin/api/health.go | 68 ++++++++++++++++++++++++++++++++++++ admin/static/js/app.js | 26 ++++++++++++++ admin/static/views/home.html | 16 ++++++++- libfic/stats.go | 35 +++++++++++++++++++ 4 files changed, 144 insertions(+), 1 deletion(-) diff --git a/admin/api/health.go b/admin/api/health.go index 133cf5af..1340d016 100644 --- a/admin/api/health.go +++ b/admin/api/health.go @@ -3,6 +3,7 @@ package api import ( "fmt" "io/ioutil" + "log" "net/http" "os" "path" @@ -10,6 +11,7 @@ import ( "time" "srs.epita.fr/fic-server/admin/pki" + "srs.epita.fr/fic-server/libfic" "github.com/gin-gonic/gin" ) @@ -31,6 +33,8 @@ func declareHealthRoutes(router *gin.RouterGroup) { }) }) router.GET("/health.json", GetHealth) + router.GET("/submissions-stats.json", GetSubmissionsStats) + router.GET("/validations-stats.json", GetValidationsStats) } type healthFileReport struct { @@ -73,3 +77,67 @@ func GetHealth(c *gin.Context) { 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/static/js/app.js b/admin/static/js/app.js index 16ef34ab..16d5d52f 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -1723,6 +1723,32 @@ angular.module("FICApp") } }) + .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 = {}; diff --git a/admin/static/views/home.html b/admin/static/views/home.html index 6f10a9d6..6312622b 100644 --- a/admin/static/views/home.html +++ b/admin/static/views/home.html @@ -1,4 +1,4 @@ -
+

Interface d'administration du challenge

diff --git a/libfic/stats.go b/libfic/stats.go index f6f2dd81..5cab0822 100644 --- a/libfic/stats.go +++ b/libfic/stats.go @@ -179,6 +179,41 @@ func GetTries(t *Team, e *Exercice) (times []time.Time, err error) { } } +// GetValidations retrieves all flag validation made by the matching Team or challenge (both can be nil to not filter). +func GetValidations(t *Team, e *Exercice) (times []time.Time, err error) { + var rows *sql.Rows + + if t == nil { + if e == nil { + rows, err = DBQuery("SELECT time FROM flag_found UNION SELECT time FROM mcq_found ORDER BY time ASC") + } else { + rows, err = DBQuery("SELECT time FROM flag_found WHERE id_exercice = ? UNION SELECT time FROM mcq_found WHERE id_exercice = ? ORDER BY time ASC", e.Id, e.Id) + } + } else { + if e == nil { + rows, err = DBQuery("SELECT time FROM flag_found WHERE id_team = ? UNION SELECT time FROM mcq_found WHERE id_team = ? ORDER BY time ASC", t.Id, t.Id) + } else { + rows, err = DBQuery("SELECT time FROM flag_found WHERE id_team = ? AND id_exercice = ? UNION SELECT time FROM mcq_found WHERE id_team = ? AND id_exercice = ? ORDER BY time ASC", t.Id, e.Id, t.Id, e.Id) + } + } + + if err != nil { + return + } else { + defer rows.Close() + + for rows.Next() { + var tm time.Time + if err = rows.Scan(&tm); err != nil { + return + } + times = append(times, tm) + } + err = rows.Err() + return + } +} + // GetTryRank generates a special rank based on number of attempts func GetTryRank() ([]int64, error) { if rows, err := DBQuery("SELECT id_team, COUNT(*) AS score FROM exercice_tries GROUP BY id_team HAVING score > 0 ORDER BY score DESC"); err != nil { From e9dd35f8ac3391ebb26b666d94d8c1092b7c5991 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 8 Jun 2022 04:39:20 +0200 Subject: [PATCH 0574/1637] settings: Can disable all submission button for maintenance --- admin/static/js/app.js | 11 +++++++++++ admin/static/views/settings.html | 18 +++++++++++++----- .../ui/src/components/ExerciceFlags.svelte | 8 +++++++- .../ui/src/components/ExerciceHints.svelte | 2 +- frontend/ui/src/components/FlagKey.svelte | 2 +- frontend/ui/src/components/FormIssue.svelte | 3 ++- .../ui/src/components/TeamChangeName.svelte | 3 ++- frontend/ui/src/stores/settings.js | 2 ++ settings/settings.go | 2 ++ 9 files changed, 41 insertions(+), 10 deletions(-) diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 16d5d52f..8e1d52ba 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -529,6 +529,7 @@ angular.module("FICApp") $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() { @@ -542,6 +543,7 @@ angular.module("FICApp") $scope.config.$promise.then(function(response) { $scope.dist_config = Object.assign({}, response); response.enableExerciceDepend = response.unlockedChallengeDepth >= 0; + response.disabledsubmitbutton = response.disablesubmitbutton && response.disablesubmitbutton.length > 0; $rootScope.settings.start = new Date(response.start); $rootScope.settings.end = new Date(response.end); $rootScope.settings.generation = new Date(response.generation); @@ -562,6 +564,13 @@ angular.module("FICApp") $scope.config.unlockedChallengeDepth = -1; }; + $scope.submitButtonStateChange = function() { + if ($scope.config.disabledsubmitbutton) + $scope.config.disablesubmitbutton = "Mise à jour en cours..."; + else + $scope.config.disablesubmitbutton = ""; + }; + $scope.saveChallengeInfo = function() { this.challenge.duration = $scope.duration; this.challenge.$update(function(response) { @@ -577,6 +586,7 @@ angular.module("FICApp") 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 != '0001-01-01T00:00:00Z') { updateQuery['t'] = this.activateTime; @@ -590,6 +600,7 @@ angular.module("FICApp") $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); $rootScope.settings.end = new Date(nEnd); $rootScope.settings.generation = new Date(nGen); diff --git a/admin/static/views/settings.html b/admin/static/views/settings.html index 060a8ca3..17dd0b36 100644 --- a/admin/static/views/settings.html +++ b/admin/static/views/settings.html @@ -158,16 +158,16 @@
@@ -206,6 +206,14 @@
+
+ +
+
diff --git a/frontend/ui/src/components/ExerciceFlags.svelte b/frontend/ui/src/components/ExerciceFlags.svelte index c176db57..74f64cc2 100644 --- a/frontend/ui/src/components/ExerciceFlags.svelte +++ b/frontend/ui/src/components/ExerciceFlags.svelte @@ -10,11 +10,13 @@ ListGroupItem, Progress, Spinner, + Tooltip, } from 'sveltestrap'; import { blake2b } from 'hash-wasm'; import { my } from '../stores/my.js'; + import { settings } from '../stores/settings.js'; import DateFormat from './DateFormat.svelte'; import FlagKey from './FlagKey.svelte'; @@ -224,13 +226,17 @@ + {#if $settings.disablesubmitbutton} + {$settings.disablesubmitbutton} + {/if}
diff --git a/frontend/ui/src/components/ExerciceHints.svelte b/frontend/ui/src/components/ExerciceHints.svelte index 70b1e0cb..212de408 100644 --- a/frontend/ui/src/components/ExerciceHints.svelte +++ b/frontend/ui/src/components/ExerciceHints.svelte @@ -117,7 +117,7 @@ {#if !(hint.content || hint.file) || (!hint.file && hint.hidden)}
{#if !(hint.content || hint.file)} -
- diff --git a/frontend/ui/src/components/TeamChangeName.svelte b/frontend/ui/src/components/TeamChangeName.svelte index 7ab8916f..2bd199f5 100644 --- a/frontend/ui/src/components/TeamChangeName.svelte +++ b/frontend/ui/src/components/TeamChangeName.svelte @@ -8,6 +8,7 @@ } from 'sveltestrap'; import { my } from '../stores/my.js'; + import { settings } from '../stores/settings.js'; export let refresh_my; @@ -101,7 +102,7 @@
- +
diff --git a/frontend/ui/src/stores/settings.js b/frontend/ui/src/stores/settings.js index e77010c2..931d44ce 100644 --- a/frontend/ui/src/stores/settings.js +++ b/frontend/ui/src/stores/settings.js @@ -18,6 +18,8 @@ function createSettingsStore() { settings.generation = new Date(settings.generation); if (settings.activateTime) settings.activateTime = new Date(settings.activateTime); + if (!settings.disablesubmitbutton) + settings.disablesubmitbutton = null; settings.recvTime = recvTime; const x_fic_time = res_settings.headers.get("x-fic-time"); diff --git a/settings/settings.go b/settings/settings.go index 37971c4e..eb2bac22 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -77,6 +77,8 @@ type Settings struct { DisplayMCQBadCount bool `json:"displayMCQBadCount,omitempty"` // EventKindness will ask browsers to delay notification interval. EventKindness bool `json:"eventKindness,omitempty"` + // DisableSubmitButton replace button by this text (eg. scheduled updates, ...). + DisableSubmitButton string `json:"disablesubmitbutton,omitempty"` } // ExistsSettings checks if the settings file can by found at the given path. From 750db69b0626300db5a51d7ddb25e6eae3dd4764 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 8 Jun 2022 04:55:19 +0200 Subject: [PATCH 0575/1637] settings: Can display a global message on all pages --- admin/static/views/settings.html | 25 +++++++++++++++++++++++++ frontend/ui/src/routes/__layout.svelte | 5 +++++ settings/settings.go | 4 ++++ 3 files changed, 34 insertions(+) diff --git a/admin/static/views/settings.html b/admin/static/views/settings.html index 17dd0b36..ec059330 100644 --- a/admin/static/views/settings.html +++ b/admin/static/views/settings.html @@ -98,6 +98,31 @@
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
diff --git a/frontend/ui/src/routes/__layout.svelte b/frontend/ui/src/routes/__layout.svelte index d3b305b8..e988262d 100644 --- a/frontend/ui/src/routes/__layout.svelte +++ b/frontend/ui/src/routes/__layout.svelte @@ -156,6 +156,11 @@ +{#if $settings.globaltopmessage} +
+ {$settings.globaltopmessage} +
+{/if}
diff --git a/settings/settings.go b/settings/settings.go index eb2bac22..8531def3 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -79,6 +79,10 @@ type Settings struct { EventKindness bool `json:"eventKindness,omitempty"` // DisableSubmitButton replace button by this text (eg. scheduled updates, ...). DisableSubmitButton string `json:"disablesubmitbutton,omitempty"` + // GlobalTopMessage display a message on top of each pages. + GlobalTopMessage string `json:"globaltopmessage,omitempty"` + // GlobalTopMessageVariant control the variant/color of the previous message. + GlobalTopMessageVariant string `json:"globaltopmessagevariant,omitempty"` } // ExistsSettings checks if the settings file can by found at the given path. From 30a665ff72847ce9b2e5d661ff0497848adcf37f Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 8 Jun 2022 10:00:01 +0200 Subject: [PATCH 0576/1637] Add theodore keys --- configs/authorized_keys | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configs/authorized_keys b/configs/authorized_keys index cc7b64ed..447e2244 100644 --- a/configs/authorized_keys +++ b/configs/authorized_keys @@ -23,6 +23,8 @@ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDt+2M0slOtHGo9WtuXe71qQSVP/lxZvZsYFtrV2Knz ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDOxGQgZMHlBoRFDXUfVhOMUPbPRhN8dD+jW8JkRG2IT/8nqT8jSxqeOqSm99RStIDtnCMB5DehdnyGTwD0uYAUsm1f1VLonbx1em2K6C4B4sQtojGwD0JX/E6CDmMRJI0d9f8xe9jWePwvy4nQePGnvZ71PgIBnbyunwB9wCZo2MexQlhVp/+ftxuCYbwPsrA6uegsC6vCvNq6yQx0Gobft4s8HGLRw2YyJfs4puEaeSq8MAM84NSIf0VyV5rpzDVAHVoXLQ9ui/TamBCqSZ0c6tn+oUhBLG0a1sIaL54esiBwgbcPjUjex/AMqK4Jk0vpGCdwge5YvW0somwwhlOwdO7jHmISQkA+h7iAL0UgjH9GX1mTMpL6prK8p9ubJ5y3puyFMEnShtjxc+lHv0EB6kvMsPVXsAaYDLRBJ+8EXyrWa/O/XuppOASx2EtVYuVclfKhD2++yGeIBImv29b/nsv1ol0pqe06regQoOSqLnr7uOY+kXL7p5SIs5MGRZbj9y9ztmM3DR2mw9LLKL9R0NF5HDLfMmaZ1yR1VYsXlWh+VyydRCoY4jJHQ2OAfaUMfrzUd5l0G76iMEhvS+B9ygihQTsva2iM1vg4ttxv/Mc0C4hxz7V4YdBmcfyQE4BMZ+3hbKVGI+cv8LI2ahXHqcqPHq7PFK8gv4bC9aTA7w== justin.puchelle # theodore.decazes +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC2Sc63GCAjbfdLVkDciSKvdsKffSIZI6Svl27XNgIoWsC59M3yh/x7uy6bqv8Krs7dvi+LWYTj4dKvFvL2+iDKHX8APEjWlQbJPghWScD0nIqB1IM4INX8WjcQ2AEiJOAXj58DnFAugwm4O1OTZj82wbmzyuoB6qvR0Psrrz5fWK0xOqgESx/d7+AhPelME/iVNaBWwjj6YAXPNYw0XTATl5w3+ge2QiHpyPADunC1W5u/cxHurs3rxWCYgkcxmFud2yMc6KOGeJyClte+2pOv6YG2BVs1sCsxphshVL6aQjltxAHnZRmOR1AwlhtA3TjBSmJAXKniA4pkIPVf3wgHYKJhqVkxppi/5uQ9SLSd9BfqswXH5/Lv3TUutYoehwNsCw6ML45rI/6JPqo5C0RzEGxj1g3dkbh0hJhi7+/x6qEq9Z98p5vx92Srj3/ibTxh8GBgBWJA3wMPJZtPRv5AA9fs4D4ws4aUEnxHhpUpmjOBbIziAS6bAp/QjFONzEA0buhgA8/XHKGvyFPW18NLqR0U+ByCWv+ZMr1J2mE2jZaHpASYJWQTiMPDU7WKyrvFYzrdVJpCxLbBNksD+RJ/NGTVlpEPZ/KSQyfjJVoZr2mFRP5nWyQq9StJYEP+1HrNJ6iaKKkJ5fQ6+VfUkz9XnyqArNV946q1nyjxECP1OQ== srs odore@LAPTOP-71BP71AI +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDEFkbXzYBalMeRKC7mCKppdVBJP25Zp1MsqrMMs3glODdT3/N1sijBWLDP9GamoTe+zWmgtuSB3GTgFuQCvxd88goeBgtn70ZsQrk7dgXRs8gHhu92JneE63E4Wmt1tP5+0Ht7npC4y64oaU5r60wx9F9IMUA4aJXNxWr/1tBsxEFHbsDW3h3lTwdlY5xPlCQhMd8yMijrkvcbpwGuUoqFVF49RpDhom7mQ66nF8yIrTzR3zxqLI9uZ6EJl3aSOf/Dr/ASj/+L+d9MdDxjDvQmffXW7utpt1Hl81iZftFEATr9lrFbv6uamoz/P/vPLeXakNzKSIBnehWQ1YRjE80kbZ83XI6bPoF14uZjDUmolrjN2haEcitfANdVeE7Y7GjaIvKY818EWWVOFb9JJtu/h57WGrwyL/ywuGfmfLvCVbfW2RZKobVmxn+KUgvFMauXGu4FOrT58XgT+H1gx6GQxsQatWFJLLUkuUjlXdhXl5RJPQx04WvcDwOGy0C7vyQoNws04kqunOqWhqW47hgYGMApy1I/XyWeTwV7ltsCeIzH0Eg2TbO7b/a2wvWuKQFUPeczP3icQ84RXlO1R8TdXKuE4MU9Ff3tfuYUJbDrIXpGlaa79AA5A7Jg+FAsxvSKMx9w8JJDOEpsE9yWt0WoiLbCdfVNzdlCe9KmtLBNaw== kali@kali ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDPhJVhmf5bDd0XNc2P8sU3dYMHUyUJSSwY0F++U9Ru7Sc7M6iUIc9OSqRn+SWVJZc8t8IB/j6L0pnai4Htm7zvWFBjF49/gf76E9QP7BY1DZPIzyid3CAMtBjR4xqL92ztkbGtBP2cvBlOmcZZdvVfMkG2OiBdtD4DnuMHeBmZQeBYMxG3Op2S/LYCCd+1ICaIZd3BoboOhHb1AQdpF6+lJueBEmjDvVaO2X9RjC2JsLY3ajy6f5Ic2ZAQqc6lQ2Qc5u5MKtwTnRYCdORbiKFxRHlwTBr4xMNV9wAt8ZDmXphAXrRMKCXGgNGob7AJxnVV6WeMiZmXqnT9apfyJPHUEUEoVt/EHbSQRRG1L1MuQfPI8z33QA7FZs6V3E6xKZX3ARgGN65E4Os7ADdRChtB2Bf4Cx63RETrog7mWXer9GJN8fQmSvytozeXlQoj6EgoUIG/x0wO87M7CL1w5S8FJJsP1PPMrCfd8Dzigsl3vJV4PkCGZvQKb2CFzbTsEFPCY+nBXhL3bA6HLJfFBsc/QIBAGkRbig1VAeihgh3oZsIY25hm3VtQsNKWVC+MroY/OKnAetfPlUcSkqqyfJbOuzWkmnhBmK9YHQjBT0cF3sHWPFwkddJOQwFepqIF9p8DiK24RpEwBcc0QMYF+sMFiwjm0pRvZdNA3MgJ7VgnLw== theodore.decazes ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDEFkbXzYBalMeRKC7mCKppdVBJP25Zp1MsqrMMs3glODdT3/N1sijBWLDP9GamoTe+zWmgtuSB3GTgFuQCvxd88goeBgtn70ZsQrk7dgXRs8gHhu92JneE63E4Wmt1tP5+0Ht7npC4y64oaU5r60wx9F9IMUA4aJXNxWr/1tBsxEFHbsDW3h3lTwdlY5xPlCQhMd8yMijrkvcbpwGuUoqFVF49RpDhom7mQ66nF8yIrTzR3zxqLI9uZ6EJl3aSOf/Dr/ASj/+L+d9MdDxjDvQmffXW7utpt1Hl81iZftFEATr9lrFbv6uamoz/P/vPLeXakNzKSIBnehWQ1YRjE80kbZ83XI6bPoF14uZjDUmolrjN2haEcitfANdVeE7Y7GjaIvKY818EWWVOFb9JJtu/h57WGrwyL/ywuGfmfLvCVbfW2RZKobVmxn+KUgvFMauXGu4FOrT58XgT+H1gx6GQxsQatWFJLLUkuUjlXdhXl5RJPQx04WvcDwOGy0C7vyQoNws04kqunOqWhqW47hgYGMApy1I/XyWeTwV7ltsCeIzH0Eg2TbO7b/a2wvWuKQFUPeczP3icQ84RXlO1R8TdXKuE4MU9Ff3tfuYUJbDrIXpGlaa79AA5A7Jg+FAsxvSKMx9w8JJDOEpsE9yWt0WoiLbCdfVNzdlCe9KmtLBNaw== theodore.decazes ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCgE3vbJDdpmHqWL2nZC2QqjsWCBTE0owOAll3qxcdrIJLQvB08jYpoaFM/WxULuGd5OHNyM97jxYrSNuBzUxpKGZuP8Nq66BXuBKDaREx/MFO4BgdQodhw5/RSigBK7YTDtonGbEpXu7Lum9kcdjbkRQGDDDEMVpq26X1I2o/QcNzXi9CE9hAD2hSiKOkvoh3FEyU7mWnV0nnXcSkyiFPSWubhgvKt1MRCUPhTn2173zfOPHL0IEECg2LQhDdGqr0e/KctHNUHMNM1gLYde2DWDB6uQQjOwvjwyVbQbR5uOCC0n3SLOcETWgWCgiTrZMeaFaDnsZIsSZzeXAEv7jWIgtuhbumKs2KEhpmC6TsxaUrB67hURvPNwwp44v6pgXgrrDbqnzRo88/nfF2iuPj9CoWss1Afp5V37FXs47AQp0FfgZRBq+180eYJOaWfqUSLFjnz7ZH0NyCQsP3SKoNZfIdrv2LsSn/41FMhN+6Y1k/IWmfdp/YZv9zodlCJ9mngMUXQZmzzQV3ZFxZikCLwCY2R2i1MUZ7wjVu//rsdQ4zgoMQpC5Ngr0GZkguW18jT87Hdi/nMflV+IICY7Pwrvxd7/llE0t6ii/Wfkb14D3NdGw9utjyfhmK+wdUkC7QTckjMnM0iDIIaKGx/VL62Q8odXwQE0y/DWtPauV3/9w== theodore.decazes From 498e3c5b63845a6658225ff4c70aaf3aad02bbbb Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 8 Jun 2022 10:00:21 +0200 Subject: [PATCH 0577/1637] evdist: Chmod temporary files --- evdist/main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/evdist/main.go b/evdist/main.go index 2aec1960..53f569c3 100644 --- a/evdist/main.go +++ b/evdist/main.go @@ -150,6 +150,8 @@ func (l *distList) treat(raw_path string) { return } + os.Chmod(tmpfile.Name(), 0644) + if err = os.Rename(tmpfile.Name(), path.Join(SettingsDistDir, bpath)); err != nil { log.Println("ERROR: Unable to move file:", err) return From 36af72d616e266b407c489b00304d1f5a7a21d44 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 8 Jun 2022 11:22:30 +0200 Subject: [PATCH 0578/1637] admin: Fix team symlink for dex generation --- admin/api/password.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/api/password.go b/admin/api/password.go index c6222766..d90cba14 100644 --- a/admin/api/password.go +++ b/admin/api/password.go @@ -210,7 +210,7 @@ func genDexConfig() ([]byte, error) { } else { // Also generate team associations for _, team := range teams { - if err := os.Symlink(fmt.Sprintf("%d", team.Id), path.Join(TeamsDir, fmt.Sprintf("%02d", team.Id))); err != nil { + 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()) } From 38857054baf3d1cc37f1eb8b6383234934a169dd Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 8 Jun 2022 11:23:55 +0200 Subject: [PATCH 0579/1637] dashboard: Fix dockerfile --- Dockerfile-dashboard | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile-dashboard b/Dockerfile-dashboard index 7b162fe3..2c049ec7 100644 --- a/Dockerfile-dashboard +++ b/Dockerfile-dashboard @@ -24,7 +24,7 @@ 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 frontend/ui/static/favicon.ico /srv/htdocs-dashboard/ +COPY dashboard/static/index.html /srv/htdocs-dashboard/ COPY admin/static/css/bootstrap.min.css qa/static/css/fic.css admin/static/css/glyphicon.css /srv/htdocs-dashboard/css/ COPY admin/static/fonts /srv/htdocs-dashboard/fonts COPY frontend/ui/static/img/ dashboard/static/img/logo-epita-bw.png dashboard/static/img/sii.png /srv/htdocs-dashboard/img/ From 7a5c1eeba71812fdae5a0a5a2731e5f30dde7812 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 8 Jun 2022 12:24:38 +0200 Subject: [PATCH 0580/1637] admin: Fix flag edition --- admin/api/exercice.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/admin/api/exercice.go b/admin/api/exercice.go index d4b7f36d..f0f55855 100644 --- a/admin/api/exercice.go +++ b/admin/api/exercice.go @@ -728,6 +728,7 @@ type uploadedFlag struct { Type string Label string Placeholder string + Help string IgnoreCase bool Multiline bool NoTrim bool @@ -826,6 +827,7 @@ func updateExerciceFlag(c *gin.Context) { } flag.Placeholder = uk.Placeholder + flag.Help = uk.Help flag.IgnoreCase = uk.IgnoreCase flag.Multiline = uk.Multiline if len(uk.Flag) > 0 { From e922171f1726db6ced904ed5190db2c7ac7c9492 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 8 Jun 2022 12:31:08 +0200 Subject: [PATCH 0581/1637] evdist: Fix some segv --- evdist/main.go | 15 ++++++++++++--- evdist/settings.go | 24 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/evdist/main.go b/evdist/main.go index 53f569c3..e68cdf35 100644 --- a/evdist/main.go +++ b/evdist/main.go @@ -89,7 +89,7 @@ func main() { v, err = settings.ReadNextSettingsFile(path.Join(settings.SettingsDir, fmt.Sprintf("%d.json", v.Id)), v.Id) if err != nil { - log.Printf("Unable to read %d.json: %s", v.Id, err.Error()) + log.Printf("Unable to read json: %s", err.Error()) } else if cur_settings, err := settings.ReadSettings(path.Join(settings.SettingsDir, settings.SettingsFile)); err != nil { log.Printf("Unable to read settings.json: %s", err.Error()) } else { @@ -110,8 +110,17 @@ func main() { if *debugINotify { log.Println("Treating event:", ev, "for", ev.Name) } - go l.treat(ev.Name) - } else if ev.Op&fsnotify.Write == fsnotify.Write { + l.treat(ev.Name) + } else if err == nil && ev.Op&watchedNotify == fsnotify.Remove && d.Mode().IsRegular() { + if *debugINotify { + log.Println("Treating deletion event:", ev, "for", ev.Name) + } + if ts, err := strconv.ParseInt(strings.TrimSuffix(path.Base(ev.Name), ".json"), 10, 64); err == nil { + log.Println("Unable to parseint", ev.Name, err.Error()) + } else { + l.DelEvent(ts) + } + } 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 } else if *debugINotify { diff --git a/evdist/settings.go b/evdist/settings.go index caba3e5d..fab363f9 100644 --- a/evdist/settings.go +++ b/evdist/settings.go @@ -71,6 +71,30 @@ func (l *distList) AddEvent(nsf *settings.NextSettingsFile) { } } +func (l *distList) DelEvent(id int64) { + l.Lock.Lock() + + istop := len(l.List) + for i, n := range l.List { + if n.Id == id { + istop = i + break + } + } + + if istop == len(l.List)-1 { + l.List = l.List[:istop] + } else if istop != len(l.List) { + l.List = append(l.List[:istop], l.List[istop+1:]...) + } + + l.Lock.Unlock() + + if istop == 0 { + l.ResetTimer() + } +} + func (l *distList) ResetTimer() { l.Lock.RLock() defer l.Lock.RUnlock() From 0a8d0dad30d53c4dfd647a10df5fde05759817f6 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 8 Jun 2022 16:37:25 +0200 Subject: [PATCH 0582/1637] Can Unlock challenge up to a certain level --- admin/api/settings.go | 1 + admin/static/views/settings.html | 7 +++++++ backend/main.go | 3 ++- libfic/team.go | 10 ++++++++++ settings/settings.go | 2 ++ 5 files changed, 22 insertions(+), 1 deletion(-) diff --git a/admin/api/settings.go b/admin/api/settings.go index 7bb907ee..e66dc4a7 100644 --- a/admin/api/settings.go +++ b/admin/api/settings.go @@ -283,6 +283,7 @@ func deleteNextSettings(c *gin.Context) { func ApplySettings(config *settings.Settings) { fic.PartialValidation = config.PartialValidation fic.UnlockedChallengeDepth = config.UnlockedChallengeDepth + fic.UnlockedChallengeUpTo = config.UnlockedChallengeUpTo fic.DisplayAllFlags = config.DisplayAllFlags fic.DisplayMCQBadCount = config.DisplayMCQBadCount fic.FirstBlood = config.FirstBlood diff --git a/admin/static/views/settings.html b/admin/static/views/settings.html index ec059330..79bbe690 100644 --- a/admin/static/views/settings.html +++ b/admin/static/views/settings.html @@ -189,6 +189,13 @@
+
+ + +
+
-
+
Paramètres de synchronisation diff --git a/admin/static/views/sync.html b/admin/static/views/sync.html index 1ccde0f1..28d4405c 100644 --- a/admin/static/views/sync.html +++ b/admin/static/views/sync.html @@ -1,4 +1,4 @@ -
+

Synchronisation @@ -14,7 +14,7 @@

-
+

Import des thèmes @@ -77,7 +77,7 @@ -

From c7968fb2569b85f45e00f15d5b851bbb5938a4e7 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 11 Jul 2022 11:11:35 +0200 Subject: [PATCH 0605/1637] admin: Add button to switch from WIP to PROD --- admin/api/settings.go | 13 +++++++++++++ admin/static/js/app.js | 17 ++++++++++++++--- admin/static/views/settings.html | 3 ++- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/admin/api/settings.go b/admin/api/settings.go index e66dc4a7..cf0df11c 100644 --- a/admin/api/settings.go +++ b/admin/api/settings.go @@ -47,6 +47,19 @@ func declareSettingsRoutes(router *gin.RouterGroup) { apiNextSettingsRoutes.DELETE("", deleteNextSettings) router.POST("/reset", reset) + + 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) { diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 69b3153a..55574e47 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -455,7 +455,7 @@ angular.module("FICApp") $rootScope.Utils = { keys : Object.keys }; - function refresh() { + $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); @@ -464,8 +464,8 @@ angular.module("FICApp") $rootScope.recvTime(response); }) } - refresh(); - $interval(refresh, 10000); + $rootScope.refreshSettings(); + $interval($rootScope.refreshSettings, 10000); $rootScope.toasts = []; $rootScope.addToast = function(kind, title, msg, yesFunc, noFunc, tmout) { @@ -655,6 +655,17 @@ angular.module("FICApp") } }); }; + $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("SyncController", function($scope, $rootScope, ROSettings, $location, $http, $interval) { diff --git a/admin/static/views/settings.html b/admin/static/views/settings.html index 0c65388f..78cda4a9 100644 --- a/admin/static/views/settings.html +++ b/admin/static/views/settings.html @@ -359,6 +359,7 @@
+

Changements anticipés

@@ -402,7 +403,7 @@
- Paramètres de synchronisation + Paramètres de synchronisation From a0013947dd241bacb6ca0d716c427b0b56baadb8 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 11 Jul 2022 11:15:51 +0200 Subject: [PATCH 0606/1637] admin: Disable syncVideos when prod mod enabled --- admin/static/views/sync.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/static/views/sync.html b/admin/static/views/sync.html index 28d4405c..d63143fc 100644 --- a/admin/static/views/sync.html +++ b/admin/static/views/sync.html @@ -50,7 +50,7 @@
- +
From 08ea1bac0de81cd41316378f659e19faf4a03aef Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Mon, 11 Jul 2022 11:22:12 +0200 Subject: [PATCH 0607/1637] admin: Arrange unlockedChallengeUpTo field --- admin/static/views/settings.html | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/admin/static/views/settings.html b/admin/static/views/settings.html index 78cda4a9..d69a8db3 100644 --- a/admin/static/views/settings.html +++ b/admin/static/views/settings.html @@ -174,13 +174,6 @@
-
- -
-
-
-