Index: milo/common/funcs.go |
diff --git a/milo/common/funcs.go b/milo/common/funcs.go |
deleted file mode 100644 |
index c2786177964e577ac9a145950bed11f0908799d7..0000000000000000000000000000000000000000 |
--- a/milo/common/funcs.go |
+++ /dev/null |
@@ -1,284 +0,0 @@ |
-// Copyright 2016 The LUCI Authors. |
-// |
-// Licensed under the Apache License, Version 2.0 (the "License"); |
-// you may not use this file except in compliance with the License. |
-// You may obtain a copy of the License at |
-// |
-// http://www.apache.org/licenses/LICENSE-2.0 |
-// |
-// Unless required by applicable law or agreed to in writing, software |
-// distributed under the License is distributed on an "AS IS" BASIS, |
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
-// See the License for the specific language governing permissions and |
-// limitations under the License. |
- |
-package common |
- |
-import ( |
- "bytes" |
- "fmt" |
- "html/template" |
- "net/http" |
- "strings" |
- "time" |
- |
- "github.com/luci/luci-go/milo/api/resp" |
-) |
- |
-// A collection of useful templating functions |
- |
-// funcMap is what gets fed into the template bundle. |
-var funcMap = template.FuncMap{ |
- "humanDuration": humanDuration, |
- "parseRFC3339": parseRFC3339, |
- "linkify": linkify, |
- "linkifySet": linkifySet, |
- "obfuscateEmail": obfuscateEmail, |
- "localTime": localTime, |
- "shortHash": shortHash, |
- "startswith": strings.HasPrefix, |
- "sub": sub, |
- "consoleHeader": consoleHeader, |
- "pagedURL": pagedURL, |
- "formatTime": formatTime, |
- "percent": percent, |
-} |
- |
-// localTime returns a <span> element with t in human format |
-// that will be converted to local timezone in the browser. |
-// Recommended usage: {{ .Date | localTime "N/A" }} |
-func localTime(ifZero string, t time.Time) template.HTML { |
- if t.IsZero() { |
- return template.HTML(template.HTMLEscapeString(ifZero)) |
- } |
- milliseconds := t.UnixNano() / 1e6 |
- return template.HTML(fmt.Sprintf( |
- `<span class="local-time" data-timestamp="%d">%s</span>`, |
- milliseconds, |
- t.Format(time.RFC850))) |
-} |
- |
-func consoleHeader(brs []resp.BuilderRef) template.HTML { |
- // First, split things into nice rows and find the max depth. |
- cat := make([][]string, len(brs)) |
- depth := 0 |
- for i, b := range brs { |
- cat[i] = b.Category |
- if len(cat[i]) > depth { |
- depth = len(cat[i]) |
- } |
- } |
- |
- result := "" |
- for row := 0; row < depth; row++ { |
- result += "<tr><th></th>" |
- // "" is the first node, " " is an empty node. |
- current := "" |
- colspan := 0 |
- for _, br := range cat { |
- colspan++ |
- var s string |
- if row >= len(br) { |
- s = " " |
- } else { |
- s = br[row] |
- } |
- if s != current || current == " " { |
- if current != "" || current == " " { |
- result += fmt.Sprintf(`<th colspan="%d">%s</th>`, colspan, current) |
- colspan = 0 |
- } |
- current = s |
- } |
- } |
- if colspan != 0 { |
- result += fmt.Sprintf(`<th colspan="%d">%s</th>`, colspan, current) |
- } |
- result += "</tr>" |
- } |
- |
- // Last row: The actual builder shortnames. |
- result += "<tr><th></th>" |
- for _, br := range brs { |
- result += fmt.Sprintf("<th>%s</th>", br.ShortName) |
- } |
- result += "</tr>" |
- return template.HTML(result) |
-} |
- |
-// humanDuration translates d into a human readable string of x units y units, |
-// where x and y could be in days, hours, minutes, or seconds, whichever is the |
-// largest. |
-func humanDuration(d time.Duration) string { |
- t := int64(d.Seconds()) |
- day := t / 86400 |
- hr := (t % 86400) / 3600 |
- |
- if day > 0 { |
- if hr != 0 { |
- return fmt.Sprintf("%d days %d hrs", day, hr) |
- } |
- return fmt.Sprintf("%d days", day) |
- } |
- |
- min := (t % 3600) / 60 |
- if hr > 0 { |
- if min != 0 { |
- return fmt.Sprintf("%d hrs %d mins", hr, min) |
- } |
- return fmt.Sprintf("%d hrs", hr) |
- } |
- |
- sec := t % 60 |
- if min > 0 { |
- if sec != 0 { |
- return fmt.Sprintf("%d mins %d secs", min, sec) |
- } |
- return fmt.Sprintf("%d mins", min) |
- } |
- |
- if sec != 0 { |
- return fmt.Sprintf("%d secs", sec) |
- } |
- |
- if d > time.Millisecond { |
- return fmt.Sprintf("%d ms", d/time.Millisecond) |
- } |
- |
- return "0" |
-} |
- |
-// obfuscateEmail converts a string containing email adddress email@address.com |
-// into email<junk>@address.com. |
-func obfuscateEmail(email string) template.HTML { |
- email = template.HTMLEscapeString(email) |
- return template.HTML(strings.Replace( |
- email, "@", "<span style=\"display:none\">ohnoyoudont</span>@", -1)) |
-} |
- |
-// parseRFC3339 parses time represented as a RFC3339 or RFC3339Nano string. |
-// If cannot parse, returns zero time. |
-func parseRFC3339(s string) time.Time { |
- t, err := time.Parse(time.RFC3339, s) |
- if err == nil { |
- return t |
- } |
- t, err = time.Parse(time.RFC3339Nano, s) |
- if err == nil { |
- return t |
- } |
- return time.Time{} |
-} |
- |
-// formatTime takes a time object and returns a formatted RFC3339 string. |
-func formatTime(t time.Time) string { |
- return t.Format(time.RFC3339) |
-} |
- |
-// linkifyTemplate is the template used in "linkify". Because the template, |
-// itself recursively invokes "linkify", we will initialize it in explicitly |
-// in "init()". |
-// |
-// linkifySetTemplate is the template used in "linkifySet". |
-var linkifyTemplate, linkifySetTemplate *template.Template |
- |
-// linkify turns a resp.LinkSet struct into a canonical link. |
-func linkify(link *resp.Link) template.HTML { |
- if link == nil { |
- return "" |
- } |
- buf := bytes.Buffer{} |
- if err := linkifyTemplate.Execute(&buf, link); err != nil { |
- panic(err) |
- } |
- return template.HTML(buf.Bytes()) |
-} |
- |
-// linkifySet turns a resp.Link struct into a canonical link. |
-func linkifySet(linkSet resp.LinkSet) template.HTML { |
- if len(linkSet) == 0 { |
- return "" |
- } |
- buf := bytes.Buffer{} |
- if err := linkifySetTemplate.Execute(&buf, linkSet); err != nil { |
- panic(err) |
- } |
- return template.HTML(buf.Bytes()) |
-} |
- |
-// sub subtracts one number from another, because apparently go templates aren't |
-// smart enough to do that. |
-func sub(a, b int) int { |
- return a - b |
-} |
- |
-// shortHash abbriviates a git hash into 6 characters. |
-func shortHash(s string) string { |
- if len(s) > 6 { |
- return s[0:6] |
- } |
- return s |
-} |
- |
-// pagedURL returns a self URL with the given cursor and limit paging options. |
-// if limit is set to 0, then inherit whatever limit is set in request. If |
-// both are unspecified, then limit is omitted. |
-func pagedURL(r *http.Request, limit int, cursor string) string { |
- if limit == 0 { |
- var err error |
- limit, err = GetLimit(r) |
- if err != nil { |
- // This should not happen because the handler should've already validated the |
- // limit earlier in the process. |
- panic(err) |
- } |
- if limit < 0 { |
- limit = 0 |
- } |
- } |
- values := r.URL.Query() |
- switch cursor { |
- case "EMPTY": |
- values.Del("cursor") |
- case "": |
- // Do nothing, just leave the cursor in. |
- default: |
- values.Set("cursor", cursor) |
- } |
- switch { |
- case limit < 0: |
- values.Del("limit") |
- case limit > 0: |
- values.Set("limit", fmt.Sprintf("%d", limit)) |
- } |
- result := *r.URL |
- result.RawQuery = values.Encode() |
- return result.String() |
-} |
- |
-// percent divides one number by a divisor and returns the percentage in string form. |
-func percent(numerator, divisor int) string { |
- p := float64(numerator) * 100.0 / float64(divisor) |
- return fmt.Sprintf("%.1f", p) |
-} |
- |
-func init() { |
- linkifySetTemplate = template.Must( |
- template.New("linkifySet"). |
- Funcs(template.FuncMap{ |
- "linkify": linkify, |
- }).Parse( |
- `{{ range $i, $link := . }}` + |
- `{{ if gt $i 0 }} {{ end }}` + |
- `{{ $link | linkify}}` + |
- `{{ end }}`)) |
- |
- linkifyTemplate = template.Must( |
- template.New("linkify"). |
- Parse( |
- `<a href="{{.URL}}">` + |
- `{{if .Img}}<img src="{{.Img}}"{{if .Alt}} alt="{{.Alt}}"{{end}}>` + |
- `{{else if .Alias}}[{{.Label}}]` + |
- `{{else}}{{.Label}}{{end}}` + |
- `</a>`)) |
-} |