package h import ( "bytes" "fmt" "html/template" "io" "net/url" "gopkg.awl.red/lx/internal/config" ) var templateBecome = template.Must(template.New("template_become").Funcs(tmplfuncs).Parse(`<!doctype html> <html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> {{.StyleTag}} <style> form .cell { height: 40px; } form .cell.price { height: 74px; } form .cell.plan-label { height: 74px; } form .cell.plan { height: 74px; display: flex; flex-flow: column; align-items: center; justify-content: flex-start; } form .cell.plan label { font-weight: bold; padding: 0; width: initial; } #pay-schedule { display: flex; justify-content: center; align-items: center; } #pay-schedule .option { display: flex; flex-flow: column; align-items: center; justify-content: center; margin: 40px; } #pay-schedule .option label { font-weight: bold; width: initial; padding: 0; } </style> </head><body> {{ $cashEnabled := .Data.CashEnabled }} {{ $cashStart := .Data.CashPlans.Start }} {{ $cashMedium := .Data.CashPlans.Medium }} {{ $cashHigh := .Data.CashPlans.High }} <title>{{ .Config.Title }} members</title> <h1>{{ .Config.Title }} members</h1> <script> function removeToast(id) { const el = document.getElementById(id) if (el) { el.remove() } } {{ if .Data.DidLogout }} function removeToastLogout() { window.history.pushState({}, document.title, window.location.pathname); removeToast('toast-logout') } setTimeout(removeToastLogout, 10000) {{ end }} {{ if .Data.BadLogin }} function removeToastBadLogin() { removeToast('toast-bad-login') window.history.pushState({}, document.title, window.location.pathname); } setTimeout(removeToastBadLogin, 10000) {{ end }} function showPlanPriceSelection(id) { [...document.querySelectorAll('.price-option')].forEach(function(el) { fontWeight = 'normal'; if (el.id === id) { fontWeight = 'bold'; } el.style.fontWeight = fontWeight; }) } window.showPlanPriceSelection = showPlanPriceSelection // ids are {[p]ay}_{[s]tart|[m]edium|[h]igh}_{m[o]nthly|[a]nnual|[c]ash} // e.g. high cash is p_h_c </script> <noscript> <style> .toast { color: purple; } </style> </noscript> {{ if .Data.DidLogout }} <div class="toast" id="toast-logout" onclick="removeToastLogout()" style="position:fixed;top:0;right:0;padding:20px;background:green;">Logged out successfully ❌</div> {{ end }} {{ if .Data.BadLogin }} <div class="toast" id="toast-bad-login" onclick="removeToastBadLogin()" style="position:fixed;top:0;right:0;padding:20px;background:red;">Error logging in ❌</div> {{ end }} <details> <summary><h2 style="display:inline;">Login</h2></summary> <form id="login" method="post" action="/_ui/login"> <div> <label for="email">Email: </label> <input id="email" name="email" type="text" autocomplete="on"></input> </div> <div> <label for="username">Username: </label> <input id="username" name="username" type="text" autocomplete="on"></input> </div> <div> <label for="password">Password: </label> <input id="password" name="password" type="password" {{ if .Config.PasswordRequired }}required{{ end }} ></input> </div> <div> <input id="submit-login" name="submit" type="submit" value="Log in"></input> </div> </form> </details> <details> <summary> <h2 style="display: inline">Sign up</h2> <p>{{AsHtml .Config.Purpose}}</p> </summary> <form id="become" method="post" action="/_ui/become" style="width: 100%; max-width: 1000px;"> <div style="display:block;"> <div id="pay-schedule"> <div class="option"> <label for="pay_monthly">Pay monthly: </label> <input id="pay_monthly" type="radio" name="pay_schedule" value="monthly"> </div> <div class="option"> <label for="pay_annual">Pay annual: </label> <input id="pay_annual" type="radio" name="pay_schedule" value="annual" {{ if not .Data.CashEnabled }}checked{{ end }}> </div> {{ if .Data.CashEnabled }} <div class="option"> <label for="pay_cash_annual">Pay annual (cash): </label> <input id="pay_cash_annual" type="radio" name="pay_schedule" value="cash_annual" checked> </div> {{ end }} </div> <div style="display: flex; flex-direction: row;"> <div style="order: 0" style="flex-shrink:0; flex-grow:0;"> <div class="cell plan-label">Plan:</div> <div class="cell price">Price:</div> <div class="cell">Anonymous links <span title="e.g. randomized 7 character slug, {{ .Data.Scheme }}://{{ .Data.Host}}/OvuS1xN">(?)</span>:</div> <div class="cell">Named links <span title="e.g. name of your choice, up to 255 characters, {{ .Data.Scheme }}://{{ .Data.Host}}/MyBusiness">(?)</span>:</div> <div class="cell">Folders <span title="e.g. reserve all names underneath a slash for logical grouping, e.g. {{ .Data.Scheme }}://{{ .Data.Host }}/mybiz, and only you can create links under it, such as {{ .Data.Scheme }}://{{ .Data.Host }}/mybiz/buy or {{ .Data.Scheme }}://{{ .Data.Host }}/mybiz/blackfriday">(?)</span>:</div> <div class="cell">Analytics:</div> <div class="cell">Analytics retention:</div> <div class="cell">Analytics granularity:</div> <div class="cell">Analytics View Limits:</div> <div class="cell">API access <span title="See {{ .Data.Scheme }}://{{ .Data.Host }}/_ui/openapi">(?)</span>:</div> </div> {{ range .Config.BusinessTiers }} {{ if or (le .FeatureLevel 0) (ge .FeatureLevel 9999) }} {{ continue }} {{ end }} <div style="order: {{.FeatureLevel}}; flex: 1 0 auto;"> <div class="cell plan"><label for="plan-{{.Name}}">{{ .DisplayName }}</label><input type="radio" id="plan-{{.Name}}" name="plan" value="{{.Name}}" {{ if eq .Name "medium"}} checked {{end}}></input></div> <div class="cell price"> <div class="price-option" id="p_{{L1 .Name}}_m">{{ AsPrice .PriceByMonth}} monthly</div> <div class="price-option" id="p_{{L1 .Name}}_a">{{ AsPrice .PriceByYear }} annual ({{ AsMonthlyPrice .PriceByYear }}/mo)</div> {{ if $cashEnabled }}<div class="price-option" id="p_{{L1 .Name}}_c"> {{ $cashPrice := 0 }} {{ if eq .Name "start" }}{{ AsPrice $cashStart}}{{ $cashPrice = $cashStart }}{{ end }} {{ if eq .Name "medium" }}{{ AsPrice $cashMedium}}{{ $cashPrice = $cashMedium }}{{ end }} {{ if eq .Name "high" }}{{ AsPrice $cashHigh}}{{ $cashPrice = $cashHigh }}{{ end }} cash annual ({{ AsMonthlyPrice $cashPrice }}/mo) </div>{{ end }}</div> <div class="cell">{{ .BackhalvesAnonymous }}</div> <div class="cell">{{ .BackhalvesNamed }}</div> <div class="cell">{{ .Folders }}</div> <div class="cell">Y</div> <div class="cell">{{ SecAsD .AnalyticsRetention }} </div> <div class="cell">{{ SecAsD .AnalyticsMaxGranularity }} </div> <div class="cell">{{ .AnalyticsViewLimit }} </div> <div class="cell">Y</div> </div> {{ end }} </div> <details id="user-account"> <summary><h3 style="display:inline;">User acount</h3></summary> <div> <label for="email">Email: </label> <input id="email" name="email" type="text" required autocomplete="on"></input> </div> <div> <label for="username">Username: </label> <input id="username" name="username" type="text" required autocomplete="on"></input> </div> <div> <label for="firstname">First name: </label> <input id="firstname" name="firstname" type="text" required autocomplete="on"></input> </div> <div> <label for="lastname">Last name: </label> <input id="lastname" name="lastname" type="text" required autocomplete="on"></input> </div> <div> <label for="password">Password: </label> <input id="password" name="password" type="password" required></input> </div> <div> <label for="password_confirm">Password (Confirm): </label> <input id="password_confirm" name="password_confirm" type="password" required></input> </div> </details> <details id="business"> <summary><h3 style="display:inline;">business (if applicable) & billing info</h3></summary> <div> <label for="business_name">Business name (type N/A if this is for a personal account): </label> <input id="business_name" name="business_name" type="text" autocomplete="on"></input> </div> <div> <label for="address_line1">Address Line 1: </label> <input required id="address_line1" name="address_line1" type="text" autocomplete="on"></input> </div> <div> <label for="address_line2">Address Line 2: </label> <input id="address_line2" name="address_line2" type="text" autocomplete="on"></input> </div> <div> <label for="address_city">City: </label> <input required id="address_city" name="address_city" type="text" autocomplete="on"></input> </div> <div> <label for="address_state">State: </label> <input required id="address_state" name="address_state" type="text" autocomplete="on"></input> </div> <div> <label for="address_postal_code">Postal Code: </label> <input required id="address_postal_code" name="address_postal_code" type="text" autocomplete="on"></input> </div> <div> <label for="address_country">Country: </label> <input required id="address_country" name="address_country" type="text" autocomplete="on" {{ if .Config.OnlyCountry }}value="{{ .Config.OnlyCountry }}" disabled {{ end }} required></input> </div> </details> <input id="submit-become" name="submit" type="submit" value="Choose plan"> </form> {{ .Data.Footer }} <script> window.showPlanPriceSelection({{ if .Data.CashEnabled }}"p_m_c"{{ else }}"p_m_a"{{ end }}) const becomeForm = document.getElementById('become') const submitPricing = document.getElementById('submit-become') let readySubmit = false; submitPricing.addEventListener('mousedown', function(e) { readySubmit = true; }) becomeForm.addEventListener('submit', function(e) { if (!readySubmit) { e.preventDefault() return false } }) becomeForm.addEventListener('change', function(e) { const targetName = e.target.name if (targetName !== "plan" && targetName !== 'pay_schedule') { return } const valueLetter = e.target.value[0] const plan = targetName === "plan" ? valueLetter : e.currentTarget.elements["plan"].value[0] const paySchedule = targetName === "pay_schedule" ? valueLetter : e.currentTarget.elements["pay_schedule"].value[0] window.showPlanPriceSelection("p_"+plan+"_"+paySchedule) }) </script> </body></html> `)) type TCashPrices struct { Start int Medium int High int } type TDataBecome struct { CashEnabled bool CashPlans TCashPrices DidLogout bool BadLogin bool Host string Scheme string Footer template.HTML } func footer(c *config.Config) string { return fmt.Sprintf(`<footer>Service by <a href="%s">%s</a>.</footer>`, c.CompanyUrl, c.Company) } func TBecome(w io.Writer, u url.Values, c *config.Config, data TDataBecome) error { _, didLogout := u["did_logout"] _, badLogin := u["bad_login"] data.DidLogout = didLogout data.BadLogin = badLogin data.Footer = template.HTML(footer(c)) var stylesheetBuf bytes.Buffer templateBaseStylesheet.Execute(&stylesheetBuf, ConfigTemplateData{Config: c}) return templateBecome.Execute(w, ConfigTemplateData{Config: c, StyleTag: template.HTML(stylesheetBuf.String()), Data: data}) }