diff --git a/src/account/create.html b/src/account/create.html new file mode 100644 index 0000000..fa24bc2 --- /dev/null +++ b/src/account/create.html @@ -0,0 +1,28 @@ + + + + Create Account - Caddy + {{include "/includes/account-head.html"}} + + + +
+
+ +
+
+

Create Account

+ or log in +
+ + + + +

+ We will send you an email to verify your account. +

+
+
+
+ + \ No newline at end of file diff --git a/src/account/index.html b/src/account/index.html new file mode 100644 index 0000000..966985a --- /dev/null +++ b/src/account/index.html @@ -0,0 +1,32 @@ + + + + Dashboard - Caddy + {{include "/includes/account-head.html"}} + + + + +
+ {{include "/includes/account-nav.html"}} +
+
+

Your packagesRegister package

+ + + + + + + + + +
Import pathListedAvailableDownloads
+ +
+
+
+ + \ No newline at end of file diff --git a/src/account/login.html b/src/account/login.html new file mode 100644 index 0000000..98c7cfd --- /dev/null +++ b/src/account/login.html @@ -0,0 +1,25 @@ + + + + Log In - Caddy + {{include "/includes/account-head.html"}} + + + +
+
+ +
+
+

Log In

+ or create account +
+ + + + Reset password +
+
+
+ + \ No newline at end of file diff --git a/src/account/logout.html b/src/account/logout.html new file mode 100644 index 0000000..09fe3f1 --- /dev/null +++ b/src/account/logout.html @@ -0,0 +1,11 @@ + + + + Logout - Caddy + {{include "/includes/account-head.html"}} + + + + Logging out... + + \ No newline at end of file diff --git a/src/account/register-package.html b/src/account/register-package.html new file mode 100644 index 0000000..ee9ae17 --- /dev/null +++ b/src/account/register-package.html @@ -0,0 +1,42 @@ + + + + Register Package - Caddy + {{include "/includes/account-head.html"}} + + + +
+ {{include "/includes/account-nav.html"}} +
+
+
+
+
+ +
+ You must use semantic import versioning. Basically, this means if your module is at v2 or higher the import path must be suffixed with /vN (where N is the major version number). +
+
+
+ +
+ Optional. Any version string recognized by Go module tooling is acceptable. +
+
+
+ +
+
+
+
+
+
+ + \ No newline at end of file diff --git a/src/account/reset-password.html b/src/account/reset-password.html new file mode 100644 index 0000000..72a4aa7 --- /dev/null +++ b/src/account/reset-password.html @@ -0,0 +1,38 @@ + + + + Reset Password - Caddy + {{include "/includes/account-head.html"}} + + + +
+
+ +
+
+

Reset Password

+ I already have a reset token +
+ + +
+
+
+
+
+ +
+
+

Reset Password

+ I still need a reset token +
+ + + + +
+
+
+ + \ No newline at end of file diff --git a/src/account/verify.html b/src/account/verify.html new file mode 100644 index 0000000..1977ede --- /dev/null +++ b/src/account/verify.html @@ -0,0 +1,24 @@ + + + + Confirm Account - Caddy + {{include "/includes/account-head.html"}} + + + +
+
+ +
+
+

Confirm Account

+ or log in +
+ + + +
+
+
+ + \ No newline at end of file diff --git a/src/download.html b/src/download.html new file mode 100644 index 0000000..e35b62b --- /dev/null +++ b/src/download.html @@ -0,0 +1,88 @@ + + + + Download Caddy + {{include "/includes/head.html"}} + + + + + + +
+
+
+ +
+ {{include "/includes/header-nav.html"}} +
+ +
+
+
+ Platform: + +
+
+
+
+ Additional packages: 0 +
+
+
+ Download +
+
+ +
+ Always comes with all standard Caddy modules. +

+ Optionally select additional packages to include in your build: ⚠️ Only choose plugins you need and trust +
+ +
+ + + + + + + +
PackageVersionModules
+
+ +
+ {{include "/includes/footer.html"}} + + diff --git a/src/includes/account-head.html b/src/includes/account-head.html new file mode 100644 index 0000000..c95ac06 --- /dev/null +++ b/src/includes/account-head.html @@ -0,0 +1,5 @@ +{{include "/includes/head.html"}} + + + + \ No newline at end of file diff --git a/src/includes/account-nav.html b/src/includes/account-nav.html new file mode 100644 index 0000000..60b2d6c --- /dev/null +++ b/src/includes/account-nav.html @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/src/includes/docs-details.html b/src/includes/docs-details.html index bc80e01..4125c81 100644 --- a/src/includes/docs-details.html +++ b/src/includes/docs-details.html @@ -1,3 +1,11 @@ +
+
+ This module does not come with Caddy. It can be added by using xcaddy or our download page. Non-standard modules may be developed by the community and are not officially endorsed or maintained by the Caddy project. The documentation is shown here only as a courtesy. +

+ Module repository: +
+
+

Description

diff --git a/src/includes/docs-header.html b/src/includes/docs-header.html index c0b252c..c12d052 100644 --- a/src/includes/docs-header.html +++ b/src/includes/docs-header.html @@ -1,4 +1,3 @@ -{{include "/includes/v1-banner.html"}}
diff --git a/src/includes/docs-nav.html b/src/includes/docs-nav.html index 5e2765b..720922a 100644 --- a/src/includes/docs-nav.html +++ b/src/includes/docs-nav.html @@ -56,5 +56,7 @@
  • Module Namespaces
  • +
    +
  • v1 Docs
  • \ No newline at end of file diff --git a/src/includes/header-nav.html b/src/includes/header-nav.html index 921ba1a..dcd7643 100644 --- a/src/includes/header-nav.html +++ b/src/includes/header-nav.html @@ -2,10 +2,10 @@ \ No newline at end of file diff --git a/src/includes/v1-banner.html b/src/includes/v1-banner.html deleted file mode 100644 index 584d340..0000000 --- a/src/includes/v1-banner.html +++ /dev/null @@ -1 +0,0 @@ -This page is about Caddy 2. If you still need v1 docs for a limited time, click here. \ No newline at end of file diff --git a/src/index.html b/src/index.html index 32da431..ceb15ba 100644 --- a/src/index.html +++ b/src/index.html @@ -9,7 +9,6 @@
    - {{include "/includes/v1-banner.html"}}
    @@ -24,13 +23,11 @@
    - Download -
    - then learn how to get started -


    - Download v1.0 (obsolete) + Download
    + then learn how to get started +

    Caddy is licensed with the Apache 2.0 open source license.
    @@ -74,7 +71,7 @@ -

    Download

    +

    Download

    Caddy is the only server to use HTTPS automatically and by default
    @@ -113,7 +110,7 @@

    Or use it as a dynamic reverse proxy to any number of backends, complete with active and passive health checks, load balancing, circuit breaking, caching, and more.

    -

    Download

    +

    Download

    Caddy is the only server to use HTTPS automatically and by default
    @@ -151,7 +148,7 @@
    - Download + Download CLI Docs
    @@ -208,7 +205,7 @@
    - Download + Download Caddyfile Docs
    @@ -267,7 +264,7 @@ All changes made through the API are persisted to disk so they can continue to be used after restarts. - Download + Download API Docs Tutorial @@ -998,7 +995,7 @@
    - Download + Download Documentation Forum
    diff --git a/src/resources/css/account/common.css b/src/resources/css/account/common.css new file mode 100644 index 0000000..38859ca --- /dev/null +++ b/src/resources/css/account/common.css @@ -0,0 +1,289 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html, body { + min-height: 100%; +} + +html { + /* by setting the min-height of both html and body to 100%, + it ensures that flex items are centered on screen, but + also allows body to overflow screen height for tall content */ + height: 100%; +} + +body { + font: 16px sans-serif; + -webkit-font-smoothing: antialiased; + background: #e8ebf0; + display: flex; +} + +a { + color: #2b9cff; + text-decoration: none; +} + +a:hover { + color: #0f6ab9; +} + +.card { + background: white; + margin: auto; + box-shadow: 0 20px 40px rgba(0, 0, 0, .1); + display: flex; + border-radius: 5px; + width: 100%; + max-width: 800px; +} + +.card section { + padding: 50px; + text-align: center; + flex-grow: 1; +} + +.card section.head { + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + flex-grow: 0; + background: #f3f7ff; + border-right: 1px solid #e8ebf0; + border-top-left-radius: 5px; + border-bottom-left-radius: 5px; +} + +.card p { + margin: 0 auto; + max-width: 400px; + line-height: 1.25em; + font-size: 14px; +} + +.card .logo { + width: 100%; + max-width: 100px; + display: block; +} + +.card h1 { + font-size: 42px; + margin-bottom: 10px; +} + +.form-fields { + padding: 20px 0; +} + +input { + font-size: 16px; + font-family: 'PT Mono', 'Source Code Pro', monospace; +} + +.card input, +.card button { + display: block; + margin: 1em auto; + padding: 8px; +} + +.card input[type=text], +.card input[type=email], +.card input[type=password] { + width: 100%; + max-width: 400px; +} + +.card button { + font-size: 16px; + font-weight: bold; + padding: 10px 20px; +} + +@media (max-width: 800px) { + .card { + flex-direction: column; + } +} + +.swal-content p { + margin: .5em 0; +} + +#reset-password-step2 { + display: none; +} + + + + + + + + + + + + + + + + + + + + +.logo-container { + text-align: center; + margin: 20px auto; +} + +.logo { + max-width: 100px; +} + +.help { + border-bottom: 1px dotted #222; + cursor: help; +} + +.beta { + font-size: 12px; +} + + +.container { + display: flex; + flex-grow: 1; + justify-content: space-between; + font-family: Maven Pro, sans-serif; +} + +.container > nav { + background: #f8faff; + width: 25%; + max-width: 250px; +} + +.container > nav ul { + list-style: none; +} + +.container > nav ul a { + display: block; + padding: 10px 20px; + color: #546c75; +} + +.container > nav ul a:hover { + color: #01324b; + background-color: #ebf3fb; +} + +.container > nav ul a.current { + background-color: #e0ecfb; + font-weight: bold; + color: #01324b; +} + +.container > main { + display: flex; + flex: 1; + align-items: center; + justify-content: center; +} + +.container > main.dashboard { + align-items: flex-start; + justify-content: flex-start; +} + +.container section { + background: white; + border-radius: 5px; + box-shadow: 0 2px 5px rgba(0, 0, 0, .08); + margin: 25px; +} + + + +input[type=text], +input[type=email], +input[type=password] { + padding: 8px; + width: 100%; + max-width: 400px; + border: 1px solid #ccc; +} + +form .field { + padding: 0 20px 20px; + max-width: 600px; + margin: 0 auto 20px auto; + border-bottom: 1px solid #eee; +} + +form label b { + display: block; + margin-bottom: 10px; +} + +form .description { + font-size: 14px; + color: #777; + margin-top: 5px; +} + +form button, +form input[type=submit] { + font-size: 16px; + margin: 0 auto; +} + +input[type=checkbox] { + cursor: pointer; + transform: scale(1.25); +} + + + +section .pad { + margin: 15px 20px; +} + +section button:not([type=submit]), +section .button:not([type=submit]) { + font-size: 14px; + padding: 6px 15px; + margin: auto; +} + + +section h1 { + font-size: 22px; + font-weight: normal; +} + +table { + border-collapse: collapse; +} + +tr { + border-bottom: 1px solid #ddd; +} + +th, +td { + padding: 10px; +} + +th { + background-color: #ebf3ff; + text-align: left; +} \ No newline at end of file diff --git a/src/resources/css/account/dashboard.css b/src/resources/css/account/dashboard.css new file mode 100644 index 0000000..711f556 --- /dev/null +++ b/src/resources/css/account/dashboard.css @@ -0,0 +1,13 @@ +input[name=path] { + font-size: 14px; + padding: 4px; + border-radius: 4px; + width: auto; + /* max-width: 450px; */ + max-width: none; +} + +a.disabled { + color: #aaa; + cursor: not-allowed; +} \ No newline at end of file diff --git a/src/resources/css/common.css b/src/resources/css/common.css index 83fc071..beee22a 100644 --- a/src/resources/css/common.css +++ b/src/resources/css/common.css @@ -12,20 +12,6 @@ body { -moz-tab-size: 4; } -#v1-banner { - display: block; - background: #5c92a4; - color: #fff; - text-align: center; - padding: 10px 20px; - font-size: 14px; - text-decoration: none; -} - -#v1-banner:hover { - background: #457a8c; -} - .wrapper { max-width: 1400px; margin-left: auto; @@ -38,6 +24,14 @@ body { text-align: center; } +.text-right { + text-align: right; +} + +.float-right { + float: right; +} + a { color: #0694f1; text-decoration: none; @@ -64,8 +58,8 @@ header nav { header nav > a { display: inline-block; - padding-left: 10px; - padding-right: 10px; + padding-left: 12px; + padding-right: 12px; text-decoration: none; color: inherit; } @@ -116,6 +110,7 @@ header nav .button { padding-bottom: 2px; } +button, .button { border-radius: 2em; padding: 10px 20px; @@ -124,43 +119,54 @@ header nav .button { transition: all .2s; text-decoration: none; display: inline-block; + border: 0; + cursor: pointer; } +button:hover, .button:hover { transform: scale(1.05); } +button:active, .button:active { transform: translateY(2px); } +button.red, .button.red { background-color: #d9552b; color: white; } +button.red:hover, .button.red:hover { background-color: #fd511a; } +button.blue, .button.blue { background-color: #0082d0; color: white; } +button.blue:hover, .button.blue:hover { background-color: #00aaff; } +button.gray, .button.gray { background-color: #4c6a79; color: white; } +button.gray:hover, .button.gray:hover { background-color: #4f8098; } +button.big, .button.big { font-size: 125%; text-transform: uppercase; @@ -169,11 +175,24 @@ header nav .button { margin-right: 20px; } +button.disabled, +.button.disabled, +button:disabled, +.button:disabled { + background-color: #aaa !important; + color: white !important; + transform: none !important; + cursor: not-allowed; +} + + +p button, p .button { font-size: 18px; padding: 12px 30px; } + article a:hover { text-decoration: underline; } diff --git a/src/resources/css/docs.css b/src/resources/css/docs.css index 332af85..09d4f85 100644 --- a/src/resources/css/docs.css +++ b/src/resources/css/docs.css @@ -431,6 +431,39 @@ iframe { margin: 1em 0 2em; } +.nonstandard-notice { + font-size: 14px; + max-width: 500px; + margin: 25px auto; + border: 1px solid #ecd200; + background: #fffddf; + border-radius: 5px; + padding: 10px; + color: #886c00; + line-height: 1.4em; + display: none; +} + +.nonstandard { + color: rgb(214, 145, 16); +} + +.nonstandard-flag { + cursor: help; + font-size: 8px; + line-height: 1em; + padding: 4px 8px; + text-transform: uppercase; + font-weight: bold; + background-color: rgb(238, 167, 34); + color: white; + border-radius: 4px; + white-space: nowrap; +} + + + + @@ -515,6 +548,10 @@ article .json a { padding-bottom: 0; } +#hovercard .nonstandard-flag { + float: right; +} + #hovercard .module-link { display: block; text-decoration: none; diff --git a/src/resources/css/download.css b/src/resources/css/download.css new file mode 100644 index 0000000..0fd5371 --- /dev/null +++ b/src/resources/css/download.css @@ -0,0 +1,303 @@ +body { + background-color: #f0f6f9; +} + +.download-bar { + display: flex; + justify-content: space-between; + background: white; + border-radius: 4px; + box-shadow: 0 3px 6px rgba(0, 0, 0, .1); + font-size: 20px; + margin-top: 20px; + position: sticky; /* uwu 💓 */ + top: 0; + z-index: 1; +} + +.download-bar > * { + padding: 15px; + display: flex; + align-items: center; +} + +#platform { + padding: 5px 15px; + font-size: 16px; +} + +#download { + margin: 0; + font-size: 16px; + font-weight: bold; +} + +.packages-explanation { + margin: 20px auto; +} + +.warning { + margin-left: 1em; + font-size: 12px; + font-weight: bold; + padding: 2px 15px; + border-radius: 1em; + border: 2px solid rgb(255, 201, 0); + color: rgb(206, 151, 0); +} + +input:disabled, +select:disabled, +#optional-packages.disabled .optpkg label { + cursor: not-allowed !important; +} + + + + + + + +.loader { + display: inline-block; + font-size: 10px; + text-indent: -9999em; + width: 20px; + height: 20px; + border-radius: 50%; + background: #aaa; + background: -moz-linear-gradient(left, #aaa 10%, rgba(255, 255, 255, 0) 42%); + background: -webkit-linear-gradient(left, #aaa 10%, rgba(255, 255, 255, 0) 42%); + background: -o-linear-gradient(left, #aaa 10%, rgba(255, 255, 255, 0) 42%); + background: -ms-linear-gradient(left, #aaa 10%, rgba(255, 255, 255, 0) 42%); + background: linear-gradient(to right, #aaa 10%, rgba(255, 255, 255, 0) 42%); + animation: load3 1.4s infinite linear; + transform: translateZ(0); + vertical-align: middle; + margin-right: .5em; + margin-top: -2px; +} +#signature .loader { + width: 12px; + height: 12px; +} +.loader:before { + width: 50%; + height: 50%; + background: #fff; + border-radius: 100% 0 0 0; + position: absolute; + top: 0; + left: 0; + content: ''; +} +.loader:after { + background: #aaa; + width: 75%; + height: 75%; + border-radius: 50%; + content: ''; + margin: auto; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; +} +@-webkit-keyframes load3 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@keyframes load3 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + + + + + + +.table-container { + overflow-x: auto; + box-shadow: 0 2px 4px rgba(0, 0, 0, .1); + border-top-left-radius: 8px; + border-top-right-radius: 8px; +} + +#optional-packages { + width: 100%; + border-spacing: 0; + border-collapse: collapse; +} + +/* #optional-packages tr { + height: 1px; +} */ + +#optional-packages th, +#optional-packages td { + border-bottom: 1px solid #dfeaf0; +} + +#optional-packages th:first-child { border-top-left-radius: 8px; } +#optional-packages th:last-child { border-top-right-radius: 8px; } + +#optional-packages th { + background: #dfe8ec; + text-align: left; + text-transform: uppercase; + letter-spacing: 2px; + color: #54676f; + font-weight: bold; + font-size: 12px; + margin-bottom: 1em; + border-color: #90a2ac; +} + +#optional-packages th:first-child { + padding-left: 4.25em; +} + +#optional-packages td { + background: #fff; + height: 1px; /* TODO: works on Chrome, but not Firefox */ + /* height: 100%; TODO: works on Firefox, but not Chrome */ + /* TODO: + see https://stackoverflow.com/a/34781198/1048862 + (could also do tr with height: 1px which gets ignored, + then td with height: inherit; but this effectively the + same as just doing a td with height: 1px.) + I don't like how either Firefox or Chrome handle this + styling (setting the height of the parent shouldn't be + required at all, the browser is rendering a height + regardless!) but I think I lean toward Firefox's as + being more correct; the hack in Firefox is setting a + flexible 100% height on the parent, rather than the hack + in Chrome which is setting a height that is too small + and stretched or ignored anyway. + */ +} + +#optional-packages th, +#optional-packages .optpkg td:first-child label, +#optional-packages td:not(:first-child) { + padding: 15px; +} + +#optional-packages .optpkg-name { + font-weight: bold; +} + +#optional-packages .optpkg input[type=checkbox] { + transform: scale(1.5); + cursor: pointer; + margin-right: 2em; +} + +#optional-packages .optpkg label { + display: flex; + align-items: center; + height: 100%; + line-height: 1em; + cursor: pointer; +} + +#optional-packages:not(.disabled) .optpkg label:hover { + background: linear-gradient(90deg, rgba(242,248,253,1) 75%, rgba(242,248,253,0) 100%); +} + +#optional-packages .optpkg.selected td { + background-color: #f2f8fd; +} + +#optional-packages .optpkg input[type=text] { + font-size: 12px; + padding: 6px; + outline: none; + text-align: center; + border: 1px solid #ccc; + border-radius: 4px; +} + +#optional-packages .optpkg input[type=text]::placeholder { + font-style: italic; +} + +#optional-packages .optpkg-no-modules { + font-size: 12px; + font-style: italic; + color: #555; +} + +#optional-packages .optpkg-module { + margin-top: .5em; + margin-bottom: .5em; +} + +#optional-packages .optpkg-module .module-name { + font-weight: bold; + font-family: 'PT Mono', monospace; +} + +#optional-packages .optpkg-module .module-description { + color: #444; + margin-left: 1em; + font-size: 14px; +} + + + + + + + +.swal-custom-content { + text-align: left; +} + +.swal-custom-content ol { + margin: 1em 0 1em 2em; +} + +.swal-custom-content li { + margin-bottom: 10px; +} + + + + + + + + + + +@media (max-width: 860px) { + .download-bar { + flex-direction: column; + align-items: center; + } +} + +@media (max-width: 600px) { + .download-bar { + font-size: inherit; + } + + .download-bar > * { + padding: 5px; + } +} diff --git a/src/resources/images/caddy-lock.png b/src/resources/images/caddy-lock.png new file mode 100644 index 0000000..d0d33cc Binary files /dev/null and b/src/resources/images/caddy-lock.png differ diff --git a/src/resources/js/account/common.js b/src/resources/js/account/common.js new file mode 100644 index 0000000..cc9bd32 --- /dev/null +++ b/src/resources/js/account/common.js @@ -0,0 +1,63 @@ +if (!loggedIn() + && window.location.pathname != '/account/login' + && window.location.pathname != '/account/create' + && window.location.pathname != '/account/verify' + && window.location.pathname != '/account/reset-password') { + window.location = '/account/login?redir='+encodeURIComponent(window.location); +} + +$(function() { + // highlight current page in left nav + var $currentPageLink = $('.container > nav a[href="'+window.location.pathname+'"]'); + $currentPageLink.addClass('current'); + + // shortcut any logout links to make the POST directly + $('a[href="/account/logout"]').click(function() { + logout(); + return false; + }); +}); + + + +function loggedIn() { + return document.cookie.indexOf('user=') > -1; +} + +function logout() { + $.post('/api/logout').done(function() { + window.location = '/'; + }).fail(function(jqxhr, status, error) { + document.cookie = 'user=; Path=/; expires=Thu, 01 Jan 1970 00:00:01 GMT;'; + swal({ + icon: "error", + title: error, + content: errorContent(jqxhr) + }).then(function() { + window.location = '/account/' + }); + }); +} + +function errorContent(jqxhr) { + var div = document.createElement('div'); + var p1 = document.createElement('p'); + p1.appendChild(document.createTextNode("Sorry, something went wrong:")); + div.appendChild(p1); + + var p2 = document.createElement('p'); + var p2b = document.createElement('b'); + p2b.appendChild(document.createTextNode(jqxhr.responseJSON ? jqxhr.responseJSON.error.message : jqxhr.status + " " + jqxhr.statusText)); + p2.appendChild(p2b) + div.appendChild(p2); + + if (jqxhr.responseJSON) { + var p3 = document.createElement('p'); + p3.appendChild(document.createTextNode("Please include this error ID if reporting:")); + p3.appendChild(document.createElement('br')); + p3.appendChild(document.createTextNode(jqxhr.responseJSON.error.id)); + div.appendChild(p3); + } + + return div; +} \ No newline at end of file diff --git a/src/resources/js/account/create.js b/src/resources/js/account/create.js new file mode 100644 index 0000000..754e830 --- /dev/null +++ b/src/resources/js/account/create.js @@ -0,0 +1,28 @@ +if (loggedIn()) window.location = '/account/'; + +$(function() { + $('form input').first().focus(); + + $('form').submit(function(event) { + $('#submit').prop('disabled', true); + + $.post($(this).prop("action"), $(this).serialize()).done(function() { + swal({ + icon: "success", + title: "Check your email", + text: "We've sent you an email with a link that expires in 48 hours. Please verify your account before you can use it." + }).then(function() { + window.location = '/account/verify'; + }); + }).fail(function(jqxhr, status, error) { + swal({ + icon: "error", + titleText: "Error", + content: errorContent(jqxhr) + }); + $('#submit').prop('disabled', false); + }); + + return false; + }); +}); \ No newline at end of file diff --git a/src/resources/js/account/dashboard.js b/src/resources/js/account/dashboard.js new file mode 100644 index 0000000..7396a73 --- /dev/null +++ b/src/resources/js/account/dashboard.js @@ -0,0 +1,125 @@ +// download package list as soon as possible +$.get("/api/user-packages").done(function(json) { + var packageList = json.result; + + // wait until the DOM has finished loading before rendering the results + $(function() { + // trying out this fancy new syntax: + // https://twitter.com/joshmanders/status/1282395540970496001 + packageList.forEach(pkg => { + var $tdPath = $(''); + var $tdListed = $(''); + var $tdAvail = $(''); + var $tdDownloads = $(''); + var $tdLinks = $('Rescan   Delete'); + + if (pkg.listed) { + $('input', $tdListed).prop('checked', true); + } + if (pkg.available) { + $('input', $tdAvail).prop('checked', true); + } + var $pathInput = $('input', $tdPath); + $pathInput.val(pkg.path).attr('size', pkg.path.length); + + var $tr = $(''); + $tr.append($tdPath) + .append($tdListed) + .append($tdAvail) + .append($tdDownloads) + .append($tdLinks); + + $('#user-packages').append($tr); + + // scroll package paths to the left so if they get + // cut off, the leaf package name is still visible + $pathInput.scrollLeft($pathInput.width()); + }); + }); +}); + +$(function() { + + // update packages when fields change + $('#user-packages').on('change', 'input', function() { + $tr = $(this).closest('tr'); + $('input', $tr).prop('disabled', true); + $.post('/api/update-package', { + id: $tr.data('package-id'), + listed: $('[name=listed]', $tr).prop('checked') ? 1 : 0, + available: $('[name=available]', $tr).prop('checked') ? 1 : 0, + path: $('[name=path]', $tr).val() + }).fail(function(jqxhr, status, error) { + swal({ + icon: "error", + title: "Could not save changes", + content: errorContent(jqxhr) + }); + }).always(function() { + $('input', $tr).prop('disabled', false); + }); + }); + + // rescan package + $('#user-packages').on('click', '.rescan-package', function() { + if ($(this).hasClass('disabled')) return; + + $tr = $(this).closest('tr'); + $('a', $tr).addClass('disabled'); + + $.post('/api/rescan-package', { + package_id: $tr.data('package-id') + }).done(function(jqxhr, status, error) { + swal({ + icon: "success", + title: "Rescan Complete", + text: "Package has been re-scanned and its documentation has been updated." + }); + }).fail(function(jqxhr, status, error) { + swal({ + icon: "error", + title: "Rescan failed", + content: errorContent(jqxhr) + }); + }).always(function() { + $('a', $tr).removeClass('disabled'); + }); + }); + + // delete package + $('#user-packages').on('click', '.delete-package', function() { + if ($(this).hasClass('disabled')) return; + + swal({ + title: "Delete package?", + text: "Deleting the package will remove it from our website.", + icon: "warning", + buttons: true, + dangerMode: true, + }).then((willDelete) => { + // abort if user cancelled + if (!willDelete) return; + + $tr = $(this).closest('tr'); + $('input', $tr).prop('disabled', true); + $.post('/api/delete-package', { + id: $tr.data('package-id') + }).done(function(jqxhr, status, error) { + $tr.remove(); + swal({ + icon: "success", + title: "Package deleted" + }); + }).fail(function(jqxhr, status, error) { + swal({ + icon: "error", + title: "Delete failed", + content: errorContent(jqxhr) + }); + }).always(function() { + $('input', $tr).prop('disabled', false); + }); + }); + }); + +}); \ No newline at end of file diff --git a/src/resources/js/account/login.js b/src/resources/js/account/login.js new file mode 100644 index 0000000..4a82e15 --- /dev/null +++ b/src/resources/js/account/login.js @@ -0,0 +1,24 @@ +if (loggedIn()) window.location = '/account/'; + +$(function() { + $('form input').first().focus(); + + $('form').submit(function(event) { + $('#submit').prop('disabled', true); + + $.post($(this).prop("action"), $(this).serialize()).done(function() { + var qsParams = new URLSearchParams(window.location.search); + var destination = qsParams.get('redir'); + window.location = destination ? destination : '/account/'; + }).fail(function(jqxhr, msg, error) { + swal({ + icon: "error", + title: "Bad credentials", + content: errorContent(jqxhr) + }); + $('#submit').prop('disabled', false); + }); + + return false; + }); +}); \ No newline at end of file diff --git a/src/resources/js/account/logout.js b/src/resources/js/account/logout.js new file mode 100644 index 0000000..f9ff2f0 --- /dev/null +++ b/src/resources/js/account/logout.js @@ -0,0 +1 @@ +logout(); \ No newline at end of file diff --git a/src/resources/js/account/register-package.js b/src/resources/js/account/register-package.js new file mode 100644 index 0000000..9f199b0 --- /dev/null +++ b/src/resources/js/account/register-package.js @@ -0,0 +1,27 @@ +$(function() { + $('form input').first().focus(); + + $('form').submit(function(event) { + $('#submit').prop('disabled', true); + + $.post($(this).prop("action"), $(this).serialize()).done(function() { + swal({ + icon: "success", + title: "It's yours", + text: "Package claimed. Its documentation is now available on our website and you are responsible for maintaining it. Thank you!" + }).then(function() { + // TODO: ... + // window.location = "/account/login"; + }); + }).fail(function(jqxhr, status, error) { + swal({ + icon: "error", + title: error, + content: errorContent(jqxhr) + }); + $('#submit').prop('disabled', false); + }); + + return false; + }); +}); \ No newline at end of file diff --git a/src/resources/js/account/reset-password.js b/src/resources/js/account/reset-password.js new file mode 100644 index 0000000..7df05f0 --- /dev/null +++ b/src/resources/js/account/reset-password.js @@ -0,0 +1,80 @@ +if (loggedIn()) window.location = '/account/'; + +$(function() { + var qsParams = new URLSearchParams(window.location.search); + var email = qsParams.get("email"); + var token = qsParams.get("token"); + $('input[name=email]').val(email); + $('input[name=token]').val(token); + if (email && token) showStep2(); + + $('form input:visible').first().focus(); + + $('#reset-password-step1').submit(function(event) { + $('button').prop('disabled', false); + + $.post($(this).prop("action"), $(this).serialize()).done(function() { + swal({ + icon: "info", + title: "Check your email", + text: "If we have an account with that email address, we just sent you some instructions." + }).then(function() { + window.location = '/'; + }); + }).fail(function(jqxhr, status, error) { + swal({ + icon: "error", + title: error, + content: errorContent(jqxhr) + }); + $('button').prop('disabled', false); + }); + + return false; + }); + + $('#reset-password-step2').submit(function(event) { + $('button').prop('disabled', false); + + $.post($(this).prop("action"), $(this).serialize()).done(function() { + swal({ + icon: "success", + title: "Reset completed", + text: "You may now log in with your new password." + }).then(function() { + window.location = '/account/login'; + }); + }).fail(function(jqxhr, status, error) { + swal({ + icon: "error", + title: "Error", + content: errorContent(jqxhr) + }); + $('button').prop('disabled', false); + }); + + return false; + }); + + $('#goto-step1').click(function(event) { + $('#reset-password-step2').hide('fast'); + $('#reset-password-step1').show('fast', function() { + $('input:visible').first().focus(); + }); + return false; + }); + $('#goto-step2').click(function(event) { + showStep2(); + return false; + }); +}); + +function showStep2() { + $('#reset-password-step1').hide('fast'); + $('#reset-password-step2').show('fast', function() { + if ($('input[name=token]').val() != "") + $('input[name=password]').focus(); + else + $('input:visible').first().focus(); + }); +} \ No newline at end of file diff --git a/src/resources/js/account/verify.js b/src/resources/js/account/verify.js new file mode 100644 index 0000000..d085f1e --- /dev/null +++ b/src/resources/js/account/verify.js @@ -0,0 +1,36 @@ +if (loggedIn()) window.location = '/account/'; + +$(function() { + $('form input').first().focus(); + + $('form').submit(function(event) { + $('#submit').prop('disabled', true); + + $.post($(this).prop("action"), $(this).serialize()).done(function() { + swal({ + icon: "success", + title: "Account confirmed", + text: "Thank you. You may now log in and use your account!" + }).then(function() { + window.location = "/account/login"; + }); + }).fail(function(jqxhr, status, error) { + swal({ + icon: "error", + title: error, + content: errorContent(jqxhr) + }); + $('#submit').prop('disabled', false); + }); + + return false; + }); + + // if info is in the query string, fill use and submit it + var qsParams = new URLSearchParams(window.location.search); + var email = qsParams.get("email"); + var acct = qsParams.get("code"); + $('input[name=email]').val(email); + $('input[name=account_id]').val(acct); + if (email && acct) $('form').submit(); +}); \ No newline at end of file diff --git a/src/resources/js/common.js b/src/resources/js/common.js index 995d20a..81578b8 100644 --- a/src/resources/js/common.js +++ b/src/resources/js/common.js @@ -7,3 +7,27 @@ document.addEventListener('DOMContentLoaded', function() { debug: false // Set debug to true if you want to inspect the dropdown }); }); + +const caddyImportPath = 'github.com/caddyserver/caddy/v2'; + +function isStandard(packagePath) { + return packagePath.startsWith(caddyImportPath); +} + +function substrBeforeLastDot(s) { + return s.substr(0, s.lastIndexOf('.')) +} + +function substrAfterLastDot(s) { + return s.substr(s.lastIndexOf('.')) +} + +function truncate(str, len) { + if (!str) return ""; + var startLen = str.length; + str = str.substring(0, len); + if (startLen > len) { + str += "..."; + } + return str; +} diff --git a/src/resources/js/docs-api.js b/src/resources/js/docs-api.js index 08b7ae6..7b43cc3 100644 --- a/src/resources/js/docs-api.js +++ b/src/resources/js/docs-api.js @@ -4,6 +4,8 @@ var pageData = {}, pageDocs = {}; var $renderbox, $hovercard; +const nonStandardFlag = 'Non-standard'; + $(function() { $renderbox = $('#renderbox'); $hovercard = $('#hovercard'); @@ -61,7 +63,12 @@ $(function() { for (var i = 0; i < pageData.namespaces[modNamespace].length; i++) { var modInfo = pageData.namespaces[modNamespace][i]; var href = canTraverse() ? '.'+elemPath+'/'+modInfo.name+'/' : './'+modNamespace+'.'+modInfo.name; - $list.append(''+modInfo.name+''+truncate(modInfo.docs, 115)+''); + var content = ' '+modInfo.name; + if (!isStandard(modInfo.package)) { + content += nonStandardFlag; + } + content += ''+truncate(modInfo.docs, 115)+''; + $list.append(content); } } $('#hovercard-module-list').html($list); @@ -88,7 +95,7 @@ $(function() { case "struct": for (var j = 0; j < bcVal.struct_fields.length; j++) { var sf = bcVal.struct_fields[j]; - bcSiblings.push({name: sf.key, path: siblingPath}) + bcSiblings.push({name: sf.key, path: siblingPath, isStandard: isStandard(bcVal.type_name)}) } break; @@ -96,7 +103,7 @@ $(function() { case "module_map": for (var j = 0; j < pageData.namespaces[bcVal.module_namespace].length; j++) { var mod = pageData.namespaces[bcVal.module_namespace][j]; - bcSiblings.push({name: mod.name, path: siblingPath}) + bcSiblings.push({name: mod.name, path: siblingPath, isStandard: isStandard(mod.package)}) } } @@ -108,7 +115,12 @@ $(function() { sibPath += "/"; } sibPath += sib.name+"/"; - $siblings.append(''+sib.name+''); + var aTag = ''; + $siblings.append(aTag); } $('#hovercard-breadcrumb-siblings').html($siblings).show(); @@ -147,6 +159,14 @@ function beginRendering(json) { pageData = json; console.log("DATA:", pageData); + // show notice if module is non-standard + if (!isStandard(pageData.structure.type_name)) { + var projectHref = 'https://'+pageData.structure.type_name; + projectHref = substrBeforeLastDot(projectHref); + $('.nonstandard-project-link').attr('href', projectHref).text(projectHref); + $('.nonstandard-notice').prepend(nonStandardFlag).show(); + } + if (pageData.structure.doc) { // for most types, just render their docs $('#top-doc').html(markdown(pageData.structure.doc)); @@ -308,16 +328,6 @@ function indent(nesting, $group) { $group.append($span); } -function truncate(str, len) { - if (!str) return ""; - var startLen = str.length; - str = str.substring(0, len); - if (startLen > len) { - str += "..."; - } - return str; -} - function makeSubmoduleList(path, value) { while (value.elems) { value = value.elems; @@ -330,7 +340,11 @@ function makeSubmoduleList(path, value) { for (var j = 0; j < pageData.namespaces[value.module_namespace].length; j++) { var submod = pageData.namespaces[value.module_namespace][j]; var href = canTraverse() ? '.'+path+'/'+submod.name+'/' : './'+value.module_namespace+'.'+submod.name; - submodList += '
  • '+submod.name+'
  • '; + var submodLink = ''+submod.name+''; + if (!isStandard(submod.package)) { + submodLink += ' '+nonStandardFlag; + } + submodList += '
  • '+submodLink+'
  • '; } } submodList += ''; diff --git a/src/resources/js/download.js b/src/resources/js/download.js new file mode 100644 index 0000000..2649d7d --- /dev/null +++ b/src/resources/js/download.js @@ -0,0 +1,221 @@ +// download package list as soon as possible +$.get("/api/packages").done(function(json) { + var packageList = json.result; + + // wait until the DOM has finished loading before rendering the results + $(function() { + const optpkgTemplate = + ''+ + ' '+ + ' '+ + ' '+ + ''; + + const optpkgModuleTemplate = + '
    '+ + ' '+ + ' '+ + '
    '; + + for (var i = 0; i < packageList.length; i++) { + var pkg = packageList[i]; + if (isStandard(pkg.path)) { + // not necessary to show, since these packages + // come with standard distribution + continue; + } + + var $optpkg = $(optpkgTemplate); + $('.optpkg-name', $optpkg).text(pkg.path); + + if (pkg.modules && pkg.modules.length > 0) { + for (var j = 0; j < pkg.modules.length; j++) { + var mod = pkg.modules[j]; + var $mod = $(optpkgModuleTemplate); + $('.module-name', $mod).attr('href', '/docs/modules/'+mod.name).text(mod.name); + $('.module-description', $mod).text(truncate(mod.docs, 120)); + $('.optpkg-modules', $optpkg).append($mod); + } + } else { + $('.optpkg-modules', $optpkg) + .addClass("optpkg-no-modules") + .text('This package does not add any modules. Either it is another kind of plugin (such as a config adapter) or this listing is in error.'); + } + $('#optional-packages').append($optpkg); + } + }); +}).fail(function(jqxhr, status, error) { + swal({ + icon: "error", + title: "Unavailable", + content: $('
    Sorry, the build server is down for maintenance right now. You can try again later or download pre-built Caddy binaries from GitHub any time.
    ')[0] + }); + $(function() { + disableFields(false); + }); +}); + +$(function() { + autoPlatform(); + + downloadButtonHtml = $('#download').html(); + + // update the page, including the download link, when form fields change + $('#optional-packages').on('change', 'input[type=checkbox]', function() { + $(this).closest('.optpkg').toggleClass('selected'); + updatePage(); + }); + $('#optional-packages').on('change keyup', 'input[name=version]', function() { + updatePage(); + }); + $('#platform').change(function() { + updatePage(); + }); + + $('#download').click(function(event) { + if ($(this).hasClass('disabled')) { + return false; + } + + disableFields(true); + + fathom.trackGoal('U9K2UTFV', 0); + + $.ajax($(this).attr('href'), { method: "HEAD" }).done(function(data, status, jqxhr) { + window.onbeforeunload = null; // disable exit confirmation before "redirecting" to download + window.location = jqxhr.getResponseHeader("Location"); + }).fail(function(jqxhr, status, error) { + handleBuildError(jqxhr, status, error); + }).always(function() { + enableFields(); + }); + + return false; + }); +}) + +// autoPlatform choooses the platform in the list that best +// matches the user's browser, if it's available. +function autoPlatform() { + // assume 32-bit linux, then change OS and architecture if justified + var os = "linux", arch = "386", arm = ""; + + // change os + if (/Macintosh/i.test(navigator.userAgent)) { + os = "darwin"; + } else if (/Windows/i.test(navigator.userAgent)) { + os = "windows"; + } else if (/FreeBSD/i.test(navigator.userAgent)) { + os = "freebsd"; + } else if (/OpenBSD/i.test(navigator.userAgent)) { + os = "openbsd"; + } + + // change architecture + if (os == "darwin" || /amd64|x64|x86_64|Win64|WOW64|i686|64-bit/i.test(navigator.userAgent)) { + arch = "amd64"; + } else if (/arm64/.test(navigator.userAgent)) { + arch = "arm64"; + } else if (/ ARM| armv/.test(navigator.userAgent)) { + arch = "arm"; + } + + // change arm version + if (arch == "arm") { + arm = "7"; // assume version 7 by default + if (/armv6/.test(navigator.userAgent)) { + arm = "6"; + } else if (/armv5/.test(navigator.userAgent)) { + arm = "5"; + } + } + + var selString = os+"-"+arch; + if (arm != "") { + selString += "-"+arm; + } + + $('#platform').val(selString); + updatePage(); +} + +function getDownloadLink() { + // get platform components + var platformParts = $('#platform').val().split("-"); + var os = platformParts[0]; + var arch = platformParts[1]; + var arm = platformParts.length > 2 ? platformParts[2] : ""; + + var qs = new URLSearchParams(); + if (os) qs.set("os", os); + if (arch) qs.set("arch", arch); + if (arm) qs.set("arm", arm); + + // get plugins and their versions + $('#optional-packages .selected').each(function() { + // get package path + var p = $('.optpkg-name', this).text().trim(); + + // get package version, if user specified one + var ver = $('input[name=version]', this).val().trim(); + if (ver) { + p += "@"+ver; + } + + qs.append("p", p); + }); + + var idempotencyKey = Math.floor(Math.random() * 99999999999999); + qs.append("idempotency", idempotencyKey); + + return "/api/download?"+qs.toString(); +} + +function handleBuildError(jqxhr, status, error) { + var $content = $('
    '); + + if (jqxhr.status == 502) { + swal({ + icon: "error", + title: "Unavailable", + content: $content.html('Sorry, the build server is down for maintenance right now. You can try again later or download pre-built Caddy binaries from GitHub.')[0] + }); + } else { + swal({ + icon: "error", + title: "Build failed", + content: $content.html('The two most common reasons are:
    1. A plugin is not compiling. The developer must release a new version that compiles.
    2. The build configuration is invalid. If you specified any versions, make sure they are correct and within the same major version as the path of the associated package.
    In the meantime, you can download Caddy from GitHub without any plugins.')[0] + }); + } +} + +function updatePage() { + $('#package-count').text($('.optpkg.selected').length); + $('#download').attr('href', getDownloadLink()); +} + +function disableFields(building) { + $('#download, #optional-packages').addClass('disabled'); + $('.download-bar select, #optional-packages input').prop('disabled', true); + if (building) { + $('#download').html('
    Building'); + + // prevent accidentally leaving the page during a build + window.onbeforeunload = function() { + return "Your custom build is in progress."; + }; + } else { + $('#download').html('Builds Unavailable'); + } +} + +function enableFields() { + $('#download, #optional-packages').removeClass('disabled'); + $('.download-bar select, #optional-packages input').prop('disabled', false); + $('#download').html(downloadButtonHtml); + + // allow user to leave page easily + window.onbeforeunload = null; +} + +var downloadButtonHtml; // to restore button to its original contents \ No newline at end of file diff --git a/src/resources/js/json-docs.js b/src/resources/js/json-docs.js index 5205e2d..e365cad 100644 --- a/src/resources/js/json-docs.js +++ b/src/resources/js/json-docs.js @@ -9,7 +9,7 @@ setPageTitle(); $.get("/api/docs/config"+configPath, function(json) { // wait until the DOM has finished loading before rendering the results $(function() { - beginRendering(json); + beginRendering(json.result); // establish the breadcrumb var $bc = $('.breadcrumbs'); diff --git a/src/resources/js/module-docs.js b/src/resources/js/module-docs.js index 89c77c5..7ac483c 100644 --- a/src/resources/js/module-docs.js +++ b/src/resources/js/module-docs.js @@ -9,21 +9,24 @@ if (moduleID) { $(function() { $('#module-docs-container').show(); $('h1').text("Module "+moduleID); - beginRendering(json); + beginRendering(json.result); }); }); } else { // populate the module list - $.get("/api/modules", function(moduleList) { + $.get("/api/modules", function(json) { + var moduleList = json.result; + // wait until the DOM has finished loading before rendering the results $(function() { $('#module-list-container').show(); $table = $('#module-list'); for (modID in moduleList) { - var doc = moduleList[modID]; + var val = moduleList[modID]; + var standard = isStandard(val.type_name); var $tr = $(''); - $tr.append(''+modID+''); - $tr.append(''+markdown(truncate(doc, 200))+''); + $tr.append(''+modID+''+(standard ? '' : ' '+nonStandardFlag)+''); + $tr.append(''+markdown(truncate(val.doc, 200))+''); $table.append($tr); } }); diff --git a/src/resources/js/sweetalert.min.js b/src/resources/js/sweetalert.min.js new file mode 100644 index 0000000..dc8f5e7 --- /dev/null +++ b/src/resources/js/sweetalert.min.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.swal=e():t.swal=e()}(this,function(){return function(t){function e(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,e),r.l=!0,r.exports}var n={};return e.m=t,e.c=n,e.d=function(t,n,o){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:o})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=8)}([function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var o="swal-button";e.CLASS_NAMES={MODAL:"swal-modal",OVERLAY:"swal-overlay",SHOW_MODAL:"swal-overlay--show-modal",MODAL_TITLE:"swal-title",MODAL_TEXT:"swal-text",ICON:"swal-icon",ICON_CUSTOM:"swal-icon--custom",CONTENT:"swal-content",FOOTER:"swal-footer",BUTTON_CONTAINER:"swal-button-container",BUTTON:o,CONFIRM_BUTTON:o+"--confirm",CANCEL_BUTTON:o+"--cancel",DANGER_BUTTON:o+"--danger",BUTTON_LOADING:o+"--loading",BUTTON_LOADER:o+"__loader"},e.default=e.CLASS_NAMES},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.getNode=function(t){var e="."+t;return document.querySelector(e)},e.stringToNode=function(t){var e=document.createElement("div");return e.innerHTML=t.trim(),e.firstChild},e.insertAfter=function(t,e){var n=e.nextSibling;e.parentNode.insertBefore(t,n)},e.removeNode=function(t){t.parentElement.removeChild(t)},e.throwErr=function(t){throw t=t.replace(/ +(?= )/g,""),"SweetAlert: "+(t=t.trim())},e.isPlainObject=function(t){if("[object Object]"!==Object.prototype.toString.call(t))return!1;var e=Object.getPrototypeOf(t);return null===e||e===Object.prototype},e.ordinalSuffixOf=function(t){var e=t%10,n=t%100;return 1===e&&11!==n?t+"st":2===e&&12!==n?t+"nd":3===e&&13!==n?t+"rd":t+"th"}},function(t,e,n){"use strict";function o(t){for(var n in t)e.hasOwnProperty(n)||(e[n]=t[n])}Object.defineProperty(e,"__esModule",{value:!0}),o(n(25));var r=n(26);e.overlayMarkup=r.default,o(n(27)),o(n(28)),o(n(29));var i=n(0),a=i.default.MODAL_TITLE,s=i.default.MODAL_TEXT,c=i.default.ICON,l=i.default.FOOTER;e.iconMarkup='\n
    ',e.titleMarkup='\n
    \n',e.textMarkup='\n
    ',e.footerMarkup='\n
    \n'},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var o=n(1);e.CONFIRM_KEY="confirm",e.CANCEL_KEY="cancel";var r={visible:!0,text:null,value:null,className:"",closeModal:!0},i=Object.assign({},r,{visible:!1,text:"Cancel",value:null}),a=Object.assign({},r,{text:"OK",value:!0});e.defaultButtonList={cancel:i,confirm:a};var s=function(t){switch(t){case e.CONFIRM_KEY:return a;case e.CANCEL_KEY:return i;default:var n=t.charAt(0).toUpperCase()+t.slice(1);return Object.assign({},r,{text:n,value:t})}},c=function(t,e){var n=s(t);return!0===e?Object.assign({},n,{visible:!0}):"string"==typeof e?Object.assign({},n,{visible:!0,text:e}):o.isPlainObject(e)?Object.assign({visible:!0},n,e):Object.assign({},n,{visible:!1})},l=function(t){for(var e={},n=0,o=Object.keys(t);n=0&&w.splice(e,1)}function s(t){var e=document.createElement("style");return t.attrs.type="text/css",l(e,t.attrs),i(t,e),e}function c(t){var e=document.createElement("link");return t.attrs.type="text/css",t.attrs.rel="stylesheet",l(e,t.attrs),i(t,e),e}function l(t,e){Object.keys(e).forEach(function(n){t.setAttribute(n,e[n])})}function u(t,e){var n,o,r,i;if(e.transform&&t.css){if(!(i=e.transform(t.css)))return function(){};t.css=i}if(e.singleton){var l=h++;n=g||(g=s(e)),o=f.bind(null,n,l,!1),r=f.bind(null,n,l,!0)}else t.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(n=c(e),o=p.bind(null,n,e),r=function(){a(n),n.href&&URL.revokeObjectURL(n.href)}):(n=s(e),o=d.bind(null,n),r=function(){a(n)});return o(t),function(e){if(e){if(e.css===t.css&&e.media===t.media&&e.sourceMap===t.sourceMap)return;o(t=e)}else r()}}function f(t,e,n,o){var r=n?"":o.css;if(t.styleSheet)t.styleSheet.cssText=x(e,r);else{var i=document.createTextNode(r),a=t.childNodes;a[e]&&t.removeChild(a[e]),a.length?t.insertBefore(i,a[e]):t.appendChild(i)}}function d(t,e){var n=e.css,o=e.media;if(o&&t.setAttribute("media",o),t.styleSheet)t.styleSheet.cssText=n;else{for(;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(document.createTextNode(n))}}function p(t,e,n){var o=n.css,r=n.sourceMap,i=void 0===e.convertToAbsoluteUrls&&r;(e.convertToAbsoluteUrls||i)&&(o=y(o)),r&&(o+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(r))))+" */");var a=new Blob([o],{type:"text/css"}),s=t.href;t.href=URL.createObjectURL(a),s&&URL.revokeObjectURL(s)}var m={},b=function(t){var e;return function(){return void 0===e&&(e=t.apply(this,arguments)),e}}(function(){return window&&document&&document.all&&!window.atob}),v=function(t){var e={};return function(n){return void 0===e[n]&&(e[n]=t.call(this,n)),e[n]}}(function(t){return document.querySelector(t)}),g=null,h=0,w=[],y=n(15);t.exports=function(t,e){if("undefined"!=typeof DEBUG&&DEBUG&&"object"!=typeof document)throw new Error("The style-loader cannot be used in a non-browser environment");e=e||{},e.attrs="object"==typeof e.attrs?e.attrs:{},e.singleton||(e.singleton=b()),e.insertInto||(e.insertInto="head"),e.insertAt||(e.insertAt="bottom");var n=r(t,e);return o(n,e),function(t){for(var i=[],a=0;athis.length)&&-1!==this.indexOf(t,e)}),Array.prototype.includes||Object.defineProperty(Array.prototype,"includes",{value:function(t,e){if(null==this)throw new TypeError('"this" is null or not defined');var n=Object(this),o=n.length>>>0;if(0===o)return!1;for(var r=0|e,i=Math.max(r>=0?r:o-Math.abs(r),0);i=0&&(t._idleTimeoutId=setTimeout(function(){t._onTimeout&&t._onTimeout()},e))},n(19),e.setImmediate=setImmediate,e.clearImmediate=clearImmediate},function(t,e,n){(function(t,e){!function(t,n){"use strict";function o(t){"function"!=typeof t&&(t=new Function(""+t));for(var e=new Array(arguments.length-1),n=0;n1)for(var n=1;n
    ',e.default=e.modalMarkup},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var o=n(0),r=o.default.OVERLAY,i='
    \n
    ';e.default=i},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var o=n(0),r=o.default.ICON;e.errorIconMarkup=function(){var t=r+"--error",e=t+"__line";return'\n
    \n \n \n
    \n '},e.warningIconMarkup=function(){var t=r+"--warning";return'\n \n \n \n '},e.successIconMarkup=function(){var t=r+"--success";return'\n \n \n\n
    \n
    \n '}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var o=n(0),r=o.default.CONTENT;e.contentMarkup='\n
    \n\n
    \n'},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var o=n(0),r=o.default.BUTTON_CONTAINER,i=o.default.BUTTON,a=o.default.BUTTON_LOADER;e.buttonMarkup='\n
    \n\n \n\n
    \n
    \n
    \n
    \n
    \n\n
    \n'},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var o=n(4),r=n(2),i=n(0),a=i.default.ICON,s=i.default.ICON_CUSTOM,c=["error","warning","success","info"],l={error:r.errorIconMarkup(),warning:r.warningIconMarkup(),success:r.successIconMarkup()},u=function(t,e){var n=a+"--"+t;e.classList.add(n);var o=l[t];o&&(e.innerHTML=o)},f=function(t,e){e.classList.add(s);var n=document.createElement("img");n.src=t,e.appendChild(n)},d=function(t){if(t){var e=o.injectElIntoModal(r.iconMarkup);c.includes(t)?u(t,e):f(t,e)}};e.default=d},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var o=n(2),r=n(4),i=function(t){navigator.userAgent.includes("AppleWebKit")&&(t.style.display="none",t.offsetHeight,t.style.display="")};e.initTitle=function(t){if(t){var e=r.injectElIntoModal(o.titleMarkup);e.textContent=t,i(e)}},e.initText=function(t){if(t){var e=document.createDocumentFragment();t.split("\n").forEach(function(t,n,o){e.appendChild(document.createTextNode(t)),n0}).forEach(function(t){b.classList.add(t)})}n&&t===c.CONFIRM_KEY&&b.classList.add(s),b.textContent=r;var g={};return g[t]=i,f.setActionValue(g),f.setActionOptionsFor(t,{closeModal:p}),b.addEventListener("click",function(){return u.onAction(t)}),m},p=function(t,e){var n=r.injectElIntoModal(l.footerMarkup);for(var o in t){var i=t[o],a=d(o,i,e);i.visible&&n.appendChild(a)}0===n.children.length&&n.remove()};e.default=p},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var o=n(3),r=n(4),i=n(2),a=n(5),s=n(6),c=n(0),l=c.default.CONTENT,u=function(t){t.addEventListener("input",function(t){var e=t.target,n=e.value;a.setActionValue(n)}),t.addEventListener("keyup",function(t){if("Enter"===t.key)return s.onAction(o.CONFIRM_KEY)}),setTimeout(function(){t.focus(),a.setActionValue("")},0)},f=function(t,e,n){var o=document.createElement(e),r=l+"__"+e;o.classList.add(r);for(var i in n){var a=n[i];o[i]=a}"input"===e&&u(o),t.appendChild(o)},d=function(t){if(t){var e=r.injectElIntoModal(i.contentMarkup),n=t.element,o=t.attributes;"string"==typeof n?f(e,n,o):e.appendChild(n)}};e.default=d},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var o=n(1),r=n(2),i=function(){var t=o.stringToNode(r.overlayMarkup);document.body.appendChild(t)};e.default=i},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var o=n(5),r=n(6),i=n(1),a=n(3),s=n(0),c=s.default.MODAL,l=s.default.BUTTON,u=s.default.OVERLAY,f=function(t){t.preventDefault(),v()},d=function(t){t.preventDefault(),g()},p=function(t){if(o.default.isOpen)switch(t.key){case"Escape":return r.onAction(a.CANCEL_KEY)}},m=function(t){if(o.default.isOpen)switch(t.key){case"Tab":return f(t)}},b=function(t){if(o.default.isOpen)return"Tab"===t.key&&t.shiftKey?d(t):void 0},v=function(){var t=i.getNode(l);t&&(t.tabIndex=0,t.focus())},g=function(){var t=i.getNode(c),e=t.querySelectorAll("."+l),n=e.length-1,o=e[n];o&&o.focus()},h=function(t){t[t.length-1].addEventListener("keydown",m)},w=function(t){t[0].addEventListener("keydown",b)},y=function(){var t=i.getNode(c),e=t.querySelectorAll("."+l);e.length&&(h(e),w(e))},x=function(t){if(i.getNode(u)===t.target)return r.onAction(a.CANCEL_KEY)},_=function(t){var e=i.getNode(u);e.removeEventListener("click",x),t&&e.addEventListener("click",x)},k=function(t){o.default.timer&&clearTimeout(o.default.timer),t&&(o.default.timer=window.setTimeout(function(){return r.onAction(a.CANCEL_KEY)},t))},O=function(t){t.closeOnEsc?document.addEventListener("keyup",p):document.removeEventListener("keyup",p),t.dangerMode?v():g(),y(),_(t.closeOnClickOutside),k(t.timer)};e.default=O},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var o=n(1),r=n(3),i=n(37),a=n(38),s={title:null,text:null,icon:null,buttons:r.defaultButtonList,content:null,className:null,closeOnClickOutside:!0,closeOnEsc:!0,dangerMode:!1,timer:null},c=Object.assign({},s);e.setDefaults=function(t){c=Object.assign({},s,t)};var l=function(t){var e=t&&t.button,n=t&&t.buttons;return void 0!==e&&void 0!==n&&o.throwErr("Cannot set both 'button' and 'buttons' options!"),void 0!==e?{confirm:e}:n},u=function(t){return o.ordinalSuffixOf(t+1)},f=function(t,e){o.throwErr(u(e)+" argument ('"+t+"') is invalid")},d=function(t,e){var n=t+1,r=e[n];o.isPlainObject(r)||void 0===r||o.throwErr("Expected "+u(n)+" argument ('"+r+"') to be a plain object")},p=function(t,e){var n=t+1,r=e[n];void 0!==r&&o.throwErr("Unexpected "+u(n)+" argument ("+r+")")},m=function(t,e,n,r){var i=typeof e,a="string"===i,s=e instanceof Element;if(a){if(0===n)return{text:e};if(1===n)return{text:e,title:r[0]};if(2===n)return d(n,r),{icon:e};f(e,n)}else{if(s&&0===n)return d(n,r),{content:e};if(o.isPlainObject(e))return p(n,r),e;f(e,n)}};e.getOpts=function(){for(var t=[],e=0;eStill the only web server to use TLS automatically and by default. Deploy and scale HTTPS effortlessly with Caddy 2.
    - Download + Download Get Started
    Caddy uses only the Apache 2.0 open source license. @@ -470,7 +470,7 @@ http://localhost {