From a7c3fe76f23f9bd5ba15adcb8a469faf53ad3769 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Tue, 7 Jan 2020 16:06:00 +0100 Subject: [PATCH 01/38] feat(config): improve configurability of VerpMode --- config/settings.yml | 4 ++-- src/Application.hs | 2 +- src/Settings.hs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config/settings.yml b/config/settings.yml index 61e0f0b03..3a3263a01 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -14,8 +14,8 @@ mail-from: email: "_env:MAILFROM_EMAIL:uniworx@localhost" mail-object-domain: "_env:MAILOBJECT_DOMAIN:localhost" mail-verp: - separator: "+" - at-replacement: "=" + separator: "_env:VERP_SEPARATOR:+" + at-replacement: "_env:VERP_AT_REPLACEMENT:=" mail-support: name: "_env:MAILSUPPORT_NAME:" email: "_env:MAILSUPPORT:uni2work@ifi.lmu.de" diff --git a/src/Application.hs b/src/Application.hs index 2da92e313..7ae3d5397 100644 --- a/src/Application.hs +++ b/src/Application.hs @@ -194,7 +194,7 @@ makeFoundation appSettings'@AppSettings{..} = do runAppLoggingT tempFoundation $ do $logInfoS "InstanceID" $ UUID.toText appInstanceID - -- logDebugS "Configuration" $ tshow appSettings' + $logDebugS "Configuration" $ tshow appSettings' smtpPool <- for appSmtpConf $ \c -> do $logDebugS "setup" "SMTP-Pool" diff --git a/src/Settings.hs b/src/Settings.hs index d70830a4f..ca4610ebb 100644 --- a/src/Settings.hs +++ b/src/Settings.hs @@ -393,7 +393,7 @@ instance FromJSON AppSettings where appMailFrom <- o .: "mail-from" appMailObjectDomain <- o .: "mail-object-domain" - appMailVerp <- o .: "mail-verp" + appMailVerp <- fromMaybe VerpNone . join <$> (o .:? "mail-verp" <|> pure Nothing) appMailSupport <- o .: "mail-support" appJobWorkers <- o .: "job-workers" From 674b94938812e93958e991367c261337fe64f2f6 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Tue, 7 Jan 2020 16:22:52 +0100 Subject: [PATCH 02/38] fix: divide by zero --- messages/uniworx/de-de-formal.msg | 1 + messages/uniworx/en-eu.msg | 1 + src/Handler/Utils/Table/Cells.hs | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/messages/uniworx/de-de-formal.msg b/messages/uniworx/de-de-formal.msg index dde4219b2..92b612528 100644 --- a/messages/uniworx/de-de-formal.msg +++ b/messages/uniworx/de-de-formal.msg @@ -1669,6 +1669,7 @@ CsvImportAborted: CSV-Import abgebrochen CsvImportExplanationLabel: Hinweise zum CSV-Import Proportion c@Text of@Text prop@Rational: #{c}/#{of} (#{rationalToFixed2 (100 * prop)}%) +ProportionNoRatio c@Text of@Text: #{c}/#{of} CourseUserCsvName tid@TermId ssh@SchoolId csh@CourseShorthand: #{foldCase (termToText (unTermKey tid))}-#{foldedCase (unSchoolKey ssh)}-#{foldedCase csh}-teilnehmer ExamUserCsvName tid@TermId ssh@SchoolId csh@CourseShorthand examn@ExamName: #{foldCase (termToText (unTermKey tid))}-#{foldedCase (unSchoolKey ssh)}-#{foldedCase csh}-#{foldedCase examn}-teilnehmer diff --git a/messages/uniworx/en-eu.msg b/messages/uniworx/en-eu.msg index 3810b4bbe..824c95333 100644 --- a/messages/uniworx/en-eu.msg +++ b/messages/uniworx/en-eu.msg @@ -1667,6 +1667,7 @@ CsvImportAborted: CSV import aborted CsvImportExplanationLabel: Informating regarding CSV import Proportion c of prop: #{c}/#{of} (#{rationalToFixed2 (100 * prop)}%) +ProportionNoRatio c of: #{c}/#{of} CourseUserCsvName tid ssh csh: #{foldCase (termToText (unTermKey tid))}-#{foldedCase (unSchoolKey ssh)}-#{foldedCase csh}-participants ExamUserCsvName tid ssh csh examn: #{foldCase (termToText (unTermKey tid))}-#{foldedCase (unSchoolKey ssh)}-#{foldedCase csh}-#{foldedCase examn}-participants diff --git a/src/Handler/Utils/Table/Cells.hs b/src/Handler/Utils/Table/Cells.hs index 295f6ceb6..5a53a8070 100644 --- a/src/Handler/Utils/Table/Cells.hs +++ b/src/Handler/Utils/Table/Cells.hs @@ -197,7 +197,9 @@ numCell :: (IsDBTable m a, ToMessage b) => b -> DBCell m a numCell = textCell . toMessage propCell :: (IsDBTable m a, Real b, ToMessage b) => b -> b -> DBCell m a -propCell curr max' = i18nCell $ MsgProportion (toMessage curr) (toMessage max') (toRational curr / toRational max') +propCell curr max' + | max' /= 0 = i18nCell $ MsgProportion (toMessage curr) (toMessage max') (toRational curr / toRational max') + | otherwise = i18nCell $ MsgProportionNoRatio (toMessage curr) (toMessage max') int64Cell :: IsDBTable m a => Int64-> DBCell m a int64Cell = numCell From b78c48465a4f42366c9a8e8ba924b8d5c2315d71 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Fri, 20 Dec 2019 19:24:43 +0100 Subject: [PATCH 03/38] feat: generate & include new favicon --- .gitignore | 1 + assets/favicon.svg | 29 + config/settings.yml | 1 + package-lock.json | 1991 ++++++++++++++++++++++++++- package.json | 3 + records.json | 26 + src/Foundation.hs | 2 + src/Settings.hs | 2 + src/Settings/StaticFiles.hs | 8 +- src/Settings/StaticFiles/Favicon.hs | 38 + webpack.config.js | 39 +- 11 files changed, 2134 insertions(+), 6 deletions(-) create mode 100644 assets/favicon.svg create mode 100644 src/Settings/StaticFiles/Favicon.hs diff --git a/.gitignore b/.gitignore index 2545f4169..681d12b11 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ test.log /.npmrc /config/webpack.yml static/wp-*/ +/config/favicon.json diff --git a/assets/favicon.svg b/assets/favicon.svg new file mode 100644 index 000000000..3ac8d6f3f --- /dev/null +++ b/assets/favicon.svg @@ -0,0 +1,29 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/config/settings.yml b/config/settings.yml index 3a3263a01..9bdb23939 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -5,6 +5,7 @@ static-dir: "_env:STATIC_DIR:static" webpack-manifest: "_env:WEBPACK_MANIFEST:config/webpack.yml" +favicon-stats: "_env:FAVICON_STATS:config/favicon.json" host: "_env:HOST:*4" # any IPv4 host port: "_env:PORT:3000" ip-from-header: "_env:IP_FROM_HEADER:false" diff --git a/package-lock.json b/package-lock.json index e25a06694..8375b2fbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2567,6 +2567,351 @@ "integrity": "sha512-EKKR4p0higjsIPKjSSkGqtweUwo/GgR/zKL4rCwzF5Z/BZ/ebJZaS8ZjGE7YUNEN63SYk2WhpJVI+l9dwfU7RQ==", "dev": true }, + "@jimp/bmp": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.9.3.tgz", + "integrity": "sha512-wXZYccgGQAsIK8DZX0wZE3gbSd2mL2+eheSJMts6I5hQjxhVRZd1Gwu425nUQGzfKCOgKYTW0nLv7/8OoOTTkw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "bmp-js": "^0.1.0", + "core-js": "^3.4.1" + } + }, + "@jimp/core": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.9.3.tgz", + "integrity": "sha512-kB9lvst1QhgYOC963SAuPgv+DdVfxTProphrSffAAoo5eLeQab/Ca3ZUeX1E/SnLSr+NGVnNCd8c9gyuKDiENg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "any-base": "^1.1.0", + "buffer": "^5.2.0", + "core-js": "^3.4.1", + "exif-parser": "^0.1.12", + "file-type": "^9.0.0", + "load-bmfont": "^1.3.1", + "mkdirp": "0.5.1", + "phin": "^2.9.1", + "pixelmatch": "^4.0.2", + "tinycolor2": "^1.4.1" + }, + "dependencies": { + "buffer": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz", + "integrity": "sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + } + } + }, + "@jimp/custom": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.9.3.tgz", + "integrity": "sha512-2E7yabQMeqjcK8+ZFu3Ja5cWyrB0zv/pmzNSDg/BBPJ59HE0fj/qcERAz6VklcjHUYRUfmE5uODsb+4DE0o/YQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/core": "^0.9.3", + "core-js": "^3.4.1" + } + }, + "@jimp/gif": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.9.3.tgz", + "integrity": "sha512-DshKgMQ8lXorI/xTRyeRkZqZ3JqgnL2aGYAhx0SkAunyHgXji27chmrOGj/6KVDBucrDf/6mSexnSoUDnlWrfA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "core-js": "^3.4.1", + "omggif": "^1.0.9" + } + }, + "@jimp/jpeg": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.9.3.tgz", + "integrity": "sha512-AJzcTJXfN9BHtpzAbICwR3+GoH0pSr6OYXbAS6yuKwz+xVn9UHrEjQb74CIzIRqrT/VWcIKg29cMQxgokzWY7w==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "core-js": "^3.4.1", + "jpeg-js": "^0.3.4" + } + }, + "@jimp/plugin-blit": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.9.3.tgz", + "integrity": "sha512-+UxCsJ3XkRSdpigpTBJ9WkdwUc3OtBlhVZdU6OL6M9ldume5Gj3rTyWvMCqytOK1tZ/+7HmxoWe4IWX31hz9qA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-blur": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.9.3.tgz", + "integrity": "sha512-RADcYjZ5vbk5ZrUiK7qv0G4xOpHtu19HWVVX9JTDbm4VByWTxPboVKlgiYLA6l+IxIXNtEqDclsADIM0s9FQhA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-color": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.9.3.tgz", + "integrity": "sha512-gHDA5GVx4/R4fitEACKmWH7hNy0aU48MZWYRxmATvuqY39KidJ0fjwp+brQ3Ivgb35AgFVc2jQYc3U/JXv4RxQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "core-js": "^3.4.1", + "tinycolor2": "^1.4.1" + } + }, + "@jimp/plugin-contain": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.9.3.tgz", + "integrity": "sha512-vdYAtp65LNDT/hMctow5o0a/SbD41/y7Z9AO7MGsfUIK92Woq90SNTWx7JplDl4HSZGrqaBONnfiEhRiYlDrdg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-cover": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.9.3.tgz", + "integrity": "sha512-yOwsvakgyS2/C4iZF1a1wg63QKfYvqb2d6k+rgY/0vaAe44JtEx+Gbg+7iOt4EaMm5BDlxRwmcA2Q8Pef8TvAQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-crop": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.9.3.tgz", + "integrity": "sha512-kqMXSyY8hrfo0idr6qY2USOWPrNqpDWs+D6Vwa+kV6SGJhj3rMTIcptQDaamIETSxbjkE8rwUu3K4Q5UD69D7w==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-displace": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.9.3.tgz", + "integrity": "sha512-0AdwxYRWDmJ2wIRIj2RR3sRmNjMhcy5Kwt9Jbi/RRnzxkRScZAiyzkNZhBul23EM7ClfjrUrZufuUvRMHxZRDw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-dither": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.9.3.tgz", + "integrity": "sha512-8OE+Xak9xepiCwSV+oAsb/gupTnttG3aDKxtpSZjwHebnr+k1VG8NgICbMSFATTVJqqZ18oj6LC+5726qHUJ9w==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-flip": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.9.3.tgz", + "integrity": "sha512-w+lzE1ZF/UOjB8qJdeIm+dLQtOK1obZwGYdCIbgxZxw4SfkkjAftJdY8o8RNOXhHDZqGu+cYQZbMKP1zcoNkyQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-gaussian": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.9.3.tgz", + "integrity": "sha512-RPrWwzlZsbWC2opSgeyWt30JU9Uwg1+GwBnoNpEMLKeqm0Dv6snASASa4zVtviGWAIq//p3Jrap7g57hKqL0Cg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-invert": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.9.3.tgz", + "integrity": "sha512-0lRsh7IPkzyYqExrZDT50h38xdlB/+KrdiDcuxWwWyIlKauLMR0kInjwf8sPeb3elPLeETmze7uwPAxrIAtsGQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-mask": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.9.3.tgz", + "integrity": "sha512-nZ0J62Hly9JtMZctlSDVgnTd8Fg2XGikzAYilSTCjzIRtbXL5Be/qSAZrMfLD3CZ8exTxdlEGRkEJI3RZKXYCw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-normalize": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.9.3.tgz", + "integrity": "sha512-0IvgTt4R15QJnoCHvvqlK56zOtCsQV7Mkx757kdNah8uyPGjadTcFBuqCaOMK943X36IIv+o7Ix7yvNUJZt4aw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-print": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.9.3.tgz", + "integrity": "sha512-pV6oX5Bhe9O/dbgrotz46Bv6u1M+/n9G0kRUunDjwzXrvON5raBFEJHQDPcTXiqPT25Gc9Ba4/Akfo/Zl6+wgQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "core-js": "^3.4.1", + "load-bmfont": "^1.4.0" + } + }, + "@jimp/plugin-resize": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.9.3.tgz", + "integrity": "sha512-YzqVE8QoDIZpVuI52v+WejwEjEEiJfNFviQfprfm5af7uSSseZgDw1sJ0koqAu+liMSY+Ewp79v2SDrKoJKqtg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-rotate": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.9.3.tgz", + "integrity": "sha512-kADY2pI3/yMyHbuyvKB4nqPoKf8DPQBU1b4zz2K7SxcwKh1krFf4Fa9mmhhDLoFwuNSy0SPb1JCMUO4BtFCFLA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "core-js": "^3.4.1" + } + }, + "@jimp/plugin-scale": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.9.3.tgz", + "integrity": "sha512-vZaiL5Qc+WrgGEfUe4Y0vG+qbT6pe2TW68/mu124E1tKVcZjHKZUeFN0Wr/hP2myN6nqTYj0/sord2OS/04JpA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "core-js": "^3.4.1" + } + }, + "@jimp/plugins": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.9.3.tgz", + "integrity": "sha512-KYCSgFGoZBNC0224X5yUnMHCZnCdUVrsu2Yo67o3XZfUgDjO81J+vdzZ0twpPQ6qLLVAP+nQ8hkRV/QzEUstMw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/plugin-blit": "^0.9.3", + "@jimp/plugin-blur": "^0.9.3", + "@jimp/plugin-color": "^0.9.3", + "@jimp/plugin-contain": "^0.9.3", + "@jimp/plugin-cover": "^0.9.3", + "@jimp/plugin-crop": "^0.9.3", + "@jimp/plugin-displace": "^0.9.3", + "@jimp/plugin-dither": "^0.9.3", + "@jimp/plugin-flip": "^0.9.3", + "@jimp/plugin-gaussian": "^0.9.3", + "@jimp/plugin-invert": "^0.9.3", + "@jimp/plugin-mask": "^0.9.3", + "@jimp/plugin-normalize": "^0.9.3", + "@jimp/plugin-print": "^0.9.3", + "@jimp/plugin-resize": "^0.9.3", + "@jimp/plugin-rotate": "^0.9.3", + "@jimp/plugin-scale": "^0.9.3", + "core-js": "^3.4.1", + "timm": "^1.6.1" + } + }, + "@jimp/png": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.9.3.tgz", + "integrity": "sha512-LJXUemDTSbTGAGEp9hNQH0uTRSB8gYeE6FsfT3M00oZincu6/WzDzl0P8E95rMjNxZqAihdTyOP3+kcrbbqX+w==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/utils": "^0.9.3", + "core-js": "^3.4.1", + "pngjs": "^3.3.3" + } + }, + "@jimp/tiff": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.9.3.tgz", + "integrity": "sha512-w9H6dT+GDHN//Srsv27JhRn7R2byzUahOGfFw7KpIn95jg0ogcxjKTo/RAGQC56sr4U092e4Npl7E85Lt934WQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "core-js": "^3.4.1", + "utif": "^2.0.1" + } + }, + "@jimp/types": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.9.3.tgz", + "integrity": "sha512-hUJKoT2IhnbO/trxNWzN19n8g+p7aKbM1R+71n4wMZnD41PzrVtz+sBBCdB+JCjBJs/i7fJt4d9z0i3Xe8m7Zw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/bmp": "^0.9.3", + "@jimp/gif": "^0.9.3", + "@jimp/jpeg": "^0.9.3", + "@jimp/png": "^0.9.3", + "@jimp/tiff": "^0.9.3", + "core-js": "^3.4.1", + "timm": "^1.6.1" + } + }, + "@jimp/utils": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.9.3.tgz", + "integrity": "sha512-9D2Of6BcjYONtl77YfmU2y5aRMLe0/O2e2aQvfCxdNwD33jRdwNdN4i3m73dpiClNquApIjL4nYGhTixA4UstA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "core-js": "^3.4.1" + } + }, "@juggle/resize-observer": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-2.5.0.tgz", @@ -3061,6 +3406,12 @@ "color-convert": "^1.9.0" } }, + "any-base": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", + "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==", + "dev": true + }, "any-observable": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", @@ -3088,12 +3439,32 @@ } } }, + "app-manifest-webpack-plugin": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/app-manifest-webpack-plugin/-/app-manifest-webpack-plugin-1.2.0.tgz", + "integrity": "sha512-u2C27waOrBwlaQTrJfuDvsSuaMMPIB7gdBkYAjXzETzzl0GXJA4adbraXxHYKANsp8+W+3izvPlCXD8L0Q7GQg==", + "dev": true, + "requires": { + "favicons": "^5.1.1", + "loader-utils": "^1.1.0" + } + }, "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -3190,6 +3561,15 @@ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, "asn1.js": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", @@ -3228,6 +3608,12 @@ } } }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -3261,6 +3647,12 @@ "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", "dev": true }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -3316,6 +3708,18 @@ } } }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.0.tgz", + "integrity": "sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A==", + "dev": true + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -4161,6 +4565,15 @@ "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", "dev": true }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, "better-assert": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", @@ -4176,12 +4589,40 @@ "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true }, + "bignumber.js": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.4.0.tgz", + "integrity": "sha1-g4qZLan51zfg9LLbC+YrsJ3Qxeg=", + "dev": true + }, "binary-extensions": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", "dev": true }, + "bl": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-3.0.0.tgz", + "integrity": "sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A==", + "dev": true, + "requires": { + "readable-stream": "^3.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "blob": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", @@ -4194,6 +4635,12 @@ "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", "dev": true }, + "bmp-js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", + "integrity": "sha1-4Fpj95amwf8l9Hcex62twUjAcjM=", + "dev": true + }, "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", @@ -4378,6 +4825,12 @@ "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", "dev": true }, + "buffer-equal": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", + "integrity": "sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs=", + "dev": true + }, "buffer-fill": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", @@ -4540,6 +4993,16 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "dev": true, + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -4582,6 +5045,12 @@ "integrity": "sha512-/xL2AbW/XWHNu1gnIrO8UitBGoFthcsDgU9VLK1/dpsoxbaD5LscHozKze05R6WLsBvLhqv78dAPozMFQBYLbQ==", "dev": true }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -4673,6 +5142,23 @@ } } }, + "clean-css": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", + "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -4789,6 +5275,18 @@ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "dev": true }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true + }, + "clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", + "dev": true + }, "clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -4800,6 +5298,23 @@ "shallow-clone": "^3.0.0" } }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + } + }, "coa": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", @@ -4852,6 +5367,17 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "color-parse": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/color-parse/-/color-parse-1.3.8.tgz", + "integrity": "sha512-1Y79qFv0n1xair3lNMTNeoFvmc3nirMVBij24zbs1f13+7fPpQClMg5b4AuKXLt3szj7BRlHMCXHplkce6XlmA==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "defined": "^1.0.0", + "is-plain-obj": "^1.1.0" + } + }, "color-string": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", @@ -4868,6 +5394,15 @@ "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, "commander": { "version": "2.20.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", @@ -4953,6 +5488,12 @@ "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", "dev": true }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, "constants-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", @@ -6602,6 +7143,15 @@ "number-is-nan": "^1.0.0" } }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, "date-fns": { "version": "1.30.1", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", @@ -6651,18 +7201,39 @@ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", "dev": true }, + "decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "dev": true, + "requires": { + "mimic-response": "^2.0.0" + } + }, "dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", "dev": true }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "deepmerge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", + "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==", + "dev": true + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -6713,6 +7284,12 @@ } } }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, "del": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", @@ -6735,6 +7312,18 @@ } } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -6766,6 +7355,12 @@ "repeating": "^2.0.0" } }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "dev": true + }, "detect-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.0.0.tgz", @@ -6824,6 +7419,15 @@ "esutils": "^2.0.2" } }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "requires": { + "utila": "~0.4" + } + }, "dom-serialize": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", @@ -6854,6 +7458,12 @@ } } }, + "dom-walk": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", + "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=", + "dev": true + }, "domain-browser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", @@ -6866,6 +7476,15 @@ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", "dev": true }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, "domutils": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", @@ -6907,6 +7526,16 @@ "stream-shift": "^1.0.0" } }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -7141,6 +7770,12 @@ "es6-symbol": "^3.1.1" } }, + "es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=", + "dev": true + }, "es6-symbol": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", @@ -7364,6 +7999,12 @@ "strip-eof": "^1.0.0" } }, + "exif-parser": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz", + "integrity": "sha1-WKnS1ywCwfbwKg70qRZicrd2CSI=", + "dev": true + }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -7399,6 +8040,12 @@ } } }, + "expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true + }, "expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", @@ -7511,6 +8158,12 @@ } } }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", @@ -7529,6 +8182,39 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "favicons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/favicons/-/favicons-5.5.0.tgz", + "integrity": "sha512-xZ4B+fZDuq2y999iorrYq4KuBT3OIZHU+CVfjOWQbjOC1OiU0xbf6pp4Ju/yAfJn7W74RVrC3Cv0oqR5CLvviw==", + "dev": true, + "requires": { + "clone": "^2.1.2", + "colors": "^1.4.0", + "core-js": "^3.4.5", + "image-size": "^0.8.3", + "jimp": "^0.9.3", + "jsontoxml": "^1.0.1", + "lodash.defaultsdeep": "^4.6.1", + "require-directory": "^2.1.1", + "sharp": "^0.23.3", + "through2": "^3.0.1", + "tinycolor2": "^1.4.1", + "to-ico": "^1.1.5", + "vinyl": "^2.2.0", + "xml2js": "^0.4.22" + }, + "dependencies": { + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + } + } + }, "figgy-pudding": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", @@ -7593,6 +8279,12 @@ } } }, + "file-type": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-9.0.0.tgz", + "integrity": "sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw==", + "dev": true + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -7734,6 +8426,23 @@ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", "dev": true }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -7762,6 +8471,12 @@ "null-check": "^1.0.0" } }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, "fs-extra": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", @@ -8377,6 +9092,44 @@ "simple-git": "^1.85.0" } }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, "get-own-enumerable-property-symbols": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz", @@ -8417,6 +9170,15 @@ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "dev": true }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, "git-raw-commits": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-1.3.6.tgz", @@ -8807,6 +9569,12 @@ "ini": "^1.3.2" } }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", + "dev": true + }, "glob": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", @@ -8842,6 +9610,24 @@ } } }, + "global": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", + "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", + "dev": true, + "requires": { + "min-document": "^2.19.0", + "process": "~0.5.1" + }, + "dependencies": { + "process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=", + "dev": true + } + } + }, "global-dirs": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", @@ -8939,6 +9725,22 @@ } } }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -8992,6 +9794,12 @@ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", "dev": true }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -9044,6 +9852,12 @@ "minimalistic-assert": "^1.0.1" } }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, "hex-color-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", @@ -9104,6 +9918,133 @@ "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", "dev": true }, + "html-minifier": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", + "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "dev": true, + "requires": { + "camel-case": "3.0.x", + "clean-css": "4.2.x", + "commander": "2.17.x", + "he": "1.2.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" + }, + "dependencies": { + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "uglify-js": { + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", + "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", + "dev": true, + "requires": { + "commander": "~2.19.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + } + } + } + } + }, + "html-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", + "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", + "dev": true, + "requires": { + "html-minifier": "^3.2.3", + "loader-utils": "^0.2.16", + "lodash": "^4.17.3", + "pretty-error": "^2.0.2", + "tapable": "^1.0.0", + "toposort": "^1.0.0", + "util.promisify": "1.0.0" + }, + "dependencies": { + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + }, + "toposort": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", + "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=", + "dev": true + } + } + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -9128,6 +10069,17 @@ "requires-port": "^1.0.0" } }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -9247,6 +10199,15 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, + "image-size": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.8.3.tgz", + "integrity": "sha512-SMtq1AJ+aqHB45c3FsB4ERK0UCiA2d3H1uq8s+8T0Pf8A3W4teyBQyaFaktH6xvZqh+npwlKU7i4fJo0r7TYTg==", + "dev": true, + "requires": { + "queue": "6.0.1" + } + }, "import-cwd": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", @@ -9398,6 +10359,12 @@ "loose-envify": "^1.0.0" } }, + "ip-regex": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-1.0.3.tgz", + "integrity": "sha1-3FiQdvZZ9BnCIgOaMzFvHHOH7/0=", + "dev": true + }, "is-absolute-url": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", @@ -9552,6 +10519,12 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, + "is-function": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", + "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=", + "dev": true + }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", @@ -9695,6 +10668,12 @@ "text-extensions": "^1.0.0" } }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -9740,6 +10719,12 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, "jasmine-core": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.5.0.tgz", @@ -9767,6 +10752,34 @@ } } }, + "jimp": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.9.3.tgz", + "integrity": "sha512-dIxvT1OMRkd3+B18XUhJ5WZ2Dw7Hp8mvjaTqfi945zZ7fga6LT22h3NLYDorHHAiy9z30KjfNnOgpBoxrdjDZg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "@jimp/custom": "^0.9.3", + "@jimp/plugins": "^0.9.3", + "@jimp/types": "^0.9.3", + "core-js": "^3.4.1", + "regenerator-runtime": "^0.13.3" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", + "dev": true + } + } + }, + "jpeg-js": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.3.6.tgz", + "integrity": "sha512-MUj2XlMB8kpe+8DJUGH/3UJm4XpI8XEgZQ+CiHDeyrGoKPdW/8FJv6ku+3UiYm5Fz3CWaL+iXmD8Q4Ap6aC1Jw==", + "dev": true + }, "js-levenshtein": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", @@ -9789,6 +10802,12 @@ "esprima": "^4.0.0" } }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -9801,6 +10820,12 @@ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -9857,6 +10882,24 @@ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true }, + "jsontoxml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/jsontoxml/-/jsontoxml-1.0.1.tgz", + "integrity": "sha512-dtKGq0K8EWQBRqcAaePSgKR4Hyjfsz/LkurHSV3Cxk4H+h2fWDeaN2jzABz+ZmOJylgXS7FGeWmbZ6jgYUMdJQ==", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, "karma": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/karma/-/karma-4.4.1.tgz", @@ -10266,6 +11309,30 @@ "figures": "^2.0.0" } }, + "load-bmfont": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.0.tgz", + "integrity": "sha512-kT63aTAlNhZARowaNYcY29Fn/QYkc52M3l6V1ifRcPewg2lvUZDAj7R6dXjOL9D0sict76op3T5+odumDSF81g==", + "dev": true, + "requires": { + "buffer-equal": "0.0.1", + "mime": "^1.3.4", + "parse-bmfont-ascii": "^1.0.3", + "parse-bmfont-binary": "^1.0.5", + "parse-bmfont-xml": "^1.1.4", + "phin": "^2.9.1", + "xhr": "^2.0.1", + "xtend": "^4.0.0" + }, + "dependencies": { + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + } + } + }, "load-json-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", @@ -10358,6 +11425,18 @@ "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", "dev": true }, + "lodash.defaultsdeep": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz", + "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==", + "dev": true + }, + "lodash.isempty": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", + "integrity": "sha1-b4bL7di+TsmHvpqvM8loTbGzHn4=", + "dev": true + }, "lodash.ismatch": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", @@ -10474,6 +11553,12 @@ "signal-exit": "^3.0.0" } }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, "lru-cache": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", @@ -10631,6 +11716,15 @@ } } }, + "merge-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-1.0.1.tgz", + "integrity": "sha512-iuPV41VWKWBIOpBsjoxjDZw8/GbSfZ2mk7N1453bwMrfzdrIk7EzBd+8UVR6rkw67th7xnk9Dytl3J+lHPdxvg==", + "dev": true, + "requires": { + "is-plain-obj": "^1.1" + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -10695,6 +11789,21 @@ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, + "mimic-response": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.0.0.tgz", + "integrity": "sha512-8ilDoEapqA4uQ3TwS0jakGONKXVJqpy+RpM+3b7pLdOjghCrEiGp9SRkFbUHAmZW9vdnrENWHjaweIoTIJExSQ==", + "dev": true + }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "dev": true, + "requires": { + "dom-walk": "^0.1.0" + } + }, "mini-css-extract-plugin": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.0.tgz", @@ -10788,6 +11897,24 @@ "minipass": "^3.0.0" } }, + "minizlib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.0.tgz", + "integrity": "sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA==", + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, "mississippi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", @@ -10877,8 +12004,7 @@ "version": "2.14.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "dev": true, - "optional": true + "dev": true }, "nanomatch": { "version": "1.2.13", @@ -10899,6 +12025,12 @@ "to-regex": "^3.0.1" } }, + "napi-build-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.1.tgz", + "integrity": "sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA==", + "dev": true + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -10929,6 +12061,32 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "requires": { + "lower-case": "^1.1.1" + } + }, + "node-abi": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.13.0.tgz", + "integrity": "sha512-9HrZGFVTR5SOu3PZAnAY2hLO36aW1wmA+FDsVkr85BTST32TLCA1H/AEcatVRAsWLyXS3bqUDYCAjq5/QGuSTA==", + "dev": true, + "requires": { + "semver": "^5.4.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, "node-libs-browser": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", @@ -10985,6 +12143,12 @@ } } }, + "noop-logger": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", + "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=", + "dev": true + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -14182,6 +15346,18 @@ "which": "^1.2.10" } }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, "nth-check": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", @@ -14219,6 +15395,12 @@ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -14420,6 +15602,12 @@ } } }, + "omggif": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz", + "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==", + "dev": true + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -14578,6 +15766,15 @@ "readable-stream": "^2.1.5" } }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "dev": true, + "requires": { + "no-case": "^2.2.0" + } + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -14601,12 +15798,40 @@ "safe-buffer": "^5.1.1" } }, + "parse-bmfont-ascii": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", + "integrity": "sha1-Eaw8P/WPfCAgqyJ2kHkQjU36AoU=", + "dev": true + }, + "parse-bmfont-binary": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz", + "integrity": "sha1-0Di0dtPp3Z2x4RoLDlOiJ5K2kAY=", + "dev": true + }, + "parse-bmfont-xml": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.4.tgz", + "integrity": "sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ==", + "dev": true, + "requires": { + "xml-parse-from-string": "^1.0.0", + "xml2js": "^0.4.5" + } + }, "parse-github-repo-url": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz", "integrity": "sha1-nn2LslKmy2ukJZUGC3v23z28H1A=", "dev": true }, + "parse-headers": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.3.tgz", + "integrity": "sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA==", + "dev": true + }, "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -14623,6 +15848,15 @@ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, + "parse-png": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/parse-png/-/parse-png-1.1.2.tgz", + "integrity": "sha1-9cKtfHmTSQmGAgooTBmu5FlxH/I=", + "dev": true, + "requires": { + "pngjs": "^3.2.0" + } + }, "parseqs": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", @@ -14727,6 +15961,18 @@ "sha.js": "^2.4.8" } }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "phin": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz", + "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==", + "dev": true + }, "picomatch": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.1.1.tgz", @@ -14760,6 +16006,15 @@ "pinkie": "^2.0.0" } }, + "pixelmatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz", + "integrity": "sha1-j0fc7FARtHe2fbA8JDvB8wheiFQ=", + "dev": true, + "requires": { + "pngjs": "^3.0.0" + } + }, "pkg-dir": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", @@ -14778,6 +16033,12 @@ "semver-compare": "^1.0.0" } }, + "pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "dev": true + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -15144,6 +16405,15 @@ "postcss": "^7.0.2" } }, + "postcss-helpers": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/postcss-helpers/-/postcss-helpers-0.3.2.tgz", + "integrity": "sha512-hppnMXY6Ehe8CgLHQCDWbyUsXvBFggdzftWzznL65MhgZsE8o8pUTYbmUbLst0rps+wyUSLIUJ0bGpV2Tzv7lw==", + "dev": true, + "requires": { + "urijs": "^1.18.12" + } + }, "postcss-image-set-function": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz", @@ -15746,6 +17016,119 @@ "uniq": "^1.0.1" } }, + "posthtml": { + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.11.6.tgz", + "integrity": "sha512-C2hrAPzmRdpuL3iH0TDdQ6XCc9M7Dcc3zEW5BLerY65G4tWWszwv6nG/ksi6ul5i2mx22ubdljgktXCtNkydkw==", + "dev": true, + "requires": { + "posthtml-parser": "^0.4.1", + "posthtml-render": "^1.1.5" + } + }, + "posthtml-match-helper": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/posthtml-match-helper/-/posthtml-match-helper-1.0.1.tgz", + "integrity": "sha1-RRJT3o5YRKNI6WOtXt13aesSlRM=", + "dev": true + }, + "posthtml-parser": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.4.2.tgz", + "integrity": "sha512-BUIorsYJTvS9UhXxPTzupIztOMVNPa/HtAm9KHni9z6qEfiJ1bpOBL5DfUOL9XAc3XkLIEzBzpph+Zbm4AdRAg==", + "dev": true, + "requires": { + "htmlparser2": "^3.9.2" + } + }, + "posthtml-render": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-1.1.5.tgz", + "integrity": "sha512-yvt54j0zCBHQVEFAuR+yHld8CZrCa/E1Z/OcFNCV1IEWTLVxT8O7nYnM4IIw1CD4r8kaRd3lc42+0lgCKgm87w==", + "dev": true + }, + "posthtml-transform": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/posthtml-transform/-/posthtml-transform-1.0.7.tgz", + "integrity": "sha512-ihbO3C1pqjgq94PjgxWyN/S0B+rW3lW3C2o+U0MHYilxDy7zft+u9r+Gk9YT+Nh5DyZyLgWj6Q9H1CR0Z/TPTg==", + "dev": true, + "requires": { + "color-parse": "^1.3.7", + "loader-utils": "^1.1.0", + "lodash": "^4.17.14", + "postcss-values-parser": "^1.5.0", + "posthtml": "^0.11.3", + "posthtml-match-helper": "^1.0.1", + "slashes": "^1.0.5", + "unquote": "^1.1.1" + }, + "dependencies": { + "postcss-values-parser": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-1.5.0.tgz", + "integrity": "sha512-3M3p+2gMp0AH3da530TlX8kiO1nxdTnc3C6vr8dMxRLIlh8UYkz0/wcwptSXjhtx2Fr0TySI7a+BHDQ8NL7LaQ==", + "dev": true, + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postsvg": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/postsvg/-/postsvg-2.2.7.tgz", + "integrity": "sha512-TyRnoyEvszrEGOzxaTycnUgJZ0W2Xnd9fOmgfuy61Qjo6JhDPhAIBQ1dspQCvdVpK9KkIlZkSETSjmbO0gVIag==", + "dev": true, + "requires": { + "clone": "^1.0.4", + "deepmerge": "^2.1.0", + "posthtml": "^0.11.3", + "posthtml-match-helper": "^1.0.1", + "posthtml-parser": "^0.4.1", + "posthtml-render": "^1.1.2" + }, + "dependencies": { + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + } + } + }, + "prebuild-install": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.3.tgz", + "integrity": "sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g==", + "dev": true, + "requires": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.7.0", + "noop-logger": "^0.1.1", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^3.0.3", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0", + "which-pm-runs": "^1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -15758,6 +17141,16 @@ "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", "dev": true }, + "pretty-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", + "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", + "dev": true, + "requires": { + "renderkid": "^2.0.1", + "utila": "~0.4" + } + }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", @@ -15806,6 +17199,12 @@ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, + "psl": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.6.0.tgz", + "integrity": "sha512-SYKKmVel98NCOYXpkwUqZqh0ahZeeKfmisiLIcEZdsb+WbLv02g/dI5BUmZnIyOe7RzZtLax81nnb2HbvC2tzA==", + "dev": true + }, "public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", @@ -15899,6 +17298,15 @@ "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", "dev": true }, + "queue": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.1.tgz", + "integrity": "sha512-AJBQabRCCNr9ANq8v77RJEv73DPbn55cdTb+Giq4X0AVnNVZvMHlYp7XlQiN+1npCZj1DuSmaA2hYVUUDgxFDg==", + "dev": true, + "requires": { + "inherits": "~2.0.3" + } + }, "quick-lru": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", @@ -15942,6 +17350,32 @@ "unpipe": "1.0.0" } }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "read-chunk": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-1.0.1.tgz", + "integrity": "sha1-X2jKswfmY/GZk1J9m1icrORmEZQ=", + "dev": true + }, "read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -16146,12 +17580,67 @@ } } }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, + "remove-files-webpack-plugin": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/remove-files-webpack-plugin/-/remove-files-webpack-plugin-1.1.3.tgz", + "integrity": "sha512-r53wQ/IlTkmcv11wri71CZ27S+GhFI5SjHbTbaAJbisPC3qGwg87vlA2C5Z1PuVA+aMI8SgimnE4SqI+ZYzu6Q==", + "dev": true + }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", "dev": true }, + "renderkid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.3.tgz", + "integrity": "sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA==", + "dev": true, + "requires": { + "css-select": "^1.1.0", + "dom-converter": "^0.2", + "htmlparser2": "^3.3.0", + "strip-ansi": "^3.0.0", + "utila": "^0.4.0" + }, + "dependencies": { + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "dev": true, + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "dev": true + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + } + } + }, "repeat-element": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", @@ -16173,6 +17662,48 @@ "is-finite": "^1.0.0" } }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + } + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -16185,6 +17716,94 @@ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", "dev": true }, + "resize-img": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/resize-img/-/resize-img-1.1.2.tgz", + "integrity": "sha1-+tZQ+vPvLFPqYxErwnLZXp2SVQ4=", + "dev": true, + "requires": { + "bmp-js": "0.0.1", + "file-type": "^3.8.0", + "get-stream": "^2.0.0", + "jimp": "^0.2.21", + "jpeg-js": "^0.1.1", + "parse-png": "^1.1.1" + }, + "dependencies": { + "bmp-js": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.0.1.tgz", + "integrity": "sha1-WtAUcJnROp84qnuZrx1ueGZu038=", + "dev": true + }, + "file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", + "dev": true + }, + "get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + } + }, + "jimp": { + "version": "0.2.28", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.2.28.tgz", + "integrity": "sha1-3VKak3GQ9ClXp5N9Gsw6d2KZbqI=", + "dev": true, + "requires": { + "bignumber.js": "^2.1.0", + "bmp-js": "0.0.3", + "es6-promise": "^3.0.2", + "exif-parser": "^0.1.9", + "file-type": "^3.1.0", + "jpeg-js": "^0.2.0", + "load-bmfont": "^1.2.3", + "mime": "^1.3.4", + "mkdirp": "0.5.1", + "pixelmatch": "^4.0.0", + "pngjs": "^3.0.0", + "read-chunk": "^1.0.1", + "request": "^2.65.0", + "stream-to-buffer": "^0.1.0", + "tinycolor2": "^1.1.2", + "url-regex": "^3.0.0" + }, + "dependencies": { + "bmp-js": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.0.3.tgz", + "integrity": "sha1-ZBE+nHzxICs3btYHvzBibr5XsYo=", + "dev": true + }, + "jpeg-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.2.0.tgz", + "integrity": "sha1-U+RI7J0mPmgyZkZ+lELSxaLvVII=", + "dev": true + } + } + }, + "jpeg-js": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.1.2.tgz", + "integrity": "sha1-E1uZLAV1yYXPoPSUoyJ+0jhYPs4=", + "dev": true + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + } + } + }, "resolve": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", @@ -16560,6 +18179,23 @@ "kind-of": "^6.0.2" } }, + "sharp": { + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.23.4.tgz", + "integrity": "sha512-fJMagt6cT0UDy9XCsgyLi0eiwWWhQRxbwGmqQT6sY8Av4s0SVsT/deg8fobBQCTDU5iXRgz0rAeXoE2LBZ8g+Q==", + "dev": true, + "requires": { + "color": "^3.1.2", + "detect-libc": "^1.0.3", + "nan": "^2.14.0", + "npmlog": "^4.1.2", + "prebuild-install": "^5.3.3", + "semver": "^6.3.0", + "simple-get": "^3.1.0", + "tar": "^5.0.5", + "tunnel-agent": "^0.6.0" + } + }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -16593,6 +18229,23 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, + "simple-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", + "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=", + "dev": true + }, + "simple-get": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz", + "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==", + "dev": true, + "requires": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "simple-git": { "version": "1.115.0", "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.115.0.tgz", @@ -16642,6 +18295,12 @@ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", "dev": true }, + "slashes": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/slashes/-/slashes-1.0.5.tgz", + "integrity": "sha1-IEeY9sYyAU1gm12JtqV6eq9wgo0=", + "dev": true + }, "slice-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", @@ -16954,6 +18613,12 @@ "through": "2" } }, + "split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "dev": true + }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -16978,6 +18643,23 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, "ssri": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", @@ -17302,6 +18984,21 @@ "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", "dev": true }, + "stream-to": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stream-to/-/stream-to-0.2.2.tgz", + "integrity": "sha1-hDBgmNhf25kLn6MAsbPM9V6O8B0=", + "dev": true + }, + "stream-to-buffer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/stream-to-buffer/-/stream-to-buffer-0.1.0.tgz", + "integrity": "sha1-JnmdkDqyAlyb1VCsRxcbAPjdgKk=", + "dev": true, + "requires": { + "stream-to": "~0.2.0" + } + }, "streamroller": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.6.tgz", @@ -17527,6 +19224,54 @@ "has-flag": "^3.0.0" } }, + "svg-mixer-utils": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/svg-mixer-utils/-/svg-mixer-utils-0.3.4.tgz", + "integrity": "sha512-szkeG+Jn6DRo7QlnUOYKslm4J6dg37I8E+tLG1PB13U6UhSlkC8teCisyT7ZRGRwTcTxmNizvu+/oe8uJUf5EA==", + "dev": true, + "requires": { + "ajv": "^6.5.1", + "anymatch": "^2.0.0", + "memory-fs": "^0.4.1", + "merge-options": "^1.0.0", + "postcss-helpers": "^0.3.2" + } + }, + "svg-transform-loader": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/svg-transform-loader/-/svg-transform-loader-2.0.8.tgz", + "integrity": "sha512-cK9PXVGV+hZoiR7lkIir+LOZ2qmJIuq0tm/emv5x7MF4PvyqY/lCNlYuctx37rSNgGE6Lhpdn8lUCZm8d/oNAQ==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "lodash.isempty": "^4.4.0", + "merge-options": "^1.0.0", + "postcss": "^7.0.14", + "posthtml-transform": "^1.0.7", + "postsvg": "^2.2.7", + "query-string": "^6.1.0", + "svg-mixer-utils": "^0.3.4" + }, + "dependencies": { + "query-string": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.9.0.tgz", + "integrity": "sha512-KG4bhCFYapExLsUHrFt+kQVEegF2agm4cpF/VNc6pZVthIfCc/GK8t8VyNIE3nyXG9DK3Tf2EGkxjR6/uRdYsA==", + "dev": true, + "requires": { + "decode-uri-component": "^0.2.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + } + }, + "strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", + "dev": true + } + } + }, "svgo": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", @@ -17610,6 +19355,72 @@ "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", "dev": true }, + "tar": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/tar/-/tar-5.0.5.tgz", + "integrity": "sha512-MNIgJddrV2TkuwChwcSNds/5E9VijOiw7kAc1y5hTNJoLDSuIyid2QtLYiCYNnICebpuvjhPQZsXwUL0O3l7OQ==", + "dev": true, + "requires": { + "chownr": "^1.1.3", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.0", + "mkdirp": "^0.5.0", + "yallist": "^4.0.0" + }, + "dependencies": { + "chownr": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "tar-fs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.0.tgz", + "integrity": "sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "mkdirp": "^0.5.1", + "pump": "^3.0.0", + "tar-stream": "^2.0.0" + } + }, + "tar-stream": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.0.tgz", + "integrity": "sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw==", + "dev": true, + "requires": { + "bl": "^3.0.0", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "terser": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/terser/-/terser-4.4.2.tgz", @@ -17813,12 +19624,24 @@ "setimmediate": "^1.0.4" } }, + "timm": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/timm/-/timm-1.6.2.tgz", + "integrity": "sha512-IH3DYDL1wMUwmIlVmMrmesw5lZD6N+ZOAFWEyLrtpoL9Bcrs9u7M/vyOnHzDD2SMs4irLkVjqxZbHrXStS/Nmw==", + "dev": true + }, "timsort": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", "dev": true }, + "tinycolor2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz", + "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=", + "dev": true + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -17846,6 +19669,27 @@ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, + "to-ico": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/to-ico/-/to-ico-1.1.5.tgz", + "integrity": "sha512-5kIh7m7bkIlqIESEZkL8gAMMzucXKfPe3hX2FoDY5HEAfD9OJU+Qh9b6Enp74w0qRcxVT5ejss66PHKqc3AVkg==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "buffer-alloc": "^1.1.0", + "image-size": "^0.5.0", + "parse-png": "^1.0.0", + "resize-img": "^1.1.0" + }, + "dependencies": { + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "dev": true + } + } + }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", @@ -17900,6 +19744,24 @@ "integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=", "dev": true }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, "trim-newlines": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", @@ -17930,6 +19792,21 @@ "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -18133,6 +20010,12 @@ "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", "dev": true }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "dev": true + }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", @@ -18142,6 +20025,12 @@ "punycode": "^2.1.0" } }, + "urijs": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.2.tgz", + "integrity": "sha512-s/UIq9ap4JPZ7H1EB5ULo/aOUbWqfDi7FKzMC2Nz+0Si8GiT1rIEaprt8hy3Vy2Ex2aJPpOQv4P4DuOZ+K1c6w==", + "dev": true + }, "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", @@ -18172,6 +20061,15 @@ "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=", "dev": true }, + "url-regex": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/url-regex/-/url-regex-3.2.0.tgz", + "integrity": "sha1-260eDJ4p4QXdCx8J9oYvf9tIJyQ=", + "dev": true, + "requires": { + "ip-regex": "^1.0.1" + } + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -18188,6 +20086,15 @@ "tmp": "0.0.x" } }, + "utif": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/utif/-/utif-2.0.1.tgz", + "integrity": "sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg==", + "dev": true, + "requires": { + "pako": "^1.0.5" + } + }, "util": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", @@ -18213,6 +20120,12 @@ "object.getownpropertydescriptors": "^2.0.3" } }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", + "dev": true + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -18247,6 +20160,31 @@ "integrity": "sha512-fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw==", "dev": true }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vinyl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "dev": true, + "requires": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + } + }, "vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", @@ -18658,6 +20596,21 @@ "isexe": "^2.0.0" } }, + "which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", + "dev": true + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -18726,6 +20679,40 @@ "ultron": "~1.1.0" } }, + "xhr": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.5.0.tgz", + "integrity": "sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ==", + "dev": true, + "requires": { + "global": "~4.3.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "xml-parse-from-string": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", + "integrity": "sha1-qQKekp09vN7RafPG4oI42VpdWig=", + "dev": true + }, + "xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true + }, "xmlhttprequest-ssl": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", diff --git a/package.json b/package.json index c943338ee..d77b8ea1e 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "@commitlint/cli": "^8.2.0", "@commitlint/config-conventional": "^8.2.0", "@fortawesome/fontawesome-pro": "^5.12.0", + "app-manifest-webpack-plugin": "^1.2.0", "autoprefixer": "^9.7.3", "babel-core": "^6.26.3", "babel-eslint": "^10.0.3", @@ -72,6 +73,7 @@ "css-loader": "^2.1.1", "eslint": "^5.16.0", "file-loader": "^5.0.2", + "html-webpack-plugin": "^3.2.0", "husky": "^2.7.0", "jasmine-core": "^3.5.0", "js-yaml": "^3.13.1", @@ -90,6 +92,7 @@ "optimize-css-assets-webpack-plugin": "^5.0.3", "postcss-loader": "^3.0.0", "postcss-preset-env": "^6.7.0", + "remove-files-webpack-plugin": "^1.1.3", "resolve-url-loader": "^3.1.1", "sass": "^1.23.7", "sass-loader": "^7.3.1", diff --git a/records.json b/records.json index 0e3c24f99..f65dcfbb3 100644 --- a/records.json +++ b/records.json @@ -739,5 +739,31 @@ "usedIds": [] } } + ], + "app-manifest-webpack-plugin for \"/home/gkleen/projects/uni2work/static/wp-4.41/iconstats-[hash].json\"": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "app-manifest-webpack-plugin for \"../../config/favicon.json\"": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } ] } \ No newline at end of file diff --git a/src/Foundation.hs b/src/Foundation.hs index 5bde9f8d4..e8923b226 100644 --- a/src/Foundation.hs +++ b/src/Foundation.hs @@ -1587,6 +1587,8 @@ siteLayout' headingOverride widget = do toWidget $(juliusFile "templates/i18n.julius") + faviconLinks + $(widgetFile "default-layout") withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet") diff --git a/src/Settings.hs b/src/Settings.hs index ca4610ebb..284173eee 100644 --- a/src/Settings.hs +++ b/src/Settings.hs @@ -78,6 +78,7 @@ data AppSettings = AppSettings { appStaticDir :: FilePath -- ^ Directory from which to serve static files. , appWebpackEntrypoints :: FilePath + , appFaviconStats :: FilePath , appDatabaseConf :: PostgresConf -- ^ Configuration settings for accessing the database. , appAutoDbMigrate :: Bool @@ -370,6 +371,7 @@ instance FromJSON AppSettings where #endif appStaticDir <- o .: "static-dir" appWebpackEntrypoints <- o .: "webpack-manifest" + appFaviconStats <- o .: "favicon-stats" appDatabaseConf <- o .: "database" appAutoDbMigrate <- o .: "auto-db-migrate" let nonEmptyHost LdapConf{..} = case ldapHost of diff --git a/src/Settings/StaticFiles.hs b/src/Settings/StaticFiles.hs index 79f750c78..4c9f711ab 100644 --- a/src/Settings/StaticFiles.hs +++ b/src/Settings/StaticFiles.hs @@ -3,11 +3,12 @@ module Settings.StaticFiles , module Yesod.EmbeddedStatic ) where -import ClassyPrelude +import ClassyPrelude.Yesod -import Settings (appStaticDir, appWebpackEntrypoints, compileTimeAppSettings) +import Settings (appStaticDir, appWebpackEntrypoints, appFaviconStats, compileTimeAppSettings) import Settings.StaticFiles.Generator import Settings.StaticFiles.Webpack +import Settings.StaticFiles.Favicon import Yesod.EmbeddedStatic -- This generates easy references to files in the static directory at compile time, @@ -27,3 +28,6 @@ import Yesod.EmbeddedStatic mkEmbeddedStatic DEV_BOOL "embeddedStatic" . pure . staticGenerator $ appStaticDir compileTimeAppSettings mkWebpackEntrypoints (appWebpackEntrypoints compileTimeAppSettings) (pure staticGenerator) $ appStaticDir compileTimeAppSettings + +faviconLinks :: MonadWidget m => m () +faviconLinks = $(mkFaviconLinks $ appFaviconStats compileTimeAppSettings) diff --git a/src/Settings/StaticFiles/Favicon.hs b/src/Settings/StaticFiles/Favicon.hs new file mode 100644 index 000000000..a530cf69a --- /dev/null +++ b/src/Settings/StaticFiles/Favicon.hs @@ -0,0 +1,38 @@ +module Settings.StaticFiles.Favicon + ( mkFaviconLinks + ) where + +import ClassyPrelude.Yesod + +import Language.Haskell.TH +import Language.Haskell.TH.Syntax hiding (Lift(..)) +import qualified Language.Haskell.TH.Syntax as TH (Lift(..)) + +import qualified Data.Aeson as JSON + +import qualified Data.Map as Map + +import Text.Blaze.Html (preEscapedToHtml) + + +mkFaviconLinks :: FilePath -- ^ Path to JSON-manifest + -> ExpQ +mkFaviconLinks manifest = do + addDependentFile manifest + htmlFragments <- decodeManifest manifest + + doE $ map (\frag -> noBindS [e|toWidgetHead $ preEscapedToHtml $(TH.lift frag)|]) htmlFragments + where + decodeManifest :: FilePath -> Q [Text] + decodeManifest manifest' = do + res <- liftIO $ JSON.eitherDecodeFileStrict' manifest' + case res of + Left err -> fail err + Right res' + | Just frags' <- res' Map.!? ("html" :: Text) + , JSON.Success frags <- JSON.fromJSON frags' + -> return frags + | otherwise + -> fail "Could not parse favicon stats" + + diff --git a/webpack.config.js b/webpack.config.js index 7faa15e17..93d802122 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -9,6 +9,8 @@ const TerserPlugin = require('terser-webpack-plugin'); const yaml = require('js-yaml'); const HashOutput = require('webpack-plugin-hash-output'); const postcssPresetEnv = require('postcss-preset-env'); +const AppManifestWebpackPlugin = require('app-manifest-webpack-plugin'); +const RemovePlugin = require('remove-files-webpack-plugin'); const webpackVersion = require('webpack/package.json').version.split('.').slice(0, 2).join('.'); const packageVersion = require('./package.json').version; @@ -67,7 +69,7 @@ module.exports = { ] }, { - test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/i, + test: /\.(woff(2)?|ttf|eot|svg)(\?.*)?$/i, use: [ { loader: 'file-loader', @@ -103,7 +105,8 @@ module.exports = { if (chunk.name) { return chunk.name; } - return chunk.modules.map(m => path.relative(m.context, m.request)).join("_"); + let modules = chunk.modules || [chunk.entryModule]; + return modules.map(m => path.relative(m.context, m.request)).join("_"); }), new webpack.NamedModulesPlugin(), new ManifestPlugin({ @@ -121,6 +124,38 @@ module.exports = { ]), new webpack.DefinePlugin({ VERSION: JSON.stringify(packageVersion) + }), + new AppManifestWebpackPlugin({ + logo: path.resolve(__dirname, 'assets/favicon.svg'), + output: '/[hash]-icons/', + prefix: `/static/res/wp-${webpackVersion}/`, + inject: false, + emitStats: true, + statsFilename: path.resolve(__dirname, 'config/favicon.json'), + persistentCache: false, + config: { + background: '#fff', + icons: { + android: false, + appleIcon: false, + appleStartup: false, + coast: false, + favicons: true, + firefox: false, + windows: false, + yandex: false + } + } + }), + new RemovePlugin({ + after: { + test: [ + { folder: path.resolve(__dirname, `static/wp-${webpackVersion}`), + method: (filePath) => { return new RegExp(/\/.*-icons\/.*\.(xml|json|webapp)$/, 'm').test(filePath); }, + recursive: true + } + ] + } }) ], From 7cf24a418791bd698e4fa4de531890ff0256d85e Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Tue, 7 Jan 2020 13:16:42 +0100 Subject: [PATCH 04/38] refactor: avoid recompilation due to static files --- src/Foundation.hs | 15 ++------------- src/Settings/StaticFiles/Webpack.hs | 23 ++++++++++++++++++++++- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/Foundation.hs b/src/Foundation.hs index e8923b226..1e8f535bc 100644 --- a/src/Foundation.hs +++ b/src/Foundation.hs @@ -1574,20 +1574,9 @@ siteLayout' headingOverride widget = do frontendDatetimeLocale <- toJSON <$> selectLanguage frontendDatetimeLocales pc <- widgetToPageContent $ do - $logDebugS "siteLayout" $ tshow webpackEntrypoint_main - forM_ webpackEntrypoint_main $ \(sRoute, mime) -> - let ctEq = (==) `on` simpleContentType - in if - | mime `ctEq` "text/css" - -> addStylesheet $ StaticR sRoute - | mime `ctEq` "application/javascript" - -> addScript $ StaticR sRoute - | otherwise - -> $logErrorS "siteLayout" [st|Unknown mime type in webpack bundle: #{tshow mime}|] - - toWidget $(juliusFile "templates/i18n.julius") - + webpackLinks_main StaticR faviconLinks + toWidget $(juliusFile "templates/i18n.julius") $(widgetFile "default-layout") withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet") diff --git a/src/Settings/StaticFiles/Webpack.hs b/src/Settings/StaticFiles/Webpack.hs index 81320818d..3fcd6c224 100644 --- a/src/Settings/StaticFiles/Webpack.hs +++ b/src/Settings/StaticFiles/Webpack.hs @@ -12,7 +12,7 @@ import qualified Data.Yaml as Yaml import qualified Data.Map as Map -import Yesod.Core (Route) +import Yesod.Core (Route, MonadLogger, MonadWidget, HandlerSite, logDebugS, logErrorS) import Yesod.EmbeddedStatic (EmbeddedStatic) import Yesod.EmbeddedStatic.Types import Network.Mime (MimeType) @@ -27,6 +27,8 @@ import Utils (nubOn) import System.FilePath (makeRelative) +import Text.Shakespeare.Text (st) + mkWebpackEntrypoints :: FilePath -- ^ Path to YAML-manifest -> [FilePath -> Generator] @@ -54,11 +56,30 @@ mkWebpackEntrypoints manifest mkGen stDir = do Just n -> tell $ pure (n, ebMimeType entry) let entryName = mkName $ "webpackEntrypoint_" <> entrypoint + widgetName = mkName $ "webpackLinks_" <> entrypoint + + staticR <- newName "staticR" sequence [ sigD entryName [t|[(Route EmbeddedStatic, MimeType)]|] , funD entryName [ clause [] (normalB . listE . map (\(n, mime) -> tupE [varE n, TH.lift mime]) $ nubOn fst entries) [] ] + , sigD widgetName [t|forall m. (MonadLogger m, MonadWidget m) => (Route EmbeddedStatic -> Route (HandlerSite m)) -> m ()|] + , funD widgetName + [ clause [varP staticR] (normalB [e| + do + $logDebugS "siteLayout" $ tshow $(varE entryName) + forM_ $(varE entryName) $ \(sRoute, mime) -> + let ctEq = (==) `on` simpleContentType + in if + | mime `ctEq` "text/css" + -> addStylesheet $ $(varE staticR) sRoute + | mime `ctEq` "application/javascript" + -> addScript $ $(varE staticR) sRoute + | otherwise + -> $logErrorS "siteLayout" [st|Unknown mime type in webpack bundle: #{tshow mime}|] + |]) [] + ] ] where decodeManifest :: FilePath -> Q (Map String [FilePath]) From c0b28326223ed39f04416d209b55cd68fe512b3d Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Tue, 7 Jan 2020 14:20:51 +0100 Subject: [PATCH 05/38] chore: benchmark node_modules size --- .gitlab-ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e94fc27d2..b24f0454d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -37,6 +37,9 @@ npm install: - install -v -m 0700 -d ~/.ssh - install -v -T -m 0644 ${SSH_KNOWN_HOSTS} ~/.ssh/known_hosts - install -v -T -m 0400 ${SSH_DEPLOY_KEY} ~/.ssh/deploy && echo "IdentityFile ~/.ssh/deploy" >> ~/.ssh/config; + after_script: + - zip -r node_modules.zip node_modules + - du -h node_modules node_modules.zip artifacts: paths: - node_modules/ From fd7700afd9435da8c41edf01235902e69550b9f2 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Tue, 7 Jan 2020 14:32:05 +0100 Subject: [PATCH 06/38] chore: quiet zip --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b24f0454d..8ca829a63 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -38,7 +38,7 @@ npm install: - install -v -T -m 0644 ${SSH_KNOWN_HOSTS} ~/.ssh/known_hosts - install -v -T -m 0400 ${SSH_DEPLOY_KEY} ~/.ssh/deploy && echo "IdentityFile ~/.ssh/deploy" >> ~/.ssh/config; after_script: - - zip -r node_modules.zip node_modules + - zip -qr node_modules.zip node_modules - du -h node_modules node_modules.zip artifacts: paths: From cc76a072c80af19c71e2b17d1915523149739d5a Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Tue, 7 Jan 2020 16:30:17 +0100 Subject: [PATCH 07/38] chore: propagate favicon.json to backend build --- .gitlab-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8ca829a63..fb4d41b19 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -39,7 +39,7 @@ npm install: - install -v -T -m 0400 ${SSH_DEPLOY_KEY} ~/.ssh/deploy && echo "IdentityFile ~/.ssh/deploy" >> ~/.ssh/config; after_script: - zip -qr node_modules.zip node_modules - - du -h node_modules node_modules.zip + - du -hs node_modules node_modules.zip artifacts: paths: - node_modules/ @@ -58,6 +58,7 @@ frontend:build: paths: - static - config/webpack.yml + - config/favicon.json name: "${CI_JOB_NAME}" expire_in: "1 day" dependencies: From eeabdb1b2b4f4a45c5b32401474a07ae5b51089c Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Tue, 7 Jan 2020 19:39:14 +0100 Subject: [PATCH 08/38] chore(release): 10.2.0 --- CHANGELOG.md | 15 +++++++++++++++ package-lock.json | 2 +- package.json | 2 +- package.yaml | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f15d98144..215e86d8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [10.2.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v10.1.0...v10.2.0) (2020-01-07) + + +### Bug Fixes + +* divide by zero ([674b949](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/674b949)) + + +### Features + +* generate & include new favicon ([b78c484](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b78c484)) +* **config:** improve configurability of VerpMode ([a7c3fe7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a7c3fe7)) + + + ## [10.1.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v10.0.1...v10.1.0) (2019-12-23) diff --git a/package-lock.json b/package-lock.json index 8375b2fbe..39368eea4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "uni2work", - "version": "10.1.0", + "version": "10.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index d77b8ea1e..e85cd6f14 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uni2work", - "version": "10.1.0", + "version": "10.2.0", "description": "", "keywords": [], "author": "", diff --git a/package.yaml b/package.yaml index 51204d9bc..9d8838372 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: uniworx -version: 10.1.0 +version: 10.2.0 dependencies: - base From 22b3780efdabdc572d6c5a08b282bb65448bf3f1 Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Tue, 19 Nov 2019 17:43:41 +0100 Subject: [PATCH 09/38] feat(async-table): no submit on locked inputs --- frontend/src/utils/async-table/async-table.js | 28 +++---- frontend/src/utils/form/datepicker.js | 73 ++++++++++++++----- 2 files changed, 70 insertions(+), 31 deletions(-) diff --git a/frontend/src/utils/async-table/async-table.js b/frontend/src/utils/async-table/async-table.js index f2d1248ab..705edb656 100644 --- a/frontend/src/utils/async-table/async-table.js +++ b/frontend/src/utils/async-table/async-table.js @@ -6,6 +6,8 @@ import * as debounce from 'lodash.debounce'; import './async-table-filter.sass'; import './async-table.sass'; +const ATTR_SUBMIT_LOCKED = 'submit-locked'; + const INPUT_DEBOUNCE = 600; const HEADER_HEIGHT = 80; @@ -153,21 +155,18 @@ export class AsyncTable { } _gatherTableFilterInputs(tableFilterForm) { - Array.from(tableFilterForm.querySelectorAll('input[type="search"]')).forEach((input) => { - this._tableFilterInputs.search.push(input); + Array.from(tableFilterForm.querySelectorAll('input')).forEach((input) => { + const inputType = input.getAttribute('type'); + if (inputType === 'search') { + this._tableFilterInputs.search.push(input); + } else if (['text','date','time','datetime-local'].includes(inputType)) { + this._tableFilterInputs.input.push(input); + } else { + this._tableFilterInputs.change.push(input); + } }); - Array.from(tableFilterForm.querySelectorAll('input[type="text"]')).forEach((input) => { - this._tableFilterInputs.input.push(input); - }); - - Array.from(tableFilterForm.querySelectorAll('input:not([type="text"]):not([type="search"])')).forEach((input) => { - this._tableFilterInputs.change.push(input); - }); - - Array.from(tableFilterForm.querySelectorAll('select')).forEach((input) => { - this._tableFilterInputs.select.push(input); - }); + Array.from(tableFilterForm.querySelectorAll('select')).forEach((input) => this._tableFilterInputs.select.push(input)); } _addTableFilterEventListeners(tableFilterForm) { @@ -186,7 +185,8 @@ export class AsyncTable { this._tableFilterInputs.input.forEach((input) => { const debouncedInput = debounce(() => { - if (input.value.length === 0 || input.value.length > 2) { + const submitLocked = input.getAttribute(ATTR_SUBMIT_LOCKED); + if ((submitLocked === 'false' || submitLocked === null) && (input.value.length === 0 || input.value.length > 2)) { this._updateFromTableFilter(tableFilterForm); } }, INPUT_DEBOUNCE); diff --git a/frontend/src/utils/form/datepicker.js b/frontend/src/utils/form/datepicker.js index c4da9d381..f91ef3421 100644 --- a/frontend/src/utils/form/datepicker.js +++ b/frontend/src/utils/form/datepicker.js @@ -6,6 +6,10 @@ import moment from 'moment'; const KEYCODE_ESCAPE = 27; const Z_INDEX_MODAL = 9999; +// should be the same as ATTR_SUBMIT_LOCKED in async-table util +// TODO move to global config +const ATTR_DATEPICKER_OPEN = 'submit-locked'; + // INTERNAL (Uni2work specific) formats for formatting dates and/or times const FORM_DATE_FORMAT = { 'date': moment.HTML5_FMT.DATE, @@ -26,21 +30,10 @@ const FORM_DATE_FORMAT_MOMENT = { 'datetime-local': `${FORM_DATE_FORMAT_DATE_MOMENT} ${FORM_DATE_FORMAT_TIME_MOMENT}`, }; -/** - * Takes a string representation of a date, an input ('previous') format and a desired output format and returns a reformatted date string. - * If the date string is not valid (i.e. cannot be parsed with the given input format string), returns the original date string; - * @param {*} dateStr string representation of a date (needs to be in format formatIn) - * @param {*} formatIn input format string - * @param {*} formatOut format string of the desired output date string - */ -function reformatDateString(dateStr, formatIn, formatOut) { - const parsedMomentDate = moment(dateStr, [formatIn, formatOut]); - return parsedMomentDate.isValid() ? parsedMomentDate.format(formatOut) : dateStr; -} - const DATEPICKER_UTIL_SELECTOR = 'input[type="date"], input[type="time"], input[type="datetime-local"]'; const DATEPICKER_INITIALIZED_CLASS = 'datepicker--initialized'; +const DATEPICKER_OPEN_CLASS = 'calendar-open'; const DATEPICKER_CONFIG = { 'global': { @@ -163,6 +156,21 @@ export class Datepicker { // mark the form input element as initialized this._element.classList.add(DATEPICKER_INITIALIZED_CLASS); + // create a mutation observer that observes the datepicker instance class and sets + // the datepicker-open DOM attribute of the input element if the datepicker has been opened + const datepickerInstanceObserver = new MutationObserver((mutations) => { + mutations.forEach(mutation => { + if (!mutation.oldValue.includes(DATEPICKER_OPEN_CLASS) && this.datepickerInstance.dt.getAttribute('class').includes(DATEPICKER_OPEN_CLASS)) { + this._element.setAttribute(ATTR_DATEPICKER_OPEN, true); + } + }); + }); + datepickerInstanceObserver.observe(this.datepickerInstance.dt, { + attributes: true, + attributeFilter: ['class'], + attributeOldValue: true, + }); + const setDatepickerDate = () => { // try to parse the current input element value with fancy and internal format string const parsedMomentDate = moment(this._element.value, FORM_DATE_FORMAT_MOMENT[this.elementType]); @@ -188,7 +196,7 @@ export class Datepicker { const focussedIsNotElement = event.relatedTarget !== this._element; const focussedIsInDocument = window.document.contains(event.relatedTarget); if (hasFocus && focussedIsNotTimepicker && focussedIsNotElement && focussedIsInDocument) - this.datepickerInstance.close(); + this.closeDatepickerInstance(); }); // close the instance on click on any element outside of the datepicker (except the input element itself) @@ -198,13 +206,13 @@ export class Datepicker { const targetIsInDocument = window.document.contains(event.target); const targetIsNotElement = event.target !== this._element; if (targetIsOutside && targetIsInDocument && targetIsNotElement) - this.datepickerInstance.close(); + this.closeDatepickerInstance(); }); // close the instance on escape keydown events this._element.addEventListener('keydown', event => { if (event.keyCode === KEYCODE_ESCAPE) { - this.datepickerInstance.close(); + this.closeDatepickerInstance(); } }); @@ -216,6 +224,24 @@ export class Datepicker { this.datepickerInstance.remove(); } + + // DATEPICKER INSTANCE CONTROL + + /** + * Closes the datepicker instance, releasing the lock on the input element. + */ + closeDatepickerInstance() { + if (!this._element.datepicker-open) { + throw new Error('Cannot close already closed datepicker instance!'); + } + + this._element.setAttribute(ATTR_DATEPICKER_OPEN, false); + this.datepickerInstance.close(); + } + + + // FORMAT METHODS + /** * Formats the value of this input element from datepicker format (i.e. DATEPICKER_CONFIG.dateFormat + " " + datetime.defaults.timeFormat) to Uni2work internal date format (i.e. FORM_DATE_FORMAT) required for form submission * @param {*} toFancy optional target format switch (boolean value; default is false). If set to a truthy value, formats the element value to fancy instead of internal date format. @@ -226,8 +252,6 @@ export class Datepicker { } } - - /** * Returns a datestring in internal format from the current state of the input element value. * @param {*} toFancy Format date from internal to fancy or vice versa. When omitted, toFancy is falsy and results in fancy -> internal @@ -265,3 +289,18 @@ export class Datepicker { return formData; } } + + +// HELPER FUNCTIONS + +/** + * Takes a string representation of a date, an input ('previous') format and a desired output format and returns a reformatted date string. + * If the date string is not valid (i.e. cannot be parsed with the given input format string), returns the original date string; + * @param {*} dateStr string representation of a date (needs to be in format formatIn) + * @param {*} formatIn input format string + * @param {*} formatOut format string of the desired output date string + */ +function reformatDateString(dateStr, formatIn, formatOut) { + const parsedMomentDate = moment(dateStr, [formatIn, formatOut]); + return parsedMomentDate.isValid() ? parsedMomentDate.format(formatOut) : dateStr; +} From bfd35dbc5c362f5dac21c259785e5f641bde2871 Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Tue, 19 Nov 2019 19:26:48 +0100 Subject: [PATCH 10/38] chore(async-table): refactor --- frontend/src/utils/async-table/async-table.js | 22 +++++++++++++++++-- frontend/src/utils/form/datepicker.js | 5 +++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/frontend/src/utils/async-table/async-table.js b/frontend/src/utils/async-table/async-table.js index 705edb656..dbb223232 100644 --- a/frontend/src/utils/async-table/async-table.js +++ b/frontend/src/utils/async-table/async-table.js @@ -184,10 +184,28 @@ export class AsyncTable { }); this._tableFilterInputs.input.forEach((input) => { + input.submitLockObserver = new MutationObserver((mutations, observer) => { + for (const mutation of mutations) { + // if the submit lock has been released, trigger an update and disconnect this observer + if (mutation.oldValue === 'true' && input.getAttribute(ATTR_SUBMIT_LOCKED) === 'false') { + this._updateFromTableFilter(tableFilterForm); + observer.disconnect(); + break; + } + } + }); + const debouncedInput = debounce(() => { - const submitLocked = input.getAttribute(ATTR_SUBMIT_LOCKED); - if ((submitLocked === 'false' || submitLocked === null) && (input.value.length === 0 || input.value.length > 2)) { + const submitLocked = input.getAttribute(ATTR_SUBMIT_LOCKED) === 'true'; + if (!submitLocked && (input.value.length === 0 || input.value.length > 2)) { this._updateFromTableFilter(tableFilterForm); + } else if (submitLocked) { + // observe the submit lock of the input element + input.submitLockObserver.observe(input, { + attributes: true, + attributeFilter: [ATTR_SUBMIT_LOCKED], + attributeOldValue: true, + }); } }, INPUT_DEBOUNCE); input.addEventListener('input', debouncedInput); diff --git a/frontend/src/utils/form/datepicker.js b/frontend/src/utils/form/datepicker.js index f91ef3421..ee7b3f5a1 100644 --- a/frontend/src/utils/form/datepicker.js +++ b/frontend/src/utils/form/datepicker.js @@ -159,11 +159,12 @@ export class Datepicker { // create a mutation observer that observes the datepicker instance class and sets // the datepicker-open DOM attribute of the input element if the datepicker has been opened const datepickerInstanceObserver = new MutationObserver((mutations) => { - mutations.forEach(mutation => { + for (const mutation of mutations) { if (!mutation.oldValue.includes(DATEPICKER_OPEN_CLASS) && this.datepickerInstance.dt.getAttribute('class').includes(DATEPICKER_OPEN_CLASS)) { this._element.setAttribute(ATTR_DATEPICKER_OPEN, true); + break; } - }); + } }); datepickerInstanceObserver.observe(this.datepickerInstance.dt, { attributes: true, From 67e472fa5e09ed2068d477ef12b12adb0ca98c4f Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Wed, 20 Nov 2019 17:44:39 +0100 Subject: [PATCH 11/38] feat(frontend): split up util registry split up setup of utils (into (DOM) setup and (event listener) start steps); moved event listener registration of datepicker and async-table util to start method(s); small diverse fixes and refactoring. FIXME: enter in datepicker inputs still cause HTTP request loop --- .../services/util-registry/util-registry.js | 3 ++ frontend/src/utils/async-table/async-table.js | 43 +++++++++---------- frontend/src/utils/form/datepicker.js | 42 ++++++++++-------- 3 files changed, 47 insertions(+), 41 deletions(-) diff --git a/frontend/src/services/util-registry/util-registry.js b/frontend/src/services/util-registry/util-registry.js index c6e866adf..29cfb380b 100644 --- a/frontend/src/services/util-registry/util-registry.js +++ b/frontend/src/services/util-registry/util-registry.js @@ -19,6 +19,8 @@ export class UtilRegistry { * element: HTMLElement | element the util is applied to * destroy: Function | function to destroy the util and remove any listeners * + * (optional) start function for registering event listeners + * * @param util Object Utility that should be added to the registry */ register(util) { @@ -52,6 +54,7 @@ export class UtilRegistry { } this._registeredUtils.forEach((util) => this.setup(util, scope)); + this._activeUtilInstances.forEach((instance) => typeof instance.start === 'function' && instance.start()); } setup(util, scope = document.body) { diff --git a/frontend/src/utils/async-table/async-table.js b/frontend/src/utils/async-table/async-table.js index dbb223232..31ab5f9bb 100644 --- a/frontend/src/utils/async-table/async-table.js +++ b/frontend/src/utils/async-table/async-table.js @@ -81,9 +81,6 @@ export class AsyncTable { throw new Error('Async Table cannot be set up without a scrolltable element!'); } - this._setupSortableHeaders(); - this._setupPagination(); - this._setupPageSizeSelect(); this._setupTableFilter(); this._processStorage(); @@ -95,11 +92,18 @@ export class AsyncTable { this._element.classList.add(ASYNC_TABLE_INITIALIZED_CLASS); } + start() { + this._startSortableHeaders(); + this._startPagination(); + this._startPageSizeSelect(); + this._startTableFilter(); + } + destroy() { console.log('TBD: Destroy AsyncTable'); } - _setupSortableHeaders() { + _startSortableHeaders() { this._ths = Array.from(this._scrollTable.querySelectorAll('th.sortable, .course-header')) .map((th) => ({ element: th })); @@ -112,7 +116,7 @@ export class AsyncTable { }); } - _setupPagination() { + _startPagination() { const pagination = this._element.querySelector('#' + this._cssIdPrefix + this._asyncTableId + '-pagination'); if (pagination) { this._pageLinks = Array.from(pagination.querySelectorAll('.page-link')) @@ -136,7 +140,7 @@ export class AsyncTable { } } - _setupPageSizeSelect() { + _startPageSizeSelect() { // pagesize form this._pagesizeForm = this._element.querySelector('#' + this._cssIdPrefix + this._asyncTableId + '-pagesize-form'); @@ -150,6 +154,12 @@ export class AsyncTable { const tableFilterForm = this._element.querySelector(ASYNC_TABLE_FILTER_FORM_SELECTOR); if (tableFilterForm) { this._gatherTableFilterInputs(tableFilterForm); + } + } + + _startTableFilter() { + const tableFilterForm = this._element.querySelector(ASYNC_TABLE_FILTER_FORM_SELECTOR); + if (tableFilterForm) { this._addTableFilterEventListeners(tableFilterForm); } } @@ -170,20 +180,7 @@ export class AsyncTable { } _addTableFilterEventListeners(tableFilterForm) { - this._tableFilterInputs.search.forEach((input) => { - const debouncedInput = debounce(() => { - if (input.value.length === 0 || input.value.length > 2) { - this._updateFromTableFilter(tableFilterForm); - } - }, INPUT_DEBOUNCE); - input.addEventListener('input', debouncedInput); - input.addEventListener('input', () => { - // set flag to ignore any currently pending requests (not debounced) - this._ignoreRequest = true; - }); - }); - - this._tableFilterInputs.input.forEach((input) => { + [...this._tableFilterInputs.search, ...this._tableFilterInputs.input].forEach((input) => { input.submitLockObserver = new MutationObserver((mutations, observer) => { for (const mutation of mutations) { // if the submit lock has been released, trigger an update and disconnect this observer @@ -196,7 +193,8 @@ export class AsyncTable { }); const debouncedInput = debounce(() => { - const submitLocked = input.getAttribute(ATTR_SUBMIT_LOCKED) === 'true'; + const submitLockedAttr = input.getAttribute(ATTR_SUBMIT_LOCKED); + const submitLocked = submitLockedAttr === 'true' || submitLockedAttr === null; if (!submitLocked && (input.value.length === 0 || input.value.length > 2)) { this._updateFromTableFilter(tableFilterForm); } else if (submitLocked) { @@ -208,6 +206,7 @@ export class AsyncTable { }); } }, INPUT_DEBOUNCE); + input.addEventListener('input', debouncedInput); input.addEventListener('input', () => { // set flag to ignore any currently pending requests (not debounced) @@ -262,7 +261,7 @@ export class AsyncTable { const url = new URL(this._storageManager.load('currentTableUrl') || window.location.href); // create new FormData and format any date values - const formData = Datepicker.unformatAll(this._massInputForm, new FormData(tableFilterForm)); + const formData = Datepicker.unformatAll(tableFilterForm, new FormData(tableFilterForm)); for (var k of url.searchParams.keys()) { url.searchParams.delete(k); diff --git a/frontend/src/utils/form/datepicker.js b/frontend/src/utils/form/datepicker.js index ee7b3f5a1..1f2603cca 100644 --- a/frontend/src/utils/form/datepicker.js +++ b/frontend/src/utils/form/datepicker.js @@ -100,13 +100,13 @@ export class Datepicker { // store the previously set type to select the input format this.elementType = this._element.getAttribute('type'); + // manually set the type attribute to text because datepicker handles displaying the date + this._element.setAttribute('type', 'text'); + // get all relevant config options for this datepicker type const datepickerGlobalConfig = DATEPICKER_CONFIG['global']; const datepickerConfig = DATEPICKER_CONFIG[this.elementType]; - // manually set the type attribute to text because datepicker handles displaying the date - this._element.setAttribute('type', 'text'); - // additional position config (optional data-datepicker-position attribute in html) that can specialize the global config const datepickerPosition = this._element.dataset.datepickerPosition; if (datepickerPosition) { @@ -155,23 +155,9 @@ export class Datepicker { // mark the form input element as initialized this._element.classList.add(DATEPICKER_INITIALIZED_CLASS); + } - // create a mutation observer that observes the datepicker instance class and sets - // the datepicker-open DOM attribute of the input element if the datepicker has been opened - const datepickerInstanceObserver = new MutationObserver((mutations) => { - for (const mutation of mutations) { - if (!mutation.oldValue.includes(DATEPICKER_OPEN_CLASS) && this.datepickerInstance.dt.getAttribute('class').includes(DATEPICKER_OPEN_CLASS)) { - this._element.setAttribute(ATTR_DATEPICKER_OPEN, true); - break; - } - } - }); - datepickerInstanceObserver.observe(this.datepickerInstance.dt, { - attributes: true, - attributeFilter: ['class'], - attributeOldValue: true, - }); - + start() { const setDatepickerDate = () => { // try to parse the current input element value with fancy and internal format string const parsedMomentDate = moment(this._element.value, FORM_DATE_FORMAT_MOMENT[this.elementType]); @@ -190,6 +176,24 @@ export class Datepicker { // change the selected date in the tail.datetime instance if the value of the input element is changed this._element.addEventListener('change', setDatepickerDate, { once: true }); + + // create a mutation observer that observes the datepicker instance class and sets + // the datepicker-open DOM attribute of the input element if the datepicker has been opened + const datepickerInstanceObserver = new MutationObserver((mutations) => { + for (const mutation of mutations) { + if (!mutation.oldValue.includes(DATEPICKER_OPEN_CLASS) && this.datepickerInstance.dt.getAttribute('class').includes(DATEPICKER_OPEN_CLASS)) { + this._element.setAttribute(ATTR_DATEPICKER_OPEN, true); + break; + } + } + }); + datepickerInstanceObserver.observe(this.datepickerInstance.dt, { + attributes: true, + attributeFilter: ['class'], + attributeOldValue: true, + }); + + // close the instance on focusout of any element if another input is focussed that is neither the timepicker nor _element window.addEventListener('focusout', event => { const hasFocus = event.relatedTarget !== null; From ddf94bf5650addbf6de40894ecda9ba6ef36e143 Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Tue, 3 Dec 2019 11:20:44 +0100 Subject: [PATCH 12/38] fix(util-registry): start setup instances and not all active instances --- frontend/src/services/util-registry/util-registry.js | 9 +++++---- frontend/src/utils/async-table/async-table.js | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/src/services/util-registry/util-registry.js b/frontend/src/services/util-registry/util-registry.js index 29cfb380b..506f34003 100644 --- a/frontend/src/services/util-registry/util-registry.js +++ b/frontend/src/services/util-registry/util-registry.js @@ -48,13 +48,14 @@ export class UtilRegistry { } setupAll(scope) { + const setupInstances = this._registeredUtils.map((util) => this.setup(util, scope)).flat(); + setupInstances.forEach((instance) => typeof instance.start === 'function' && instance.start()); + if (DEBUG_MODE > 1) { - console.info('registered js utilities:'); - console.table(this._registeredUtils); + console.info('setup js instances:'); + console.table(setupInstances); } - this._registeredUtils.forEach((util) => this.setup(util, scope)); - this._activeUtilInstances.forEach((instance) => typeof instance.start === 'function' && instance.start()); } setup(util, scope = document.body) { diff --git a/frontend/src/utils/async-table/async-table.js b/frontend/src/utils/async-table/async-table.js index 31ab5f9bb..7ac679c5f 100644 --- a/frontend/src/utils/async-table/async-table.js +++ b/frontend/src/utils/async-table/async-table.js @@ -216,6 +216,7 @@ export class AsyncTable { this._tableFilterInputs.change.forEach((input) => { input.addEventListener('change', () => { + //if (this._element.classList.contains(ASYNC_TABLE_LOADING_CLASS)) this._updateFromTableFilter(tableFilterForm); }); }); From 00584f95901786ce211f64e1edcfeedab2299c45 Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Tue, 3 Dec 2019 11:35:25 +0100 Subject: [PATCH 13/38] feat(util-registry): more debug info for setup util instances --- frontend/src/services/util-registry/util-registry.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/services/util-registry/util-registry.js b/frontend/src/services/util-registry/util-registry.js index 506f34003..bd4f49c9f 100644 --- a/frontend/src/services/util-registry/util-registry.js +++ b/frontend/src/services/util-registry/util-registry.js @@ -55,7 +55,6 @@ export class UtilRegistry { console.info('setup js instances:'); console.table(setupInstances); } - } setup(util, scope = document.body) { @@ -90,7 +89,7 @@ export class UtilRegistry { } this._activeUtilInstances.push(...instances); - return instances; + return instances.map(instance => ({ scope: scope, util: util, ...instance })); } find(name) { From 4ae6745aac8f68b640b5fc724575618ebb9ef144 Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Tue, 3 Dec 2019 16:09:46 +0100 Subject: [PATCH 14/38] chore(util-registry): refactor + new unit tests for start --- frontend/src/app.js | 2 +- frontend/src/app.spec.js | 4 +- .../services/util-registry/util-registry.js | 20 +++++-- .../util-registry/util-registry.spec.js | 58 +++++++++++++++++-- frontend/src/utils/async-table/async-table.js | 2 +- .../src/utils/async-table/async-table.spec.js | 2 +- frontend/src/utils/check-all/check-all.js | 2 +- .../src/utils/check-all/check-all.spec.js | 2 +- frontend/src/utils/mass-input/mass-input.js | 2 +- frontend/src/utils/modal/modal.js | 2 +- 10 files changed, 79 insertions(+), 17 deletions(-) diff --git a/frontend/src/app.js b/frontend/src/app.js index 8b26b08a2..66a9760b9 100644 --- a/frontend/src/app.js +++ b/frontend/src/app.js @@ -15,7 +15,7 @@ export class App { constructor() { this.utilRegistry.setApp(this); - document.addEventListener('DOMContentLoaded', () => this.utilRegistry.setupAll()); + document.addEventListener('DOMContentLoaded', () => this.utilRegistry.initAll()); } registerUtilities(utils) { diff --git a/frontend/src/app.spec.js b/frontend/src/app.spec.js index e682f0e28..f1f5df476 100644 --- a/frontend/src/app.spec.js +++ b/frontend/src/app.spec.js @@ -24,9 +24,9 @@ describe('App', () => { }); it('should setup all utlites when page is done loading', () => { - spyOn(global.App.utilRegistry, 'setupAll'); + spyOn(global.App.utilRegistry, 'initAll'); document.dispatchEvent(new Event('DOMContentLoaded')); - expect(global.App.utilRegistry.setupAll).toHaveBeenCalled(); + expect(global.App.utilRegistry.initAll).toHaveBeenCalled(); }); describe('provides services', () => { diff --git a/frontend/src/services/util-registry/util-registry.js b/frontend/src/services/util-registry/util-registry.js index bd4f49c9f..f5c409351 100644 --- a/frontend/src/services/util-registry/util-registry.js +++ b/frontend/src/services/util-registry/util-registry.js @@ -13,6 +13,9 @@ export class UtilRegistry { * name: string | utils name, e.g. 'example' * selector: string | utils selector, e.g. '[uw-example]' * setup: Function | utils setup function, see below + * + * optional util properties: + * start: Function | utils start function, see below * * setup function must return instance object with at least these properties: * name: string | utils name @@ -47,14 +50,19 @@ export class UtilRegistry { this._appInstance = appInstance; } - setupAll(scope) { + initAll(scope) { const setupInstances = this._registeredUtils.map((util) => this.setup(util, scope)).flat(); - setupInstances.forEach((instance) => typeof instance.start === 'function' && instance.start()); + + setupInstances + .filter((instance) => instance && typeof instance.start === 'function') + .forEach((instance) => this.start(instance)); if (DEBUG_MODE > 1) { - console.info('setup js instances:'); + console.info('initialized js util instances:'); console.table(setupInstances); } + + return setupInstances; } setup(util, scope = document.body) { @@ -74,7 +82,7 @@ export class UtilRegistry { utilInstance = new util(element, this._appInstance); } catch(err) { if (DEBUG_MODE > 0) { - console.warn('Error while trying to initialize a utility!', { util , element, err }); + console.error('Error while trying to initialize a utility!', { util , element, err }); } } @@ -92,6 +100,10 @@ export class UtilRegistry { return instances.map(instance => ({ scope: scope, util: util, ...instance })); } + start(instance) { + instance.start(); + } + find(name) { return this._registeredUtils.find((util) => util.name === name); } diff --git a/frontend/src/services/util-registry/util-registry.spec.js b/frontend/src/services/util-registry/util-registry.spec.js index 5f29f6a3c..087ec0817 100644 --- a/frontend/src/services/util-registry/util-registry.spec.js +++ b/frontend/src/services/util-registry/util-registry.spec.js @@ -103,26 +103,70 @@ describe('UtilRegistry', () => { }); }); - describe('setupAll()', () => { + describe('initAll()', () => { it('should setup all the utilities', () => { spyOn(utilRegistry, 'setup'); utilRegistry.register(TestUtil1); utilRegistry.register(TestUtil2); - utilRegistry.setupAll(); + utilRegistry.register(TestUtil3); + utilRegistry.initAll(); - expect(utilRegistry.setup.calls.count()).toBe(2); + expect(utilRegistry.setup.calls.count()).toBe(3); expect(utilRegistry.setup.calls.argsFor(0)).toEqual([TestUtil1, undefined]); expect(utilRegistry.setup.calls.argsFor(1)).toEqual([TestUtil2, undefined]); + expect(utilRegistry.setup.calls.argsFor(2)).toEqual([TestUtil3, undefined]); }); it('should pass the given scope', () => { spyOn(utilRegistry, 'setup'); utilRegistry.register(TestUtil1); const scope = document.createElement('div'); - utilRegistry.setupAll(scope); + utilRegistry.initAll(scope); expect(utilRegistry.setup).toHaveBeenCalledWith(TestUtil1, scope); }); + + describe('starts startable util instances', () => { + let testScope, + testElement1, + testElement2, + testElement3, + testElement4; + + beforeEach(() => { + testScope = document.createElement('div'); + + testElement1 = document.createElement('div'); + testElement2 = document.createElement('div'); + testElement3 = document.createElement('div'); + testElement4 = document.createElement('div'); + + testElement1.classList.add('util1'); + testElement2.id = 'util2'; + testElement3.classList.add('util3'); + testElement4.classList.add('util3'); + + testScope.appendChild(testElement1); + testScope.appendChild(testElement2); + testScope.appendChild(testElement3); + testScope.appendChild(testElement4); + }); + + it('should start instances that provide a start function', () => { + spyOn(utilRegistry, 'start'); + utilRegistry.register(TestUtil3); + utilRegistry.initAll(testScope); + expect(utilRegistry.start.calls.count()).toBe(2); + }); + + it('should not start instances that do not provide a start function', () => { + spyOn(utilRegistry, 'start'); + utilRegistry.register(TestUtil1); + utilRegistry.register(TestUtil2); + utilRegistry.initAll(testScope); + expect(utilRegistry.start).not.toHaveBeenCalled(); + }); + }); }); }); @@ -138,6 +182,12 @@ class TestUtil1 { @Utility({ selector: '#util2' }) class TestUtil2 { } +@Utility({ selector: '.util3' }) +class TestUtil3 { + constructor() {} + start() {} +} + @Utility({ selector: '#throws' }) class ThrowingUtil { constructor() { diff --git a/frontend/src/utils/async-table/async-table.js b/frontend/src/utils/async-table/async-table.js index 7ac679c5f..69e67250b 100644 --- a/frontend/src/utils/async-table/async-table.js +++ b/frontend/src/utils/async-table/async-table.js @@ -364,7 +364,7 @@ export class AsyncTable { // update table with new this._element.innerHTML = response.element.innerHTML; - this._app.utilRegistry.setupAll(this._element); + this._app.utilRegistry.initAll(this._element); if (callback && typeof callback === 'function') { this._storageManager.save('cssIdPrefix', response.idPrefix); diff --git a/frontend/src/utils/async-table/async-table.spec.js b/frontend/src/utils/async-table/async-table.spec.js index 7dc36be0c..d72b4c1ec 100644 --- a/frontend/src/utils/async-table/async-table.spec.js +++ b/frontend/src/utils/async-table/async-table.spec.js @@ -8,7 +8,7 @@ const AppTestMock = { parseResponse: () => {}, }, utilRegistry: { - setupAll: () => {}, + initAll: () => {}, }, }; diff --git a/frontend/src/utils/check-all/check-all.js b/frontend/src/utils/check-all/check-all.js index 340dca0b1..d196aae51 100644 --- a/frontend/src/utils/check-all/check-all.js +++ b/frontend/src/utils/check-all/check-all.js @@ -93,7 +93,7 @@ export class CheckAll { th.insertBefore(this._checkAllCheckbox, th.firstChild); // set up new checkbox - this._app.utilRegistry.setupAll(th); + this._app.utilRegistry.initAll(th); this._checkAllCheckbox.addEventListener('input', () => this._onCheckAllCheckboxInput()); this._setupCheckboxListeners(); diff --git a/frontend/src/utils/check-all/check-all.spec.js b/frontend/src/utils/check-all/check-all.spec.js index a04e50c41..b9697fe3c 100644 --- a/frontend/src/utils/check-all/check-all.spec.js +++ b/frontend/src/utils/check-all/check-all.spec.js @@ -2,7 +2,7 @@ import { CheckAll } from './check-all'; const MOCK_APP = { utilRegistry: { - setupAll: () => {}, + initAll: () => {}, }, }; diff --git a/frontend/src/utils/mass-input/mass-input.js b/frontend/src/utils/mass-input/mass-input.js index 403c626ac..dee124a1a 100644 --- a/frontend/src/utils/mass-input/mass-input.js +++ b/frontend/src/utils/mass-input/mass-input.js @@ -155,7 +155,7 @@ export class MassInput { this._reset(); - this._app.utilRegistry.setupAll(this._element); + this._app.utilRegistry.initAll(this._element); } _serializeForm(submitButton, enctype) { diff --git a/frontend/src/utils/modal/modal.js b/frontend/src/utils/modal/modal.js index 52a9a2b99..c67b13ac7 100644 --- a/frontend/src/utils/modal/modal.js +++ b/frontend/src/utils/modal/modal.js @@ -177,6 +177,6 @@ export class Modal { this._element.insertBefore(modalContent, null); // setup any newly arrived utils - this._app.utilRegistry.setupAll(this._element); + this._app.utilRegistry.initAll(this._element); } } From 86dd3a96d2d6aa03e5c11a8225f76b64c229c133 Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Tue, 3 Dec 2019 17:42:40 +0100 Subject: [PATCH 15/38] refactor(util-registry): small change --- frontend/src/services/util-registry/util-registry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/services/util-registry/util-registry.js b/frontend/src/services/util-registry/util-registry.js index f5c409351..8638c947d 100644 --- a/frontend/src/services/util-registry/util-registry.js +++ b/frontend/src/services/util-registry/util-registry.js @@ -97,7 +97,7 @@ export class UtilRegistry { } this._activeUtilInstances.push(...instances); - return instances.map(instance => ({ scope: scope, util: util, ...instance })); + return instances.map((instance) => ({ scope: scope, util: util, ...instance })); } start(instance) { From ea5351e4833697ab0ce048e3067b0caf7fb77b5a Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Wed, 4 Dec 2019 14:59:44 +0100 Subject: [PATCH 16/38] chore(util-registry): fix tests --- .../src/services/util-registry/util-registry.js | 6 +----- .../services/util-registry/util-registry.spec.js | 14 ++++++++------ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/frontend/src/services/util-registry/util-registry.js b/frontend/src/services/util-registry/util-registry.js index 8638c947d..24251414e 100644 --- a/frontend/src/services/util-registry/util-registry.js +++ b/frontend/src/services/util-registry/util-registry.js @@ -55,7 +55,7 @@ export class UtilRegistry { setupInstances .filter((instance) => instance && typeof instance.start === 'function') - .forEach((instance) => this.start(instance)); + .forEach((instance) => instance.start()); if (DEBUG_MODE > 1) { console.info('initialized js util instances:'); @@ -100,10 +100,6 @@ export class UtilRegistry { return instances.map((instance) => ({ scope: scope, util: util, ...instance })); } - start(instance) { - instance.start(); - } - find(name) { return this._registeredUtils.find((util) => util.name === name); } diff --git a/frontend/src/services/util-registry/util-registry.spec.js b/frontend/src/services/util-registry/util-registry.spec.js index 087ec0817..cb9ef95b7 100644 --- a/frontend/src/services/util-registry/util-registry.spec.js +++ b/frontend/src/services/util-registry/util-registry.spec.js @@ -153,18 +153,20 @@ describe('UtilRegistry', () => { }); it('should start instances that provide a start function', () => { - spyOn(utilRegistry, 'start'); utilRegistry.register(TestUtil3); - utilRegistry.initAll(testScope); - expect(utilRegistry.start.calls.count()).toBe(2); + const initializedInstances = utilRegistry.initAll(testScope); + + expect(initializedInstances.length).toBe(2); + expect(initializedInstances.map((instance) => instance.util)).toEqual([TestUtil3,TestUtil3]); }); it('should not start instances that do not provide a start function', () => { - spyOn(utilRegistry, 'start'); utilRegistry.register(TestUtil1); utilRegistry.register(TestUtil2); - utilRegistry.initAll(testScope); - expect(utilRegistry.start).not.toHaveBeenCalled(); + const initializedInstances = utilRegistry.initAll(testScope); + + expect(initializedInstances.length).toBe(2); + expect(initializedInstances.map((instance) => instance.util)).toEqual([TestUtil1,TestUtil2]); }); }); }); From 2620fb2f9569368fdaafe3924c02f2f5a2671818 Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Thu, 5 Dec 2019 13:33:40 +0100 Subject: [PATCH 17/38] fix(util-registry): fix initAll and tests --- .../services/util-registry/util-registry.js | 19 +++++++++++++------ .../util-registry/util-registry.spec.js | 14 ++++++++------ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/frontend/src/services/util-registry/util-registry.js b/frontend/src/services/util-registry/util-registry.js index 24251414e..e035903fa 100644 --- a/frontend/src/services/util-registry/util-registry.js +++ b/frontend/src/services/util-registry/util-registry.js @@ -51,18 +51,25 @@ export class UtilRegistry { } initAll(scope) { + let startedInstances = []; const setupInstances = this._registeredUtils.map((util) => this.setup(util, scope)).flat(); - setupInstances - .filter((instance) => instance && typeof instance.start === 'function') - .forEach((instance) => instance.start()); + setupInstances.forEach((utilInstance) => { + if (utilInstance) { + const instance = utilInstance.instance; + if (instance && typeof instance.start === 'function') { + instance.start(); + startedInstances.push(instance); + } + } + }); if (DEBUG_MODE > 1) { console.info('initialized js util instances:'); console.table(setupInstances); } - return setupInstances; + return startedInstances; } setup(util, scope = document.body) { @@ -91,13 +98,13 @@ export class UtilRegistry { console.info('Got utility instance for utility "' + util.name + '"', { utilInstance }); } - instances.push(utilInstance); + instances.push({ util: util, scope: scope, element: element, instance: utilInstance }); } }); } this._activeUtilInstances.push(...instances); - return instances.map((instance) => ({ scope: scope, util: util, ...instance })); + return instances; } find(name) { diff --git a/frontend/src/services/util-registry/util-registry.spec.js b/frontend/src/services/util-registry/util-registry.spec.js index cb9ef95b7..510033cf8 100644 --- a/frontend/src/services/util-registry/util-registry.spec.js +++ b/frontend/src/services/util-registry/util-registry.spec.js @@ -97,8 +97,12 @@ describe('UtilRegistry', () => { const setupUtilities = utilRegistry.setup(TestUtil1, testScope); expect(setupUtilities).toBeTruthy(); - expect(setupUtilities[0].app).toBe(fakeApp); - expect(setupUtilities[1].app).toBe(fakeApp); + setupUtilities.forEach((setupUtility) => { + expect(setupUtility).toBeTruthy(); + expect(setupUtility.instance).toBeTruthy(); + }); + expect(setupUtilities[0].instance.app).toBe(fakeApp); + expect(setupUtilities[1].instance.app).toBe(fakeApp); }); }); }); @@ -157,16 +161,14 @@ describe('UtilRegistry', () => { const initializedInstances = utilRegistry.initAll(testScope); expect(initializedInstances.length).toBe(2); - expect(initializedInstances.map((instance) => instance.util)).toEqual([TestUtil3,TestUtil3]); }); it('should not start instances that do not provide a start function', () => { utilRegistry.register(TestUtil1); utilRegistry.register(TestUtil2); - const initializedInstances = utilRegistry.initAll(testScope); + const startedInstances = utilRegistry.initAll(testScope); - expect(initializedInstances.length).toBe(2); - expect(initializedInstances.map((instance) => instance.util)).toEqual([TestUtil1,TestUtil2]); + expect(startedInstances.length).toBe(0); }); }); }); From cd3e72c0f1389a80786df1f4e2433a2152cf3d55 Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Tue, 7 Jan 2020 11:16:00 +0100 Subject: [PATCH 18/38] fix(async-table): bind callback in updateTableFrom call --- frontend/src/utils/async-table/async-table.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/utils/async-table/async-table.js b/frontend/src/utils/async-table/async-table.js index 69e67250b..3c8d114e1 100644 --- a/frontend/src/utils/async-table/async-table.js +++ b/frontend/src/utils/async-table/async-table.js @@ -255,7 +255,7 @@ export class AsyncTable { }; } this._ignoreRequest = false; - this._updateTableFrom(url, callback); + this._updateTableFrom(url, callback && callback.bind(this)); } _serializeTableFilterToURL(tableFilterForm) { From 49bafe1276e13f0961e9d327b6506b97de39d079 Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Wed, 8 Jan 2020 08:30:14 +0100 Subject: [PATCH 19/38] fix: fix app frontend test --- frontend/src/app.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app.spec.js b/frontend/src/app.spec.js index f1f5df476..545e0f048 100644 --- a/frontend/src/app.spec.js +++ b/frontend/src/app.spec.js @@ -23,7 +23,7 @@ describe('App', () => { expect(global.App).toBeTruthy(); }); - it('should setup all utlites when page is done loading', () => { + it('should init all utlites when page is done loading', () => { spyOn(global.App.utilRegistry, 'initAll'); document.dispatchEvent(new Event('DOMContentLoaded')); expect(global.App.utilRegistry.initAll).toHaveBeenCalled(); From 6b51cc5e53ab7686f2394cd80bc4ee4fc426c8d5 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Wed, 8 Jan 2020 22:56:30 +0100 Subject: [PATCH 20/38] fix: tweak debouncing & canceling --- frontend/src/utils/async-table/async-table.js | 43 ++-- package-lock.json | 200 +----------------- package.json | 1 + webpack.config.js | 96 ++++----- 4 files changed, 83 insertions(+), 257 deletions(-) diff --git a/frontend/src/utils/async-table/async-table.js b/frontend/src/utils/async-table/async-table.js index 3c8d114e1..e40ecc86d 100644 --- a/frontend/src/utils/async-table/async-table.js +++ b/frontend/src/utils/async-table/async-table.js @@ -3,12 +3,14 @@ import { StorageManager, LOCATION } from '../../lib/storage-manager/storage-mana import { Datepicker } from '../form/datepicker'; import { HttpClient } from '../../services/http-client/http-client'; import * as debounce from 'lodash.debounce'; +import * as throttle from 'lodash.throttle'; import './async-table-filter.sass'; import './async-table.sass'; const ATTR_SUBMIT_LOCKED = 'submit-locked'; const INPUT_DEBOUNCE = 600; +const FILTER_DEBOUNCE = 100; const HEADER_HEIGHT = 80; const ASYNC_TABLE_LOCAL_STORAGE_KEY = 'ASYNC_TABLE'; @@ -35,6 +37,8 @@ export class AsyncTable { _scrollTable; _cssIdPrefix = ''; + _cancelPendingUpdates = []; + _tableFilterInputs = { search: [], input: [], @@ -60,7 +64,7 @@ export class AsyncTable { if (this._element.classList.contains(ASYNC_TABLE_INITIALIZED_CLASS)) { return false; } - + // param asyncTableDbHeader if (this._element.dataset.asyncTableDbHeader !== undefined) { this._asyncTableHeader = this._element.dataset.asyncTableDbHeader; @@ -180,56 +184,62 @@ export class AsyncTable { } _addTableFilterEventListeners(tableFilterForm) { + const debouncedUpdateFromTableFilter = throttle((() => this._updateFromTableFilter(tableFilterForm)).bind(this), FILTER_DEBOUNCE, { leading: true, trailing: false }); + [...this._tableFilterInputs.search, ...this._tableFilterInputs.input].forEach((input) => { - input.submitLockObserver = new MutationObserver((mutations, observer) => { + const submitLockObserver = new MutationObserver((mutations, observer) => { for (const mutation of mutations) { // if the submit lock has been released, trigger an update and disconnect this observer - if (mutation.oldValue === 'true' && input.getAttribute(ATTR_SUBMIT_LOCKED) === 'false') { - this._updateFromTableFilter(tableFilterForm); + if (mutation.target === input && mutation.attributeName === ATTR_SUBMIT_LOCKED && mutation.oldValue === 'true' && mutation.target.getAttribute(mutation.attributeName) === 'false') { + debouncedUpdateFromTableFilter(); observer.disconnect(); break; } } }); + this._cancelPendingUpdates.push(() => { submitLockObserver.disconnect(); }); const debouncedInput = debounce(() => { const submitLockedAttr = input.getAttribute(ATTR_SUBMIT_LOCKED); - const submitLocked = submitLockedAttr === 'true' || submitLockedAttr === null; + const submitLocked = submitLockedAttr === 'true'; if (!submitLocked && (input.value.length === 0 || input.value.length > 2)) { - this._updateFromTableFilter(tableFilterForm); - } else if (submitLocked) { + debouncedUpdateFromTableFilter(); + } else if (submitLockedAttr === 'true') { // observe the submit lock of the input element - input.submitLockObserver.observe(input, { + submitLockObserver.observe(input, { attributes: true, attributeFilter: [ATTR_SUBMIT_LOCKED], attributeOldValue: true, }); } }, INPUT_DEBOUNCE); + this._cancelPendingUpdates.push(debouncedInput.cancel); - input.addEventListener('input', debouncedInput); input.addEventListener('input', () => { - // set flag to ignore any currently pending requests (not debounced) this._ignoreRequest = true; + debouncedInput(); }); }); this._tableFilterInputs.change.forEach((input) => { input.addEventListener('change', () => { //if (this._element.classList.contains(ASYNC_TABLE_LOADING_CLASS)) - this._updateFromTableFilter(tableFilterForm); + this._ignoreRequest = true; + debouncedUpdateFromTableFilter(); }); }); this._tableFilterInputs.select.forEach((input) => { input.addEventListener('change', () => { - this._updateFromTableFilter(tableFilterForm); + this._ignoreRequest = true; + debouncedUpdateFromTableFilter(); }); }); tableFilterForm.addEventListener('submit', (event) =>{ event.preventDefault(); - this._updateFromTableFilter(tableFilterForm); + this._ignoreRequest = true; + debouncedUpdateFromTableFilter(); }); } @@ -254,7 +264,6 @@ export class AsyncTable { } }; } - this._ignoreRequest = false; this._updateTableFrom(url, callback && callback.bind(this)); } @@ -339,6 +348,12 @@ export class AsyncTable { // fetches new sorted element from url with params and replaces contents of current element _updateTableFrom(url, callback) { + const cancelPendingUpdates = (() => { + this._cancelPendingUpdates.forEach(f => f()); + }).bind(this); + [0, INPUT_DEBOUNCE * 1.5, FILTER_DEBOUNCE * 1.5].forEach(delay => window.setTimeout(cancelPendingUpdates, delay)); + this._ignoreRequest = false; + this._element.classList.add(ASYNC_TABLE_LOADING_CLASS); const headers = { diff --git a/package-lock.json b/package-lock.json index 39368eea4..772738a45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5367,17 +5367,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "color-parse": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/color-parse/-/color-parse-1.3.8.tgz", - "integrity": "sha512-1Y79qFv0n1xair3lNMTNeoFvmc3nirMVBij24zbs1f13+7fPpQClMg5b4AuKXLt3szj7BRlHMCXHplkce6XlmA==", - "dev": true, - "requires": { - "color-name": "^1.0.0", - "defined": "^1.0.0", - "is-plain-obj": "^1.1.0" - } - }, "color-string": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", @@ -7228,12 +7217,6 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, - "deepmerge": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", - "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==", - "dev": true - }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -7284,12 +7267,6 @@ } } }, - "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", - "dev": true - }, "del": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", @@ -11431,12 +11408,6 @@ "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==", "dev": true }, - "lodash.isempty": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", - "integrity": "sha1-b4bL7di+TsmHvpqvM8loTbGzHn4=", - "dev": true - }, "lodash.ismatch": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", @@ -11468,6 +11439,11 @@ "lodash._reinterpolate": "~3.0.0" } }, + "lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" + }, "lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -11716,15 +11692,6 @@ } } }, - "merge-options": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-1.0.1.tgz", - "integrity": "sha512-iuPV41VWKWBIOpBsjoxjDZw8/GbSfZ2mk7N1453bwMrfzdrIk7EzBd+8UVR6rkw67th7xnk9Dytl3J+lHPdxvg==", - "dev": true, - "requires": { - "is-plain-obj": "^1.1" - } - }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -16405,15 +16372,6 @@ "postcss": "^7.0.2" } }, - "postcss-helpers": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/postcss-helpers/-/postcss-helpers-0.3.2.tgz", - "integrity": "sha512-hppnMXY6Ehe8CgLHQCDWbyUsXvBFggdzftWzznL65MhgZsE8o8pUTYbmUbLst0rps+wyUSLIUJ0bGpV2Tzv7lw==", - "dev": true, - "requires": { - "urijs": "^1.18.12" - } - }, "postcss-image-set-function": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz", @@ -17016,88 +16974,6 @@ "uniq": "^1.0.1" } }, - "posthtml": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.11.6.tgz", - "integrity": "sha512-C2hrAPzmRdpuL3iH0TDdQ6XCc9M7Dcc3zEW5BLerY65G4tWWszwv6nG/ksi6ul5i2mx22ubdljgktXCtNkydkw==", - "dev": true, - "requires": { - "posthtml-parser": "^0.4.1", - "posthtml-render": "^1.1.5" - } - }, - "posthtml-match-helper": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/posthtml-match-helper/-/posthtml-match-helper-1.0.1.tgz", - "integrity": "sha1-RRJT3o5YRKNI6WOtXt13aesSlRM=", - "dev": true - }, - "posthtml-parser": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.4.2.tgz", - "integrity": "sha512-BUIorsYJTvS9UhXxPTzupIztOMVNPa/HtAm9KHni9z6qEfiJ1bpOBL5DfUOL9XAc3XkLIEzBzpph+Zbm4AdRAg==", - "dev": true, - "requires": { - "htmlparser2": "^3.9.2" - } - }, - "posthtml-render": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-1.1.5.tgz", - "integrity": "sha512-yvt54j0zCBHQVEFAuR+yHld8CZrCa/E1Z/OcFNCV1IEWTLVxT8O7nYnM4IIw1CD4r8kaRd3lc42+0lgCKgm87w==", - "dev": true - }, - "posthtml-transform": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/posthtml-transform/-/posthtml-transform-1.0.7.tgz", - "integrity": "sha512-ihbO3C1pqjgq94PjgxWyN/S0B+rW3lW3C2o+U0MHYilxDy7zft+u9r+Gk9YT+Nh5DyZyLgWj6Q9H1CR0Z/TPTg==", - "dev": true, - "requires": { - "color-parse": "^1.3.7", - "loader-utils": "^1.1.0", - "lodash": "^4.17.14", - "postcss-values-parser": "^1.5.0", - "posthtml": "^0.11.3", - "posthtml-match-helper": "^1.0.1", - "slashes": "^1.0.5", - "unquote": "^1.1.1" - }, - "dependencies": { - "postcss-values-parser": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-1.5.0.tgz", - "integrity": "sha512-3M3p+2gMp0AH3da530TlX8kiO1nxdTnc3C6vr8dMxRLIlh8UYkz0/wcwptSXjhtx2Fr0TySI7a+BHDQ8NL7LaQ==", - "dev": true, - "requires": { - "flatten": "^1.0.2", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - } - } - }, - "postsvg": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/postsvg/-/postsvg-2.2.7.tgz", - "integrity": "sha512-TyRnoyEvszrEGOzxaTycnUgJZ0W2Xnd9fOmgfuy61Qjo6JhDPhAIBQ1dspQCvdVpK9KkIlZkSETSjmbO0gVIag==", - "dev": true, - "requires": { - "clone": "^1.0.4", - "deepmerge": "^2.1.0", - "posthtml": "^0.11.3", - "posthtml-match-helper": "^1.0.1", - "posthtml-parser": "^0.4.1", - "posthtml-render": "^1.1.2" - }, - "dependencies": { - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true - } - } - }, "prebuild-install": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.3.tgz", @@ -18295,12 +18171,6 @@ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", "dev": true }, - "slashes": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/slashes/-/slashes-1.0.5.tgz", - "integrity": "sha1-IEeY9sYyAU1gm12JtqV6eq9wgo0=", - "dev": true - }, "slice-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", @@ -18613,12 +18483,6 @@ "through": "2" } }, - "split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", - "dev": true - }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -19224,54 +19088,6 @@ "has-flag": "^3.0.0" } }, - "svg-mixer-utils": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/svg-mixer-utils/-/svg-mixer-utils-0.3.4.tgz", - "integrity": "sha512-szkeG+Jn6DRo7QlnUOYKslm4J6dg37I8E+tLG1PB13U6UhSlkC8teCisyT7ZRGRwTcTxmNizvu+/oe8uJUf5EA==", - "dev": true, - "requires": { - "ajv": "^6.5.1", - "anymatch": "^2.0.0", - "memory-fs": "^0.4.1", - "merge-options": "^1.0.0", - "postcss-helpers": "^0.3.2" - } - }, - "svg-transform-loader": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/svg-transform-loader/-/svg-transform-loader-2.0.8.tgz", - "integrity": "sha512-cK9PXVGV+hZoiR7lkIir+LOZ2qmJIuq0tm/emv5x7MF4PvyqY/lCNlYuctx37rSNgGE6Lhpdn8lUCZm8d/oNAQ==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "lodash.isempty": "^4.4.0", - "merge-options": "^1.0.0", - "postcss": "^7.0.14", - "posthtml-transform": "^1.0.7", - "postsvg": "^2.2.7", - "query-string": "^6.1.0", - "svg-mixer-utils": "^0.3.4" - }, - "dependencies": { - "query-string": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.9.0.tgz", - "integrity": "sha512-KG4bhCFYapExLsUHrFt+kQVEegF2agm4cpF/VNc6pZVthIfCc/GK8t8VyNIE3nyXG9DK3Tf2EGkxjR6/uRdYsA==", - "dev": true, - "requires": { - "decode-uri-component": "^0.2.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - } - }, - "strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", - "dev": true - } - } - }, "svgo": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", @@ -20025,12 +19841,6 @@ "punycode": "^2.1.0" } }, - "urijs": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.2.tgz", - "integrity": "sha512-s/UIq9ap4JPZ7H1EB5ULo/aOUbWqfDi7FKzMC2Nz+0Si8GiT1rIEaprt8hy3Vy2Ex2aJPpOQv4P4DuOZ+K1c6w==", - "dev": true - }, "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", diff --git a/package.json b/package.json index e85cd6f14..6bea4e881 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,7 @@ "@babel/runtime": "^7.7.6", "@juggle/resize-observer": "^2.5.0", "core-js": "^3.4.8", + "lodash.throttle": "^4.1.1", "moment": "^2.24.0", "npm": "^6.13.4", "tail.datetime": "git+ssh://git@gitlab2.rz.ifi.lmu.de/uni2work/tail.DateTime.git#master", diff --git a/webpack.config.js b/webpack.config.js index 93d802122..3cc5b8379 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -19,51 +19,51 @@ module.exports = { module: { rules: [ { - loader: 'babel-loader', + loader: 'babel-loader', - options: { - plugins: ['syntax-dynamic-import'], + options: { + plugins: ['syntax-dynamic-import'], - presets: [ - [ - '@babel/preset-env', - { - modules: false, - targets: { - edge: "17", - firefox: "50", - chrome: "60", - safari: "11.1", - ie: "11", - }, - useBuiltIns: "usage", - corejs: 3 - } - ] - ] - }, - test: /\.js$/i, - exclude: /node_modules/, + presets: [ + [ + '@babel/preset-env', + { + modules: false, + targets: { + edge: "17", + firefox: "50", + chrome: "60", + safari: "11.1", + ie: "11", + }, + useBuiltIns: "usage", + corejs: 3 + } + ] + ] + }, + test: /\.js$/i, + exclude: /node_modules/, }, { test: /\.css$/i, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { sourceMap: true }}, { loader: 'postcss-loader', options: { - sourceMap: true, - plugins: () => [ postcssPresetEnv ] - }}, + sourceMap: true, + plugins: () => [ postcssPresetEnv ] + }}, { loader: 'resolve-url-loader', options: { sourceMap: true }} ] }, { - test: /\.s(c|a)ss$/i, + test: /\.s(c|a)ss$/i, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { sourceMap: true }}, { loader: 'postcss-loader', options: { - sourceMap: true, - plugins: () => [ postcssPresetEnv ] - }}, + sourceMap: true, + plugins: () => [ postcssPresetEnv ] + }}, { loader: 'resolve-url-loader', options: { sourceMap: true }}, { loader: 'sass-loader', options: { implementation: require('sass'), sourceMap: true }} ] @@ -134,27 +134,27 @@ module.exports = { statsFilename: path.resolve(__dirname, 'config/favicon.json'), persistentCache: false, config: { - background: '#fff', - icons: { - android: false, - appleIcon: false, - appleStartup: false, - coast: false, - favicons: true, - firefox: false, - windows: false, - yandex: false - } + background: '#fff', + icons: { + android: false, + appleIcon: false, + appleStartup: false, + coast: false, + favicons: true, + firefox: false, + windows: false, + yandex: false + } } }), new RemovePlugin({ after: { - test: [ - { folder: path.resolve(__dirname, `static/wp-${webpackVersion}`), - method: (filePath) => { return new RegExp(/\/.*-icons\/.*\.(xml|json|webapp)$/, 'm').test(filePath); }, - recursive: true - } - ] + test: [ + { folder: path.resolve(__dirname, `static/wp-${webpackVersion}`), + method: (filePath) => { return new RegExp(/\/.*-icons\/.*\.(xml|json|webapp)$/, 'm').test(filePath); }, + recursive: true + } + ] } }) ], @@ -177,7 +177,7 @@ module.exports = { sourceMap: true }), new OptimizeCSSAssetsPlugin({ - cssProcessorOptions: { + cssProcessorOptions: { map: { inline: false } From 068632b11753f58e8142608db3dc139d0c84f93f Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Sat, 11 Jan 2020 22:02:12 +0100 Subject: [PATCH 21/38] feat: well known files --- .gitignore | 5 +- .gitlab-ci.yml | 2 +- config/favicon.json | 77 ++ config/mimetypes | 2 + {static => config}/robots.txt | 0 config/settings.yml | 4 +- messages/uniworx/de-de-formal.msg | 3 +- messages/uniworx/en-eu.msg | 3 +- package-lock.json | 1964 ++++++--------------------- package.json | 4 +- routes | 5 +- shell.nix | 2 +- src/Application.hs | 1 - src/Foundation.hs | 5 +- src/Handler/Common.hs | 29 - src/Import/NoFoundation.hs | 5 +- src/Settings.hs | 6 +- src/Settings/StaticFiles.hs | 14 +- src/Settings/StaticFiles/Favicon.hs | 38 - src/Settings/WellKnownFiles.hs | 11 + src/Settings/WellKnownFiles/TH.hs | 192 +++ src/Utils.hs | 4 +- static/favicon.ico | Bin 101484 -> 0 bytes webpack.config.js | 96 +- 24 files changed, 830 insertions(+), 1642 deletions(-) create mode 100644 config/favicon.json rename {static => config}/robots.txt (100%) delete mode 100644 src/Handler/Common.hs delete mode 100644 src/Settings/StaticFiles/Favicon.hs create mode 100644 src/Settings/WellKnownFiles.hs create mode 100644 src/Settings/WellKnownFiles/TH.hs delete mode 100644 static/favicon.ico diff --git a/.gitignore b/.gitignore index 681d12b11..878dea26a 100644 --- a/.gitignore +++ b/.gitignore @@ -35,5 +35,6 @@ test.log /.stack-work.lock /.npmrc /config/webpack.yml -static/wp-*/ -/config/favicon.json +/static +/well-known +/**/tmp-* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fb4d41b19..fa414baa8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -33,7 +33,7 @@ npm install: - n stable - npm install -g npm - hash -r - - apt-get install openssh-client -y + - apt-get -y install openssh-client exiftool - install -v -m 0700 -d ~/.ssh - install -v -T -m 0644 ${SSH_KNOWN_HOSTS} ~/.ssh/known_hosts - install -v -T -m 0400 ${SSH_DEPLOY_KEY} ~/.ssh/deploy && echo "IdentityFile ~/.ssh/deploy" >> ~/.ssh/config; diff --git a/config/favicon.json b/config/favicon.json new file mode 100644 index 000000000..2bb896654 --- /dev/null +++ b/config/favicon.json @@ -0,0 +1,77 @@ +{ + "masterPicture": "assets/favicon.svg", + "design": { + "desktop_browser": {}, + "ios": { + "picture_aspect": "background_and_margin", + "margin": "5%", + "background_color": "#ffffff", + "startup_image": { + "background_color": "#ffffff" + }, + "app_name": "Uni2work", + "assets": { + "ios6_and_prior_icons": false, + "ios7_and_later_icons": true, + "precomposed_icons": true, + "declare_only_default_icon": true + } + }, + "windows": { + "picture_aspect": "white_silhouette", + "background_color": "#0a9342", + "app_name": "Uni2work" + }, + "firefox_app": { + "picture_aspect": "circle", + "keep_picture_in_circle": false, + "circle_inner_margin": "5%", + "background_color": "#ffffff", + "overlay": false, + "manifest": { + "app_name": "Uni2work", + "app_description": { + "_i18n": true, + "de-de-formal": "Ein webbasiertes Lehrverwaltungssystem der LMU München", + "en-eu": "A web based teaching management system at LMU Munich" + }, + "developer_name": "Uni2work-Team", + "developer_url": "https://uni2work.ifi.lmu.de/info", + "display": "browser", + "start_url": "/" + } + }, + "android_chrome": { + "picture_aspect": "shadow", + "manifest": { + "name": "Uni2work", + "display": "browser", + "orientation": "portrait", + "start_url": "/" + }, + "assets": { + "legacy_icon": true, + "low_resolution_icons": false + } + }, + "safari_pinned_tab": { + "picture_aspect": "silhouette", + "theme_color": "#0a9342" + }, + "coast": { + "picture_aspect": "background_and_margin", + "background_color": "#ffffff", + "margin": "10%" + }, + "open_graph": { + "picture_aspect": "background_and_margin", + "background_color": "#ffffff", + "margin": "10%", + "ratio": "square" + } + }, + "settings": { + "html_code_file": true + }, + "versioning": false +} diff --git a/config/mimetypes b/config/mimetypes index 29bc4291f..5d3158e6e 100644 --- a/config/mimetypes +++ b/config/mimetypes @@ -43,6 +43,8 @@ application/java-vm class application/javascript js application/json json application/jsonml+json jsonml +application/manifest+json webmanifest +application/x-web-app-manifest+json webapp application/lost+xml lostxml application/mac-binhex40 hqx application/mac-compactpro cpt diff --git a/static/robots.txt b/config/robots.txt similarity index 100% rename from static/robots.txt rename to config/robots.txt diff --git a/config/settings.yml b/config/settings.yml index 9bdb23939..e348b6e91 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -4,8 +4,10 @@ # See https://github.com/yesodweb/yesod/wiki/Configuration#parsing-numeric-values-as-strings static-dir: "_env:STATIC_DIR:static" +well-known-dir: "_env:WELL_KNOWN_DIR:well-known" +well-known-link-file: "html_code.html" + webpack-manifest: "_env:WEBPACK_MANIFEST:config/webpack.yml" -favicon-stats: "_env:FAVICON_STATS:config/favicon.json" host: "_env:HOST:*4" # any IPv4 host port: "_env:PORT:3000" ip-from-header: "_env:IP_FROM_HEADER:false" diff --git a/messages/uniworx/de-de-formal.msg b/messages/uniworx/de-de-formal.msg index 92b612528..07248ce88 100644 --- a/messages/uniworx/de-de-formal.msg +++ b/messages/uniworx/de-de-formal.msg @@ -1208,8 +1208,7 @@ BreadcrumbTerm: Semester BreadcrumbSchool: Institut BreadcrumbUser: Benutzer BreadcrumbStatic: Statische Resource -BreadcrumbFavicon: Favicon -BreadcrumbRobots: robots.txt +BreadcrumbWellKnown: Benannte statische Resource BreadcrumbMetrics: Metriken BreadcrumbLecturerInvite: Einladung zum Kursverwalter BreadcrumbExamOfficeUserInvite: Einladung bzgl. Prüfungsleistungen diff --git a/messages/uniworx/en-eu.msg b/messages/uniworx/en-eu.msg index 824c95333..f071b9dae 100644 --- a/messages/uniworx/en-eu.msg +++ b/messages/uniworx/en-eu.msg @@ -1207,8 +1207,7 @@ BreadcrumbTerm: Semester BreadcrumbSchool: Department BreadcrumbUser: User BreadcrumbStatic: Static resource -BreadcrumbFavicon: Favicon -BreadcrumbRobots: robots.txt +BreadcrumbWellKnown: Named static resource BreadcrumbMetrics: Metrics BreadcrumbLecturerInvite: Invitation to be a course administrator BreadcrumbExamOfficeUserInvite: Invitation regarding exam achievements diff --git a/package-lock.json b/package-lock.json index 772738a45..f76bb83f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2567,351 +2567,6 @@ "integrity": "sha512-EKKR4p0higjsIPKjSSkGqtweUwo/GgR/zKL4rCwzF5Z/BZ/ebJZaS8ZjGE7YUNEN63SYk2WhpJVI+l9dwfU7RQ==", "dev": true }, - "@jimp/bmp": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.9.3.tgz", - "integrity": "sha512-wXZYccgGQAsIK8DZX0wZE3gbSd2mL2+eheSJMts6I5hQjxhVRZd1Gwu425nUQGzfKCOgKYTW0nLv7/8OoOTTkw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "bmp-js": "^0.1.0", - "core-js": "^3.4.1" - } - }, - "@jimp/core": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.9.3.tgz", - "integrity": "sha512-kB9lvst1QhgYOC963SAuPgv+DdVfxTProphrSffAAoo5eLeQab/Ca3ZUeX1E/SnLSr+NGVnNCd8c9gyuKDiENg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "any-base": "^1.1.0", - "buffer": "^5.2.0", - "core-js": "^3.4.1", - "exif-parser": "^0.1.12", - "file-type": "^9.0.0", - "load-bmfont": "^1.3.1", - "mkdirp": "0.5.1", - "phin": "^2.9.1", - "pixelmatch": "^4.0.2", - "tinycolor2": "^1.4.1" - }, - "dependencies": { - "buffer": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz", - "integrity": "sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==", - "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - } - } - }, - "@jimp/custom": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.9.3.tgz", - "integrity": "sha512-2E7yabQMeqjcK8+ZFu3Ja5cWyrB0zv/pmzNSDg/BBPJ59HE0fj/qcERAz6VklcjHUYRUfmE5uODsb+4DE0o/YQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/core": "^0.9.3", - "core-js": "^3.4.1" - } - }, - "@jimp/gif": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.9.3.tgz", - "integrity": "sha512-DshKgMQ8lXorI/xTRyeRkZqZ3JqgnL2aGYAhx0SkAunyHgXji27chmrOGj/6KVDBucrDf/6mSexnSoUDnlWrfA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "core-js": "^3.4.1", - "omggif": "^1.0.9" - } - }, - "@jimp/jpeg": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.9.3.tgz", - "integrity": "sha512-AJzcTJXfN9BHtpzAbICwR3+GoH0pSr6OYXbAS6yuKwz+xVn9UHrEjQb74CIzIRqrT/VWcIKg29cMQxgokzWY7w==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "core-js": "^3.4.1", - "jpeg-js": "^0.3.4" - } - }, - "@jimp/plugin-blit": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.9.3.tgz", - "integrity": "sha512-+UxCsJ3XkRSdpigpTBJ9WkdwUc3OtBlhVZdU6OL6M9ldume5Gj3rTyWvMCqytOK1tZ/+7HmxoWe4IWX31hz9qA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "core-js": "^3.4.1" - } - }, - "@jimp/plugin-blur": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.9.3.tgz", - "integrity": "sha512-RADcYjZ5vbk5ZrUiK7qv0G4xOpHtu19HWVVX9JTDbm4VByWTxPboVKlgiYLA6l+IxIXNtEqDclsADIM0s9FQhA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "core-js": "^3.4.1" - } - }, - "@jimp/plugin-color": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.9.3.tgz", - "integrity": "sha512-gHDA5GVx4/R4fitEACKmWH7hNy0aU48MZWYRxmATvuqY39KidJ0fjwp+brQ3Ivgb35AgFVc2jQYc3U/JXv4RxQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "core-js": "^3.4.1", - "tinycolor2": "^1.4.1" - } - }, - "@jimp/plugin-contain": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.9.3.tgz", - "integrity": "sha512-vdYAtp65LNDT/hMctow5o0a/SbD41/y7Z9AO7MGsfUIK92Woq90SNTWx7JplDl4HSZGrqaBONnfiEhRiYlDrdg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "core-js": "^3.4.1" - } - }, - "@jimp/plugin-cover": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.9.3.tgz", - "integrity": "sha512-yOwsvakgyS2/C4iZF1a1wg63QKfYvqb2d6k+rgY/0vaAe44JtEx+Gbg+7iOt4EaMm5BDlxRwmcA2Q8Pef8TvAQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "core-js": "^3.4.1" - } - }, - "@jimp/plugin-crop": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.9.3.tgz", - "integrity": "sha512-kqMXSyY8hrfo0idr6qY2USOWPrNqpDWs+D6Vwa+kV6SGJhj3rMTIcptQDaamIETSxbjkE8rwUu3K4Q5UD69D7w==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "core-js": "^3.4.1" - } - }, - "@jimp/plugin-displace": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.9.3.tgz", - "integrity": "sha512-0AdwxYRWDmJ2wIRIj2RR3sRmNjMhcy5Kwt9Jbi/RRnzxkRScZAiyzkNZhBul23EM7ClfjrUrZufuUvRMHxZRDw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "core-js": "^3.4.1" - } - }, - "@jimp/plugin-dither": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.9.3.tgz", - "integrity": "sha512-8OE+Xak9xepiCwSV+oAsb/gupTnttG3aDKxtpSZjwHebnr+k1VG8NgICbMSFATTVJqqZ18oj6LC+5726qHUJ9w==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "core-js": "^3.4.1" - } - }, - "@jimp/plugin-flip": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.9.3.tgz", - "integrity": "sha512-w+lzE1ZF/UOjB8qJdeIm+dLQtOK1obZwGYdCIbgxZxw4SfkkjAftJdY8o8RNOXhHDZqGu+cYQZbMKP1zcoNkyQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "core-js": "^3.4.1" - } - }, - "@jimp/plugin-gaussian": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.9.3.tgz", - "integrity": "sha512-RPrWwzlZsbWC2opSgeyWt30JU9Uwg1+GwBnoNpEMLKeqm0Dv6snASASa4zVtviGWAIq//p3Jrap7g57hKqL0Cg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "core-js": "^3.4.1" - } - }, - "@jimp/plugin-invert": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.9.3.tgz", - "integrity": "sha512-0lRsh7IPkzyYqExrZDT50h38xdlB/+KrdiDcuxWwWyIlKauLMR0kInjwf8sPeb3elPLeETmze7uwPAxrIAtsGQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "core-js": "^3.4.1" - } - }, - "@jimp/plugin-mask": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.9.3.tgz", - "integrity": "sha512-nZ0J62Hly9JtMZctlSDVgnTd8Fg2XGikzAYilSTCjzIRtbXL5Be/qSAZrMfLD3CZ8exTxdlEGRkEJI3RZKXYCw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "core-js": "^3.4.1" - } - }, - "@jimp/plugin-normalize": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.9.3.tgz", - "integrity": "sha512-0IvgTt4R15QJnoCHvvqlK56zOtCsQV7Mkx757kdNah8uyPGjadTcFBuqCaOMK943X36IIv+o7Ix7yvNUJZt4aw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "core-js": "^3.4.1" - } - }, - "@jimp/plugin-print": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.9.3.tgz", - "integrity": "sha512-pV6oX5Bhe9O/dbgrotz46Bv6u1M+/n9G0kRUunDjwzXrvON5raBFEJHQDPcTXiqPT25Gc9Ba4/Akfo/Zl6+wgQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "core-js": "^3.4.1", - "load-bmfont": "^1.4.0" - } - }, - "@jimp/plugin-resize": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.9.3.tgz", - "integrity": "sha512-YzqVE8QoDIZpVuI52v+WejwEjEEiJfNFviQfprfm5af7uSSseZgDw1sJ0koqAu+liMSY+Ewp79v2SDrKoJKqtg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "core-js": "^3.4.1" - } - }, - "@jimp/plugin-rotate": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.9.3.tgz", - "integrity": "sha512-kADY2pI3/yMyHbuyvKB4nqPoKf8DPQBU1b4zz2K7SxcwKh1krFf4Fa9mmhhDLoFwuNSy0SPb1JCMUO4BtFCFLA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "core-js": "^3.4.1" - } - }, - "@jimp/plugin-scale": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.9.3.tgz", - "integrity": "sha512-vZaiL5Qc+WrgGEfUe4Y0vG+qbT6pe2TW68/mu124E1tKVcZjHKZUeFN0Wr/hP2myN6nqTYj0/sord2OS/04JpA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "core-js": "^3.4.1" - } - }, - "@jimp/plugins": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.9.3.tgz", - "integrity": "sha512-KYCSgFGoZBNC0224X5yUnMHCZnCdUVrsu2Yo67o3XZfUgDjO81J+vdzZ0twpPQ6qLLVAP+nQ8hkRV/QzEUstMw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/plugin-blit": "^0.9.3", - "@jimp/plugin-blur": "^0.9.3", - "@jimp/plugin-color": "^0.9.3", - "@jimp/plugin-contain": "^0.9.3", - "@jimp/plugin-cover": "^0.9.3", - "@jimp/plugin-crop": "^0.9.3", - "@jimp/plugin-displace": "^0.9.3", - "@jimp/plugin-dither": "^0.9.3", - "@jimp/plugin-flip": "^0.9.3", - "@jimp/plugin-gaussian": "^0.9.3", - "@jimp/plugin-invert": "^0.9.3", - "@jimp/plugin-mask": "^0.9.3", - "@jimp/plugin-normalize": "^0.9.3", - "@jimp/plugin-print": "^0.9.3", - "@jimp/plugin-resize": "^0.9.3", - "@jimp/plugin-rotate": "^0.9.3", - "@jimp/plugin-scale": "^0.9.3", - "core-js": "^3.4.1", - "timm": "^1.6.1" - } - }, - "@jimp/png": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.9.3.tgz", - "integrity": "sha512-LJXUemDTSbTGAGEp9hNQH0uTRSB8gYeE6FsfT3M00oZincu6/WzDzl0P8E95rMjNxZqAihdTyOP3+kcrbbqX+w==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.9.3", - "core-js": "^3.4.1", - "pngjs": "^3.3.3" - } - }, - "@jimp/tiff": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.9.3.tgz", - "integrity": "sha512-w9H6dT+GDHN//Srsv27JhRn7R2byzUahOGfFw7KpIn95jg0ogcxjKTo/RAGQC56sr4U092e4Npl7E85Lt934WQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "core-js": "^3.4.1", - "utif": "^2.0.1" - } - }, - "@jimp/types": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.9.3.tgz", - "integrity": "sha512-hUJKoT2IhnbO/trxNWzN19n8g+p7aKbM1R+71n4wMZnD41PzrVtz+sBBCdB+JCjBJs/i7fJt4d9z0i3Xe8m7Zw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/bmp": "^0.9.3", - "@jimp/gif": "^0.9.3", - "@jimp/jpeg": "^0.9.3", - "@jimp/png": "^0.9.3", - "@jimp/tiff": "^0.9.3", - "core-js": "^3.4.1", - "timm": "^1.6.1" - } - }, - "@jimp/utils": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.9.3.tgz", - "integrity": "sha512-9D2Of6BcjYONtl77YfmU2y5aRMLe0/O2e2aQvfCxdNwD33jRdwNdN4i3m73dpiClNquApIjL4nYGhTixA4UstA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "core-js": "^3.4.1" - } - }, "@juggle/resize-observer": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-2.5.0.tgz", @@ -3406,12 +3061,6 @@ "color-convert": "^1.9.0" } }, - "any-base": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", - "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==", - "dev": true - }, "any-observable": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", @@ -3439,32 +3088,12 @@ } } }, - "app-manifest-webpack-plugin": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/app-manifest-webpack-plugin/-/app-manifest-webpack-plugin-1.2.0.tgz", - "integrity": "sha512-u2C27waOrBwlaQTrJfuDvsSuaMMPIB7gdBkYAjXzETzzl0GXJA4adbraXxHYKANsp8+W+3izvPlCXD8L0Q7GQg==", - "dev": true, - "requires": { - "favicons": "^5.1.1", - "loader-utils": "^1.1.0" - } - }, "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "dev": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -3561,15 +3190,6 @@ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, "asn1.js": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", @@ -3608,12 +3228,6 @@ } } }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -3647,12 +3261,6 @@ "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", "dev": true }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -3708,17 +3316,41 @@ } } }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.0.tgz", - "integrity": "sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A==", - "dev": true + "axios": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz", + "integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==", + "dev": true, + "requires": { + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "dev": true, + "requires": { + "debug": "=3.1.0" + } + }, + "is-buffer": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", + "dev": true + } + } }, "babel-code-frame": { "version": "6.26.0", @@ -4565,15 +4197,6 @@ "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", "dev": true }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, "better-assert": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", @@ -4589,11 +4212,15 @@ "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true }, - "bignumber.js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.4.0.tgz", - "integrity": "sha1-g4qZLan51zfg9LLbC+YrsJ3Qxeg=", - "dev": true + "binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", + "dev": true, + "requires": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + } }, "binary-extensions": { "version": "1.13.1", @@ -4601,28 +4228,6 @@ "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", "dev": true }, - "bl": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-3.0.0.tgz", - "integrity": "sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A==", - "dev": true, - "requires": { - "readable-stream": "^3.0.1" - }, - "dependencies": { - "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, "blob": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", @@ -4635,12 +4240,6 @@ "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", "dev": true }, - "bmp-js": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", - "integrity": "sha1-4Fpj95amwf8l9Hcex62twUjAcjM=", - "dev": true - }, "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", @@ -4825,12 +4424,6 @@ "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", "dev": true }, - "buffer-equal": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", - "integrity": "sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs=", - "dev": true - }, "buffer-fill": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", @@ -4849,6 +4442,12 @@ "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", "dev": true }, + "buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=", + "dev": true + }, "builtin-status-codes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", @@ -5045,11 +4644,14 @@ "integrity": "sha512-/xL2AbW/XWHNu1gnIrO8UitBGoFthcsDgU9VLK1/dpsoxbaD5LscHozKze05R6WLsBvLhqv78dAPozMFQBYLbQ==", "dev": true }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true + "chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", + "dev": true, + "requires": { + "traverse": ">=0.3.0 <0.4" + } }, "chalk": { "version": "2.4.2", @@ -5068,6 +4670,76 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "cheerio": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", + "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=", + "dev": true, + "requires": { + "css-select": "~1.2.0", + "dom-serializer": "~0.1.0", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash.assignin": "^4.0.9", + "lodash.bind": "^4.1.4", + "lodash.defaults": "^4.0.1", + "lodash.filter": "^4.4.0", + "lodash.flatten": "^4.2.0", + "lodash.foreach": "^4.3.0", + "lodash.map": "^4.4.0", + "lodash.merge": "^4.4.0", + "lodash.pick": "^4.2.1", + "lodash.reduce": "^4.4.0", + "lodash.reject": "^4.4.0", + "lodash.some": "^4.4.0" + }, + "dependencies": { + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "dev": true, + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "dev": true + }, + "dom-serializer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "dev": true, + "requires": { + "domelementtype": "^1.3.0", + "entities": "^1.1.1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + } + } + }, "chokidar": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", @@ -5275,18 +4947,6 @@ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "dev": true }, - "clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true - }, - "clone-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", - "dev": true - }, "clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -5298,23 +4958,6 @@ "shallow-clone": "^3.0.0" } }, - "clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", - "dev": true - }, - "cloneable-readable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", - "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "process-nextick-args": "^2.0.0", - "readable-stream": "^2.3.5" - } - }, "coa": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", @@ -5383,15 +5026,6 @@ "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, "commander": { "version": "2.20.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", @@ -5477,12 +5111,6 @@ "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", "dev": true }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true - }, "constants-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", @@ -7132,15 +6760,6 @@ "number-is-nan": "^1.0.0" } }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, "date-fns": { "version": "1.30.1", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", @@ -7190,27 +6809,12 @@ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", "dev": true }, - "decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "dev": true, - "requires": { - "mimic-response": "^2.0.0" - } - }, "dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", "dev": true }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -7289,18 +6893,6 @@ } } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true - }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -7332,12 +6924,6 @@ "repeating": "^2.0.0" } }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "dev": true - }, "detect-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.0.0.tgz", @@ -7435,12 +7021,6 @@ } } }, - "dom-walk": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", - "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=", - "dev": true - }, "domain-browser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", @@ -7503,16 +7083,6 @@ "stream-shift": "^1.0.0" } }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -7747,12 +7317,6 @@ "es6-symbol": "^3.1.1" } }, - "es6-promise": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", - "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=", - "dev": true - }, "es6-symbol": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", @@ -7976,12 +7540,6 @@ "strip-eof": "^1.0.0" } }, - "exif-parser": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz", - "integrity": "sha1-WKnS1ywCwfbwKg70qRZicrd2CSI=", - "dev": true - }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -8017,12 +7575,6 @@ } } }, - "expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "dev": true - }, "expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", @@ -8068,6 +7620,17 @@ "chardet": "^0.7.0", "iconv-lite": "^0.4.24", "tmp": "^0.0.33" + }, + "dependencies": { + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + } } }, "extglob": { @@ -8135,12 +7698,6 @@ } } }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", @@ -8159,39 +7716,6 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, - "favicons": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/favicons/-/favicons-5.5.0.tgz", - "integrity": "sha512-xZ4B+fZDuq2y999iorrYq4KuBT3OIZHU+CVfjOWQbjOC1OiU0xbf6pp4Ju/yAfJn7W74RVrC3Cv0oqR5CLvviw==", - "dev": true, - "requires": { - "clone": "^2.1.2", - "colors": "^1.4.0", - "core-js": "^3.4.5", - "image-size": "^0.8.3", - "jimp": "^0.9.3", - "jsontoxml": "^1.0.1", - "lodash.defaultsdeep": "^4.6.1", - "require-directory": "^2.1.1", - "sharp": "^0.23.3", - "through2": "^3.0.1", - "tinycolor2": "^1.4.1", - "to-ico": "^1.1.5", - "vinyl": "^2.2.0", - "xml2js": "^0.4.22" - }, - "dependencies": { - "through2": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", - "dev": true, - "requires": { - "readable-stream": "2 || 3" - } - } - } - }, "figgy-pudding": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", @@ -8256,12 +7780,6 @@ } } }, - "file-type": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-9.0.0.tgz", - "integrity": "sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw==", - "dev": true - }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -8403,23 +7921,6 @@ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", "dev": true }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -8448,12 +7949,6 @@ "null-check": "^1.0.0" } }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, "fs-extra": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", @@ -9046,6 +8541,18 @@ } } }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -9069,44 +8576,6 @@ "simple-git": "^1.85.0" } }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, "get-own-enumerable-property-symbols": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz", @@ -9147,15 +8616,6 @@ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "dev": true }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, "git-raw-commits": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-1.3.6.tgz", @@ -9546,16 +9006,10 @@ "ini": "^1.3.2" } }, - "github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", - "dev": true - }, "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -9587,24 +9041,6 @@ } } }, - "global": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", - "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", - "dev": true, - "requires": { - "min-document": "^2.19.0", - "process": "~0.5.1" - }, - "dependencies": { - "process": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", - "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=", - "dev": true - } - } - }, "global-dirs": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", @@ -9702,22 +9138,6 @@ } } }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "dev": true, - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - } - }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -9771,12 +9191,6 @@ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", "dev": true }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true - }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -10046,17 +9460,6 @@ "requires-port": "^1.0.0" } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -10176,15 +9579,6 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, - "image-size": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.8.3.tgz", - "integrity": "sha512-SMtq1AJ+aqHB45c3FsB4ERK0UCiA2d3H1uq8s+8T0Pf8A3W4teyBQyaFaktH6xvZqh+npwlKU7i4fJo0r7TYTg==", - "dev": true, - "requires": { - "queue": "6.0.1" - } - }, "import-cwd": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", @@ -10336,12 +9730,6 @@ "loose-envify": "^1.0.0" } }, - "ip-regex": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-1.0.3.tgz", - "integrity": "sha1-3FiQdvZZ9BnCIgOaMzFvHHOH7/0=", - "dev": true - }, "is-absolute-url": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", @@ -10496,12 +9884,6 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, - "is-function": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", - "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=", - "dev": true - }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", @@ -10645,12 +10027,6 @@ "text-extensions": "^1.0.0" } }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -10696,12 +10072,6 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, "jasmine-core": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.5.0.tgz", @@ -10729,34 +10099,6 @@ } } }, - "jimp": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.9.3.tgz", - "integrity": "sha512-dIxvT1OMRkd3+B18XUhJ5WZ2Dw7Hp8mvjaTqfi945zZ7fga6LT22h3NLYDorHHAiy9z30KjfNnOgpBoxrdjDZg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/custom": "^0.9.3", - "@jimp/plugins": "^0.9.3", - "@jimp/types": "^0.9.3", - "core-js": "^3.4.1", - "regenerator-runtime": "^0.13.3" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", - "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", - "dev": true - } - } - }, - "jpeg-js": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.3.6.tgz", - "integrity": "sha512-MUj2XlMB8kpe+8DJUGH/3UJm4XpI8XEgZQ+CiHDeyrGoKPdW/8FJv6ku+3UiYm5Fz3CWaL+iXmD8Q4Ap6aC1Jw==", - "dev": true - }, "js-levenshtein": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", @@ -10779,12 +10121,6 @@ "esprima": "^4.0.0" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -10797,12 +10133,6 @@ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -10859,24 +10189,6 @@ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true }, - "jsontoxml": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/jsontoxml/-/jsontoxml-1.0.1.tgz", - "integrity": "sha512-dtKGq0K8EWQBRqcAaePSgKR4Hyjfsz/LkurHSV3Cxk4H+h2fWDeaN2jzABz+ZmOJylgXS7FGeWmbZ6jgYUMdJQ==", - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, "karma": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/karma/-/karma-4.4.1.tgz", @@ -11007,6 +10319,15 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -11286,30 +10607,6 @@ "figures": "^2.0.0" } }, - "load-bmfont": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.0.tgz", - "integrity": "sha512-kT63aTAlNhZARowaNYcY29Fn/QYkc52M3l6V1ifRcPewg2lvUZDAj7R6dXjOL9D0sict76op3T5+odumDSF81g==", - "dev": true, - "requires": { - "buffer-equal": "0.0.1", - "mime": "^1.3.4", - "parse-bmfont-ascii": "^1.0.3", - "parse-bmfont-binary": "^1.0.5", - "parse-bmfont-xml": "^1.1.4", - "phin": "^2.9.1", - "xhr": "^2.0.1", - "xtend": "^4.0.0" - }, - "dependencies": { - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - } - } - }, "load-json-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", @@ -11396,16 +10693,46 @@ "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", "dev": true }, + "lodash.assignin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", + "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=", + "dev": true + }, + "lodash.bind": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", + "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=", + "dev": true + }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", "dev": true }, - "lodash.defaultsdeep": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz", - "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==", + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", + "dev": true + }, + "lodash.filter": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", + "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=", + "dev": true + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "dev": true + }, + "lodash.foreach": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", + "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=", "dev": true }, "lodash.ismatch": { @@ -11414,12 +10741,48 @@ "integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=", "dev": true }, + "lodash.map": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", + "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=", + "dev": true + }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", "dev": true }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=", + "dev": true + }, + "lodash.reduce": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=", + "dev": true + }, + "lodash.reject": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", + "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=", + "dev": true + }, + "lodash.some": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", + "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=", + "dev": true + }, "lodash.template": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz", @@ -11599,6 +10962,42 @@ "object-visit": "^1.0.0" } }, + "match-stream": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/match-stream/-/match-stream-0.0.2.tgz", + "integrity": "sha1-mesFAJOzTf+t5CG5rAtBCpz6F88=", + "dev": true, + "requires": { + "buffers": "~0.1.1", + "readable-stream": "~1.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, "matcher": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/matcher/-/matcher-1.1.1.tgz", @@ -11698,6 +11097,18 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, + "metaparser": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/metaparser/-/metaparser-1.0.7.tgz", + "integrity": "sha1-wGmaZoageovOGsBrYulGLC5mqso=", + "dev": true, + "requires": { + "async": "*", + "cheerio": "*", + "mkdirp": "*", + "underscore": "*" + } + }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -11756,21 +11167,6 @@ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, - "mimic-response": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.0.0.tgz", - "integrity": "sha512-8ilDoEapqA4uQ3TwS0jakGONKXVJqpy+RpM+3b7pLdOjghCrEiGp9SRkFbUHAmZW9vdnrENWHjaweIoTIJExSQ==", - "dev": true - }, - "min-document": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", - "dev": true, - "requires": { - "dom-walk": "^0.1.0" - } - }, "mini-css-extract-plugin": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.0.tgz", @@ -11864,24 +11260,6 @@ "minipass": "^3.0.0" } }, - "minizlib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.0.tgz", - "integrity": "sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA==", - "dev": true, - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "dependencies": { - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, "mississippi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", @@ -11971,7 +11349,8 @@ "version": "2.14.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "dev": true + "dev": true, + "optional": true }, "nanomatch": { "version": "1.2.13", @@ -11992,12 +11371,6 @@ "to-regex": "^3.0.1" } }, - "napi-build-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.1.tgz", - "integrity": "sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA==", - "dev": true - }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -12037,23 +11410,6 @@ "lower-case": "^1.1.1" } }, - "node-abi": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.13.0.tgz", - "integrity": "sha512-9HrZGFVTR5SOu3PZAnAY2hLO36aW1wmA+FDsVkr85BTST32TLCA1H/AEcatVRAsWLyXS3bqUDYCAjq5/QGuSTA==", - "dev": true, - "requires": { - "semver": "^5.4.1" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, "node-libs-browser": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", @@ -12110,11 +11466,45 @@ } } }, - "noop-logger": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", - "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=", - "dev": true + "node-unzip-2": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/node-unzip-2/-/node-unzip-2-0.2.8.tgz", + "integrity": "sha512-fmJi73zTRW7RSo/1wyrKc2srKMwb3L6Ppke/7elzQ0QRt6sUjfiIcVsWdrqO5uEHAdvRKXjoySuo4HYe5BB0rw==", + "dev": true, + "requires": { + "binary": "~0.3.0", + "fstream": "~1.0.12", + "match-stream": "~0.0.2", + "pullstream": "~0.4.0", + "readable-stream": "~1.0.0", + "setimmediate": "~1.0.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } }, "normalize-package-data": { "version": "2.5.0", @@ -15313,18 +14703,6 @@ "which": "^1.2.10" } }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, "nth-check": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", @@ -15362,12 +14740,6 @@ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -15569,12 +14941,6 @@ } } }, - "omggif": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz", - "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==", - "dev": true - }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -15668,6 +15034,12 @@ "integrity": "sha512-Ue462G+UIFoyQmOzapGIKWS3d/9NHeD/018WGEDZIhN2/VaQpVXbofMcZX0socv1fw4/tmEn7Vd3McOdPZfKzQ==", "dev": true }, + "over": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/over/-/over-0.0.5.tgz", + "integrity": "sha1-8phS5w/X4l82DgE6jsRMgq7bVwg=", + "dev": true + }, "p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", @@ -15765,40 +15137,12 @@ "safe-buffer": "^5.1.1" } }, - "parse-bmfont-ascii": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", - "integrity": "sha1-Eaw8P/WPfCAgqyJ2kHkQjU36AoU=", - "dev": true - }, - "parse-bmfont-binary": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz", - "integrity": "sha1-0Di0dtPp3Z2x4RoLDlOiJ5K2kAY=", - "dev": true - }, - "parse-bmfont-xml": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.4.tgz", - "integrity": "sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ==", - "dev": true, - "requires": { - "xml-parse-from-string": "^1.0.0", - "xml2js": "^0.4.5" - } - }, "parse-github-repo-url": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz", "integrity": "sha1-nn2LslKmy2ukJZUGC3v23z28H1A=", "dev": true }, - "parse-headers": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.3.tgz", - "integrity": "sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA==", - "dev": true - }, "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -15815,15 +15159,6 @@ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, - "parse-png": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/parse-png/-/parse-png-1.1.2.tgz", - "integrity": "sha1-9cKtfHmTSQmGAgooTBmu5FlxH/I=", - "dev": true, - "requires": { - "pngjs": "^3.2.0" - } - }, "parseqs": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", @@ -15928,18 +15263,6 @@ "sha.js": "^2.4.8" } }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "phin": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz", - "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==", - "dev": true - }, "picomatch": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.1.1.tgz", @@ -15973,15 +15296,6 @@ "pinkie": "^2.0.0" } }, - "pixelmatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz", - "integrity": "sha1-j0fc7FARtHe2fbA8JDvB8wheiFQ=", - "dev": true, - "requires": { - "pngjs": "^3.0.0" - } - }, "pkg-dir": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", @@ -16000,12 +15314,6 @@ "semver-compare": "^1.0.0" } }, - "pngjs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", - "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", - "dev": true - }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -16974,37 +16282,6 @@ "uniq": "^1.0.1" } }, - "prebuild-install": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.3.tgz", - "integrity": "sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g==", - "dev": true, - "requires": { - "detect-libc": "^1.0.3", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", - "napi-build-utils": "^1.0.1", - "node-abi": "^2.7.0", - "noop-logger": "^0.1.1", - "npmlog": "^4.0.1", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^3.0.3", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0", - "which-pm-runs": "^1.0.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -17075,12 +16352,6 @@ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, - "psl": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.6.0.tgz", - "integrity": "sha512-SYKKmVel98NCOYXpkwUqZqh0ahZeeKfmisiLIcEZdsb+WbLv02g/dI5BUmZnIyOe7RzZtLax81nnb2HbvC2tzA==", - "dev": true - }, "public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", @@ -17095,6 +16366,44 @@ "safe-buffer": "^5.1.2" } }, + "pullstream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/pullstream/-/pullstream-0.4.1.tgz", + "integrity": "sha1-1vs79a7Wl+gxFQ6xACwlo/iuExQ=", + "dev": true, + "requires": { + "over": ">= 0.0.5 < 1", + "readable-stream": "~1.0.31", + "setimmediate": ">= 1.0.2 < 2", + "slice-stream": ">= 1.0.0 < 2" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -17174,15 +16483,6 @@ "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", "dev": true }, - "queue": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.1.tgz", - "integrity": "sha512-AJBQabRCCNr9ANq8v77RJEv73DPbn55cdTb+Giq4X0AVnNVZvMHlYp7XlQiN+1npCZj1DuSmaA2hYVUUDgxFDg==", - "dev": true, - "requires": { - "inherits": "~2.0.3" - } - }, "quick-lru": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", @@ -17226,32 +16526,6 @@ "unpipe": "1.0.0" } }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "read-chunk": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-1.0.1.tgz", - "integrity": "sha1-X2jKswfmY/GZk1J9m1icrORmEZQ=", - "dev": true - }, "read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -17346,6 +16620,15 @@ "readable-stream": "^2.0.2" } }, + "real-favicon-webpack-plugin": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/real-favicon-webpack-plugin/-/real-favicon-webpack-plugin-0.2.3.tgz", + "integrity": "sha512-w9CS4DdISimLk+hD1qAqVstWfkAXLpnU7a7UOmArHR1pDnmB4SYBO/fwfuu+ObWEcWtIkfsAhJaD6eKSN4Bq6A==", + "dev": true, + "requires": { + "rfg-api": "^0.5.0" + } + }, "redent": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", @@ -17538,48 +16821,6 @@ "is-finite": "^1.0.0" } }, - "replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", - "dev": true - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - } - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -17592,94 +16833,6 @@ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", "dev": true }, - "resize-img": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/resize-img/-/resize-img-1.1.2.tgz", - "integrity": "sha1-+tZQ+vPvLFPqYxErwnLZXp2SVQ4=", - "dev": true, - "requires": { - "bmp-js": "0.0.1", - "file-type": "^3.8.0", - "get-stream": "^2.0.0", - "jimp": "^0.2.21", - "jpeg-js": "^0.1.1", - "parse-png": "^1.1.1" - }, - "dependencies": { - "bmp-js": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.0.1.tgz", - "integrity": "sha1-WtAUcJnROp84qnuZrx1ueGZu038=", - "dev": true - }, - "file-type": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", - "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", - "dev": true - }, - "get-stream": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", - "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", - "dev": true, - "requires": { - "object-assign": "^4.0.1", - "pinkie-promise": "^2.0.0" - } - }, - "jimp": { - "version": "0.2.28", - "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.2.28.tgz", - "integrity": "sha1-3VKak3GQ9ClXp5N9Gsw6d2KZbqI=", - "dev": true, - "requires": { - "bignumber.js": "^2.1.0", - "bmp-js": "0.0.3", - "es6-promise": "^3.0.2", - "exif-parser": "^0.1.9", - "file-type": "^3.1.0", - "jpeg-js": "^0.2.0", - "load-bmfont": "^1.2.3", - "mime": "^1.3.4", - "mkdirp": "0.5.1", - "pixelmatch": "^4.0.0", - "pngjs": "^3.0.0", - "read-chunk": "^1.0.1", - "request": "^2.65.0", - "stream-to-buffer": "^0.1.0", - "tinycolor2": "^1.1.2", - "url-regex": "^3.0.0" - }, - "dependencies": { - "bmp-js": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.0.3.tgz", - "integrity": "sha1-ZBE+nHzxICs3btYHvzBibr5XsYo=", - "dev": true - }, - "jpeg-js": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.2.0.tgz", - "integrity": "sha1-U+RI7J0mPmgyZkZ+lELSxaLvVII=", - "dev": true - } - } - }, - "jpeg-js": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.1.2.tgz", - "integrity": "sha1-E1uZLAV1yYXPoPSUoyJ+0jhYPs4=", - "dev": true - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - } - } - }, "resolve": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", @@ -17851,6 +17004,19 @@ "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==", "dev": true }, + "rfg-api": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/rfg-api/-/rfg-api-0.5.0.tgz", + "integrity": "sha512-wd6BcVoBsEHlbfsaB1WD4Z/Xis4uEP+Qctd3u2jxXR5yTkCYENaB/m3Jsk0G2qToKgAeE+6tbsN6T0n8DHcSaw==", + "dev": true, + "requires": { + "axios": "^0.18.0", + "fstream": "^1.0.2", + "metaparser": "^1.0.7", + "mkdirp": "^0.5.0", + "node-unzip-2": "^0.2.7" + } + }, "rgb-regex": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", @@ -18055,23 +17221,6 @@ "kind-of": "^6.0.2" } }, - "sharp": { - "version": "0.23.4", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.23.4.tgz", - "integrity": "sha512-fJMagt6cT0UDy9XCsgyLi0eiwWWhQRxbwGmqQT6sY8Av4s0SVsT/deg8fobBQCTDU5iXRgz0rAeXoE2LBZ8g+Q==", - "dev": true, - "requires": { - "color": "^3.1.2", - "detect-libc": "^1.0.3", - "nan": "^2.14.0", - "npmlog": "^4.1.2", - "prebuild-install": "^5.3.3", - "semver": "^6.3.0", - "simple-get": "^3.1.0", - "tar": "^5.0.5", - "tunnel-agent": "^0.6.0" - } - }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -18105,23 +17254,6 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, - "simple-concat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", - "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=", - "dev": true - }, - "simple-get": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz", - "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==", - "dev": true, - "requires": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "simple-git": { "version": "1.115.0", "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.115.0.tgz", @@ -18182,6 +17314,41 @@ "is-fullwidth-code-point": "^2.0.0" } }, + "slice-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-stream/-/slice-stream-1.0.0.tgz", + "integrity": "sha1-WzO9ZvATsaf4ZGCwPUY97DmtPqA=", + "dev": true, + "requires": { + "readable-stream": "~1.0.31" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -18507,23 +17674,6 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, "ssri": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", @@ -18848,21 +17998,6 @@ "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", "dev": true }, - "stream-to": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stream-to/-/stream-to-0.2.2.tgz", - "integrity": "sha1-hDBgmNhf25kLn6MAsbPM9V6O8B0=", - "dev": true - }, - "stream-to-buffer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/stream-to-buffer/-/stream-to-buffer-0.1.0.tgz", - "integrity": "sha1-JnmdkDqyAlyb1VCsRxcbAPjdgKk=", - "dev": true, - "requires": { - "stream-to": "~0.2.0" - } - }, "streamroller": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.6.tgz", @@ -19171,72 +18306,6 @@ "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", "dev": true }, - "tar": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/tar/-/tar-5.0.5.tgz", - "integrity": "sha512-MNIgJddrV2TkuwChwcSNds/5E9VijOiw7kAc1y5hTNJoLDSuIyid2QtLYiCYNnICebpuvjhPQZsXwUL0O3l7OQ==", - "dev": true, - "requires": { - "chownr": "^1.1.3", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.0", - "mkdirp": "^0.5.0", - "yallist": "^4.0.0" - }, - "dependencies": { - "chownr": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", - "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "tar-fs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.0.tgz", - "integrity": "sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==", - "dev": true, - "requires": { - "chownr": "^1.1.1", - "mkdirp": "^0.5.1", - "pump": "^3.0.0", - "tar-stream": "^2.0.0" - } - }, - "tar-stream": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.0.tgz", - "integrity": "sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw==", - "dev": true, - "requires": { - "bl": "^3.0.0", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "dependencies": { - "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, "terser": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/terser/-/terser-4.4.2.tgz", @@ -19440,31 +18509,19 @@ "setimmediate": "^1.0.4" } }, - "timm": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/timm/-/timm-1.6.2.tgz", - "integrity": "sha512-IH3DYDL1wMUwmIlVmMrmesw5lZD6N+ZOAFWEyLrtpoL9Bcrs9u7M/vyOnHzDD2SMs4irLkVjqxZbHrXStS/Nmw==", - "dev": true - }, "timsort": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", "dev": true }, - "tinycolor2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz", - "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=", - "dev": true - }, "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", + "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", "dev": true, "requires": { - "os-tmpdir": "~1.0.2" + "rimraf": "^2.6.3" } }, "to-array": { @@ -19485,27 +18542,6 @@ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, - "to-ico": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/to-ico/-/to-ico-1.1.5.tgz", - "integrity": "sha512-5kIh7m7bkIlqIESEZkL8gAMMzucXKfPe3hX2FoDY5HEAfD9OJU+Qh9b6Enp74w0qRcxVT5ejss66PHKqc3AVkg==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "buffer-alloc": "^1.1.0", - "image-size": "^0.5.0", - "parse-png": "^1.0.0", - "resize-img": "^1.1.0" - }, - "dependencies": { - "image-size": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", - "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", - "dev": true - } - } - }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", @@ -19560,23 +18596,11 @@ "integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=", "dev": true }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "dev": true, - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - } - } + "traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=", + "dev": true }, "trim-newlines": { "version": "1.0.0", @@ -19608,21 +18632,6 @@ "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -19692,6 +18701,12 @@ "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", "dev": true }, + "underscore": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.2.tgz", + "integrity": "sha512-D39qtimx0c1fI3ya1Lnhk3E9nONswSKhnffBI0gME9C99fYOkNi04xs8K6pePLhvl1frbDemkaBQ5ikWllR2HQ==", + "dev": true + }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", @@ -19871,15 +18886,6 @@ "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=", "dev": true }, - "url-regex": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/url-regex/-/url-regex-3.2.0.tgz", - "integrity": "sha1-260eDJ4p4QXdCx8J9oYvf9tIJyQ=", - "dev": true, - "requires": { - "ip-regex": "^1.0.1" - } - }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -19894,15 +18900,17 @@ "requires": { "lru-cache": "4.1.x", "tmp": "0.0.x" - } - }, - "utif": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/utif/-/utif-2.0.1.tgz", - "integrity": "sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg==", - "dev": true, - "requires": { - "pako": "^1.0.5" + }, + "dependencies": { + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + } } }, "util": { @@ -19970,31 +18978,6 @@ "integrity": "sha512-fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw==", "dev": true }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "vinyl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", - "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", - "dev": true, - "requires": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" - } - }, "vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", @@ -20406,21 +19389,6 @@ "isexe": "^2.0.0" } }, - "which-pm-runs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", - "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", - "dev": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -20489,40 +19457,6 @@ "ultron": "~1.1.0" } }, - "xhr": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.5.0.tgz", - "integrity": "sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ==", - "dev": true, - "requires": { - "global": "~4.3.0", - "is-function": "^1.0.1", - "parse-headers": "^2.0.0", - "xtend": "^4.0.0" - } - }, - "xml-parse-from-string": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", - "integrity": "sha1-qQKekp09vN7RafPG4oI42VpdWig=", - "dev": true - }, - "xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "dev": true, - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - }, - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "dev": true - }, "xmlhttprequest-ssl": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", diff --git a/package.json b/package.json index 6bea4e881..3b247c56d 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,6 @@ "@commitlint/cli": "^8.2.0", "@commitlint/config-conventional": "^8.2.0", "@fortawesome/fontawesome-pro": "^5.12.0", - "app-manifest-webpack-plugin": "^1.2.0", "autoprefixer": "^9.7.3", "babel-core": "^6.26.3", "babel-eslint": "^10.0.3", @@ -73,6 +72,7 @@ "css-loader": "^2.1.1", "eslint": "^5.16.0", "file-loader": "^5.0.2", + "glob": "^7.1.6", "html-webpack-plugin": "^3.2.0", "husky": "^2.7.0", "jasmine-core": "^3.5.0", @@ -92,6 +92,7 @@ "optimize-css-assets-webpack-plugin": "^5.0.3", "postcss-loader": "^3.0.0", "postcss-preset-env": "^6.7.0", + "real-favicon-webpack-plugin": "^0.2.3", "remove-files-webpack-plugin": "^1.1.3", "resolve-url-loader": "^3.1.1", "sass": "^1.23.7", @@ -100,6 +101,7 @@ "standard-version": "^6.0.1", "style-loader": "^0.23.1", "terser-webpack-plugin": "^2.2.3", + "tmp": "^0.1.0", "typeface-roboto": "0.0.75", "typeface-source-sans-pro": "0.0.75", "webpack": "^4.41.2", diff --git a/routes b/routes index bb73c541a..24e863319 100644 --- a/routes +++ b/routes @@ -39,8 +39,6 @@ /static StaticR EmbeddedStatic appStatic !free /auth AuthR Auth getAuth !free -/favicon.ico FaviconR GET !free -/robots.txt RobotsR GET !free /metrics MetricsR GET / HomeR GET !free @@ -204,6 +202,7 @@ /msgs MessageListR GET POST /msg/#{CryptoUUIDSystemMessage} MessageR GET POST !timeANDreadANDauthentication - !/#UUID CryptoUUIDDispatchR GET !free -- just redirect -- !/*{CI FilePath} CryptoFileNameDispatchR GET !free -- Disabled until preliminary check for valid cID exists + +!/*WellKnownFileName WellKnownR GET !free \ No newline at end of file diff --git a/shell.nix b/shell.nix index 7079bd42a..76e71b9ec 100644 --- a/shell.nix +++ b/shell.nix @@ -19,7 +19,7 @@ let ''; override = oldAttrs: { - nativeBuildInputs = oldAttrs.nativeBuildInputs ++ (with pkgs; [ nodejs-12_x postgresql openldap google-chrome ]) ++ (with haskellPackages; [ stack yesod-bin hlint cabal-install weeder ]); + nativeBuildInputs = oldAttrs.nativeBuildInputs ++ (with pkgs; [ nodejs-12_x postgresql openldap google-chrome exiftool ]) ++ (with haskellPackages; [ stack yesod-bin hlint cabal-install weeder ]); shellHook = '' export PROMPT_INFO="${oldAttrs.name}" diff --git a/src/Application.hs b/src/Application.hs index 7ae3d5397..3f4fa2e66 100644 --- a/src/Application.hs +++ b/src/Application.hs @@ -101,7 +101,6 @@ import Data.List (cycle) -- Import all relevant handler modules here. -- (HPack takes care to add new modules to our cabal file nowadays.) -import Handler.Common import Handler.Home import Handler.Info import Handler.Help diff --git a/src/Foundation.hs b/src/Foundation.hs index 1e8f535bc..050b6d1aa 100644 --- a/src/Foundation.hs +++ b/src/Foundation.hs @@ -1575,8 +1575,8 @@ siteLayout' headingOverride widget = do pc <- widgetToPageContent $ do webpackLinks_main StaticR - faviconLinks toWidget $(juliusFile "templates/i18n.julius") + wellKnownHtmlLinks $(widgetFile "default-layout") withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet") @@ -1627,8 +1627,7 @@ i18nCrumb msg mbR = do instance YesodBreadcrumbs UniWorX where breadcrumb (AuthR _) = i18nCrumb MsgMenuLogin $ Just HomeR breadcrumb (StaticR _) = i18nCrumb MsgBreadcrumbStatic Nothing - breadcrumb FaviconR = i18nCrumb MsgBreadcrumbFavicon Nothing - breadcrumb RobotsR = i18nCrumb MsgBreadcrumbRobots Nothing + breadcrumb (WellKnownR _) = i18nCrumb MsgBreadcrumbWellKnown Nothing breadcrumb MetricsR = i18nCrumb MsgBreadcrumbMetrics Nothing breadcrumb HomeR = i18nCrumb MsgMenuHome Nothing diff --git a/src/Handler/Common.hs b/src/Handler/Common.hs deleted file mode 100644 index da1330be9..000000000 --- a/src/Handler/Common.hs +++ /dev/null @@ -1,29 +0,0 @@ --- | Common handler functions. -module Handler.Common - ( getFaviconR - , getRobotsR - ) where - -import Data.FileEmbed (embedFile) -import Import hiding (embedFile) - --- These handlers embed files in the executable at compile time to avoid a --- runtime dependency, and for efficiency. - -getFaviconR :: Handler TypedContent -getFaviconR = do - let content = $(embedFile "static/favicon.ico") - - setEtagHashable content - - return $ TypedContent "image/x-icon" - $ toContent content - -getRobotsR :: Handler TypedContent -getRobotsR = do - let content = $(embedFile "static/robots.txt") - - setEtagHashable content - - return $ TypedContent typePlain - $ toContent content diff --git a/src/Import/NoFoundation.hs b/src/Import/NoFoundation.hs index f71b0c662..b722fb338 100644 --- a/src/Import/NoFoundation.hs +++ b/src/Import/NoFoundation.hs @@ -12,8 +12,9 @@ import Utils.Tokens as Import import Utils.Frontend.Modal as Import import Utils.Lens as Import -import Settings as Import -import Settings.StaticFiles as Import +import Settings as Import +import Settings.StaticFiles as Import +import Settings.WellKnownFiles as Import import CryptoID as Import import Audit as Import diff --git a/src/Settings.hs b/src/Settings.hs index 284173eee..76dea50e7 100644 --- a/src/Settings.hs +++ b/src/Settings.hs @@ -78,7 +78,8 @@ data AppSettings = AppSettings { appStaticDir :: FilePath -- ^ Directory from which to serve static files. , appWebpackEntrypoints :: FilePath - , appFaviconStats :: FilePath + , appWellKnownDir :: FilePath + , appWellKnownLinkFile :: FilePath , appDatabaseConf :: PostgresConf -- ^ Configuration settings for accessing the database. , appAutoDbMigrate :: Bool @@ -370,8 +371,9 @@ instance FromJSON AppSettings where False #endif appStaticDir <- o .: "static-dir" + appWellKnownDir <- o .: "well-known-dir" + appWellKnownLinkFile <- o .: "well-known-link-file" appWebpackEntrypoints <- o .: "webpack-manifest" - appFaviconStats <- o .: "favicon-stats" appDatabaseConf <- o .: "database" appAutoDbMigrate <- o .: "auto-db-migrate" let nonEmptyHost LdapConf{..} = case ldapHost of diff --git a/src/Settings/StaticFiles.hs b/src/Settings/StaticFiles.hs index 4c9f711ab..a4138d193 100644 --- a/src/Settings/StaticFiles.hs +++ b/src/Settings/StaticFiles.hs @@ -1,14 +1,19 @@ +{-# OPTIONS_GHC -fno-warn-unused-top-binds #-} +-- Listing only files directly used by consumers of this module +-- prevents rebuilds if files change, that are not directly used (like +-- webpack bundles) module Settings.StaticFiles - ( module Settings.StaticFiles + ( img_lmu_sigillum_svg + , webpackLinks_main + , embeddedStatic , module Yesod.EmbeddedStatic ) where import ClassyPrelude.Yesod -import Settings (appStaticDir, appWebpackEntrypoints, appFaviconStats, compileTimeAppSettings) +import Settings (appStaticDir, appWebpackEntrypoints, compileTimeAppSettings) import Settings.StaticFiles.Generator import Settings.StaticFiles.Webpack -import Settings.StaticFiles.Favicon import Yesod.EmbeddedStatic -- This generates easy references to files in the static directory at compile time, @@ -28,6 +33,3 @@ import Yesod.EmbeddedStatic mkEmbeddedStatic DEV_BOOL "embeddedStatic" . pure . staticGenerator $ appStaticDir compileTimeAppSettings mkWebpackEntrypoints (appWebpackEntrypoints compileTimeAppSettings) (pure staticGenerator) $ appStaticDir compileTimeAppSettings - -faviconLinks :: MonadWidget m => m () -faviconLinks = $(mkFaviconLinks $ appFaviconStats compileTimeAppSettings) diff --git a/src/Settings/StaticFiles/Favicon.hs b/src/Settings/StaticFiles/Favicon.hs deleted file mode 100644 index a530cf69a..000000000 --- a/src/Settings/StaticFiles/Favicon.hs +++ /dev/null @@ -1,38 +0,0 @@ -module Settings.StaticFiles.Favicon - ( mkFaviconLinks - ) where - -import ClassyPrelude.Yesod - -import Language.Haskell.TH -import Language.Haskell.TH.Syntax hiding (Lift(..)) -import qualified Language.Haskell.TH.Syntax as TH (Lift(..)) - -import qualified Data.Aeson as JSON - -import qualified Data.Map as Map - -import Text.Blaze.Html (preEscapedToHtml) - - -mkFaviconLinks :: FilePath -- ^ Path to JSON-manifest - -> ExpQ -mkFaviconLinks manifest = do - addDependentFile manifest - htmlFragments <- decodeManifest manifest - - doE $ map (\frag -> noBindS [e|toWidgetHead $ preEscapedToHtml $(TH.lift frag)|]) htmlFragments - where - decodeManifest :: FilePath -> Q [Text] - decodeManifest manifest' = do - res <- liftIO $ JSON.eitherDecodeFileStrict' manifest' - case res of - Left err -> fail err - Right res' - | Just frags' <- res' Map.!? ("html" :: Text) - , JSON.Success frags <- JSON.fromJSON frags' - -> return frags - | otherwise - -> fail "Could not parse favicon stats" - - diff --git a/src/Settings/WellKnownFiles.hs b/src/Settings/WellKnownFiles.hs new file mode 100644 index 000000000..2e6d416a2 --- /dev/null +++ b/src/Settings/WellKnownFiles.hs @@ -0,0 +1,11 @@ +module Settings.WellKnownFiles + ( WellKnownFileName + , getWellKnownR + , wellKnownHtmlLinks + ) where + +import Settings.WellKnownFiles.TH + +import Settings (appWellKnownDir, appWellKnownLinkFile, compileTimeAppSettings) + +mkWellKnown "de-de-formal" (appWellKnownDir compileTimeAppSettings) (appWellKnownLinkFile compileTimeAppSettings) diff --git a/src/Settings/WellKnownFiles/TH.hs b/src/Settings/WellKnownFiles/TH.hs new file mode 100644 index 000000000..890184588 --- /dev/null +++ b/src/Settings/WellKnownFiles/TH.hs @@ -0,0 +1,192 @@ +module Settings.WellKnownFiles.TH + ( mkWellKnown + ) where + +import ClassyPrelude.Yesod +import Utils + +import Language.Haskell.TH +import Language.Haskell.TH.Syntax hiding (Lift(..)) +import qualified Language.Haskell.TH.Syntax as TH (Lift(..)) + +import System.Directory.Tree + +import qualified Data.ByteString as BS + +import Utils.Lens.TH +import Control.Lens +import Data.Set.Lens + +import qualified Data.Text as Text +import qualified Data.Text.Encoding as Text +import Data.Char as Char (isAlphaNum, toUpper) + +import qualified Data.Map as Map +import qualified Data.Set as Set + +import Data.List.NonEmpty (NonEmpty(..)) + +import Data.HashMap.Strict (HashMap) +import qualified Data.HashMap.Strict as HashMap + +import qualified Data.HashSet as HashSet + +import System.FilePath ((), splitDirectories, makeRelative) + +import Settings.Mime + +import Text.Blaze.Html (preEscapedToHtml) + +nWellKnownFileName :: Name +nWellKnownFileName = mkName "WellKnownFileName" + +nwellKnownFileNames :: Name +nwellKnownFileNames = mkName "wellKnownFileNames" + +ngetWellKnownR :: Name +ngetWellKnownR = mkName "getWellKnownR" + +nwellKnownHtmlLinks :: Name +nwellKnownHtmlLinks = mkName "wellKnownHtmlLinks" + + + +mkWellKnown :: Lang -- ^ Default language + -> FilePath -- ^ Base directory + -> FilePath -- ^ Link file (@html_code.html@) + -> DecsQ +mkWellKnown defLang wellKnownBase wellKnownLinks = do + inputFiles <- fmap dirTree . liftIO $ readDirectoryWith (\f -> (f, ) <$> BS.readFile f) wellKnownBase + + mapM_ qAddDependentFile $ inputFiles ^.. folded . _1 + + -- languageFiles :: Map Lang [(FilePath, ByteString)] + languageFiles <- if + | Dir{contents} <- inputFiles + -> return . Map.fromList $ do + (language, lContents) <- contents ^.. folded . $(multifocusL 2) _name id + Dir{} <- pure lContents + let + lContents' :: [(FilePath, ByteString)] + lContents' = flip mapMaybe (flattenDir lContents) $ \pFile -> do + File{..} <- pure pFile + guard $ name /= wellKnownLinks + return $ file & _1 %~ makeRelative (wellKnownBase language) + return (Text.pack language, lContents') + | otherwise + -> fail "wellKnownBase is not a directory" + + fLanguages <- if + | defLang `Set.member` Map.keysSet languageFiles + , let languages' = Set.delete defLang $ Map.keysSet languageFiles + -> return $ defLang :| Set.toList languages' + | otherwise + -> fail "default language is missing in wellKnownBase" + + -- languageLinks :: Map Lang ByteString + languageLinks <- if + | Dir{contents} <- inputFiles + -> return . Map.fromList $ do + (language, lContents) <- contents ^.. folded . $(multifocusL 2) _name id + Dir{} <- pure lContents + let + lContents' :: [ByteString] + lContents' = flip mapMaybe (flattenDir lContents) $ \pFile -> do + File{..} <- pure pFile + guard $ name == wellKnownLinks + return $ file ^. _2 + c <- lContents' + return (Text.pack language, c) + | otherwise + -> fail "wellKnownBase is not a directory" + + lLanguages <- if + | defLang `Set.member` Map.keysSet languageLinks + , let languages' = Set.delete defLang $ Map.keysSet languageLinks + -> return $ defLang :| Set.toList languages' + | otherwise + -> fail "default language is missing in wellKnownBase" + + + fVar <- newName "f" + hVar <- newName "h" + lVar <- newName "l" + + let fileNames = setOf (folded . folded . _1) languageFiles + fileContents = Map.fromListWith (<>) $ do + (lang, fs) <- Map.toList languageFiles + (fName, fContent) <- fs + return ((fContent, mimeLookup $ Text.pack fName), Set.singleton (lang, fName)) + + wellKnownFileName = dataD + (cxt []) + nWellKnownFileName + [] + Nothing + [ normalC (mkName $ fNameManip fName) [] + | fName <- Set.toList fileNames + ] + (pure $ derivClause Nothing [[t|Eq|], [t|Ord|], [t|Bounded|], [t|Enum|], [t|Read|], [t|Show|], [t|Generic|], [t|Typeable|]]) + wellKnownFileNameMapSig = sigD + nwellKnownFileNames + [t|HashMap [Text] $(conT nWellKnownFileName)|] + wellKnownFileNameMap = funD + nwellKnownFileNames + [ clause [] (normalB $ [e|HashMap.fromList|] `appE` listE [ [e|($(TH.lift . map Text.pack $ splitDirectories fName), $(conE . mkName $ fNameManip fName))|] | fName <- Set.toList fileNames ]) [] + ] + wellKnownFileNamePathMultiPiece = instanceD + (cxt []) + (conT ''PathMultiPiece `appT` conT nWellKnownFileName) + [ funD 'toPathMultiPiece + [ clause [conP (mkName $ fNameManip fName) []] (normalB . TH.lift . map Text.pack $ splitDirectories fName) [] + | fName <- Set.toList fileNames + ] + , funD 'fromPathMultiPiece $ + [ clause [] (normalB [e|flip HashMap.lookup $(varE nwellKnownFileNames)|]) [] + ] + ] + wellKnownFileNameHashable = instanceD + (cxt []) + (conT ''Hashable `appT` conT nWellKnownFileName) + [] + + getWellKnownRSig = sigD + ngetWellKnownR + [t|forall m. MonadHandler m => $(conT nWellKnownFileName) -> m TypedContent|] + getWellKnownR = funD + ngetWellKnownR + [ clause [varP fVar] (normalB [e|$(varE hVar) =<< selectLanguage fLanguages|]) + [ funD hVar $ + [ clause [varP lVar] (guardedB + [ (,) <$> normalG [e|HashSet.member ($(varE lVar), $(varE fVar)) $ HashSet.fromList $(listE [ tupE [TH.lift l, conE . mkName $ fNameManip fName] | (l, fName) <- Set.toList xs ])|] + <*> [e|TypedContent mime (toContent fContent) <$ setEtag $(TH.lift $ hashToText (mime, fContent))|] + ]) [] + | ((fContent, mime), xs) <- Map.toList fileContents + ] ++ pure (clause [wildP] (normalB [e|notFound|]) []) + ] + ] + + wellKnownHtmlLinksSig = sigD + nwellKnownHtmlLinks + [t|forall m. MonadWidget m => m ()|] + wellKnownHtmlLinks = funD + nwellKnownHtmlLinks + [ clause [] (normalB [e|toWidgetHead . preEscapedToHtml . $(varE hVar) =<< selectLanguage lLanguages|]) + [ sigD hVar [t|Text -> Text|] + , funD hVar $ + [ clause [varP lVar] (guardedB + [ (,) <$> normalG [|$(varE lVar) == lang|] + <*> TH.lift (Text.filter (`notElem` ['\r', '\n']) $ Text.decodeUtf8 c) + ]) [] + | (lang, c) <- Map.toList languageLinks + ] ++ pure (clause [wildP] (normalB [e|mempty|]) []) + ] + ] + + sequence + [ wellKnownFileName, wellKnownFileNameMapSig, wellKnownFileNameMap, wellKnownFileNamePathMultiPiece, wellKnownFileNameHashable + , getWellKnownRSig, getWellKnownR + , wellKnownHtmlLinksSig, wellKnownHtmlLinks + ] + where + fNameManip = Text.unpack . mconcat . over (traverse . _head) Char.toUpper . filter (not . null) . Text.split (not . isAlphaNum) . Text.pack diff --git a/src/Utils.hs b/src/Utils.hs index 6b15e3fd9..f266cece4 100644 --- a/src/Utils.hs +++ b/src/Utils.hs @@ -913,11 +913,11 @@ cachedHereBinary = do [e| \k -> cachedByBinary (loc, k) |] hashToText :: Hashable a => a -> Text -hashToText = decodeUtf8 . Base64.encode . toStrict . Binary.encode . hash +hashToText = Text.dropWhileEnd (== '=') . decodeUtf8 . Base64.encode . toStrict . Binary.encode . hash setEtagHashable, setWeakEtagHashable :: (MonadHandler m, Hashable a) => a -> m () setEtagHashable = setEtag . hashToText -setWeakEtagHashable = setEtag . hashToText +setWeakEtagHashable = setWeakEtag . hashToText setLastModified :: MonadHandler m => UTCTime -> m () setLastModified lastModified = do diff --git a/static/favicon.ico b/static/favicon.ico deleted file mode 100644 index 85f9e0d7af640a7f64e6956416a1926e1bef22f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 101484 zcmeG_4RBV~l`n=G5-h=SI_^5%3EjHYQWcu6b#;||M7vbo6}wdtEhRH;R4NWy5c~m> zYRB4v8cjf?*ut*kVo{JTE~p5J!)kXCm#z+?AOz_wRB23rmIw*?_MZ3NeL3%ZymRvP z=F6AFlbM_Q?mPGA{NLYqKhG=hPW9lgz#Hjxna5kqb8T&qK6hw=_x&QzOQpherROau zE%3&S3DVE}jpx1aEmLx2m>%JI-EAPZ?hIpLY-W;;kcZ9Xa~6@^8-l(nG&m_VWw2w_n=P z*fI0q;kTBK-QB*e?$)n7amUA_j+9N3pwa%be*mZeCJ$d$U({*(u(u zL%auyy&-3KL%-yW_(yN#6JBaeL2Xqg;N4qVK6B~V=1tFRZ6EuHsq&Xo=Bz(@*)#8^ z4;*^q!fojfI_8({*i`hnj_Hpy-?0C{Tk|G&oHgpwf`rOW-pX^W%gVNGDY#|nhAo{p z-uL0$k1oDxYQ>M9UcY%u=bK07H;+1U{r1Kwx3|1p-ue4k$5zff{PyGzJ16|ly77yS zz4VvqUu|9g^rEAeKk-!h>GI}X@87Whvg{h4w)W^LAHRNhb$P|kb6bkqs-{kuzqsw& z)%Ubc+i?Cb8$ViJK4tFYK=FlpYhjo)AJ>s702zPtT`#vSie%s)8#Kcj_%Z|}aldS3d?wQp?cEPwp@9j|mi{K)h{3Km^-KI4_7zWr+<2ms17lTR&6RvUEjF0apCYW$1i$zb>sF;)0_7n-m&GA z$z?^0j{WZs+FSQdyZMszihU;zUR^VJNn>l4it&d%twne4*i~IU?}elPTHZK&R%gr4 zcQn5L&gz=y&)>1=*o38r)|ICZJb37i>P-_Ly7~4Q=WW>cg^w@Hl4Bd%I|^Rdu&?9& zr8gD+X7ka1pILkH=&$_lq7@sSeQVCtL#tXE@4exsn%0)3Z=JILyq`^M|MrX7QZemo zw{BSZ`Q`2NUfBPc>zaSkTv4&13n zv%h!m#7oj!_WypvzSFO2T=w41=WqXRde5Ym_FeNku6$un&B_J$pK>xM5R*D~8}I(x z@v+Bmd+Lv4XHP#-edw%Ni#{0tM(4kj*BrTR)2KJocl=-L+h@-GyJPLfu=g~so3d#C z^qqHCcYW{B@s7=h*3GP)^!Mq*_a6Iod24YGol%I##`|i|Iq;jB=eKTq=LgL{XOlLDp8Gq~#G1qNyrNroJ-DTF z#Xr?ddF97X?fa`2S1uU6Y;o(TBStSKw;XR>zj1BLy7K0WPE=3b^ryFu-Mn=1q7TQf zKQgtt>-fyslb8G%4PL+f#E`MmUw?K>=d_mBR$jFH#j;nQT>H0Erp&vm;-Pnjzcab! z{>e*f?%D9l%B>Td4?KT3ONzF)7v8vQ&wEGLlvjNH#PxR-PucPM8?C=vymiv9ZB>6f zI-|O4_4z>DIU9DL+4$x5hqLqpC(b;0e(7(oTJXwireE`}{Khp`tgftj;9T=u3olHk z2fbqOSKys7%=1I|BJWYEZs5cR1MtjFCOSIN1X%&+N~8^D6(miy*HEsQmNF{=7G? z^3S^n@``}UKd;b0om)4(z^&^!(Z(oe9d%KsG@KgIA*!C~U*LnX{7{8hIp%fR~ zHQ22N4mW^F^AE}QN&J=M<3^o1g(=KGCHy4%xV0OAN&I!C&$YaSpUPkGe`^2i`hjF$ zl6|`CH^06s;ivY`uRlxtk@&0fchzvW^6LK`&)=#4SO3rGfMj28*6Utc!cWJ4I{xEy zpuhM3&g(Y|i|Z_MagM4{b_MjUof1ZH@9IEmk-~x0+ zU*+$}zyV%dytvALfD6zO{d~(``aW5je{gFX0F&4w?O&u8oBIKn zAl091IER}L6ZX#~{LI>k`M!Y(!u-&zPqOzbsQe`f@NktszrV?IPd;BF@mH!p*Ki3x z$-hbXv9=F>LG52+VNT&Hf3<&B2ekiZ?N9QLEN{)YkgdEAfD(QZeG-c5|8@M&>VWqD zk_qJ$KG5yI@-g%62^S>&a=hPe++*f`KPHIdTgUb#<+!||HSCUc&{ced&CW z)V?`Gzl;r>>;b0oSNStKAgv!s`pwxsz;dmB5`LWR15Cn?vweU`?W_Is=LcQ&PvXDI zKezl>`{y-K?LW8r$8+EF)c$kJf3<&J1J(X>tA9NAKLz$L-fzy|e<$%zf}8QP+x>dN z{8egSg0pKQJ(wSlt2cL$F+plyg0pLbW`9unepJ5So|lh=p9EL=OB~|iDt~_clINZb zRr^=_XLW#C8-ukW&i4aM)<8ao+P~UArvuvm^Y$Y_S=-m~U#rAF39jQmi9%KZh|A8<@L6;C?tsza{)6_~i>rwz&kBHbc9_9}zzz zpUasbwJ*Wlzwc1_b39i0tNn92p!P5Qz6*c~YC_)o`woDN9(DZ$nMyMEuL7vE|2(369NpE*OEVuH$F<z$E^v{R{jR_8+wW=lQ4h&+~^J`@y(S z@ULqB0)M%&zxMy;`;`8@{-garuit~-{)O}T#y_&>|E2MdtAFi*ezN#Ej~@j7Nc8u6 z`xod_`MaH8Rr$O5SG9khf6n87wSWG6o4_CS|2zk@|L^DHe?h;s|JVMX(*fT4&HA$A z`il1d+W)gUAk0s7{>NLWbbiKld@0U<9Phu&HGa9SU-FL<+!;UN`w8dzrS(shKR^Ev z`cG&1sQlUQbDa4r>AxUfwSRv6ooo5zx_(K%65JU-wSTpLMhBerOY(o(|7-ux>4304 zm23UXb^Vfmm*CF$F}4qLL+AR{{@MOTlCK1p;b0oSNmsmK<%G5FagT)Ht74D?spNIH|taC|5+I@eLsu~B>qZpwr!Qa zcs5+eelEL{D+V1JJD|5QWqbS{|RZdt|!R(QPt ztXx!5;}UuS1-1%~H$IH}Z2<$z_xzueJ-{UTSmyKs&_(u8wii%ftC093-7l>2XW8qi z{MG-v=m4)>?ohRVcOG&zxX51pe@R-J|7-uxX`c*J`uWWllnaaN>TpGN2RQ`ikwmqRt<=+!7 z4c1m0|FrqD?Z7kq^=;a=1~G2EkJ5hgQM)1WeVc~X;Lm43$c4U2Lvxnz zq2Tk7Hi8cjG5-OdzD#4j`^|T^4^JT~(aYXXM~(QDhyQ}6eNbP(p^bp^LHm81_K8L9 z=Ot|#wM_5R7||VYy&I2DO&S>PGd-XfdjoirENM1-p;3^| zof!L|-)#xsvJn4J7yquq$+%5nr9aM3m495{M1PKSAMBvN>p#_ZvHv3UnLaRugS-93 z>!bXm{HOR*dL(>kep(-G5BoNF#++w4@x76anQNdx{DJ9DW9t=4fBc>|m4AFbJJ|I<T4Gy$C9WT@JX1o^vZkhg$m(m|wZ;|AOWe?Ak-%@!h zKb3#UwU`H#pIG?u~Cs2>C$+J3LwKWR_tSNUTdp!zr1 z^q-E+rT#L*{)F|nZ0pN5Ka~efi}jKDiI|_xKa_rU{WV*DW}BbNo2JG3K4tQE?SJV# zw{-m?cGefx;>f+Y8U8f0|BbI#ll{RA|3k)4lKho^VSGyDktwhE@A#_x&6+@ccj))} zPxk+)z0lwJqVhNM-Pn1+jPk@DrTrqjHv-SJK3d+{{+0gN@8nT_tNi1?aXr}dU)qnL z_xw}(X*xrH$oc{8Z)th@y9*ihk@178{VVMKoW=xaaMe|irr zJwHR~lhV?ijZ%3Er})tIQdj#|`s2R?LHSMPA*EIR{O?vs{G)IxkKWdQ+TK7*yW0Oi zqnF~V^!M)h56a*Eo|fd_eHQ^x0yh$nI3O^19p2dBXAIxB1@B6eW4}%VKY8qx!YvS{ zbaHIU`tXy-X%Ff)SR}-4@V?YDRV2lR5+9xr8<-Dg;x;Is5ZJnbiBOJ>eezAdggD(+ z8p!PAF|Zw^mk`4}5o1iKG9kJr;%xVOBJeo}a|QsBEHMp|)`S zc)c9Ag`boB59v1}9B}=(9>qU6TY~xH!hh`Vl4KOCAD_iX`p}cIJ;{S*jh_?CqO;|R zd9Z&|{QbKCUGyhg|A5b@<17gUjMPxUD!2-RH3t z|6q>+^Dt}vqw~Qr{?~V(3&s3ToNFUlC(Wso=Jhzg;veoC4G{igJB{hj!tQg0Sby+y zEI%4AVSUE=@!8o}S>&&>)gQ$_+NX@hdwt1&d_EDc*I~IQJtv*TOUjSmu3Vm>E5_ZI(?>d)8vZ1pE!cqX+wSn*H85iB@HMB+|hM1|+kc&}H{=@ofiMqHXs zx50Z7dv{iJ4u`rj;-bC5`*>W~RM$@IE6p=bQZZB>quT@IEsxG_O4K zFBY53g|(jf>v8-^n4iR@pOhI^8m7kMLO7?@WZES$oYNXnBV`tY2L9b=m2o&Nf|ecKmz@0myabuj-5&xzpqHar`C*#114rv*F; z_UCQA5;JVRzHZ;PYuD=|`G)k`d5DaEaQ?;e$JS%(we8S)?0cKfma}^axo$s$-{IK~ zif6Xz=-fawUkmhs)|0RHt({taTe(^MtX%BAwtb%z+kCd1%@^(Sg?zAfo$vOe`H00U zoS$2~?Y^|-kHy!*ZEWY-HlN+QwQ#Gy79Neq>^fq;+K>Ep;J+-sHa7Ui{aT2xh1;0& z(XJI)xQ(qIM0TF<_CvmB>V2@ciR5MOiSNFj&(dRKyp{~lww%@TC||f9YvDHTZ|z6> zBO!mHJvpnV(Y^R&56IrXXY+;oZrReG_x8K((${~Bzm=EWi$Qq$`K;fw`K(>o@^-u* z!4ve)mNWVC(!SMas+SS|b}u31Q&PU9_Y(d2YG0C{UC+1vEgJWRYhqT;NPhl!(AFFA z&)OA{zs(o%hxpr^w;%R@QTyS#sHH2TAB*ff_+1E_kM@VQ-xTT3bNf_o_Iq_pmyIL- zg!XCWX5n`2G|Ct1heUtA+qdg$c6?&#p)p+xq;Q+I_1HB(iT-@IZ}HAmEYY8-ke=j$(F&Iml^#|}Z zmR=fzod$EQa9;0^>4I-kOg+%ohVyq*kI9S6LmI}dejZZ}d?ALzGrWU75Ar}>Tu0Qd znOo9wH0{qDeO!h;>ep*%3hE4gQwi#YxfU(w&%Z*v{js*G19;{86?hJLV}E3Ta&4A9KOG@V;co3;gi&!T1x(8(I>}2YC9l*t}t#tx-K8T_ppyhF%o;=&~@VRVLq31pL xs^`@mGXCMeJn!^}J@2avJnsuXHvYr-jI$w}?Ri5+2H{i_55LnaH=D)g{|DNUjMM-C diff --git a/webpack.config.js b/webpack.config.js index 3cc5b8379..cc8b9918d 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,5 +1,11 @@ const webpack = require('webpack'); const path = require('path'); +const tmp = require('tmp'); +tmp.setGracefulCleanup(); +const fs = require('fs'); +const glob = require('glob'); +const { execSync } = require('child_process'); + const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const ManifestPlugin = require('webpack-manifest-plugin'); @@ -9,8 +15,8 @@ const TerserPlugin = require('terser-webpack-plugin'); const yaml = require('js-yaml'); const HashOutput = require('webpack-plugin-hash-output'); const postcssPresetEnv = require('postcss-preset-env'); -const AppManifestWebpackPlugin = require('app-manifest-webpack-plugin'); const RemovePlugin = require('remove-files-webpack-plugin'); +const RealFaviconPlugin = require('real-favicon-webpack-plugin'); const webpackVersion = require('webpack/package.json').version.split('.').slice(0, 2).join('.'); const packageVersion = require('./package.json').version; @@ -116,7 +122,9 @@ module.exports = { serialize: yaml.safeDump }), new CleanWebpackPlugin({ - cleanOnceBeforeBuildPatterns: path.resolve(__dirname, 'static', 'wp-*') + cleanOnceBeforeBuildPatterns: [ path.resolve(__dirname, 'static'), + path.resolve(__dirname, 'well-known'), + ] }), new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), new CopyPlugin([ @@ -125,38 +133,64 @@ module.exports = { new webpack.DefinePlugin({ VERSION: JSON.stringify(packageVersion) }), - new AppManifestWebpackPlugin({ - logo: path.resolve(__dirname, 'assets/favicon.svg'), - output: '/[hash]-icons/', - prefix: `/static/res/wp-${webpackVersion}/`, - inject: false, - emitStats: true, - statsFilename: path.resolve(__dirname, 'config/favicon.json'), - persistentCache: false, - config: { - background: '#fff', - icons: { - android: false, - appleIcon: false, - appleStartup: false, - coast: false, - favicons: true, - firefox: false, - windows: false, - yandex: false + ...(() => { + const faviconJson = require('./config/favicon.json'); + const langs = new Set(); + function findLangs(json) { + if (json && json._i18n) { + Object.keys(json).forEach(key => { + if (key !== '_i18n') { + langs.add(key); + } + }) + } else if (Array.isArray(json)) { + json.forEach(elem => findLangs(elem)); + } else if (typeof json === 'object') { + Object.keys(json).forEach(key => findLangs(json[key])); } } - }), - new RemovePlugin({ - after: { - test: [ - { folder: path.resolve(__dirname, `static/wp-${webpackVersion}`), - method: (filePath) => { return new RegExp(/\/.*-icons\/.*\.(xml|json|webapp)$/, 'm').test(filePath); }, - recursive: true - } - ] + findLangs(faviconJson); + + function selectLang(lang, json) { + if (json && json._i18n) { + return json[lang]; + } else if (Array.isArray(json)) { + return json.map(elem => selectLang(lang, elem)); + } else if (typeof json === 'object') { + return Object.fromEntries(Object.entries(json).map(([k, v]) => [k, selectLang(lang, v)])); + } else { + return json; + } } - }) + + const langJsons = {}; + Array.from(langs).forEach(lang => { + langJsons[lang] = selectLang(lang, faviconJson); + }); + + return Array.from(langs).map(lang => { + const tmpobj = tmp.fileSync({ dir: ".", postfix: ".json" }); + fs.writeSync(tmpobj.fd, JSON.stringify(langJsons[lang])); + fs.close(tmpobj.fd); + + return [ + new RealFaviconPlugin({ + faviconJson: `./${tmpobj.name}`, + outputPath: path.resolve(__dirname, 'well-known', lang), + inject: false + }), + new CopyPlugin([ + { from: 'config/robots.txt', to: path.resolve(__dirname, 'well-known', lang, 'robots.txt') }, + ]) + ]; + }).flat(1); + })(), + { apply: compiler => compiler.hooks.afterEmit.tap('AfterEmitPlugin', compilation => { + Array.from(glob.sync(path.resolve(__dirname, 'well-known', '**', '*.@(png)'))).forEach(imageFile => { + execSync(`exiftool -overwrite_original -all= ${imageFile}`, { stdio: 'inherit' }); + }); + }) + } ], output: { From 8e731223be18036fb1b1383b57e5fc5fb935596a Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Sat, 11 Jan 2020 22:03:26 +0100 Subject: [PATCH 22/38] chore: remove ignored /static --- static/img/lmu/sigillum.svg | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 static/img/lmu/sigillum.svg diff --git a/static/img/lmu/sigillum.svg b/static/img/lmu/sigillum.svg deleted file mode 100644 index 78538233a..000000000 --- a/static/img/lmu/sigillum.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - image/svg+xml - - - - - - - - - - - - From f853e1816a4e36c8a0b5f0744625485db353a2fd Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Sat, 11 Jan 2020 22:16:45 +0100 Subject: [PATCH 23/38] chore: improve call to exiftool --- webpack.config.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index cc8b9918d..24e9c7aef 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -186,9 +186,9 @@ module.exports = { }).flat(1); })(), { apply: compiler => compiler.hooks.afterEmit.tap('AfterEmitPlugin', compilation => { - Array.from(glob.sync(path.resolve(__dirname, 'well-known', '**', '*.@(png)'))).forEach(imageFile => { - execSync(`exiftool -overwrite_original -all= ${imageFile}`, { stdio: 'inherit' }); - }); + const imgFiles = glob.sync(path.resolve(__dirname, 'well-known', '**', '*.@(png)')); + const imgFilesArgs = Array.from(imgFiles).join(" "); + execSync(`exiftool -overwrite_original -all= ${imgFilesArgs}`, { stdio: 'inherit' }); }) } ], From 66fd3c8c76f38072e1e0d2b34bfc7aac653c9700 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Sat, 11 Jan 2020 22:49:03 +0100 Subject: [PATCH 24/38] chore: fix tests --- .gitlab-ci.yml | 2 +- src/Settings/WellKnownFiles.hs | 2 +- test/FoundationSpec.hs | 5 +++++ test/Handler/CommonSpec.hs | 6 +++--- test/TestImport.hs | 1 + 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fa414baa8..b6e358687 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -57,8 +57,8 @@ frontend:build: artifacts: paths: - static + - well-known - config/webpack.yml - - config/favicon.json name: "${CI_JOB_NAME}" expire_in: "1 day" dependencies: diff --git a/src/Settings/WellKnownFiles.hs b/src/Settings/WellKnownFiles.hs index 2e6d416a2..91b4dfd9a 100644 --- a/src/Settings/WellKnownFiles.hs +++ b/src/Settings/WellKnownFiles.hs @@ -1,5 +1,5 @@ module Settings.WellKnownFiles - ( WellKnownFileName + ( WellKnownFileName(..) , getWellKnownR , wellKnownHtmlLinks ) where diff --git a/test/FoundationSpec.hs b/test/FoundationSpec.hs index d995cee9e..eddebfdfb 100644 --- a/test/FoundationSpec.hs +++ b/test/FoundationSpec.hs @@ -24,6 +24,11 @@ instance Arbitrary (Route EmbeddedStatic) where paramNum <- getNonNegative <$> arbitrary params <- replicateM paramNum $ (,) <$> printableText' <*> printableText return $ embeddedResourceR path params + + +instance Arbitrary WellKnownFileName where + arbitrary = genericArbitrary + shrink = genericShrink instance Arbitrary SchoolR where arbitrary = genericArbitrary diff --git a/test/Handler/CommonSpec.hs b/test/Handler/CommonSpec.hs index e1920fb6f..8e63b9d1b 100644 --- a/test/Handler/CommonSpec.hs +++ b/test/Handler/CommonSpec.hs @@ -6,12 +6,12 @@ spec :: Spec spec = withApp $ do describe "robots.txt" $ do it "gives a 200" $ do - get RobotsR + get $ WellKnownR RobotsTxt statusIs 200 it "has correct User-agent" $ do - get RobotsR + get $ WellKnownR RobotsTxt bodyContains "User-agent: *" describe "favicon.ico" $ do it "gives a 200" $ do - get FaviconR + get $ WellKnownR FaviconIco statusIs 200 diff --git a/test/TestImport.hs b/test/TestImport.hs index 8e71a84f6..2b13743ab 100644 --- a/test/TestImport.hs +++ b/test/TestImport.hs @@ -53,6 +53,7 @@ import Control.Monad.Catch as X hiding (Handler(..)) import Control.Monad.Trans.Resource (runResourceT) import Settings +import Settings.WellKnownFiles as X import Data.CaseInsensitive as X (CI) import qualified Data.CaseInsensitive as CI From 14bb020fe9d8f7579269468c9fb55a1dc373d145 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Thu, 9 Jan 2020 21:50:21 +0100 Subject: [PATCH 25/38] feat: support exam registration including room (ExamRoomFifo) --- messages/uniworx/de-de-formal.msg | 19 +++++-- messages/uniworx/en-eu.msg | 19 +++++-- routes | 1 + src/Foundation.hs | 91 ++++++++++++++++++++++++++++--- src/Handler/Exam/Register.hs | 71 ++++++++++++++++++------ src/Handler/Exam/Show.hs | 56 ++++++++++++++++--- src/Handler/Utils/Form.hs | 3 + src/Model/Types/Exam.hs | 1 + src/Model/Types/Security.hs | 2 + templates/exam-show.hamlet | 50 ++++++++++------- templates/widgets/tooltip.hamlet | 6 +- 11 files changed, 252 insertions(+), 67 deletions(-) diff --git a/messages/uniworx/de-de-formal.msg b/messages/uniworx/de-de-formal.msg index 07248ce88..fb215fdd6 100644 --- a/messages/uniworx/de-de-formal.msg +++ b/messages/uniworx/de-de-formal.msg @@ -12,6 +12,7 @@ BtnCourseDeregister: Vom Kurs abmelden BtnCourseApply: Zum Kurs bewerben BtnCourseRetractApplication: Bewerbung zum Kurs zurückziehen BtnExamRegister: Anmelden zur Prüfung +BtnExamRegisterOccurrence: Anmelden zum Prüfungstermin/-raum BtnExamDeregister: Von der Prüfung abmelden BtnHijack: Sitzung übernehmen BtnSave: Speichern @@ -99,6 +100,7 @@ CourseCapacity: Kapazität CourseCapacityTip: Anzahl erlaubter Kursanmeldungen, leer lassen für unbeschränkte Kurskapazität CourseNoCapacity: In diesem Kurs sind keine Plätze mehr frei. TutorialNoCapacity: In dieser Übung sind keine Plätze mehr frei. +ExamOccurrenceNoCapacity: Zu diesem Termin/Raum sind keine Plätze mehr frei. CourseNotEmpty: In diesem Kurs sind momentan Teilnehmer angemeldet. CourseRegistration: Kursanmeldung CourseRegisterOpen: Anmeldung möglich @@ -409,6 +411,7 @@ UnauthorizedCorrectorAny: Sie sind nicht als Korrektor für eine Veranstaltung e UnauthorizedRegistered: Sie sind nicht als Teilnehmer für diese Veranstaltung registriert. UnauthorizedAllocationRegistered: Sie sind nicht als Teilnehmer für diese Zentralanmeldung registriert. UnauthorizedExamResult: Sie haben keine Ergebnisse in dieser Prüfung. +UnauthorizedExamOccurrenceRegistration: Anmeldung zur Klausur erfolgt nicht inkl. Raum/Termin. UnauthorizedParticipant: Angegebener Benutzer ist nicht als Teilnehmer dieser Veranstaltung registriert. UnauthorizedParticipantSelf: Sie sind kein Teilnehmer dieser Veranstaltung. UnauthorizedApplicant: Angegebener Benutzer hat sich nicht für diese Veranstaltung beworben. @@ -1263,6 +1266,8 @@ AuthTagAllocationRegistered: Nutzer nimmt an der Zentralanmeldung teil AuthTagTutorialRegistered: Nutzer ist Tutoriumsteilnehmer AuthTagExamRegistered: Nutzer ist Prüfungsteilnehmer AuthTagExamResult: Nutzer hat Prüfungsergebnisse +AuthTagExamOccurrenceRegistered: Nutzer ist für Prüfungsraum/-termin angemeldet +AuthTagExamOccurrenceRegistration: Anmeldung zur Klausur erfolgt inkl. Raum/Termin AuthTagParticipant: Nutzer ist mit Kurs assoziiert AuthTagApplicant: Nutzer ist mit Bewerber zum Kurs AuthTagRegisterGroup: Nutzer ist nicht Mitglied eines anderen Tutoriums mit der selben Registrierungs-Gruppe @@ -1522,6 +1527,8 @@ ExamNoBonus': Kein automatischer Bonus ExamBonusPoints': Umrechnung von Übungspunkten ExamBonusManual': Manuelle Berechnung +ExamRegisterForOccurrence: Anmeldung zur Klausur erfolgt durch Anmeldung zu einem Termin/Raum + ExamBonusAchieved: Bonuspunkte ExamEditHeading examn@ExamName: #{examn} bearbeiten @@ -1534,17 +1541,19 @@ ExamBonusRound: Bonus runden auf ExamBonusRoundNonPositive: Vielfaches, auf das gerundet werden soll, muss positiv und größer null sein ExamBonusRoundTip: Bonuspunkte werden kaufmännisch auf ein Vielfaches der angegeben Zahl gerundet. -ExamAutomaticOccurrenceAssignment: Automatische Termin- bzw. Raumzuteilung -ExamAutomaticOccurrenceAssignmentTip: Sollen Prüfungsteilnehmer automatisch auf die zur Verfügung stehenden Räume bzw. Termine verteilt werden? Manuelle Umverteilung bzw. vorheriges Festlegen von Zuteilungen einzelner Teilnehmer ist trotzdem möglich. +ExamAutomaticOccurrenceAssignment: Automatische oder selbständige Termin- bzw. Raumzuteilung +ExamAutomaticOccurrenceAssignmentTip: Sollen Prüfungsteilnehmer automatisch auf die zur Verfügung stehenden Räume bzw. Termine verteilt werden oder sich selbstständig einen Raum bzw. Termin aussuchen dürfen? Manuelle Umverteilung bzw. vorheriges Festlegen von Zuteilungen einzelner Teilnehmer ist trotzdem möglich. ExamOccurrenceRule: Verfahren ExamOccurrenceRuleParticipant: Termin- bzw. Raumzuteilungsverfahren -ExamRoomManual': Keine automatische Zuteilung +ExamRoomManual': Keine automatische bzw. selbstständige Zuteilung ExamRoomSurname': Nach Nachname ExamRoomMatriculation': Nach Matrikelnummer ExamRoomRandom': Zufällig pro Teilnehmer +ExamRoomFifo': Auswahl durch Teilnehmer bei Anmeldung ExamOccurrence: Termin/Raum ExamNoOccurrence: Kein Termin/Raum +ExamNoSuchOccurrence: Termin/Raum existiert nicht (mehr) ExamOccurrences: Prüfungen ExamRooms: Räume ExamRoomAlreadyExists: Prüfung ist bereits eingetragen @@ -1557,7 +1566,8 @@ ExamRoomStart: Beginn ExamRoomEnd: Ende ExamRoomDescription: Beschreibung ExamTimeTip: Nur zur Information der Studierenden, die tatsächliche Zeitangabe erfolgt pro Prüfung -ExamRoomRegistered: Zugeteilt +ExamRoomAssigned: Zugeteilt +ExamRoomRegistered: Anmeldung ExamOccurrenceStart: Prüfungsbeginn @@ -1603,6 +1613,7 @@ ExamDeregisteredSuccess exam@ExamName: Erfolgreich von der Prüfung #{exam} abge ExamRegistered: Zur Prüfung angemeldet ExamNotRegistered: Nicht zur Prüfung angemeldet ExamRegistration: Prüfungsanmeldung +ExamLoginToRegister: Um sich zum Kurs anzumelden müssen Sie zunächst in Uni2work anmelden ExamRegisterToMustBeAfterRegisterFrom: "Anmeldung ab" muss vor "Anmeldung bis" liegen ExamDeregisterUntilMustBeAfterRegisterFrom: "Abmeldung bis" muss nach "Anmeldung bis" liegen diff --git a/messages/uniworx/en-eu.msg b/messages/uniworx/en-eu.msg index f071b9dae..a39d5fd77 100644 --- a/messages/uniworx/en-eu.msg +++ b/messages/uniworx/en-eu.msg @@ -12,6 +12,7 @@ BtnCourseDeregister: Leave course BtnCourseApply: Apply for course BtnCourseRetractApplication: Retract application BtnExamRegister: Enrol for exam +BtnExamRegisterOccurrence: Enrol for exam occurrence/room BtnExamDeregister: Leave exam BtnHijack: Hijack session BtnSave: Save @@ -99,6 +100,7 @@ CourseCapacity: Capacity CourseCapacityTip: Maximum permissable number of enrolments for this course; leave empty for unlimited capacity CourseNoCapacity: Course has reached maximum capacity TutorialNoCapacity: Tutorial has reached maximum capacity +ExamOccurrenceNoCapacity: Occurrence/Room has reached maximum capacity CourseNotEmpty: There are currently no participants enrolled for this course. CourseRegistration: Enrolment CourseRegisterOpen: Enrolment is allowed @@ -407,6 +409,7 @@ UnauthorizedCorrectorAny: You are no corrector for any course. UnauthorizedRegistered: You are no participant in this course. UnauthorizedAllocationRegistered: You are no participant in this central allocation. UnauthorizedExamResult: You have no results in this exam. +UnauthorizedExamOccurrenceRegistration: Registration for exam is not done including occurrence/room. UnauthorizedParticipant: The specified user is no participant of this course. UnauthorizedParticipantSelf: You are no participant of this course. UnauthorizedApplicant: The specified user is no applicant for this course. @@ -1262,6 +1265,8 @@ AuthTagAllocationRegistered: User participates in central allocation AuthTagTutorialRegistered: User is tutorial participant AuthTagExamRegistered: User is exam participant AuthTagExamResult: User has an exam result +AuthTagExamOccurrenceRegistered: User is registered for exam occurrence/room +AuthTagExamOccurrenceRegistration: Registration for exam is done including occurrence/room AuthTagParticipant: User participates in course AuthTagApplicant: User is applicant for course AuthTagRegisterGroup: User is not participant in any tutorial of the same registration group @@ -1520,6 +1525,8 @@ ExamNoBonus': No automatic exam bonus ExamBonusPoints': Compute from exercise achievements ExamBonusManual': Manual computation +ExamRegisterForOccurrence: Registration for this exam is done by registering for an occurrence/room + ExamBonusAchieved: Bonus points ExamEditHeading examn: Edit #{examn} @@ -1532,17 +1539,19 @@ ExamBonusRound: Round bonus to ExamBonusRoundNonPositive: Rounding multiple must be positive and greater than zero ExamBonusRoundTip: Bonus points are rounded commercially to a multiple of the given number -ExamAutomaticOccurrenceAssignment: Automatically assign occurrence/room -ExamAutomaticOccurrenceAssignmentTip: Should exam participants be distributed automatically among the configured occurrences/rooms? Manipulation of the distribution and manually assigning participants remains possible. +ExamAutomaticOccurrenceAssignment: Selection of occurrences/rooms for/by participants +ExamAutomaticOccurrenceAssignmentTip: Should exam participants be distributed automatically among the configured occurrences/rooms? Should they instead be permitted to autonomously choose an occurrence/a room? Manipulation of the distribution and manually assigning participants remains possible. ExamOccurrenceRule: Procedure ExamOccurrenceRuleParticipant: Occurrence/room assignment procedure -ExamRoomManual': No automatic assignment +ExamRoomManual': No automatic or autonomous assignment ExamRoomSurname': By surname ExamRoomMatriculation': By matriculation ExamRoomRandom': Randomly +ExamRoomFifo': Selected by the participants when registering ExamOccurrence: Occurrence/room ExamNoOccurrence: No occurrence/room +ExamNoSuchOccurrence: Occurrence/Room does not exist (anymore) ExamOccurrences: Exams ExamRooms: Rooms ExamRoomAlreadyExists: Occurrence already configured @@ -1555,7 +1564,8 @@ ExamRoomStart: Start ExamRoomEnd: End ExamRoomDescription: Description ExamTimeTip: Only for informational purposes. The actual times are set for each occurrence/room -ExamRoomRegistered: Assigned +ExamRoomAssigned: Assigned +ExamRoomRegistered: Registration ExamOccurrenceStart: Exam starts @@ -1601,6 +1611,7 @@ ExamDeregisteredSuccess exam: Successufly deregistered from the exam #{exam} ExamRegistered: Registered for the exam ExamNotRegistered: Not registered for the exam ExamRegistration: Exam registration +ExamLoginToRegister: Your need to login to Uni2work before you can register for this course. ExamRegisterToMustBeAfterRegisterFrom: "Register to" must be after "register from" ExamDeregisterUntilMustBeAfterRegisterFrom: "Deregister until" must be after "register from" diff --git a/routes b/routes index 24e863319..2304d6d34 100644 --- a/routes +++ b/routes @@ -173,6 +173,7 @@ /users/new EAddUserR GET POST /users/invite EInviteR GET POST /register ERegisterR POST !timeANDcourse-registeredAND¬exam-registered !timeANDexam-registeredAND¬exam-result + /register/#ExamOccurrenceName ERegisterOccR POST !exam-occurrence-registrationANDtimeANDcapacityANDcourse-registeredAND¬exam-occurrence-registered !exam-occurrence-registrationANDtimeANDexam-occurrence-registeredAND¬exam-result /grades EGradesR GET POST !exam-office /apps CApplicationsR GET POST !/apps/files CAppsFilesR GET diff --git a/src/Foundation.hs b/src/Foundation.hs index 050b6d1aa..dd390b7b8 100644 --- a/src/Foundation.hs +++ b/src/Foundation.hs @@ -504,9 +504,9 @@ tagAccessPredicate AuthTime = APDB $ \mAuthId route _ -> case route of course <- $cachedHereBinary (tid, ssh, csh) . MaybeT . getKeyBy $ TermSchoolCourseShort tid ssh csh Entity eId Exam{..} <- $cachedHereBinary (course, examn) . MaybeT . getBy $ UniqueExam course examn cTime <- liftIO getCurrentTime - registered <- case mAuthId of - Just uid -> $cachedHereBinary (eId, uid) . lift . existsBy $ UniqueExamRegistration eId uid - Nothing -> return False + registration <- case mAuthId of + Just uid -> $cachedHereBinary (eId, uid) . lift . getBy $ UniqueExamRegistration eId uid + Nothing -> return Nothing let visible = NTop examVisibleFrom <= NTop (Just cTime) @@ -515,11 +515,23 @@ tagAccessPredicate AuthTime = APDB $ \mAuthId route _ -> case route of EUsersR -> guard $ NTop examStart <= NTop (Just cTime) && NTop (Just cTime) <= NTop examFinished ERegisterR - | not registered -> guard $ visible - && NTop examRegisterFrom <= NTop (Just cTime) - && NTop (Just cTime) <= NTop examRegisterTo - | otherwise -> guard $ visible - && NTop (Just cTime) <= NTop examDeregisterUntil + | is _Nothing registration + -> guard $ visible + && NTop examRegisterFrom <= NTop (Just cTime) + && NTop (Just cTime) <= NTop examRegisterTo + | otherwise + -> guard $ visible + && NTop (Just cTime) <= NTop examDeregisterUntil + ERegisterOccR occn -> do + occId <- (>>= hoistMaybe) . $cachedHereBinary (eId, occn) . lift . getKeyBy $ UniqueExamOccurrence eId occn + if + | (registration >>= examRegistrationOccurrence . entityVal) == Just occId + -> guard $ visible + && NTop (Just cTime) <= NTop examDeregisterUntil + | otherwise + -> guard $ visible + && NTop examRegisterFrom <= NTop (Just cTime) + && NTop (Just cTime) <= NTop examRegisterTo _ -> return () return Authorized @@ -758,6 +770,59 @@ tagAccessPredicate AuthTutorialRegistered = APDB $ \mAuthId route _ -> case rout guardMExceptT isRegistered (unauthorizedI MsgUnauthorizedRegistered) return Authorized r -> $unsupportedAuthPredicate AuthTutorialRegistered r +tagAccessPredicate AuthExamOccurrenceRegistration = APDB $ \_ route _ -> case route of + CExamR tid ssh csh examn _ -> exceptT return return $ do + isOccurrenceRegistration <- $cachedHereBinary (tid, ssh, csh, examn) . lift . E.selectExists . E.from $ \(course `E.InnerJoin` exam) -> do + E.on $ course E.^. CourseId E.==. exam E.^. ExamCourse + E.where_ $ course E.^. CourseTerm E.==. E.val tid + E.&&. course E.^. CourseSchool E.==. E.val ssh + E.&&. course E.^. CourseShorthand E.==. E.val csh + E.&&. exam E.^. ExamName E.==. E.val examn + E.&&. exam E.^. ExamOccurrenceRule E.==. E.val (Just ExamRoomFifo) + guardMExceptT isOccurrenceRegistration (unauthorizedI MsgUnauthorizedExamOccurrenceRegistration) + return Authorized + r -> $unsupportedAuthPredicate AuthExamOccurrenceRegistration r +tagAccessPredicate AuthExamOccurrenceRegistered = APDB $ \mAuthId route _ -> case route of + CExamR tid ssh csh examn (ERegisterOccR occn) -> exceptT return return $ do + authId <- maybeExceptT AuthenticationRequired $ return mAuthId + hasRegistration <- $cachedHereBinary (authId, tid, ssh, csh, examn, occn) . lift . E.selectExists . E.from $ \(course `E.InnerJoin` exam `E.InnerJoin` examRegistration `E.InnerJoin` examOccurrence) -> do + E.on $ E.just (examOccurrence E.^. ExamOccurrenceId) E.==. examRegistration E.^. ExamRegistrationOccurrence + E.on $ exam E.^. ExamId E.==. examRegistration E.^. ExamRegistrationExam + E.on $ course E.^. CourseId E.==. exam E.^. ExamCourse + E.where_ $ examRegistration E.^. ExamRegistrationUser E.==. E.val authId + E.&&. examOccurrence E.^. ExamOccurrenceName E.==. E.val occn + E.&&. course E.^. CourseTerm E.==. E.val tid + E.&&. course E.^. CourseSchool E.==. E.val ssh + E.&&. course E.^. CourseShorthand E.==. E.val csh + E.&&. exam E.^. ExamName E.==. E.val examn + guardMExceptT hasRegistration (unauthorizedI MsgUnauthorizedRegistered) + return Authorized + CExamR tid ssh csh examn _ -> exceptT return return $ do + authId <- maybeExceptT AuthenticationRequired $ return mAuthId + hasRegistration <- $cachedHereBinary (authId, tid, ssh, csh, examn) . lift . E.selectExists . E.from $ \(course `E.InnerJoin` exam `E.InnerJoin` examRegistration) -> do + E.on $ exam E.^. ExamId E.==. examRegistration E.^. ExamRegistrationExam + E.on $ course E.^. CourseId E.==. exam E.^. ExamCourse + E.where_ $ examRegistration E.^. ExamRegistrationUser E.==. E.val authId + E.&&. course E.^. CourseTerm E.==. E.val tid + E.&&. course E.^. CourseSchool E.==. E.val ssh + E.&&. course E.^. CourseShorthand E.==. E.val csh + E.&&. exam E.^. ExamName E.==. E.val examn + E.&&. E.not_ (E.isNothing $ examRegistration E.^. ExamRegistrationOccurrence) + guardMExceptT hasRegistration (unauthorizedI MsgUnauthorizedRegistered) + return Authorized + CourseR tid ssh csh _ -> exceptT return return $ do + authId <- maybeExceptT AuthenticationRequired $ return mAuthId + hasRegistration <- $cachedHereBinary (authId, tid, ssh, csh) . lift . E.selectExists . E.from $ \(course `E.InnerJoin` exam `E.InnerJoin` examRegistration) -> do + E.on $ exam E.^. ExamId E.==. examRegistration E.^. ExamRegistrationExam + E.on $ course E.^. CourseId E.==. exam E.^. ExamCourse + E.where_ $ examRegistration E.^. ExamRegistrationUser E.==. E.val authId + E.&&. course E.^. CourseTerm E.==. E.val tid + E.&&. course E.^. CourseSchool E.==. E.val ssh + E.&&. course E.^. CourseShorthand E.==. E.val csh + E.&&. E.not_ (E.isNothing $ examRegistration E.^. ExamRegistrationOccurrence) + guardMExceptT hasRegistration (unauthorizedI MsgUnauthorizedRegistered) + return Authorized + r -> $unsupportedAuthPredicate AuthExamOccurrenceRegistered r tagAccessPredicate AuthExamRegistered = APDB $ \mAuthId route _ -> case route of CExamR tid ssh csh examn _ -> exceptT return return $ do authId <- maybeExceptT AuthenticationRequired $ return mAuthId @@ -768,7 +833,7 @@ tagAccessPredicate AuthExamRegistered = APDB $ \mAuthId route _ -> case route of E.&&. course E.^. CourseTerm E.==. E.val tid E.&&. course E.^. CourseSchool E.==. E.val ssh E.&&. course E.^. CourseShorthand E.==. E.val csh - E.&&. exam E.^. ExamName E.==. E.val examn + E.&&. exam E.^. ExamName E.==. E.val examn guardMExceptT hasRegistration (unauthorizedI MsgUnauthorizedRegistered) return Authorized CourseR tid ssh csh _ -> exceptT return return $ do @@ -933,6 +998,13 @@ tagAccessPredicate AuthApplicant = APDB $ \mAuthId route _ -> case route of E.&&. course E.^. CourseSchool E.==. E.val ssh E.&&. course E.^. CourseShorthand E.==. E.val csh tagAccessPredicate AuthCapacity = APDB $ \_ route _ -> case route of + CExamR tid ssh csh examn (ERegisterOccR occn) -> maybeT (unauthorizedI MsgExamOccurrenceNoCapacity) $ do + cid <- $cachedHereBinary (tid, ssh, csh) . MaybeT . getKeyBy $ TermSchoolCourseShort tid ssh csh + eid <- $cachedHereBinary (cid, examn) . MaybeT . getKeyBy $ UniqueExam cid examn + Entity occId ExamOccurrence{..} <- $cachedHereBinary (eid, occn) . MaybeT . getBy $ UniqueExamOccurrence eid occn + registered <- $cachedHereBinary occId . lift $ fromIntegral <$> count [ ExamRegistrationOccurrence ==. Just occId, ExamRegistrationExam ==. eid ] + guard $ examOccurrenceCapacity > registered + return Authorized CTutorialR tid ssh csh tutn _ -> maybeT (unauthorizedI MsgTutorialNoCapacity) $ do cid <- $cachedHereBinary (tid, ssh, csh) . MaybeT . getKeyBy $ TermSchoolCourseShort tid ssh csh Entity tutId Tutorial{..} <- $cachedHereBinary (cid, tutn) . MaybeT . getBy $ UniqueTutorial cid tutn @@ -1791,6 +1863,7 @@ instance YesodBreadcrumbs UniWorX where ECInviteR -> i18nCrumb MsgBreadcrumbExamCorrectorInvite . Just $ CExamR tid ssh csh examn EShowR EInviteR -> i18nCrumb MsgBreadcrumbExamParticipantInvite . Just $ CExamR tid ssh csh examn EShowR ERegisterR -> i18nCrumb MsgBreadcrumbExamRegister . Just $ CExamR tid ssh csh examn EShowR + ERegisterOccR _occn -> i18nCrumb MsgBreadcrumbExamRegister . Just $ CExamR tid ssh csh examn EShowR breadcrumb (CourseR tid ssh csh (TutorialR tutn sRoute)) = case sRoute of TUsersR -> maybeT (i18nCrumb MsgBreadcrumbTutorial . Just $ CourseR tid ssh csh CTutorialListR) $ do diff --git a/src/Handler/Exam/Register.hs b/src/Handler/Exam/Register.hs index cc8e387a7..f040df069 100644 --- a/src/Handler/Exam/Register.hs +++ b/src/Handler/Exam/Register.hs @@ -1,6 +1,7 @@ module Handler.Exam.Register ( ButtonExamRegister(..) , postERegisterR + , postERegisterOccR ) where import Import @@ -8,45 +9,81 @@ import Import import Handler.Utils import Handler.Utils.Exam +import Database.Persist.Sql (deleteWhereCount) + -- Dedicated ExamRegistrationButton -data ButtonExamRegister = BtnExamRegister | BtnExamDeregister - deriving (Enum, Eq, Ord, Bounded, Read, Show, Generic, Typeable) +data ButtonExamRegister = BtnExamRegisterOccurrence + | BtnExamRegister + | BtnExamDeregister + deriving (Enum, Bounded, Eq, Ord, Read, Show, Generic, Typeable) instance Universe ButtonExamRegister instance Finite ButtonExamRegister -nullaryPathPiece ''ButtonExamRegister $ camelToPathPiece' 1 -embedRenderMessage ''UniWorX ''ButtonExamRegister id +nullaryPathPiece ''ButtonExamRegister $ camelToPathPiece' 2 + instance Button UniWorX ButtonExamRegister where - btnClasses BtnExamRegister = [BCIsButton, BCPrimary] - btnClasses BtnExamDeregister = [BCIsButton, BCDanger] + btnClasses BtnExamRegisterOccurrence = [BCIsButton, BCPrimary] + btnClasses BtnExamRegister = [BCIsButton, BCPrimary] + btnClasses BtnExamDeregister = [BCIsButton, BCDanger] - btnLabel BtnExamRegister = [whamlet|#{iconExamRegister True} _{MsgBtnExamRegister}|] - btnLabel BtnExamDeregister = [whamlet|#{iconExamRegister False} _{MsgBtnExamDeregister}|] + btnLabel BtnExamRegisterOccurrence = [whamlet|#{iconExamRegister True } _{MsgBtnExamRegisterOccurrence}|] + btnLabel BtnExamRegister = [whamlet|#{iconExamRegister True } _{MsgBtnExamRegister}|] + btnLabel BtnExamDeregister = [whamlet|#{iconExamRegister False} _{MsgBtnExamDeregister}|] postERegisterR :: TermId -> SchoolId -> CourseShorthand -> ExamName -> Handler Html - postERegisterR tid ssh csh examn = do Entity uid User{..} <- requireAuth Entity eId Exam{..} <- runDB $ fetchExam tid ssh csh examn - ((btnResult, _), _) <- runFormPost buttonForm + ((btnResult, _), _) <- runFormPost $ buttonForm' [BtnExamRegister, BtnExamDeregister] formResult btnResult $ \case + BtnExamDeregister -> do + runDB $ do + deleted <- deleteWhereCount [ExamRegistrationExam ==. eId, ExamRegistrationUser ==. uid] + unless (deleted <= 0) $ + audit $ TransactionExamDeregister eId uid + addMessageIconI Success IconExamRegisterFalse $ MsgExamDeregisteredSuccess examn + redirect $ CExamR tid ssh csh examn EShowR BtnExamRegister -> do runDB $ do now <- liftIO getCurrentTime - insert_ $ ExamRegistration eId uid Nothing now + void $ upsertBy (UniqueExamRegistration eId uid) (ExamRegistration eId uid Nothing now) [ExamRegistrationTime =. now] audit $ TransactionExamRegister eId uid - addMessageIconI Success IconExamRegisterTrue $ MsgExamRegisteredSuccess examn + addMessageIconI Success IconExamRegisterTrue $ MsgExamRegisteredSuccess examn redirect $ CExamR tid ssh csh examn EShowR + _other -> error "Unexpected due to definition of buttonForm'" + + redirect $ CExamR tid ssh csh examn EShowR + +postERegisterOccR :: TermId -> SchoolId -> CourseShorthand -> ExamName -> ExamOccurrenceName -> Handler Html +postERegisterOccR tid ssh csh examn occn = do + Entity uid User{..} <- requireAuth + (Entity eId Exam{..}, Entity occId ExamOccurrence{..}) <- runDB $ do + eexam@(Entity eId _) <- fetchExam tid ssh csh examn + occ <- getBy404 $ UniqueExamOccurrence eId occn + return (eexam, occ) + + ((btnResult, _), _) <- runFormPost buttonForm + + formResult btnResult $ \case BtnExamDeregister -> do runDB $ do - deleteBy $ UniqueExamRegistration eId uid - audit $ TransactionExamDeregister eId uid - addMessageIconI Info IconExamRegisterFalse $ MsgExamDeregisteredSuccess examn - -- yes, it's a success message, but it should be visually different from a positive success, since most will just note the positive green color! See discussion on commit 5f4925a4 + deleted <- deleteWhereCount [ExamRegistrationExam ==. eId, ExamRegistrationUser ==. uid] + unless (deleted <= 0) $ + audit $ TransactionExamDeregister eId uid + addMessageIconI Success IconExamRegisterFalse $ MsgExamDeregisteredSuccess examn redirect $ CExamR tid ssh csh examn EShowR + BtnExamRegisterOccurrence -> do + runDB $ do + now <- liftIO getCurrentTime + void $ upsertBy (UniqueExamRegistration eId uid) (ExamRegistration eId uid (Just occId) now) [ExamRegistrationOccurrence =. Just occId, ExamRegistrationTime =. now] + audit $ TransactionExamRegister eId uid + addMessageIconI Success IconExamRegisterTrue $ MsgExamRegisteredSuccess examn + redirect $ CExamR tid ssh csh examn EShowR + _other -> error "Unexpected due to definition of buttonForm'" - invalidArgs ["Register/Deregister button required"] + redirect $ CExamR tid ssh csh examn EShowR + diff --git a/src/Handler/Exam/Show.hs b/src/Handler/Exam/Show.hs index 9ee2da005..138a50391 100644 --- a/src/Handler/Exam/Show.hs +++ b/src/Handler/Exam/Show.hs @@ -30,7 +30,7 @@ getEShowR tid ssh csh examn = do let gradingVisible = NTop (Just cTime) >= NTop examFinished gradingShown <- or2M (return gradingVisible) . hasReadAccessTo $ CExamR tid ssh csh examn EEditR - let occurrenceAssignmentsVisible = NTop (Just cTime) >= NTop examPublishOccurrenceAssignments + let occurrenceAssignmentsVisible = NTop (Just cTime) >= NTop examPublishOccurrenceAssignments || examOccurrenceRule == Just ExamRoomFifo occurrenceAssignmentsShown <- or2M (return occurrenceAssignmentsVisible) . hasReadAccessTo $ CExamR tid ssh csh examn EEditR examParts <- sortOn (view $ _entityVal . _examPartNumber) <$> selectList [ ExamPartExam ==. eId ] [ Asc ExamPartName ] @@ -58,10 +58,16 @@ getEShowR tid ssh csh examn = do E.orderBy [E.desc registered, E.asc $ examOccurrence E.^. ExamOccurrenceStart, E.asc $ examOccurrence E.^. ExamOccurrenceRoom] return (examOccurrence, registered) - let occurrences = map (over _2 E.unValue) occurrencesRaw + registered <- for mUid $ getBy . UniqueExamRegistration eId + mayRegister <- if + | examOccurrenceRule == Just ExamRoomFifo -> anyM occurrencesRaw $ \(Entity _ ExamOccurrence{..}, _) -> + hasWriteAccessTo . CExamR tid ssh csh examName $ ERegisterOccR examOccurrenceName + | otherwise -> hasWriteAccessTo $ CExamR tid ssh csh examName ERegisterR - registered <- for mUid $ existsBy . UniqueExamRegistration eId - mayRegister <- (== Authorized) <$> evalAccessDB (CExamR tid ssh csh examName ERegisterR) True + let occurrences = sortOn sortPred $ map (over _2 E.unValue) occurrencesRaw + where + sortPred (Entity _ ExamOccurrence{..}, registered') + = (Down $ registered' && not mayRegister, examOccurrenceStart, examOccurrenceRoom) lecturerInfoShown <- hasReadAccessTo $ CExamR tid ssh csh examn EEditR @@ -83,12 +89,18 @@ getEShowR tid ssh csh examn = do ] hasRegistration = any snd occurrences - + + mayRegister' <- fmap ((Map.!) . Map.fromList) . for (Nothing : map Just occurrences) $ \case + Nothing -> + fmap (Nothing, ) . hasWriteAccessTo $ CExamR tid ssh csh examName ERegisterR + Just (Entity occId ExamOccurrence{..}, _) -> + fmap (Just occId, ) . hasWriteAccessTo . CExamR tid ssh csh examName $ ERegisterOccR examOccurrenceName let examTimes = all (\(Entity _ ExamOccurrence{..}, _) -> Just examOccurrenceStart == examStart && examOccurrenceEnd == examEnd) occurrences - registerWidget - | Just isRegistered <- registered - , mayRegister = Just $ do + registerWidget mOcc + | isRegistered <- is _Just $ join registered + , examOccurrenceRule /= Just ExamRoomFifo || (isRegistered && not (any snd occurrences)) + , mayRegister' (entityKey <$> mOcc) = Just $ do (examRegisterForm, examRegisterEnctype) <- liftHandler . generateFormPost . buttonForm' $ bool [BtnExamRegister] [BtnExamDeregister] isRegistered [whamlet|

@@ -102,11 +114,37 @@ getEShowR tid ssh csh examn = do , formEncoding = examRegisterEnctype , formSubmit = FormNoSubmit } - | fromMaybe False registered = Just [whamlet|_{MsgExamRegistered}|] + | examOccurrenceRule == Just ExamRoomFifo + , Just (Entity occId ExamOccurrence{..}) <- mOcc + , isRegistered <- (== Just occId) $ examRegistrationOccurrence . entityVal =<< join registered + , mayRegister' (Just occId) = Just $ do + (examRegisterForm, examRegisterEnctype) <- liftHandler . generateFormPost . buttonForm' $ bool [BtnExamRegisterOccurrence] [BtnExamDeregister] isRegistered + wrapForm examRegisterForm def + { formAction = Just . SomeRoute . CExamR tid ssh csh examName $ ERegisterOccR examOccurrenceName + , formEncoding = examRegisterEnctype + , formSubmit = FormNoSubmit + } + | is _Nothing mOcc + , is _Nothing registered + = Just [whamlet|_{MsgExamLoginToRegister}|] + | is _Nothing mOcc + , isRegistered <- is _Just $ join registered + = Just + [whamlet| +

+ $if isRegistered + _{MsgExamRegistered} + $else + _{MsgExamNotRegistered} + $if mayRegister + ^{messageTooltip =<< messageI Info MsgExamRegisterForOccurrence} + |] | otherwise = Nothing showMaxPoints = any (has $ _entityVal . _examPartMaxPoints . _Just) examParts showAchievedPoints = not $ null results + showOccurrenceRegisterColumn = occurrenceAssignmentsShown || (mayRegister && examOccurrenceRule == Just ExamRoomFifo) + markUnregisteredOccurrences mOcc = occurrenceAssignmentsShown && hasRegistration && isn't _Just (registerWidget mOcc) let heading = prependCourseTitle tid ssh csh $ CI.original examName diff --git a/src/Handler/Utils/Form.hs b/src/Handler/Utils/Form.hs index 57c590204..197e3156b 100644 --- a/src/Handler/Utils/Form.hs +++ b/src/Handler/Utils/Form.hs @@ -557,6 +557,7 @@ examBonusRuleForm prev = multiActionA actions (fslI MsgExamBonusRule) $ classify data ExamOccurrenceRule' = ExamRoomSurname' | ExamRoomMatriculation' | ExamRoomRandom' + | ExamRoomFifo' deriving (Eq, Ord, Read, Show, Enum, Bounded, Generic, Typeable) instance Universe ExamOccurrenceRule' instance Finite ExamOccurrenceRule' @@ -569,6 +570,7 @@ classifyExamOccurrenceRule = \case ExamRoomSurname -> ExamRoomSurname' ExamRoomMatriculation -> ExamRoomMatriculation' ExamRoomRandom -> ExamRoomRandom' + ExamRoomFifo -> ExamRoomFifo' examOccurrenceRuleForm :: Maybe ExamOccurrenceRule -> AForm Handler ExamOccurrenceRule examOccurrenceRuleForm = fmap reverseClassify . areq (selectField optionsFinite) (fslI MsgExamOccurrenceRule) . fmap classifyExamOccurrenceRule @@ -577,6 +579,7 @@ examOccurrenceRuleForm = fmap reverseClassify . areq (selectField optionsFinite) ExamRoomSurname' -> ExamRoomSurname ExamRoomMatriculation' -> ExamRoomMatriculation ExamRoomRandom' -> ExamRoomRandom + ExamRoomFifo' -> ExamRoomFifo data ExamGradingRule' = ExamGradingKey' deriving (Eq, Ord, Read, Show, Enum, Bounded, Generic, Typeable) diff --git a/src/Model/Types/Exam.hs b/src/Model/Types/Exam.hs index afd09396e..40e6852d5 100644 --- a/src/Model/Types/Exam.hs +++ b/src/Model/Types/Exam.hs @@ -139,6 +139,7 @@ derivePersistFieldJSON ''ExamBonusRule data ExamOccurrenceRule = ExamRoomSurname | ExamRoomMatriculation | ExamRoomRandom + | ExamRoomFifo deriving (Show, Read, Eq, Ord, Generic, Typeable) deriveJSON defaultOptions { fieldLabelModifier = camelToPathPiece' 1 diff --git a/src/Model/Types/Security.hs b/src/Model/Types/Security.hs index a1df33f56..95f11c217 100644 --- a/src/Model/Types/Security.hs +++ b/src/Model/Types/Security.hs @@ -54,6 +54,8 @@ data AuthTag -- sortiert nach gewünschter Reihenfolge auf /authpreds, d.h. Prä | AuthCourseRegistered | AuthTutorialRegistered | AuthExamRegistered + | AuthExamOccurrenceRegistered + | AuthExamOccurrenceRegistration | AuthExamResult | AuthParticipant | AuthApplicant diff --git a/templates/exam-show.hamlet b/templates/exam-show.hamlet index 44ff63632..9d385d87b 100644 --- a/templates/exam-show.hamlet +++ b/templates/exam-show.hamlet @@ -84,7 +84,7 @@ $maybe desc <- examDescription \ ^{isVisible False}

_{classifyExamOccurrenceRule occurrenceRule} - $maybe registerWdgt <- registerWidget + $maybe registerWdgt <- registerWidget Nothing
_{MsgExamRegistration}
^{registerWdgt} @@ -103,31 +103,39 @@ $if not (null occurrences) _{MsgExamRoomName} \ ^{isVisible False} - $if occurrenceAssignmentsShown - - _{MsgExamRoomRegistered} - $if not occurrenceAssignmentsVisible - \ ^{isVisible False} _{MsgExamRoom} $if not examTimes _{MsgExamRoomTime} + $if showOccurrenceRegisterColumn + + $if examOccurrenceRule == Just ExamRoomFifo + _{MsgExamRoomRegistered} + $else + _{MsgExamRoomAssigned} + $if not occurrenceAssignmentsVisible + \ ^{isVisible False} _{MsgExamRoomDescription} - $forall (Entity _occId ExamOccurrence{examOccurrenceName, examOccurrenceRoom, examOccurrenceStart, examOccurrenceEnd, examOccurrenceDescription}, registered) <- occurrences - - $if occurrenceNamesShown - #{examOccurrenceName} - $if occurrenceAssignmentsShown - - $if registered - #{iconOK} - #{examOccurrenceRoom} - $if not examTimes - - ^{formatTimeRangeW SelFormatDateTime examOccurrenceStart examOccurrenceEnd} - - $maybe desc <- examOccurrenceDescription - #{desc} + $forall (occurrence, registered) <- occurrences + $with Entity _occId ExamOccurrence{examOccurrenceName, examOccurrenceRoom, examOccurrenceStart, examOccurrenceEnd, examOccurrenceDescription} <- occurrence + $with registerWdgt <- registerWidget (Just occurrence) + + $if occurrenceNamesShown + #{examOccurrenceName} + #{examOccurrenceRoom} + $if not examTimes + + ^{formatTimeRangeW SelFormatDateTime examOccurrenceStart examOccurrenceEnd} + $if showOccurrenceRegisterColumn + + $maybe registerWdgt' <- registerWdgt + ^{registerWdgt'} + $nothing + $if registered + #{iconOK} + + $maybe desc <- examOccurrenceDescription + #{desc} $if gradingShown && not (null examParts)
diff --git a/templates/widgets/tooltip.hamlet b/templates/widgets/tooltip.hamlet index 75e208b14..41de610a3 100644 --- a/templates/widgets/tooltip.hamlet +++ b/templates/widgets/tooltip.hamlet @@ -1,7 +1,7 @@ $newline never -
-
+ + -
+ ^{tooltip} From 83fa9c9c69a6e986c341e8283698f6c23ced2a90 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Sat, 11 Jan 2020 23:44:47 +0100 Subject: [PATCH 26/38] fix: improve exam occurrence ui --- messages/uniworx/de-de-formal.msg | 4 ++-- messages/uniworx/en-eu.msg | 2 +- models/exams.model | 2 +- src/Foundation.hs | 2 +- src/Handler/Exam/Form.hs | 4 ++-- src/Handler/Exam/Show.hs | 10 +++++----- src/Handler/Utils/Form.hs | 11 +++++++---- src/Model/Migration.hs | 7 +++++++ src/Model/Types/Exam.hs | 5 +++-- templates/exam-show.hamlet | 6 +++--- test/Database.hs | 2 +- 11 files changed, 33 insertions(+), 22 deletions(-) diff --git a/messages/uniworx/de-de-formal.msg b/messages/uniworx/de-de-formal.msg index fb215fdd6..ba9234bc4 100644 --- a/messages/uniworx/de-de-formal.msg +++ b/messages/uniworx/de-de-formal.msg @@ -1541,8 +1541,8 @@ ExamBonusRound: Bonus runden auf ExamBonusRoundNonPositive: Vielfaches, auf das gerundet werden soll, muss positiv und größer null sein ExamBonusRoundTip: Bonuspunkte werden kaufmännisch auf ein Vielfaches der angegeben Zahl gerundet. -ExamAutomaticOccurrenceAssignment: Automatische oder selbständige Termin- bzw. Raumzuteilung -ExamAutomaticOccurrenceAssignmentTip: Sollen Prüfungsteilnehmer automatisch auf die zur Verfügung stehenden Räume bzw. Termine verteilt werden oder sich selbstständig einen Raum bzw. Termin aussuchen dürfen? Manuelle Umverteilung bzw. vorheriges Festlegen von Zuteilungen einzelner Teilnehmer ist trotzdem möglich. +ExamAutomaticOccurrenceAssignment: Termin- bzw. Raumzuteilung +ExamAutomaticOccurrenceAssignmentTip: Sollen Prüfungsteilnehmer automatisch auf die zur Verfügung stehenden Räume bzw. Termine verteilt werden, sich selbstständig einen Raum bzw. Termin aussuchen dürfen oder manuell durch Kursverwalter zugeteilt werden? Manuelle Umverteilung bzw. vorheriges Festlegen von Zuteilungen einzelner Teilnehmer ist trotzdem möglich. ExamOccurrenceRule: Verfahren ExamOccurrenceRuleParticipant: Termin- bzw. Raumzuteilungsverfahren ExamRoomManual': Keine automatische bzw. selbstständige Zuteilung diff --git a/messages/uniworx/en-eu.msg b/messages/uniworx/en-eu.msg index a39d5fd77..de38ca936 100644 --- a/messages/uniworx/en-eu.msg +++ b/messages/uniworx/en-eu.msg @@ -1540,7 +1540,7 @@ ExamBonusRoundNonPositive: Rounding multiple must be positive and greater than z ExamBonusRoundTip: Bonus points are rounded commercially to a multiple of the given number ExamAutomaticOccurrenceAssignment: Selection of occurrences/rooms for/by participants -ExamAutomaticOccurrenceAssignmentTip: Should exam participants be distributed automatically among the configured occurrences/rooms? Should they instead be permitted to autonomously choose an occurrence/a room? Manipulation of the distribution and manually assigning participants remains possible. +ExamAutomaticOccurrenceAssignmentTip: Should exam participants be distributed automatically among the configured occurrences/rooms, should they instead be permitted to autonomously choose an occurrence/a room, or should they be assigned to occurrences/rooms manually by course administrators? Manipulation of the distribution and manually assigning participants remains possible. ExamOccurrenceRule: Procedure ExamOccurrenceRuleParticipant: Occurrence/room assignment procedure ExamRoomManual': No automatic or autonomous assignment diff --git a/models/exams.model b/models/exams.model index c23917bc7..5baa6e711 100644 --- a/models/exams.model +++ b/models/exams.model @@ -3,7 +3,7 @@ Exam name ExamName gradingRule ExamGradingRule Maybe bonusRule ExamBonusRule Maybe - occurrenceRule ExamOccurrenceRule Maybe + occurrenceRule ExamOccurrenceRule visibleFrom UTCTime Maybe registerFrom UTCTime Maybe registerTo UTCTime Maybe diff --git a/src/Foundation.hs b/src/Foundation.hs index dd390b7b8..07cd31a94 100644 --- a/src/Foundation.hs +++ b/src/Foundation.hs @@ -778,7 +778,7 @@ tagAccessPredicate AuthExamOccurrenceRegistration = APDB $ \_ route _ -> case ro E.&&. course E.^. CourseSchool E.==. E.val ssh E.&&. course E.^. CourseShorthand E.==. E.val csh E.&&. exam E.^. ExamName E.==. E.val examn - E.&&. exam E.^. ExamOccurrenceRule E.==. E.val (Just ExamRoomFifo) + E.&&. exam E.^. ExamOccurrenceRule E.==. E.val ExamRoomFifo guardMExceptT isOccurrenceRegistration (unauthorizedI MsgUnauthorizedExamOccurrenceRegistration) return Authorized r -> $unsupportedAuthPredicate AuthExamOccurrenceRegistration r diff --git a/src/Handler/Exam/Form.hs b/src/Handler/Exam/Form.hs index 1604a4207..5b8cc8723 100644 --- a/src/Handler/Exam/Form.hs +++ b/src/Handler/Exam/Form.hs @@ -39,7 +39,7 @@ data ExamForm = ExamForm , efPublicStatistics :: Bool , efGradingRule :: Maybe ExamGradingRule , efBonusRule :: Maybe ExamBonusRule - , efOccurrenceRule :: Maybe ExamOccurrenceRule + , efOccurrenceRule :: ExamOccurrenceRule , efCorrectors :: Set (Either UserEmail UserId) , efExamParts :: Set ExamPartForm } @@ -96,7 +96,7 @@ examForm template html = do <*> apopt checkBoxField (fslI MsgExamPublicStatistics & setTooltip MsgExamPublicStatisticsTip) (efPublicStatistics <$> template <|> Just True) <*> optionalActionA (examGradingRuleForm $ efGradingRule =<< template) (fslI MsgExamAutomaticGrading & setTooltip MsgExamAutomaticGradingTip) (is _Just . efGradingRule <$> template) <*> optionalActionA (examBonusRuleForm $ efBonusRule =<< template) (fslI MsgExamBonus) (is _Just . efBonusRule <$> template) - <*> optionalActionA (examOccurrenceRuleForm $ efOccurrenceRule =<< template) (fslI MsgExamAutomaticOccurrenceAssignment & setTooltip MsgExamAutomaticOccurrenceAssignmentTip) (is _Just . efOccurrenceRule <$> template) + <*> (examOccurrenceRuleForm $ efOccurrenceRule <$> template) <* aformSection MsgExamFormCorrection <*> examCorrectorsForm (efCorrectors <$> template) <* aformSection MsgExamFormParts diff --git a/src/Handler/Exam/Show.hs b/src/Handler/Exam/Show.hs index 138a50391..cc31d91f1 100644 --- a/src/Handler/Exam/Show.hs +++ b/src/Handler/Exam/Show.hs @@ -30,7 +30,7 @@ getEShowR tid ssh csh examn = do let gradingVisible = NTop (Just cTime) >= NTop examFinished gradingShown <- or2M (return gradingVisible) . hasReadAccessTo $ CExamR tid ssh csh examn EEditR - let occurrenceAssignmentsVisible = NTop (Just cTime) >= NTop examPublishOccurrenceAssignments || examOccurrenceRule == Just ExamRoomFifo + let occurrenceAssignmentsVisible = NTop (Just cTime) >= NTop examPublishOccurrenceAssignments || examOccurrenceRule == ExamRoomFifo occurrenceAssignmentsShown <- or2M (return occurrenceAssignmentsVisible) . hasReadAccessTo $ CExamR tid ssh csh examn EEditR examParts <- sortOn (view $ _entityVal . _examPartNumber) <$> selectList [ ExamPartExam ==. eId ] [ Asc ExamPartName ] @@ -60,7 +60,7 @@ getEShowR tid ssh csh examn = do registered <- for mUid $ getBy . UniqueExamRegistration eId mayRegister <- if - | examOccurrenceRule == Just ExamRoomFifo -> anyM occurrencesRaw $ \(Entity _ ExamOccurrence{..}, _) -> + | examOccurrenceRule == ExamRoomFifo -> anyM occurrencesRaw $ \(Entity _ ExamOccurrence{..}, _) -> hasWriteAccessTo . CExamR tid ssh csh examName $ ERegisterOccR examOccurrenceName | otherwise -> hasWriteAccessTo $ CExamR tid ssh csh examName ERegisterR @@ -99,7 +99,7 @@ getEShowR tid ssh csh examn = do let examTimes = all (\(Entity _ ExamOccurrence{..}, _) -> Just examOccurrenceStart == examStart && examOccurrenceEnd == examEnd) occurrences registerWidget mOcc | isRegistered <- is _Just $ join registered - , examOccurrenceRule /= Just ExamRoomFifo || (isRegistered && not (any snd occurrences)) + , examOccurrenceRule /= ExamRoomFifo || (isRegistered && not (any snd occurrences)) , mayRegister' (entityKey <$> mOcc) = Just $ do (examRegisterForm, examRegisterEnctype) <- liftHandler . generateFormPost . buttonForm' $ bool [BtnExamRegister] [BtnExamDeregister] isRegistered [whamlet| @@ -114,7 +114,7 @@ getEShowR tid ssh csh examn = do , formEncoding = examRegisterEnctype , formSubmit = FormNoSubmit } - | examOccurrenceRule == Just ExamRoomFifo + | examOccurrenceRule == ExamRoomFifo , Just (Entity occId ExamOccurrence{..}) <- mOcc , isRegistered <- (== Just occId) $ examRegistrationOccurrence . entityVal =<< join registered , mayRegister' (Just occId) = Just $ do @@ -143,7 +143,7 @@ getEShowR tid ssh csh examn = do showMaxPoints = any (has $ _entityVal . _examPartMaxPoints . _Just) examParts showAchievedPoints = not $ null results - showOccurrenceRegisterColumn = occurrenceAssignmentsShown || (mayRegister && examOccurrenceRule == Just ExamRoomFifo) + showOccurrenceRegisterColumn = occurrenceAssignmentsShown || (mayRegister && examOccurrenceRule == ExamRoomFifo) markUnregisteredOccurrences mOcc = occurrenceAssignmentsShown && hasRegistration && isn't _Just (registerWidget mOcc) let heading = prependCourseTitle tid ssh csh $ CI.original examName diff --git a/src/Handler/Utils/Form.hs b/src/Handler/Utils/Form.hs index 197e3156b..4bcf7e775 100644 --- a/src/Handler/Utils/Form.hs +++ b/src/Handler/Utils/Form.hs @@ -554,10 +554,11 @@ examBonusRuleForm prev = multiActionA actions (fslI MsgExamBonusRule) $ classify ) ] -data ExamOccurrenceRule' = ExamRoomSurname' +data ExamOccurrenceRule' = ExamRoomManual' + | ExamRoomFifo' + | ExamRoomSurname' | ExamRoomMatriculation' | ExamRoomRandom' - | ExamRoomFifo' deriving (Eq, Ord, Read, Show, Enum, Bounded, Generic, Typeable) instance Universe ExamOccurrenceRule' instance Finite ExamOccurrenceRule' @@ -567,19 +568,21 @@ embedRenderMessage ''UniWorX ''ExamOccurrenceRule' id classifyExamOccurrenceRule :: ExamOccurrenceRule -> ExamOccurrenceRule' classifyExamOccurrenceRule = \case + ExamRoomManual -> ExamRoomManual' ExamRoomSurname -> ExamRoomSurname' ExamRoomMatriculation -> ExamRoomMatriculation' ExamRoomRandom -> ExamRoomRandom' ExamRoomFifo -> ExamRoomFifo' examOccurrenceRuleForm :: Maybe ExamOccurrenceRule -> AForm Handler ExamOccurrenceRule -examOccurrenceRuleForm = fmap reverseClassify . areq (selectField optionsFinite) (fslI MsgExamOccurrenceRule) . fmap classifyExamOccurrenceRule +examOccurrenceRuleForm = fmap reverseClassify . areq (selectField optionsFinite) (fslI MsgExamAutomaticOccurrenceAssignment & setTooltip MsgExamAutomaticOccurrenceAssignmentTip) . fmap classifyExamOccurrenceRule where reverseClassify = \case + ExamRoomManual' -> ExamRoomManual + ExamRoomFifo' -> ExamRoomFifo ExamRoomSurname' -> ExamRoomSurname ExamRoomMatriculation' -> ExamRoomMatriculation ExamRoomRandom' -> ExamRoomRandom - ExamRoomFifo' -> ExamRoomFifo data ExamGradingRule' = ExamGradingKey' deriving (Eq, Ord, Read, Show, Enum, Bounded, Generic, Typeable) diff --git a/src/Model/Migration.hs b/src/Model/Migration.hs index 0a18babde..9859c9115 100644 --- a/src/Model/Migration.hs +++ b/src/Model/Migration.hs @@ -599,6 +599,13 @@ customMigrations = Map.fromListWith (>>) ALTER TABLE "study_features" DROP COLUMN "sub_field"; |] ) + , ( AppliedMigrationKey [migrationVersion|29.0.0|] [version|30.0.0|] + , whenM (tableExists "exam") $ + [executeQQ| + UPDATE "exam" SET "occurrence_rule" = #{ExamRoomManual} WHERE "occurrence_rule" IS NULL; + ALTER TABLE "exam" ALTER COLUMN "occurrence_rule" SET NOT NULL; + |] + ) ] diff --git a/src/Model/Types/Exam.hs b/src/Model/Types/Exam.hs index 40e6852d5..7e7ce52bc 100644 --- a/src/Model/Types/Exam.hs +++ b/src/Model/Types/Exam.hs @@ -136,10 +136,11 @@ deriveJSON defaultOptions } ''ExamBonusRule derivePersistFieldJSON ''ExamBonusRule -data ExamOccurrenceRule = ExamRoomSurname +data ExamOccurrenceRule = ExamRoomManual + | ExamRoomFifo + | ExamRoomSurname | ExamRoomMatriculation | ExamRoomRandom - | ExamRoomFifo deriving (Show, Read, Eq, Ord, Generic, Typeable) deriveJSON defaultOptions { fieldLabelModifier = camelToPathPiece' 1 diff --git a/templates/exam-show.hamlet b/templates/exam-show.hamlet index 9d385d87b..3652474ef 100644 --- a/templates/exam-show.hamlet +++ b/templates/exam-show.hamlet @@ -76,14 +76,14 @@ $maybe desc <- examDescription \ ^{isVisible False}
^{examBonusW bonusRule} - $maybe occurrenceRule <- examOccurrenceRule + $if examOccurrenceRule /= ExamRoomManual $if occurrenceAssignmentsShown
_{MsgExamOccurrenceRuleParticipant} $if not occurrenceAssignmentsVisible \ ^{isVisible False}
- _{classifyExamOccurrenceRule occurrenceRule} + _{classifyExamOccurrenceRule examOccurrenceRule} $maybe registerWdgt <- registerWidget Nothing
_{MsgExamRegistration}
^{registerWdgt} @@ -108,7 +108,7 @@ $if not (null occurrences) _{MsgExamRoomTime} $if showOccurrenceRegisterColumn - $if examOccurrenceRule == Just ExamRoomFifo + $if examOccurrenceRule == ExamRoomFifo _{MsgExamRoomRegistered} $else _{MsgExamRoomAssigned} diff --git a/test/Database.hs b/test/Database.hs index e037be9a6..8335ef6ac 100755 --- a/test/Database.hs +++ b/test/Database.hs @@ -504,7 +504,7 @@ fillDb = do , examName = "Klausur" , examGradingRule = Nothing , examBonusRule = Nothing - , examOccurrenceRule = Nothing + , examOccurrenceRule = ExamRoomManual , examVisibleFrom = Just now , examRegisterFrom = Just now , examRegisterTo = Just $ addUTCTime (14 * nominalDay) now From 727b89bf4b80b1f5c83e3f6eeebe5c9da7226596 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Sat, 11 Jan 2020 23:57:23 +0100 Subject: [PATCH 27/38] fix: improve labeling of button to switch exam occurrence --- messages/uniworx/de-de-formal.msg | 1 + messages/uniworx/en-eu.msg | 1 + src/Handler/Exam/Register.hs | 5 ++++- src/Handler/Exam/Show.hs | 2 +- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/messages/uniworx/de-de-formal.msg b/messages/uniworx/de-de-formal.msg index ba9234bc4..4ebfac7e6 100644 --- a/messages/uniworx/de-de-formal.msg +++ b/messages/uniworx/de-de-formal.msg @@ -13,6 +13,7 @@ BtnCourseApply: Zum Kurs bewerben BtnCourseRetractApplication: Bewerbung zum Kurs zurückziehen BtnExamRegister: Anmelden zur Prüfung BtnExamRegisterOccurrence: Anmelden zum Prüfungstermin/-raum +BtnExamSwitchOccurrence: Zu Prüfungstermin/-raum wechseln BtnExamDeregister: Von der Prüfung abmelden BtnHijack: Sitzung übernehmen BtnSave: Speichern diff --git a/messages/uniworx/en-eu.msg b/messages/uniworx/en-eu.msg index de38ca936..effb79604 100644 --- a/messages/uniworx/en-eu.msg +++ b/messages/uniworx/en-eu.msg @@ -13,6 +13,7 @@ BtnCourseApply: Apply for course BtnCourseRetractApplication: Retract application BtnExamRegister: Enrol for exam BtnExamRegisterOccurrence: Enrol for exam occurrence/room +BtnExamSwitchOccurrence: Switch to exam occurrence/room BtnExamDeregister: Leave exam BtnHijack: Hijack session BtnSave: Save diff --git a/src/Handler/Exam/Register.hs b/src/Handler/Exam/Register.hs index f040df069..744c76625 100644 --- a/src/Handler/Exam/Register.hs +++ b/src/Handler/Exam/Register.hs @@ -14,6 +14,7 @@ import Database.Persist.Sql (deleteWhereCount) -- Dedicated ExamRegistrationButton data ButtonExamRegister = BtnExamRegisterOccurrence + | BtnExamSwitchOccurrence | BtnExamRegister | BtnExamDeregister deriving (Enum, Bounded, Eq, Ord, Read, Show, Generic, Typeable) @@ -23,10 +24,12 @@ nullaryPathPiece ''ButtonExamRegister $ camelToPathPiece' 2 instance Button UniWorX ButtonExamRegister where btnClasses BtnExamRegisterOccurrence = [BCIsButton, BCPrimary] + btnClasses BtnExamSwitchOccurrence = [BCIsButton, BCPrimary] btnClasses BtnExamRegister = [BCIsButton, BCPrimary] btnClasses BtnExamDeregister = [BCIsButton, BCDanger] btnLabel BtnExamRegisterOccurrence = [whamlet|#{iconExamRegister True } _{MsgBtnExamRegisterOccurrence}|] + btnLabel BtnExamSwitchOccurrence = [whamlet|_{MsgBtnExamSwitchOccurrence}|] btnLabel BtnExamRegister = [whamlet|#{iconExamRegister True } _{MsgBtnExamRegister}|] btnLabel BtnExamDeregister = [whamlet|#{iconExamRegister False} _{MsgBtnExamDeregister}|] @@ -76,7 +79,7 @@ postERegisterOccR tid ssh csh examn occn = do audit $ TransactionExamDeregister eId uid addMessageIconI Success IconExamRegisterFalse $ MsgExamDeregisteredSuccess examn redirect $ CExamR tid ssh csh examn EShowR - BtnExamRegisterOccurrence -> do + btn | btn `elem` [BtnExamRegisterOccurrence, BtnExamSwitchOccurrence] -> do runDB $ do now <- liftIO getCurrentTime void $ upsertBy (UniqueExamRegistration eId uid) (ExamRegistration eId uid (Just occId) now) [ExamRegistrationOccurrence =. Just occId, ExamRegistrationTime =. now] diff --git a/src/Handler/Exam/Show.hs b/src/Handler/Exam/Show.hs index cc31d91f1..e072b9e71 100644 --- a/src/Handler/Exam/Show.hs +++ b/src/Handler/Exam/Show.hs @@ -118,7 +118,7 @@ getEShowR tid ssh csh examn = do , Just (Entity occId ExamOccurrence{..}) <- mOcc , isRegistered <- (== Just occId) $ examRegistrationOccurrence . entityVal =<< join registered , mayRegister' (Just occId) = Just $ do - (examRegisterForm, examRegisterEnctype) <- liftHandler . generateFormPost . buttonForm' $ bool [BtnExamRegisterOccurrence] [BtnExamDeregister] isRegistered + (examRegisterForm, examRegisterEnctype) <- liftHandler . generateFormPost . buttonForm' $ bool [bool BtnExamRegisterOccurrence BtnExamSwitchOccurrence . is _Just $ join registered] [BtnExamDeregister] isRegistered wrapForm examRegisterForm def { formAction = Just . SomeRoute . CExamR tid ssh csh examName $ ERegisterOccR examOccurrenceName , formEncoding = examRegisterEnctype From 5391ce0af730582bb7f6887e5b7760153c66eeb5 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Sun, 12 Jan 2020 12:05:28 +0100 Subject: [PATCH 28/38] chore(release): 10.3.0 --- CHANGELOG.md | 24 ++++++++++++++++++++++++ package-lock.json | 2 +- package.json | 2 +- package.yaml | 2 +- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 215e86d8a..67d9ff907 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,30 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [10.3.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v10.2.0...v10.3.0) (2020-01-12) + + +### Bug Fixes + +* fix app frontend test ([49bafe1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/49bafe1)) +* improve exam occurrence ui ([83fa9c9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/83fa9c9)) +* improve labeling of button to switch exam occurrence ([727b89b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/727b89b)) +* tweak debouncing & canceling ([6b51cc5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6b51cc5)) +* **async-table:** bind callback in updateTableFrom call ([cd3e72c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cd3e72c)) +* **util-registry:** fix initAll and tests ([2620fb2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2620fb2)) +* **util-registry:** start setup instances and not all active instances ([ddf94bf](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ddf94bf)) + + +### Features + +* support exam registration including room (ExamRoomFifo) ([14bb020](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/14bb020)) +* well known files ([068632b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/068632b)) +* **async-table:** no submit on locked inputs ([22b3780](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/22b3780)) +* **frontend:** split up util registry ([67e472f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/67e472f)) +* **util-registry:** more debug info for setup util instances ([00584f9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/00584f9)) + + + ## [10.2.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v10.1.0...v10.2.0) (2020-01-07) diff --git a/package-lock.json b/package-lock.json index f76bb83f5..5f51ce4a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "uni2work", - "version": "10.2.0", + "version": "10.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 3b247c56d..78b76b0ee 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uni2work", - "version": "10.2.0", + "version": "10.3.0", "description": "", "keywords": [], "author": "", diff --git a/package.yaml b/package.yaml index 9d8838372..77d7be724 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: uniworx -version: 10.2.0 +version: 10.3.0 dependencies: - base From 71e90a18178dcab90ee68519b15e44e51ff79a91 Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Tue, 7 Jan 2020 16:30:25 +0100 Subject: [PATCH 29/38] feat(hide-columns): add hider label th attr --- frontend/src/utils/hide-columns/hide-columns.js | 3 ++- messages/uniworx/de-de-formal.msg | 2 ++ messages/uniworx/en-eu.msg | 2 ++ src/Handler/Tutorial/List.hs | 3 ++- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/frontend/src/utils/hide-columns/hide-columns.js b/frontend/src/utils/hide-columns/hide-columns.js index 712de7d68..fa7122228 100644 --- a/frontend/src/utils/hide-columns/hide-columns.js +++ b/frontend/src/utils/hide-columns/hide-columns.js @@ -4,6 +4,7 @@ import './hide-columns.sass'; const HIDE_COLUMNS_CONTAINER_IDENT = 'uw-hide-columns'; const TABLE_HEADER_IDENT = 'uw-hide-column-header'; +const HIDE_COLUMNS_HIDER_LABEL = 'uw-hide-columns--hider-label'; const TABLE_UTILS_ATTR = 'table-utils'; const TABLE_UTILS_CONTAINER_SELECTOR = `[${TABLE_UTILS_ATTR}]`; @@ -80,7 +81,7 @@ export class HideColumns { const hiderContent = document.createElement('span'); hiderContent.classList.add('table-hider__label'); - hiderContent.innerHTML = th.innerText; + hiderContent.innerHTML = th.getAttribute(HIDE_COLUMNS_HIDER_LABEL) || th.innerText; hider.appendChild(hiderContent); this.addHeaderHider(th, hider); diff --git a/messages/uniworx/de-de-formal.msg b/messages/uniworx/de-de-formal.msg index 4ebfac7e6..81f167a74 100644 --- a/messages/uniworx/de-de-formal.msg +++ b/messages/uniworx/de-de-formal.msg @@ -1435,6 +1435,8 @@ TutorialRoomPlaceholder: Raum TutorialTutors: Tutoren TutorialTutorAlreadyAdded: Ein Tutor mit dieser E-Mail ist bereits für dieses Tutorium eingetragen +TutorialActionsHead: Aktionen + OccurrenceNoneScheduled: (Noch) keine planmäßigen Termine OccurrenceNoneExceptions: (Noch) keine Termin-Ausnahmen diff --git a/messages/uniworx/en-eu.msg b/messages/uniworx/en-eu.msg index effb79604..0651e5591 100644 --- a/messages/uniworx/en-eu.msg +++ b/messages/uniworx/en-eu.msg @@ -1433,6 +1433,8 @@ TutorialRoomPlaceholder: Room TutorialTutors: Tutors TutorialTutorAlreadyAdded: An user with this email address is already registered as tutor +TutorialActionsHead: Actions + OccurrenceNoneScheduled: No regular occurrences (yet) OccurrenceNoneExceptions: No exceptions (yet) diff --git a/src/Handler/Tutorial/List.hs b/src/Handler/Tutorial/List.hs index 11ae3dc41..dc936bd63 100644 --- a/src/Handler/Tutorial/List.hs +++ b/src/Handler/Tutorial/List.hs @@ -16,6 +16,7 @@ import qualified Data.CaseInsensitive as CI getCTutorialListR :: TermId -> SchoolId -> CourseShorthand -> Handler Html getCTutorialListR tid ssh csh = do Entity cid Course{..} <- runDB . getBy404 $ TermSchoolCourseShort tid ssh csh + MsgRenderer mr <- getMsgRenderer let tutorialDBTable = DBTable{..} @@ -51,7 +52,7 @@ getCTutorialListR tid ssh csh = do , sortable (Just "register-from") (i18nCell MsgTutorialRegisterFrom) $ \DBRow{ dbrOutput = (Entity _ Tutorial{..}, _) } -> maybeDateTimeCell tutorialRegisterFrom , sortable (Just "register-to") (i18nCell MsgTutorialRegisterTo) $ \DBRow{ dbrOutput = (Entity _ Tutorial{..}, _) } -> maybeDateTimeCell tutorialRegisterTo , sortable (Just "deregister-until") (i18nCell MsgTutorialDeregisterUntil) $ \DBRow{ dbrOutput = (Entity _ Tutorial{..}, _) } -> maybeDateTimeCell tutorialDeregisterUntil - , sortable Nothing mempty $ \DBRow{ dbrOutput = (Entity _ Tutorial{..}, _) } -> cell $ do + , sortable Nothing (mempty & cellAttrs <>~ pure ("uw-hide-columns--hider-label", mr MsgTutorialActionsHead)) $ \DBRow{ dbrOutput = (Entity _ Tutorial{..}, _) } -> cell $ do linkButton mempty [whamlet|_{MsgTutorialEdit}|] [BCIsButton] . SomeRoute $ CTutorialR tid ssh csh tutorialName TEditR linkButton mempty [whamlet|_{MsgTutorialDelete}|] [BCIsButton, BCDanger] . SomeRoute $ CTutorialR tid ssh csh tutorialName TDeleteR ] From ccafd955b9b9659d05e34aadd0bd8fdc15ac44d9 Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Tue, 7 Jan 2020 16:47:15 +0100 Subject: [PATCH 30/38] feat(hide-columns): add hider labels for material list --- src/Handler/Material.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Handler/Material.hs b/src/Handler/Material.hs index 55ed2afa0..e080ba0b9 100644 --- a/src/Handler/Material.hs +++ b/src/Handler/Material.hs @@ -104,6 +104,7 @@ getMaterialListR tid ssh csh = do now <- liftIO getCurrentTime seeAllModificationTimestamps <- hasWriteAccessTo $ CourseR tid ssh csh MaterialNewR -- ordinary users should not see modification dates older than visibility + MsgRenderer mr <- getMsgRenderer table <- runDB $ do cid <- getKeyBy404 $ TermSchoolCourseShort tid ssh csh let row2material = view $ _dbrOutput . _1 . _entityVal @@ -127,9 +128,9 @@ getMaterialListR tid ssh csh = do $ foldMap (textCell . CI.original) . materialType . row2material , sortable (Just "name") (i18nCell MsgMaterialName) $ liftA2 anchorCell matLink toWgt . materialName . row2material - , sortable (toNothingS "description") mempty + , sortable (toNothingS "description") (mempty & cellAttrs <>~ pure ("uw-hide-columns--hider-label", mr MsgMaterialDescription)) $ foldMap modalCell . materialDescription . row2material - , sortable (toNothingS "zip-archive") mempty + , sortable (toNothingS "zip-archive") (mempty & cellAttrs <>~ pure ("uw-hide-columns--hider-label", mr MsgMaterialFiles)) $ \DBRow{ dbrOutput = (Entity _ Material{..}, E.Value fileNum) } -> if | fileNum == 0 -> mempty | otherwise -> fileCell $ filesLink materialName From 03e4ac1ccac94dc815b9341fbd547ee548d5f839 Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Tue, 7 Jan 2020 17:48:23 +0100 Subject: [PATCH 31/38] feat(hide-columns): add hider labels for tutorial list on course page --- src/Handler/Course/Show.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Handler/Course/Show.hs b/src/Handler/Course/Show.hs index ccc97291f..fa0951c5f 100644 --- a/src/Handler/Course/Show.hs +++ b/src/Handler/Course/Show.hs @@ -113,7 +113,9 @@ getCShowR tid ssh csh = do | otherwise -> return . modal $(widgetFile "course/login-to-register") . Left . SomeRoute $ AuthR LoginR registrationOpen <- hasWriteAccessTo $ CourseR tid ssh csh CRegisterR - + + MsgRenderer mr <- getMsgRenderer + let tutorialDBTable = DBTable{..} where @@ -151,7 +153,7 @@ getCShowR tid ssh csh = do E.where_ $ participant E.^. TutorialParticipantTutorial E.==. E.val tutid in return $ E.val tutorialCapacity' E.-. numParticipants return . toWidget $ tshow freeCapacity - , sortable Nothing mempty $ \DBRow{ dbrOutput = Entity tutId Tutorial{..} } -> sqlCell $ do + , sortable Nothing (mempty & cellAttrs <>~ pure ("uw-hide-columns--hider-label", mr MsgTutorialActionsHead)) $ \DBRow{ dbrOutput = Entity tutId Tutorial{..} } -> sqlCell $ do mayRegister <- (== Authorized) <$> evalAccessDB (CTutorialR tid ssh csh tutorialName TRegisterR) True isRegistered <- case mbAid of Nothing -> return False From eba58d83a04009f712f46fb2a4b123081b8ead84 Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Tue, 7 Jan 2020 18:37:10 +0100 Subject: [PATCH 32/38] feat(hide-columns): add more hider labels --- messages/uniworx/de-de-formal.msg | 4 ++-- messages/uniworx/en-eu.msg | 4 ++-- src/Handler/Course/Show.hs | 2 +- src/Handler/Tutorial/List.hs | 2 +- src/Handler/Users.hs | 3 ++- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/messages/uniworx/de-de-formal.msg b/messages/uniworx/de-de-formal.msg index 81f167a74..13d8acbbc 100644 --- a/messages/uniworx/de-de-formal.msg +++ b/messages/uniworx/de-de-formal.msg @@ -1435,8 +1435,6 @@ TutorialRoomPlaceholder: Raum TutorialTutors: Tutoren TutorialTutorAlreadyAdded: Ein Tutor mit dieser E-Mail ist bereits für dieses Tutorium eingetragen -TutorialActionsHead: Aktionen - OccurrenceNoneScheduled: (Noch) keine planmäßigen Termine OccurrenceNoneExceptions: (Noch) keine Termin-Ausnahmen @@ -1663,6 +1661,8 @@ ExamUserMarkedSynchronised n@Int: #{n} #{pluralDE n "Prüfungsleistung" "Prüfun ExamOfficeExamUsersHeading: Prüfungsleistungen +ActionsHead: Aktionen + CsvFile: CSV-Datei CsvImport: CSV-Import CsvExport: CSV-Export diff --git a/messages/uniworx/en-eu.msg b/messages/uniworx/en-eu.msg index 0651e5591..b49b918f8 100644 --- a/messages/uniworx/en-eu.msg +++ b/messages/uniworx/en-eu.msg @@ -1433,8 +1433,6 @@ TutorialRoomPlaceholder: Room TutorialTutors: Tutors TutorialTutorAlreadyAdded: An user with this email address is already registered as tutor -TutorialActionsHead: Actions - OccurrenceNoneScheduled: No regular occurrences (yet) OccurrenceNoneExceptions: No exceptions (yet) @@ -1661,6 +1659,8 @@ ExamUserMarkedSynchronised n: Successfully marked #{n} #{pluralEN n "exam achiev ExamOfficeExamUsersHeading: Exam achievements +ActionsHead: Actions + CsvFile: CSV file CsvImport: CSV import CsvExport: CSV export diff --git a/src/Handler/Course/Show.hs b/src/Handler/Course/Show.hs index fa0951c5f..278df79e8 100644 --- a/src/Handler/Course/Show.hs +++ b/src/Handler/Course/Show.hs @@ -153,7 +153,7 @@ getCShowR tid ssh csh = do E.where_ $ participant E.^. TutorialParticipantTutorial E.==. E.val tutid in return $ E.val tutorialCapacity' E.-. numParticipants return . toWidget $ tshow freeCapacity - , sortable Nothing (mempty & cellAttrs <>~ pure ("uw-hide-columns--hider-label", mr MsgTutorialActionsHead)) $ \DBRow{ dbrOutput = Entity tutId Tutorial{..} } -> sqlCell $ do + , sortable Nothing (mempty & cellAttrs <>~ pure ("uw-hide-columns--hider-label", mr MsgActionsHead)) $ \DBRow{ dbrOutput = Entity tutId Tutorial{..} } -> sqlCell $ do mayRegister <- (== Authorized) <$> evalAccessDB (CTutorialR tid ssh csh tutorialName TRegisterR) True isRegistered <- case mbAid of Nothing -> return False diff --git a/src/Handler/Tutorial/List.hs b/src/Handler/Tutorial/List.hs index dc936bd63..67094db9f 100644 --- a/src/Handler/Tutorial/List.hs +++ b/src/Handler/Tutorial/List.hs @@ -52,7 +52,7 @@ getCTutorialListR tid ssh csh = do , sortable (Just "register-from") (i18nCell MsgTutorialRegisterFrom) $ \DBRow{ dbrOutput = (Entity _ Tutorial{..}, _) } -> maybeDateTimeCell tutorialRegisterFrom , sortable (Just "register-to") (i18nCell MsgTutorialRegisterTo) $ \DBRow{ dbrOutput = (Entity _ Tutorial{..}, _) } -> maybeDateTimeCell tutorialRegisterTo , sortable (Just "deregister-until") (i18nCell MsgTutorialDeregisterUntil) $ \DBRow{ dbrOutput = (Entity _ Tutorial{..}, _) } -> maybeDateTimeCell tutorialDeregisterUntil - , sortable Nothing (mempty & cellAttrs <>~ pure ("uw-hide-columns--hider-label", mr MsgTutorialActionsHead)) $ \DBRow{ dbrOutput = (Entity _ Tutorial{..}, _) } -> cell $ do + , sortable Nothing (mempty & cellAttrs <>~ pure ("uw-hide-columns--hider-label", mr MsgActionsHead)) $ \DBRow{ dbrOutput = (Entity _ Tutorial{..}, _) } -> cell $ do linkButton mempty [whamlet|_{MsgTutorialEdit}|] [BCIsButton] . SomeRoute $ CTutorialR tid ssh csh tutorialName TEditR linkButton mempty [whamlet|_{MsgTutorialDelete}|] [BCIsButton, BCDanger] . SomeRoute $ CTutorialR tid ssh csh tutorialName TDeleteR ] diff --git a/src/Handler/Users.hs b/src/Handler/Users.hs index a803af4df..2a580d03f 100644 --- a/src/Handler/Users.hs +++ b/src/Handler/Users.hs @@ -71,6 +71,7 @@ instance Button UniWorX AllUsersAction where getUsersR, postUsersR :: Handler Html getUsersR = postUsersR postUsersR = do + MsgRenderer mr <- getMsgRenderer let dbtColonnade = mconcat $ [ dbRow @@ -100,7 +101,7 @@ postUsersR = do $forall (E.Value sh) <- schools
  • #{sh} |] - , sortable Nothing mempty $ \inp@DBRow{ dbrOutput = Entity uid _ } -> FormCell + , sortable Nothing (mempty & cellAttrs <>~ pure ("hide-columns--hider-label", mr MsgActionsHead)) $ \inp@DBRow{ dbrOutput = Entity uid _ } -> FormCell { formCellAttrs = [] , formCellLens = id , formCellContents = do From b03c10f09810796f43d313dd17b15f5c39c8e5a3 Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Wed, 8 Jan 2020 17:08:53 +0100 Subject: [PATCH 33/38] feat(hide-columns): opt-out on select columns --- frontend/src/utils/hide-columns/hide-columns.js | 3 ++- src/Handler/Utils/Table/Pagination.hs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/utils/hide-columns/hide-columns.js b/frontend/src/utils/hide-columns/hide-columns.js index fa7122228..71a70028b 100644 --- a/frontend/src/utils/hide-columns/hide-columns.js +++ b/frontend/src/utils/hide-columns/hide-columns.js @@ -5,6 +5,7 @@ import './hide-columns.sass'; const HIDE_COLUMNS_CONTAINER_IDENT = 'uw-hide-columns'; const TABLE_HEADER_IDENT = 'uw-hide-column-header'; const HIDE_COLUMNS_HIDER_LABEL = 'uw-hide-columns--hider-label'; +const HIDE_COLUMNS_NO_HIDE = 'uw-hide-columns--no-hide'; const TABLE_UTILS_ATTR = 'table-utils'; const TABLE_UTILS_CONTAINER_SELECTOR = `[${TABLE_UTILS_ATTR}]`; @@ -67,7 +68,7 @@ export class HideColumns { hideColumnsContainer.insertBefore(this._tableUtilContainer, tableContainer); } - this._element.querySelectorAll('th').forEach(th => this.setupHideButton(th)); + [...this._element.querySelectorAll('th')].filter(th => !th.hasAttribute(HIDE_COLUMNS_NO_HIDE)).forEach(th => this.setupHideButton(th)); } setupHideButton(th) { diff --git a/src/Handler/Utils/Table/Pagination.hs b/src/Handler/Utils/Table/Pagination.hs index 89a6eeec1..06164debb 100644 --- a/src/Handler/Utils/Table/Pagination.hs +++ b/src/Handler/Utils/Table/Pagination.hs @@ -1382,7 +1382,7 @@ dbSelect :: forall x h r i a. (Headedness h, Monoid' x) -> (DBRow r -> MForm (HandlerFor UniWorX) i) -> Colonnade h (DBRow r) (DBCell (MForm (HandlerFor UniWorX)) x) -- dbSelect resLens selLens genIndex = Colonnade.singleton (headednessPure $ i18nCell MsgSelectColumn) $ formCell resLens genIndex genForm -dbSelect resLens selLens genIndex = Colonnade.singleton (headednessPure $ mempty) $ formCell resLens genIndex genForm +dbSelect resLens selLens genIndex = Colonnade.singleton (headednessPure $ mempty & cellAttrs <>~ pure ("uw-hide-columns--no-hide","")) $ formCell resLens genIndex genForm where genForm _ mkUnique = do (selResult, selWidget) <- mreq checkBoxField (fsUniq mkUnique "select") (Just False) From 03bcf56487c5bf363bf6d9f595b735208e3fa87f Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Wed, 8 Jan 2020 17:35:34 +0100 Subject: [PATCH 34/38] fix(hide-columns): no hide-columns in tail.datetime --- frontend/src/utils/hide-columns/hide-columns.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/utils/hide-columns/hide-columns.js b/frontend/src/utils/hide-columns/hide-columns.js index 71a70028b..af01de392 100644 --- a/frontend/src/utils/hide-columns/hide-columns.js +++ b/frontend/src/utils/hide-columns/hide-columns.js @@ -46,8 +46,8 @@ export class HideColumns { throw new Error('Hide Columns utility cannot be setup without an element!'); } - // do not provide hide-column ability in tables inside modals or async forms with response - if (element.closest('[uw-modal], .async-form__response')) { + // do not provide hide-column ability in tables inside modals, async forms with response or tail.datetime instances + if (element.closest('[uw-modal], .async-form__response, .tail-datetime-calendar')) { return false; } From 9053b878c98fcc8cccd5c1a6b241eb36c372f087 Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Wed, 8 Jan 2020 21:24:13 +0100 Subject: [PATCH 35/38] fix(hide-columns): bump storage manager minor version --- frontend/src/utils/hide-columns/hide-columns.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/utils/hide-columns/hide-columns.js b/frontend/src/utils/hide-columns/hide-columns.js index af01de392..0c0187f23 100644 --- a/frontend/src/utils/hide-columns/hide-columns.js +++ b/frontend/src/utils/hide-columns/hide-columns.js @@ -23,7 +23,7 @@ const CELL_ORIGINAL_COLSPAN = 'uw-hide-column-original-colspan'; }) export class HideColumns { - _storageManager = new StorageManager('HIDE_COLUMNS', '1.0.0', { location: LOCATION.LOCAL }); + _storageManager = new StorageManager('HIDE_COLUMNS', '1.1.0', { location: LOCATION.LOCAL }); _element; _elementWrapper; From 6c05a8f09fdd0d1679c3c5b33277829901d2e8c0 Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Tue, 7 Jan 2020 16:30:25 +0100 Subject: [PATCH 36/38] feat(hide-columns): add hider label th attr --- messages/uniworx/de-de-formal.msg | 2 ++ messages/uniworx/en-eu.msg | 2 ++ src/Handler/Tutorial/List.hs | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/messages/uniworx/de-de-formal.msg b/messages/uniworx/de-de-formal.msg index 13d8acbbc..bd534ed7e 100644 --- a/messages/uniworx/de-de-formal.msg +++ b/messages/uniworx/de-de-formal.msg @@ -1435,6 +1435,8 @@ TutorialRoomPlaceholder: Raum TutorialTutors: Tutoren TutorialTutorAlreadyAdded: Ein Tutor mit dieser E-Mail ist bereits für dieses Tutorium eingetragen +TutorialActionsHead: Aktionen + OccurrenceNoneScheduled: (Noch) keine planmäßigen Termine OccurrenceNoneExceptions: (Noch) keine Termin-Ausnahmen diff --git a/messages/uniworx/en-eu.msg b/messages/uniworx/en-eu.msg index b49b918f8..dd20a08ff 100644 --- a/messages/uniworx/en-eu.msg +++ b/messages/uniworx/en-eu.msg @@ -1433,6 +1433,8 @@ TutorialRoomPlaceholder: Room TutorialTutors: Tutors TutorialTutorAlreadyAdded: An user with this email address is already registered as tutor +TutorialActionsHead: Actions + OccurrenceNoneScheduled: No regular occurrences (yet) OccurrenceNoneExceptions: No exceptions (yet) diff --git a/src/Handler/Tutorial/List.hs b/src/Handler/Tutorial/List.hs index 67094db9f..dc936bd63 100644 --- a/src/Handler/Tutorial/List.hs +++ b/src/Handler/Tutorial/List.hs @@ -52,7 +52,7 @@ getCTutorialListR tid ssh csh = do , sortable (Just "register-from") (i18nCell MsgTutorialRegisterFrom) $ \DBRow{ dbrOutput = (Entity _ Tutorial{..}, _) } -> maybeDateTimeCell tutorialRegisterFrom , sortable (Just "register-to") (i18nCell MsgTutorialRegisterTo) $ \DBRow{ dbrOutput = (Entity _ Tutorial{..}, _) } -> maybeDateTimeCell tutorialRegisterTo , sortable (Just "deregister-until") (i18nCell MsgTutorialDeregisterUntil) $ \DBRow{ dbrOutput = (Entity _ Tutorial{..}, _) } -> maybeDateTimeCell tutorialDeregisterUntil - , sortable Nothing (mempty & cellAttrs <>~ pure ("uw-hide-columns--hider-label", mr MsgActionsHead)) $ \DBRow{ dbrOutput = (Entity _ Tutorial{..}, _) } -> cell $ do + , sortable Nothing (mempty & cellAttrs <>~ pure ("uw-hide-columns--hider-label", mr MsgTutorialActionsHead)) $ \DBRow{ dbrOutput = (Entity _ Tutorial{..}, _) } -> cell $ do linkButton mempty [whamlet|_{MsgTutorialEdit}|] [BCIsButton] . SomeRoute $ CTutorialR tid ssh csh tutorialName TEditR linkButton mempty [whamlet|_{MsgTutorialDelete}|] [BCIsButton, BCDanger] . SomeRoute $ CTutorialR tid ssh csh tutorialName TDeleteR ] From 3553df23ca75eab3c324d9b67aa11ce01212c230 Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Tue, 7 Jan 2020 17:48:23 +0100 Subject: [PATCH 37/38] feat(hide-columns): add hider labels for tutorial list on course page --- src/Handler/Course/Show.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Handler/Course/Show.hs b/src/Handler/Course/Show.hs index 278df79e8..fa0951c5f 100644 --- a/src/Handler/Course/Show.hs +++ b/src/Handler/Course/Show.hs @@ -153,7 +153,7 @@ getCShowR tid ssh csh = do E.where_ $ participant E.^. TutorialParticipantTutorial E.==. E.val tutid in return $ E.val tutorialCapacity' E.-. numParticipants return . toWidget $ tshow freeCapacity - , sortable Nothing (mempty & cellAttrs <>~ pure ("uw-hide-columns--hider-label", mr MsgActionsHead)) $ \DBRow{ dbrOutput = Entity tutId Tutorial{..} } -> sqlCell $ do + , sortable Nothing (mempty & cellAttrs <>~ pure ("uw-hide-columns--hider-label", mr MsgTutorialActionsHead)) $ \DBRow{ dbrOutput = Entity tutId Tutorial{..} } -> sqlCell $ do mayRegister <- (== Authorized) <$> evalAccessDB (CTutorialR tid ssh csh tutorialName TRegisterR) True isRegistered <- case mbAid of Nothing -> return False From 555c4aebebbd050ec00ad0b4369c196502301d7a Mon Sep 17 00:00:00 2001 From: Sarah Vaupel Date: Tue, 7 Jan 2020 18:37:10 +0100 Subject: [PATCH 38/38] feat(hide-columns): add more hider labels --- messages/uniworx/de-de-formal.msg | 2 -- messages/uniworx/en-eu.msg | 2 -- src/Handler/Course/Show.hs | 2 +- src/Handler/Tutorial/List.hs | 2 +- 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/messages/uniworx/de-de-formal.msg b/messages/uniworx/de-de-formal.msg index bd534ed7e..13d8acbbc 100644 --- a/messages/uniworx/de-de-formal.msg +++ b/messages/uniworx/de-de-formal.msg @@ -1435,8 +1435,6 @@ TutorialRoomPlaceholder: Raum TutorialTutors: Tutoren TutorialTutorAlreadyAdded: Ein Tutor mit dieser E-Mail ist bereits für dieses Tutorium eingetragen -TutorialActionsHead: Aktionen - OccurrenceNoneScheduled: (Noch) keine planmäßigen Termine OccurrenceNoneExceptions: (Noch) keine Termin-Ausnahmen diff --git a/messages/uniworx/en-eu.msg b/messages/uniworx/en-eu.msg index dd20a08ff..b49b918f8 100644 --- a/messages/uniworx/en-eu.msg +++ b/messages/uniworx/en-eu.msg @@ -1433,8 +1433,6 @@ TutorialRoomPlaceholder: Room TutorialTutors: Tutors TutorialTutorAlreadyAdded: An user with this email address is already registered as tutor -TutorialActionsHead: Actions - OccurrenceNoneScheduled: No regular occurrences (yet) OccurrenceNoneExceptions: No exceptions (yet) diff --git a/src/Handler/Course/Show.hs b/src/Handler/Course/Show.hs index fa0951c5f..278df79e8 100644 --- a/src/Handler/Course/Show.hs +++ b/src/Handler/Course/Show.hs @@ -153,7 +153,7 @@ getCShowR tid ssh csh = do E.where_ $ participant E.^. TutorialParticipantTutorial E.==. E.val tutid in return $ E.val tutorialCapacity' E.-. numParticipants return . toWidget $ tshow freeCapacity - , sortable Nothing (mempty & cellAttrs <>~ pure ("uw-hide-columns--hider-label", mr MsgTutorialActionsHead)) $ \DBRow{ dbrOutput = Entity tutId Tutorial{..} } -> sqlCell $ do + , sortable Nothing (mempty & cellAttrs <>~ pure ("uw-hide-columns--hider-label", mr MsgActionsHead)) $ \DBRow{ dbrOutput = Entity tutId Tutorial{..} } -> sqlCell $ do mayRegister <- (== Authorized) <$> evalAccessDB (CTutorialR tid ssh csh tutorialName TRegisterR) True isRegistered <- case mbAid of Nothing -> return False diff --git a/src/Handler/Tutorial/List.hs b/src/Handler/Tutorial/List.hs index dc936bd63..67094db9f 100644 --- a/src/Handler/Tutorial/List.hs +++ b/src/Handler/Tutorial/List.hs @@ -52,7 +52,7 @@ getCTutorialListR tid ssh csh = do , sortable (Just "register-from") (i18nCell MsgTutorialRegisterFrom) $ \DBRow{ dbrOutput = (Entity _ Tutorial{..}, _) } -> maybeDateTimeCell tutorialRegisterFrom , sortable (Just "register-to") (i18nCell MsgTutorialRegisterTo) $ \DBRow{ dbrOutput = (Entity _ Tutorial{..}, _) } -> maybeDateTimeCell tutorialRegisterTo , sortable (Just "deregister-until") (i18nCell MsgTutorialDeregisterUntil) $ \DBRow{ dbrOutput = (Entity _ Tutorial{..}, _) } -> maybeDateTimeCell tutorialDeregisterUntil - , sortable Nothing (mempty & cellAttrs <>~ pure ("uw-hide-columns--hider-label", mr MsgTutorialActionsHead)) $ \DBRow{ dbrOutput = (Entity _ Tutorial{..}, _) } -> cell $ do + , sortable Nothing (mempty & cellAttrs <>~ pure ("uw-hide-columns--hider-label", mr MsgActionsHead)) $ \DBRow{ dbrOutput = (Entity _ Tutorial{..}, _) } -> cell $ do linkButton mempty [whamlet|_{MsgTutorialEdit}|] [BCIsButton] . SomeRoute $ CTutorialR tid ssh csh tutorialName TEditR linkButton mempty [whamlet|_{MsgTutorialDelete}|] [BCIsButton, BCDanger] . SomeRoute $ CTutorialR tid ssh csh tutorialName TDeleteR ]