func QueryEscape(s string) string {
	const hexchars = "0123456789abcdef"
	last := 0
	numHex := 0
	for i := 0; i < len(s); i++ {
		c := s[i]
		if '0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' ||
			c == '-' || c == '.' || c == '_' {
			continue
		}
		last = i + 1
		numHex++
	}
	if numHex == 0 {
		return s
	}
	j := 0
	b := make([]byte, len(s)+2*numHex)
	for i := 0; i < last; i++ {
		c := s[i]
		if '0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' ||
			c == '-' || c == '.' || c == '_' {
			b[j] = c
		} else {
			b[j] = '%'
			j++
			b[j] = hexchars[c>>4]
			j++
			b[j] = hexchars[c&0xF]
		}
		j++
	}
	if j != len(b) {
		copy(b[j:], s[last:])
	}
	return string(b)
}
QueryEscape escapes the string, so it can be safely placed
inside a URL query.
package builtin
import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"math"
	"reflect"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"
	"unicode"
	"unicode/utf8"
	"github.com/open2b/scriggo"
	"github.com/open2b/scriggo/internal/thirdparties"
	"github.com/open2b/scriggo/native"
)
func Abbreviate(s string, n int) string {
	const spaces = " \n\r\t\f"  
	s = strings.TrimRight(s, spaces)
	if len(s) <= n {
		return s
	}
	if n < 3 {
		return ""
	}
	p := 0
	n2 := 0
	for i := range s {
		switch p {
		case n - 2:
			n2 = i
		case n:
			break
		}
		p++
	}
	if p < n {
		return s
	}
	if p = strings.LastIndexAny(s[:n2], spaces); p > 0 {
		s = strings.TrimRight(s[:p], spaces)
	} else {
		s = ""
	}
	if l := len(s) - 1; l >= 0 && (s[l] == '.' || s[l] == ',') {
		s = s[:l]
	}
	return s + "..."
}
func Abs(x int) int {
	if x < 0 {
		return -x
	}
	return x
}
func Base64(s string) string {
	if s == "" {
		return s
	}
	return base64.StdEncoding.EncodeToString([]byte(s))
}
func Capitalize(s string) string {
	for i, r := range s {
		if isSeparator(r) {
			continue
		}
		if unicode.IsUpper(r) {
			return s
		}
		r = unicode.ToUpper(r)
		b := strings.Builder{}
		b.Grow(len(s))
		b.WriteString(s[:i])
		b.WriteRune(r)
		b.WriteString(s[i+utf8.RuneLen(r):])
		return b.String()
	}
	return s
}
func CapitalizeAll(s string) string {
	prev := ' '
	return strings.Map(func(r rune) rune {
		if isSeparator(prev) {
			prev = r
			return unicode.ToUpper(r)
		}
		prev = r
		return r
	}, s)
}
func Date(year, month, day, hour, min, sec, nsec int, location string) (Time, error) {
	loc, err := time.LoadLocation(location)
	if err != nil {
		return Time{}, replacePrefix(err, "time", "date")
	}
	return NewTime(time.Date(year, time.Month(month), day, hour, min, sec, nsec, loc)), nil
}
func FormatFloat(f float64, format string, precision int) string {
	switch format {
	case "e", "f", "g":
	default:
		panic("formatFloat: invalid format " + strconv.Quote(format))
	}
	if precision < -1 || precision > 1000 {
		panic("formatFloat: invalid precision " + strconv.Itoa(precision))
	}
	return strconv.FormatFloat(f, format[0], precision, 64)
}
func FormatInt(i int, base int) string {
	if base < 2 || base > 36 {
		panic("formatInt: invalid base " + strconv.Itoa(base))
	}
	return strconv.FormatInt(int64(i), base)
}
func HasPrefix(s, prefix string) bool {
	return strings.HasPrefix(s, prefix)
}
func HasSuffix(s, suffix string) bool {
	return strings.HasPrefix(s, suffix)
}
func Hex(s string) string {
	if s == "" {
		return s
	}
	return hex.EncodeToString([]byte(s))
}
func HmacSHA1(message, key string) string {
	mac := hmac.New(sha1.New, []byte(key))
	_, _ = io.WriteString(mac, message)
	s := base64.StdEncoding.EncodeToString(mac.Sum(nil))
	return s
}
func HmacSHA256(message, key string) string {
	mac := hmac.New(sha256.New, []byte(key))
	_, _ = io.WriteString(mac, message)
	s := base64.StdEncoding.EncodeToString(mac.Sum(nil))
	return s
}
func HtmlEscape(s string) native.HTML {
	return scriggo.HTMLEscape(s)
}
func Index(s, substr string) int {
	return strings.Index(s, substr)
}
func IndexAny(s, chars string) int {
	return strings.IndexAny(s, chars)
}
func Join(elems []string, sep string) string {
	return strings.Join(elems, sep)
}
func LastIndex(s, substr string) int {
	return strings.LastIndex(s, substr)
}
func MarshalJSON(v interface{}) (native.JSON, error) {
	b, err := json.Marshal(v)
	if err != nil {
		return "", replacePrefix(err, "json", "marshalJSON")
	}
	return native.JSON(b), nil
}
func MarshalJSONIndent(v interface{}, prefix, indent string) (native.JSON, error) {
	if !onlyJSONWhitespace(prefix) {
		return "", errors.New("marshalJSONIndent: prefix does not contain only whitespace")
	}
	if !onlyJSONWhitespace(indent) {
		return "", errors.New("marshalJSONIndent: indent does not contain only whitespace")
	}
	b, err := json.MarshalIndent(v, prefix, indent)
	if err != nil {
		return "", errors.New(err.Error())
	}
	return native.JSON(b), nil
}
func Max(x, y int) int {
	if x < y {
		return y
	}
	return x
}
func Md5(s string) string {
	h := md5.New()
	h.Write([]byte(s))
	return hex.EncodeToString(h.Sum(nil))
}
func Min(x, y int) int {
	if y < x {
		return y
	}
	return x
}
func Now() Time {
	return NewTime(time.Now())
}
func ParseDuration(s string) (Duration, error) {
	d, err := time.ParseDuration(s)
	if err != nil {
		return 0, replacePrefix(err, "time", "parseDuration")
	}
	return d, nil
}
func ParseFloat(s string) (float64, error) {
	if strings.HasPrefix(s, "0x") {
		return 0, errors.New("parseFloat: parsing " + strconv.Quote(s) + ": invalid syntax")
	}
	f, err := strconv.ParseFloat(s, 64)
	if err != nil {
		e := err.(*strconv.NumError)
		return 0, errors.New("parseFloat: parsing " + strconv.Quote(s) + ": " + e.Err.Error())
	}
	if math.IsNaN(f) || math.IsInf(f, 0) {
		return 0, errors.New("parseFloat: parsing " + strconv.Quote(s) + ": invalid syntax")
	}
	return f, nil
}
func ParseInt(s string, base int) (int, error) {
	if base == 0 {
		return 0, errors.New("parseInt: parsing " + strconv.Quote(s) + ": invalid base 0")
	}
	i, err := strconv.ParseInt(s, base, 0)
	if err != nil {
		e := err.(*strconv.NumError)
		return 0, errors.New("parseInt: parsing " + strconv.Quote(s) + ": " + e.Err.Error())
	}
	return int(i), nil
}
func ParseTime(layout, value string) (Time, error) {
	if layout == "" {
		for _, layout = range thirdparties.TimeLayouts {
			t, err := time.Parse(layout, value)
			if err == nil {
				return NewTime(t), nil
			}
		}
		return Time{}, fmt.Errorf("parseTime: cannot parse \"%s\"", value)
	}
	t, err := time.Parse(layout, value)
	if err != nil {
		return Time{}, replacePrefix(err, "time", "parseTime")
	}
	return NewTime(t), nil
}
func Pow(x, y float64) float64 {
	return math.Pow(x, y)
}