[
  {
    "path": ".gitignore",
    "content": "# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# Folders\n_obj\n_test\n\n# Architecture specific extensions/prefixes\n*.[568vq]\n[568vq].out\n\n*.cgo1.go\n*.cgo2.c\n_cgo_defun.c\n_cgo_gotypes.go\n_cgo_export.*\n\n_testmain.go\n\n*.exe\n*.test\n*.prof\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: go\ngo:\n  - 1.4\n  - 1.5\n  - 1.6\n  - 1.7\n  - 1.8\n  - tip\nenv:\n  global:\n    - BUILD_GOARCH=amd64\n  matrix:\n    - BUILD_GOOS=linux\n    - BUILD_GOOS=darwin\n    - BUILD_GOOS=windows\nbefore_install:\n  - go get github.com/axw/gocov/gocov\n  - go get github.com/mattn/goveralls\n  - if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi\nscript:\n    - $HOME/gopath/bin/goveralls -service=travis-ci\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 Kyoung-chan Lee (leekchan@gmail.com)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "# gtf - a useful set of Golang Template Functions\n[![Build Status](https://travis-ci.org/leekchan/gtf.svg?branch=master)](https://travis-ci.org/leekchan/gtf)\n[![Coverage Status](https://coveralls.io/repos/leekchan/gtf/badge.svg?branch=master&service=github)](https://coveralls.io/github/leekchan/gtf?branch=master)\n[![GoDoc](https://godoc.org/github.com/leekchan/gtf?status.svg)](https://godoc.org/github.com/leekchan/gtf)\n\ngtf is a useful set of Golang Template Functions. The goal of this project is implementing all built-in template filters of Django & Jinja2. \n\n## Basic usages\n\n### Method 1 : Uses gtf.New\n\ngtf.New is a wrapper function of [template.New](https://golang.org/pkg/html/template/#New). It automatically adds the gtf functions to the template's function map and returns [template.Template](http://golang.org/pkg/html/template/#Template).\n\n```Go\npackage main\n\nimport (\n\t\"net/http\"\n\t\"github.com/leekchan/gtf\"\n)\n\nfunc main() {\n    http.HandleFunc(\"/\", func(w http.ResponseWriter, r *http.Request) {\n\t\tfilesize := 554832114\n\t\ttpl, _ := gtf.New(\"test\").Parse(\"{{ . | filesizeformat }}\")\n\t\ttpl.Execute(w, filesize)\n\t})\n    http.ListenAndServe(\":8080\", nil)\n}\n```\n\n### Method 2 : Adds gtf functions to the existing template.\n\nYou can also add the gtf functions to the existing template(html/template package). Just call \".Funcs(gtf.GtfFuncMap)\".\n\n```Go\npackage main\n\nimport (\n\t\"net/http\"\n\t\"html/template\"\n\t\n\t\"github.com/leekchan/gtf\"\n)\n\nfunc main() {\n    http.HandleFunc(\"/\", func(w http.ResponseWriter, r *http.Request) {\n\t\tfilesize := 554832114\n\t\ttpl, _ := template.New(\"test\").Funcs(gtf.GtfFuncMap).Parse(\"{{ . | filesizeformat }}\")\n\t\ttpl.Execute(w, filesize)\n\t})\n    http.ListenAndServe(\":8080\", nil)\n}\n```\n\nWhen you use the \"text/template\" package, call \".Funcs(gtf.GtfTextFuncMap)\".\n\n```Go\npackage main\n\nimport (\n\t\"os\"\n\t\"text/template\"\n\t\n\t\"github.com/leekchan/gtf\"\n)\n\nfunc main() {\n\tfilesize := 554832114\n\ttpl, _ := template.New(\"test\").Funcs(gtf.GtfTextFuncMap).Parse(\"{{ . | filesizeformat }}\")\n\ttpl.Execute(os.Stdout, filesize)\n}\n```\n\n\n## Integration\n\nYou can use gtf with any web frameworks (revel, beego, martini, gin, etc) which use the Golang's built-in [html/template package](http://golang.org/pkg/html/template/).\n\n\n### Injection\n\nYou can inject gtf functions into your webframework's original FuncMap by calling \"gtf.Inject\" / \"gtf.ForceInject\" / \"gtf.InjectWithPrefix\".\n\n#### gtf.Inject\n\ngtf.Inject injects gtf functions into the passed FuncMap. It does not overwrite the original function which have same name as a gtf function.\n\n```Go\nInject(originalFuncMap)\n```\n\n#### gtf.ForceInject\n\ngtf.ForceInject injects gtf functions into the passed FuncMap. It overwrites the original function which have same name as a gtf function.\n\n```Go\nForceInject(originalFuncMap)\n```\n\n\n#### gtf.InjectWithPrefix\n\ngtf.Inject injects gtf functions into the passed FuncMap. It prefixes the gtf functions with the specified prefix. If there are many function which have same names as the gtf functions, you can use this function to prefix the gtf functions.\n\n\n```Go\nInjectWithPrefix(originalFuncMap, \"gtf_\") // prefix : gtf_\n```\n\n\n### [Revel](http://revel.github.io/) integration\n\nCalling \"gtf.Inject(revel.TemplateFuncs)\" injects gtf functions into revel.TemplateFuncs. Just add this one line in init() of init.go, and use gtf functions in your templates! :)\n\n```Go\n// init.go\n\npackage app\n\nimport \"github.com/revel/revel\"\nimport \"github.com/leekchan/gtf\"\n\nfunc init() {\n    gtf.Inject(revel.TemplateFuncs)\n}\n```\n\n\n### [Beego](http://beego.me/) integration\n\nAdd these three lines before \"beego.Run()\" in your main() function. This code snippet will inject gtf functions into beego's FuncMap.\n\n```Go\nfor k, v := range gtf.GtfFuncMap {\n    beego.AddFuncMap(k, v)\n}\n```\n\n**Full example:**\n\n```Go\npackage main\n\nimport (\n    \"github.com/astaxie/beego\"\n    \"github.com/beego/i18n\"\n\n    \"github.com/beego/samples/WebIM/controllers\"\n    \n    \"github.com/leekchan/gtf\"\n)\n\nconst (\n    APP_VER = \"0.1.1.0227\"\n)\n\nfunc main() {\n    beego.Info(beego.AppName, APP_VER)\n\n    // Register routers.\n    beego.Router(\"/\", &controllers.AppController{})\n    // Indicate AppController.Join method to handle POST requests.\n    beego.Router(\"/join\", &controllers.AppController{}, \"post:Join\")\n\n    // Long polling.\n    beego.Router(\"/lp\", &controllers.LongPollingController{}, \"get:Join\")\n    beego.Router(\"/lp/post\", &controllers.LongPollingController{})\n    beego.Router(\"/lp/fetch\", &controllers.LongPollingController{}, \"get:Fetch\")\n\n    // WebSocket.\n    beego.Router(\"/ws\", &controllers.WebSocketController{})\n    beego.Router(\"/ws/join\", &controllers.WebSocketController{}, \"get:Join\")\n\n    // Register template functions.\n    beego.AddFuncMap(\"i18n\", i18n.Tr)\n    \n    // Register gtf functions.\n    for k, v := range gtf.GtfFuncMap {\n        beego.AddFuncMap(k, v)\n    }\n\n    beego.Run()\n}\n```\n\n\n### Other web frameworks (TODO)\n\nI will add the detailed integration guides for other web frameworks soon!\n\n\n## Safety\nAll gtf functions have their own recovery logics. The basic behavior of the recovery logic is silently swallowing all unexpected panics. All gtf functions would not make any panics in runtime. (**Production Ready!**)\n\nIf a panic occurs inside a gtf function, the function will silently swallow the panic and return \"\" (empty string). If you meet any unexpected empty output, [please make an issue](https://github.com/leekchan/gtf/issues/new)! :)\n\n\n\n## Reference\n\n### Index\n\n* [replace](#replace)\n* [findreplace](#findreplace)\n* [default](#default)\n* [length](#length)\n* [lower](#lower)\n* [upper](#upper)\n* [truncatechars](#truncatechars)\n* [urlencode](#urlencode)\n* [wordcount](#wordcount)\n* [divisibleby](#divisibleby)\n* [lengthis](#lengthis)\n* [trim](#trim)\n* [capfirst](#capfirst)\n* [pluralize](#pluralize)\n* [yesno](#yesno)\n* [rjust](#rjust)\n* [ljust](#ljust)\n* [center](#center)\n* [filesizeformat](#filesizeformat)\n* [apnumber](#apnumber)\n* [intcomma](#intcomma)\n* [ordinal](#ordinal)\n* [first](#first)\n* [last](#last)\n* [join](#join)\n* [slice](#slice)\n* [random](#random)\n* [striptags](#striptags)\n\n\n\n#### replace\n\nRemoves all values of arg from the given string.\n\n* supported value types : string\n* supported argument types : string\n\n```\n{{ value | replace \" \" }}\n```\nIf value is \"The Go Programming Language\", the output will be \"TheGoProgrammingLanguage\".\n\n\n\n#### findreplace\n\nReplaces all instances of the first argument with the second.\n\n* supported value types : string\n* supported argument types : string\n\n```\n{{ value | findreplace \" \" \"-\" }}\n```\nIf value is \"The Go Programming Language\", the output will be \"The-Go-Programming-Language\".\n\n\n\n#### default\n\n1. If the given string is \"\"(empty string), uses the given default argument.\n1. If the given array/slice/map is empty, uses the given default argument.\n1. If the given boolean value is false, uses the given default argument.\n\n* supported value types : string, array, slice, map, boolean\n* supported argument types : all\n\n```\n{{ value | default \"default value\" }}\n```\nIf value is \"\"(the empty string), the output will be \"default value\".\n\n\n\n#### length\n\nReturns the length of the given string/array/slice/map.\n\n* supported value types : string, array, slice, map\n\nThis function also supports unicode strings.\n\n```\n{{ value | length }}\n```\nIf value is \"The Go Programming Language\", the output will be 27.\n\n\n\n#### lower\n\nConverts the given string into all lowercase.\n\n* supported value types : string\n\n```\n{{ value | lower }}\n```\nIf value is \"The Go Programming Language\", the output will be \"the go programming language\".\n\n\n\n#### upper\n\nConverts the given string into all uppercase.\n\n* supported value types : string\n\n```\n{{ value | upper }}\n```\nIf value is \"The Go Programming Language\", the output will be \"THE GO PROGRAMMING LANGUAGE\".\n\n\n\n#### truncatechars\n\nTruncates the given string if it is longer than the specified number of characters. Truncated strings will end with a translatable ellipsis sequence (\"...\")\n\n* supported value types : string\n\n**Argument:** Number of characters to truncate to\n\nThis function also supports unicode strings.\n\n```\n{{ value | truncatechars 12 }}\n```\n\n**Examples**\n\n1. If input is {{ \"The Go Programming Language\" | truncatechars 12 }}, the output will be \"The Go Pr...\". (basic string)\n1. If input is {{ \"안녕하세요. 반갑습니다.\" | truncatechars 12 }}, the output will be \"안녕하세요. 반갑...\". (unicode)\n1. If input is {{ \"안녕하세요. The Go Programming Language\" | truncatechars 30 }}, the output will be \"안녕하세요. The Go Programming L...\". (unicode)\n1. If input is {{ \"The\" | truncatechars 30 }}, the output will be \"The\". (If the length of the given string is shorter than the argument, the output will be the original string.)\n1. If input is {{ \"The Go Programming Language\" | truncatechars 3 }}, the output will be \"The\". (If the argument is less than or equal to 3, the output will not contain \"...\".)\n1. If input is {{ \"The Go\" | truncatechars -1 }}, the output will be \"The Go\". (If the argument is less than 0, the argument will be ignored.)\n\n\n\n#### urlencode\n\nEscapes the given string for use in a URL.\n\n* supported value types : string\n\n```\n{{ value | urlencode }}\n```\n\nIf value is \"http://www.example.org/foo?a=b&c=d\", the output will be \"http%3A%2F%2Fwww.example.org%2Ffoo%3Fa%3Db%26c%3Dd\".\n\n\n\n#### wordcount\n\nReturns the number of words.\n\n* supported value types : string\n\n```\n{{ value | wordcount }}\n```\n\nIf value is \"The Go Programming Language\", the output will be 4.\n\n\n\n#### divisibleby\n\nReturns true if the value is divisible by the argument.\n\n* supported value types : int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64\n* supported argument types : int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64\n\n```\n{{ value | divisibleby 3 }}\n```\n\n**Examples**\n\n1. If input is {{ 21 | divisibleby 3 }}, the output will be true.\n1. If input is {{ 21 | divisibleby 4 }}, the output will be false.\n1. If input is {{ 3.0 | divisibleby 1.5 }}, the output will be true.\n\n\n\n#### lengthis\n\nReturns true if the value's length is the argument, or false otherwise.\n\n* supported value types : string, array, slice, map\n* supported argument types : int\n\n```\n{{ value | lengthis 3 }}\n```\n\nThis function also supports unicode strings.\n\n**Examples**\n\n1. If input is {{ \"Go\" | lengthis 2 }}, the output will be true.\n1. If input is {{ \"안녕하세요. Go!\" | lengthis 10 }}, the output will be true.\n\n\n\n#### trim\n\nStrips leading and trailing whitespace. \n\n* supported value types : string\n\n```\n{{ value | trim }}\n```\n\n\n\n#### capfirst\n\nCapitalizes the first character of the given string.\n\n* supported value types : string\n\n```\n{{ value | capfirst }}\n```\n\nIf value is \"the go programming language\", the output will be \"The go programming language\".\n\n\n\n#### pluralize\n\nReturns a plural suffix if the value is not 1. You can specify both a singular and plural suffix, separated by a comma.\n\n**Argument:** singular and plural suffix. \n\n1. \"s\" --> specify a singular suffix.\n2. \"y,ies\" --> specify both a singular and plural suffix.\n\n* supported value types : int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64\n* supported argument types : string\n\n```\n{{ value | pluralize \"s\" }}\n{{ value | pluralize \"y,ies\" }}\n```\n\n**Examples**\n\n1. You have 0 message{{ 0 | pluralize \"s\" }} --> You have 0 messages\n2. You have 1 message{{ 1 | pluralize \"s\" }} --> You have 1 message\n3. 0 cand{{ 0 | pluralize \"y,ies\" }} --> 0 candies\n4. 1 cand{{ 1 | pluralize \"y,ies\" }} --> 1 candy\n5. 2 cand{{ 2 | pluralize \"y,ies\" }} --> 2 candies\n\n\n\n#### yesno\n\nReturns argument strings according to the given boolean value.\n\n* supported value types : boolean\n* supported argument types : string\n\n**Argument:** any value for true and false\n\n```\n{{ value | yesno \"yes!\" \"no!\" }}\n```\n\n\n#### rjust\n\nRight-aligns the given string in a field of a given width. This function also supports unicode strings. \n\n* supported value types : string\n\n```\n{{ value | rjust 10 }}\n```\n\n**Examples**\n\n1. If input is {{ \"Go\" | rjust 10 }}, the output will be \"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Go\".\n1. If input is {{ \"안녕하세요\" | rjust 10 }}, the output will be \"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;안녕하세요\".\n\n\n\n#### ljust\n\nLeft-aligns the given string in a field of a given width. This function also supports unicode strings. \n\n* supported value types : string\n\n```\n{{ value | ljust 10 }}\n```\n\n**Examples**\n\n1. If input is {{ \"Go\" | ljust 10 }}, the output will be \"Go&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\".\n1. If input is {{ \"안녕하세요\" | ljust 10 }}, the output will be \"안녕하세요&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\".\n\n\n\n#### center\n\nCenters the given string in a field of a given width. This function also supports unicode strings. \n\n* supported value types : string\n\n```\n{{ value | center 10 }}\n```\n\n**Examples**\n\n1. If input is {{ \"Go\" | center 10 }}, the output will be \"&nbsp;&nbsp;&nbsp;&nbsp;Go&nbsp;&nbsp;&nbsp;&nbsp;\".\n1. If input is {{ \"안녕하세요\" | center 10 }}, the output will be \"&nbsp;&nbsp;안녕하세요&nbsp;&nbsp;&nbsp;\".\n\n\n\n#### filesizeformat\n\nFormats the value like a human readable file size.\n\n* supported value types : int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64\n\n```\n{{ value | filesizeformat }}\n```\n\n**Examples**\n\n1. {{ 234 | filesizeformat }} --> \"234 bytes\"\n1. {{ 12345 | filesizeformat }} --> \"12.1 KB\"\n1. {{ 12345.35335 | filesizeformat }} --> \"12.1 KB\"\n1. {{ 1048576 | filesizeformat } --> \"1 MB\"\n1. {{ 554832114 | filesizeformat }} --> \"529.1 MB\"\n1. {{ 14868735121 | filesizeformat }} --> \"13.8 GB\"\n1. {{ 14868735121365 | filesizeformat }} --> \"13.5 TB\"\n1. {{ 1486873512136523 | filesizeformat }} --> \"1.3 PB\"\n\n\n\n#### apnumber\n\nFor numbers 1-9, returns the number spelled out. Otherwise, returns the number. \n\n* supported value types : int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64\n\n```\n{{ value | apnumber }}\n```\n\n**Examples**\n\n1. {{ 1 | apnumber }} --> one\n1. {{ 2 | apnumber }} --> two\n1. {{ 3 | apnumber }} --> three\n1. {{ 9 | apnumber }} --> nine\n1. {{ 10 | apnumber }} --> 10\n1. {{ 1000 | apnumber }} --> 1000\n\n\n\n#### intcomma\n\nConverts an integer to a string containing commas every three digits.\n\n* supported value types : int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64\n\n```\n{{ value | intcomma }}\n```\n\n**Examples**\n\n1. {{ 1000 | intcomma }} --> 1,000\n1. {{ -1000 | intcomma }} --> -1,000\n1. {{ 1578652313 | intcomma }} --> 1,578,652,313\n\n\n\n#### ordinal\n\nConverts an integer to its ordinal as a string.\n\n* supported value types : int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64\n\n```\n{{ value | ordinal }}\n```\n\n**Examples**\n\n1. {{ 1 | ordinal }} --> 1st\n1. {{ 2 | ordinal }} --> 2nd\n1. {{ 3 | ordinal }} --> 3rd\n1. {{ 11 | ordinal }} --> 11th\n1. {{ 12 | ordinal }} --> 12th\n1. {{ 13 | ordinal }} --> 13th\n1. {{ 14 | ordinal }} --> 14th\n\n\n\n#### first\n\nReturns the first item in the given value.\n\n* supported value types : string, slice, array\n\nThis function also supports unicode strings.\n\n```\n{{ value | first }}\n```\n\n**Examples**\n\n1. If value is the string \"The go programming language\", the output will be the string \"T\".\n1. If value is the string \"안녕하세요\", the output will be the string \"안\". (unicode)\n1. If value is the slice []string{\"go\", \"python\", \"ruby\"}, the output will be the string \"go\".\n1. If value is the array [3]string{\"go\", \"python\", \"ruby\"}, the output will be the string \"go\".\n\n\n\n#### last\n\nReturns the last item in the given value.\n\n* supported value types : string, slice, array\n\nThis function also supports unicode strings.\n\n```\n{{ value | last }}\n```\n\n**Examples**\n\n1. If value is the string \"The go programming language\", the output will be the string \"e\".\n1. If value is the string \"안녕하세요\", the output will be the string \"요\". (unicode)\n1. If value is the slice []string{\"go\", \"python\", \"ruby\"}, the output will be the string \"ruby\".\n1. If value is the array [3]string{\"go\", \"python\", \"ruby\"}, the output will be the string \"ruby\".\n\n\n\n\n#### join\n\nConcatenates the given slice to create a single string. The given argument (separator) will be placed between elements in the resulting string.\n\n```\n{{ value | join \" \" }}\n```\n\nIf value is the slice []string{\"go\", \"python\", \"ruby\"}, the output will be the string \"go python ruby\"\n\n\n\n\n#### slice\n\nReturns a slice of the given value. The first argument is the start position, and the second argument is the end position.\n\n* supported value types : string, slice\n* supported argument types : int\n\nThis function also supports unicode strings.\n\n```\n{{ value | slice 0 2 }}\n```\n\n**Examples**\n\n1. If input is {{ \"The go programming language\" | slice 0 6 }}, the output will be \"The go\".\n1. If input is {{ \"안녕하세요\" | slice 0 2 }}, the output will be \"안녕\". (unicode)\n1. If input is {{ []string{\"go\", \"python\", \"ruby\"} | slice 0 2 }}, the output will be []string{\"go\", \"python\"}.\n\n\n\n\n#### random\n\nReturns a random item from the given value.\n\n* supported value types : string, slice, array\n\nThis function also supports unicode strings.\n\n```\n{{ value | random }}\n```\n\n**Examples**\n\n1. If input is {{ \"The go programming language\" | random }}, the output could be \"T\".\n1. If input is {{ \"안녕하세요\" | random }}, the output could be \"안\". (unicode)\n1. If input is {{ []string{\"go\", \"python\", \"ruby\"} | random }}, the output could be \"go\".\n1. If input is {{ [3]string{\"go\", \"python\", \"ruby\"} | random }}, the output could be \"go\".\n\n\n\n\n#### randomintrange\n\nReturns a random integer value between the first and second argument\n\n* supported value types : int\n\n```\n{{ value | randomintrange 0 5}}\n```\n\n**Examples**\n\n1. If input is {{ randomintrange 0 5 }}, the output could be \"4\".\n\n\n\n\n#### striptags\n\nMakes all possible efforts to strip all [X]HTML tags from given value.\n\n* supported value types : string\n\nThis function also supports unicode strings.\n\n```\n{{ value | striptags }}\n```\n\n**Examples**\n\n1. If input is {{ \"&lt;strong&gt;text&lt;/strong&gt;\" | striptags }}, the output will be \"text\".\n1. If input is {{ \"&lt;strong&gt;&lt;em&gt;&#50504;&#45397;&#54616;&#49464;&#50836;&lt;/em&gt;&lt;/strong&gt;\" | striptags }}, the output will be \"안녕하세요\". (unicode)\n1. If input is {{ \"&lt;a href=\"/link\"&gt;text &lt;strong&gt;&#50504;&#45397;&#54616;&#49464;&#50836;&lt;/strong&gt;&lt;/a&gt;\" | striptags }}, the output will be \"text 안녕하세요\".\n\n\n\n\n\n## Goal\nThe first goal is implementing all built-in template filters of Django & Jinja2.\n\n* [Django | Built-in filter reference](https://docs.djangoproject.com/en/1.8/ref/templates/builtins/#built-in-filter-reference)\n* [Jinja2 | List of Builtin Filters](http://jinja.pocoo.org/docs/dev/templates/#builtin-filters)\n\nThe final goal is building a ultimate set which contains hundreds of useful template functions.\n\n\n## Contributing\nI love pull requests :) You can add any useful template functions by submitting a pull request!\n"
  },
  {
    "path": "gtf.go",
    "content": "package gtf\n\nimport (\n\t\"fmt\"\n\thtmlTemplate \"html/template\"\n\t\"math\"\n\t\"math/rand\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strings\"\n\ttextTemplate \"text/template\"\n\t\"time\"\n)\n\nvar striptagsRegexp = regexp.MustCompile(\"<[^>]*?>\")\n\n// recovery will silently swallow all unexpected panics.\nfunc recovery() {\n\trecover()\n}\n\nvar GtfTextFuncMap = textTemplate.FuncMap{\n\t\"replace\": func(s1 string, s2 string) string {\n\t\tdefer recovery()\n\n\t\treturn strings.Replace(s2, s1, \"\", -1)\n\t},\n\t\"findreplace\": func(s1 string, s2 string, s3 string) string {\n\t\tdefer recovery()\n\n\t\treturn strings.Replace(s3, s1, s2, -1)\n\t},\n\t\"title\": func(s string) string {\n\t\tdefer recovery()\n\t\treturn strings.Title(s)\n\t},\n\t\"default\": func(arg interface{}, value interface{}) interface{} {\n\t\tdefer recovery()\n\n\t\tv := reflect.ValueOf(value)\n\t\tswitch v.Kind() {\n\t\tcase reflect.String, reflect.Slice, reflect.Array, reflect.Map:\n\t\t\tif v.Len() == 0 {\n\t\t\t\treturn arg\n\t\t\t}\n\t\tcase reflect.Bool:\n\t\t\tif !v.Bool() {\n\t\t\t\treturn arg\n\t\t\t}\n\t\tdefault:\n\t\t\treturn value\n\t\t}\n\n\t\treturn value\n\t},\n\t\"length\": func(value interface{}) int {\n\t\tdefer recovery()\n\n\t\tv := reflect.ValueOf(value)\n\t\tswitch v.Kind() {\n\t\tcase reflect.Slice, reflect.Array, reflect.Map:\n\t\t\treturn v.Len()\n\t\tcase reflect.String:\n\t\t\treturn len([]rune(v.String()))\n\t\t}\n\n\t\treturn 0\n\t},\n\t\"lower\": func(s string) string {\n\t\tdefer recovery()\n\n\t\treturn strings.ToLower(s)\n\t},\n\t\"upper\": func(s string) string {\n\t\tdefer recovery()\n\n\t\treturn strings.ToUpper(s)\n\t},\n\t\"truncatechars\": func(n int, s string) string {\n\t\tdefer recovery()\n\n\t\tif n < 0 {\n\t\t\treturn s\n\t\t}\n\n\t\tr := []rune(s)\n\t\trLength := len(r)\n\n\t\tif n >= rLength {\n\t\t\treturn s\n\t\t}\n\n\t\tif n > 3 && rLength > 3 {\n\t\t\treturn string(r[:n-3]) + \"...\"\n\t\t}\n\n\t\treturn string(r[:n])\n\t},\n\t\"urlencode\": func(s string) string {\n\t\tdefer recovery()\n\n\t\treturn url.QueryEscape(s)\n\t},\n\t\"wordcount\": func(s string) int {\n\t\tdefer recovery()\n\n\t\treturn len(strings.Fields(s))\n\t},\n\t\"divisibleby\": func(arg interface{}, value interface{}) bool {\n\t\tdefer recovery()\n\n\t\tvar v float64\n\t\tswitch value.(type) {\n\t\tcase int, int8, int16, int32, int64:\n\t\t\tv = float64(reflect.ValueOf(value).Int())\n\t\tcase uint, uint8, uint16, uint32, uint64:\n\t\t\tv = float64(reflect.ValueOf(value).Uint())\n\t\tcase float32, float64:\n\t\t\tv = reflect.ValueOf(value).Float()\n\t\tdefault:\n\t\t\treturn false\n\t\t}\n\n\t\tvar a float64\n\t\tswitch arg.(type) {\n\t\tcase int, int8, int16, int32, int64:\n\t\t\ta = float64(reflect.ValueOf(arg).Int())\n\t\tcase uint, uint8, uint16, uint32, uint64:\n\t\t\ta = float64(reflect.ValueOf(arg).Uint())\n\t\tcase float32, float64:\n\t\t\ta = reflect.ValueOf(arg).Float()\n\t\tdefault:\n\t\t\treturn false\n\t\t}\n\n\t\treturn math.Mod(v, a) == 0\n\t},\n\t\"lengthis\": func(arg int, value interface{}) bool {\n\t\tdefer recovery()\n\n\t\tv := reflect.ValueOf(value)\n\t\tswitch v.Kind() {\n\t\tcase reflect.Slice, reflect.Array, reflect.Map:\n\t\t\treturn v.Len() == arg\n\t\tcase reflect.String:\n\t\t\treturn len([]rune(v.String())) == arg\n\t\t}\n\n\t\treturn false\n\t},\n\t\"trim\": func(s string) string {\n\t\tdefer recovery()\n\n\t\treturn strings.TrimSpace(s)\n\t},\n\t\"capfirst\": func(s string) string {\n\t\tdefer recovery()\n\n\t\treturn strings.ToUpper(string(s[0])) + s[1:]\n\t},\n\t\"pluralize\": func(arg string, value interface{}) string {\n\t\tdefer recovery()\n\n\t\tflag := false\n\t\tv := reflect.ValueOf(value)\n\t\tswitch v.Kind() {\n\t\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:\n\t\t\tflag = v.Int() == 1\n\t\tcase reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:\n\t\t\tflag = v.Uint() == 1\n\t\tdefault:\n\t\t\treturn \"\"\n\t\t}\n\n\t\tif !strings.Contains(arg, \",\") {\n\t\t\targ = \",\" + arg\n\t\t}\n\n\t\tbits := strings.Split(arg, \",\")\n\n\t\tif len(bits) > 2 {\n\t\t\treturn \"\"\n\t\t}\n\n\t\tif flag {\n\t\t\treturn bits[0]\n\t\t}\n\n\t\treturn bits[1]\n\t},\n\t\"yesno\": func(yes string, no string, value bool) string {\n\t\tdefer recovery()\n\n\t\tif value {\n\t\t\treturn yes\n\t\t}\n\n\t\treturn no\n\t},\n\t\"rjust\": func(arg int, value string) string {\n\t\tdefer recovery()\n\n\t\tn := arg - len([]rune(value))\n\n\t\tif n > 0 {\n\t\t\tvalue = strings.Repeat(\" \", n) + value\n\t\t}\n\n\t\treturn value\n\t},\n\t\"ljust\": func(arg int, value string) string {\n\t\tdefer recovery()\n\n\t\tn := arg - len([]rune(value))\n\n\t\tif n > 0 {\n\t\t\tvalue = value + strings.Repeat(\" \", n)\n\t\t}\n\n\t\treturn value\n\t},\n\t\"center\": func(arg int, value string) string {\n\t\tdefer recovery()\n\n\t\tn := arg - len([]rune(value))\n\n\t\tif n > 0 {\n\t\t\tleft := n / 2\n\t\t\tright := n - left\n\t\t\tvalue = strings.Repeat(\" \", left) + value + strings.Repeat(\" \", right)\n\t\t}\n\n\t\treturn value\n\t},\n\t\"filesizeformat\": func(value interface{}) string {\n\t\tdefer recovery()\n\n\t\tvar size float64\n\n\t\tv := reflect.ValueOf(value)\n\t\tswitch v.Kind() {\n\t\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:\n\t\t\tsize = float64(v.Int())\n\t\tcase reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:\n\t\t\tsize = float64(v.Uint())\n\t\tcase reflect.Float32, reflect.Float64:\n\t\t\tsize = v.Float()\n\t\tdefault:\n\t\t\treturn \"\"\n\t\t}\n\n\t\tvar KB float64 = 1 << 10\n\t\tvar MB float64 = 1 << 20\n\t\tvar GB float64 = 1 << 30\n\t\tvar TB float64 = 1 << 40\n\t\tvar PB float64 = 1 << 50\n\n\t\tfilesizeFormat := func(filesize float64, suffix string) string {\n\t\t\treturn strings.Replace(fmt.Sprintf(\"%.1f %s\", filesize, suffix), \".0\", \"\", -1)\n\t\t}\n\n\t\tvar result string\n\t\tif size < KB {\n\t\t\tresult = filesizeFormat(size, \"bytes\")\n\t\t} else if size < MB {\n\t\t\tresult = filesizeFormat(size/KB, \"KB\")\n\t\t} else if size < GB {\n\t\t\tresult = filesizeFormat(size/MB, \"MB\")\n\t\t} else if size < TB {\n\t\t\tresult = filesizeFormat(size/GB, \"GB\")\n\t\t} else if size < PB {\n\t\t\tresult = filesizeFormat(size/TB, \"TB\")\n\t\t} else {\n\t\t\tresult = filesizeFormat(size/PB, \"PB\")\n\t\t}\n\n\t\treturn result\n\t},\n\t\"apnumber\": func(value interface{}) interface{} {\n\t\tdefer recovery()\n\n\t\tname := [10]string{\"one\", \"two\", \"three\", \"four\", \"five\",\n\t\t\t\"six\", \"seven\", \"eight\", \"nine\"}\n\n\t\tv := reflect.ValueOf(value)\n\t\tswitch v.Kind() {\n\t\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:\n\t\t\tif v.Int() < 10 {\n\t\t\t\treturn name[v.Int()-1]\n\t\t\t}\n\t\tcase reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:\n\t\t\tif v.Uint() < 10 {\n\t\t\t\treturn name[v.Uint()-1]\n\t\t\t}\n\t\t}\n\n\t\treturn value\n\t},\n\t\"intcomma\": func(value interface{}) string {\n\t\tdefer recovery()\n\n\t\tv := reflect.ValueOf(value)\n\n\t\tvar x uint\n\t\tminus := false\n\t\tswitch v.Kind() {\n\t\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:\n\t\t\tif v.Int() < 0 {\n\t\t\t\tminus = true\n\t\t\t\tx = uint(-v.Int())\n\t\t\t} else {\n\t\t\t\tx = uint(v.Int())\n\t\t\t}\n\t\tcase reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:\n\t\t\tx = uint(v.Uint())\n\t\tdefault:\n\t\t\treturn \"\"\n\t\t}\n\n\t\tvar result string\n\t\tfor x >= 1000 {\n\t\t\tresult = fmt.Sprintf(\",%03d%s\", x%1000, result)\n\t\t\tx /= 1000\n\t\t}\n\t\tresult = fmt.Sprintf(\"%d%s\", x, result)\n\n\t\tif minus {\n\t\t\tresult = \"-\" + result\n\t\t}\n\n\t\treturn result\n\t},\n\t\"ordinal\": func(value interface{}) string {\n\t\tdefer recovery()\n\n\t\tv := reflect.ValueOf(value)\n\n\t\tvar x uint\n\t\tswitch v.Kind() {\n\t\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:\n\t\t\tif v.Int() < 0 {\n\t\t\t\treturn \"\"\n\t\t\t}\n\t\t\tx = uint(v.Int())\n\t\tcase reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:\n\t\t\tx = uint(v.Uint())\n\t\tdefault:\n\t\t\treturn \"\"\n\t\t}\n\n\t\tsuffixes := [10]string{\"th\", \"st\", \"nd\", \"rd\", \"th\", \"th\", \"th\", \"th\", \"th\", \"th\"}\n\n\t\tswitch x % 100 {\n\t\tcase 11, 12, 13:\n\t\t\treturn fmt.Sprintf(\"%d%s\", x, suffixes[0])\n\t\t}\n\n\t\treturn fmt.Sprintf(\"%d%s\", x, suffixes[x%10])\n\t},\n\t\"first\": func(value interface{}) interface{} {\n\t\tdefer recovery()\n\n\t\tv := reflect.ValueOf(value)\n\n\t\tswitch v.Kind() {\n\t\tcase reflect.String:\n\t\t\treturn string([]rune(v.String())[0])\n\t\tcase reflect.Slice, reflect.Array:\n\t\t\treturn v.Index(0).Interface()\n\t\t}\n\n\t\treturn \"\"\n\t},\n\t\"last\": func(value interface{}) interface{} {\n\t\tdefer recovery()\n\n\t\tv := reflect.ValueOf(value)\n\n\t\tswitch v.Kind() {\n\t\tcase reflect.String:\n\t\t\tstr := []rune(v.String())\n\t\t\treturn string(str[len(str)-1])\n\t\tcase reflect.Slice, reflect.Array:\n\t\t\treturn v.Index(v.Len() - 1).Interface()\n\t\t}\n\n\t\treturn \"\"\n\t},\n\t\"join\": func(arg string, value []string) string {\n\t\tdefer recovery()\n\n\t\treturn strings.Join(value, arg)\n\t},\n\t\"slice\": func(start int, end int, value interface{}) interface{} {\n\t\tdefer recovery()\n\n\t\tv := reflect.ValueOf(value)\n\n\t\tif start < 0 {\n\t\t\tstart = 0\n\t\t}\n\n\t\tswitch v.Kind() {\n\t\tcase reflect.String:\n\t\t\tstr := []rune(v.String())\n\n\t\t\tif end > len(str) {\n\t\t\t\tend = len(str)\n\t\t\t}\n\n\t\t\treturn string(str[start:end])\n\t\tcase reflect.Slice:\n\t\t\treturn v.Slice(start, end).Interface()\n\t\t}\n\t\treturn \"\"\n\t},\n\t\"random\": func(value interface{}) interface{} {\n\t\tdefer recovery()\n\n\t\trand.Seed(time.Now().UTC().UnixNano())\n\n\t\tv := reflect.ValueOf(value)\n\n\t\tswitch v.Kind() {\n\t\tcase reflect.String:\n\t\t\tstr := []rune(v.String())\n\t\t\treturn string(str[rand.Intn(len(str))])\n\t\tcase reflect.Slice, reflect.Array:\n\t\t\treturn v.Index(rand.Intn(v.Len())).Interface()\n\t\t}\n\n\t\treturn \"\"\n\t},\n\t\"randomintrange\": func(min, max int, value interface{}) int {\n\t\tdefer recovery()\n\n\t\trand.Seed(time.Now().UTC().UnixNano())\n\t\treturn rand.Intn(max-min) + min\n\t},\n\t\"striptags\": func(s string) string {\n\t\treturn strings.TrimSpace(striptagsRegexp.ReplaceAllString(s, \"\"))\n\t},\n}\n\nvar GtfFuncMap = htmlTemplate.FuncMap(GtfTextFuncMap)\n\n// gtf.New is a wrapper function of template.New(https://golang.org/pkg/html/template/#New).\n// It automatically adds the gtf functions to the template's function map\n// and returns template.Template(http://golang.org/pkg/html/template/#Template).\nfunc New(name string) *htmlTemplate.Template {\n\treturn htmlTemplate.New(name).Funcs(GtfFuncMap)\n}\n\n// gtf.Inject injects gtf functions into the passed FuncMap.\n// It does not overwrite the original function which have same name as a gtf function.\nfunc Inject(funcs map[string]interface{}) {\n\tfor k, v := range GtfFuncMap {\n\t\tif _, ok := funcs[k]; !ok {\n\t\t\tfuncs[k] = v\n\t\t}\n\t}\n}\n\n// gtf.ForceInject injects gtf functions into the passed FuncMap.\n// It overwrites the original function which have same name as a gtf function.\nfunc ForceInject(funcs map[string]interface{}) {\n\tfor k, v := range GtfFuncMap {\n\t\tfuncs[k] = v\n\t}\n}\n\n// gtf.Inject injects gtf functions into the passed FuncMap.\n// It prefixes the gtf functions with the specified prefix.\n// If there are many function which have same names as the gtf functions,\n// you can use this function to prefix the gtf functions.\nfunc InjectWithPrefix(funcs map[string]interface{}, prefix string) {\n\tfor k, v := range GtfFuncMap {\n\t\tfuncs[prefix+k] = v\n\t}\n}\n"
  },
  {
    "path": "gtf_test.go",
    "content": "package gtf\n\nimport (\n\t\"bytes\"\n\t\"html/template\"\n\t\"strconv\"\n\t\"testing\"\n)\n\nfunc AssertEqual(t *testing.T, buffer *bytes.Buffer, testString string) {\n\tif buffer.String() != testString {\n\t\tt.Errorf(\"Expected %s, got %s\", testString, buffer.String())\n\t}\n\tbuffer.Reset()\n}\n\nfunc AssertIntInRange(t *testing.T, buffer *bytes.Buffer, min, max int) {\n\tparsedInt, err := strconv.Atoi(buffer.String())\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif parsedInt < min || parsedInt > max {\n\t\tt.Errorf(\"Expected to be within the range of %d and %d, got %d\", min, max, parsedInt)\n\t}\n\tbuffer.Reset()\n}\n\nfunc ParseTest(buffer *bytes.Buffer, body string, data interface{}) {\n\ttpl := New(\"test\")\n\ttpl.Parse(body)\n\ttpl.Execute(buffer, data)\n}\n\nfunc CustomParseTest(funcMap map[string]interface{}, buffer *bytes.Buffer, body string, data interface{}) {\n\ttpl := template.New(\"test\").Funcs(funcMap)\n\ttpl.Parse(body)\n\ttpl.Execute(buffer, data)\n}\n\nfunc TestGtfFuncMap(t *testing.T) {\n\tvar buffer bytes.Buffer\n\n\tParseTest(&buffer, \"{{ \\\"The Go Programming Language\\\" | replace \\\" \\\" }}\", \"\")\n\tAssertEqual(t, &buffer, \"TheGoProgrammingLanguage\")\n\n\tParseTest(&buffer, \"{{ \\\"The Go Programming Language\\\" | findreplace \\\" \\\" \\\"X\\\" }}\", \"\")\n\tAssertEqual(t, &buffer, \"TheXGoXProgrammingXLanguage\")\n\n\tParseTest(&buffer, \"{{ \\\"the go programming language\\\" | title }}\", \"\")\n\tAssertEqual(t, &buffer, \"The Go Programming Language\")\n\n\tParseTest(&buffer, \"{{ \\\"The Go Programming Language\\\" | default \\\"default value\\\" }}\", \"\")\n\tAssertEqual(t, &buffer, \"The Go Programming Language\")\n\n\tParseTest(&buffer, \"{{ \\\"\\\" | default \\\"default value\\\" }}\", \"\")\n\tAssertEqual(t, &buffer, \"default value\")\n\n\tParseTest(&buffer, \"{{ . | default \\\"default value\\\" }}\", []string{\"go\", \"python\", \"ruby\"})\n\tAssertEqual(t, &buffer, \"[go python ruby]\")\n\n\tParseTest(&buffer, \"{{ . | default 10 }}\", []int{})\n\tAssertEqual(t, &buffer, \"10\")\n\n\tParseTest(&buffer, \"{{ . | default \\\"empty\\\" }}\", false)\n\tAssertEqual(t, &buffer, \"empty\")\n\n\tParseTest(&buffer, \"{{ . | default \\\"empty\\\" }}\", 1)\n\tAssertEqual(t, &buffer, \"1\")\n\n\tParseTest(&buffer, \"{{ \\\"The Go Programming Language\\\" | length }}\", \"\")\n\tAssertEqual(t, &buffer, \"27\")\n\n\tParseTest(&buffer, \"{{ \\\"안녕하세요\\\" | length }}\", \"\")\n\tAssertEqual(t, &buffer, \"5\")\n\n\tParseTest(&buffer, \"{{ . | length }}\", []string{\"go\", \"python\", \"ruby\"})\n\tAssertEqual(t, &buffer, \"3\")\n\n\tParseTest(&buffer, \"{{ . | length }}\", false)\n\tAssertEqual(t, &buffer, \"0\")\n\n\tParseTest(&buffer, \"{{ \\\"The Go Programming Language\\\" | lower }}\", \"\")\n\tAssertEqual(t, &buffer, \"the go programming language\")\n\n\tParseTest(&buffer, \"{{ \\\"The Go Programming Language\\\" | upper }}\", \"\")\n\tAssertEqual(t, &buffer, \"THE GO PROGRAMMING LANGUAGE\")\n\n\tParseTest(&buffer, \"{{ \\\"안녕하세요. 반갑습니다.\\\" | truncatechars 12 }}\", \"\")\n\tAssertEqual(t, &buffer, \"안녕하세요. 반갑...\")\n\n\tParseTest(&buffer, \"{{ \\\"The Go Programming Language\\\" | truncatechars 12 }}\", \"\")\n\tAssertEqual(t, &buffer, \"The Go Pr...\")\n\n\tParseTest(&buffer, \"{{ \\\"안녕하세요. The Go Programming Language\\\" | truncatechars 30 }}\", \"\")\n\tAssertEqual(t, &buffer, \"안녕하세요. The Go Programming L...\")\n\n\tParseTest(&buffer, \"{{ \\\"The\\\" | truncatechars 30 }}\", \"\")\n\tAssertEqual(t, &buffer, \"The\")\n\n\tParseTest(&buffer, \"{{ \\\"The Go Programming Language\\\" | truncatechars 3 }}\", \"\")\n\tAssertEqual(t, &buffer, \"The\")\n\n\tParseTest(&buffer, \"{{ \\\"The Go\\\" | truncatechars 6 }}\", \"\")\n\tAssertEqual(t, &buffer, \"The Go\")\n\n\tParseTest(&buffer, \"{{ \\\"The Go\\\" | truncatechars 30 }}\", \"\")\n\tAssertEqual(t, &buffer, \"The Go\")\n\n\tParseTest(&buffer, \"{{ \\\"The Go\\\" | truncatechars 0 }}\", \"\")\n\tAssertEqual(t, &buffer, \"\")\n\n\tParseTest(&buffer, \"{{ \\\"The Go\\\" | truncatechars -1 }}\", \"\")\n\tAssertEqual(t, &buffer, \"The Go\")\n\n\tParseTest(&buffer, \"{{ \\\"http://www.example.org/foo?a=b&c=d\\\" | urlencode }}\", \"\")\n\tAssertEqual(t, &buffer, \"http%3A%2F%2Fwww.example.org%2Ffoo%3Fa%3Db%26c%3Dd\")\n\n\tParseTest(&buffer, \"{{ \\\"The Go Programming Language\\\" | wordcount }}\", \"\")\n\tAssertEqual(t, &buffer, \"4\")\n\n\tParseTest(&buffer, \"{{ \\\"      The      Go       Programming      Language        \\\" | wordcount }}\", \"\")\n\tAssertEqual(t, &buffer, \"4\")\n\n\tParseTest(&buffer, \"{{ 21 | divisibleby 3 }}\", \"\")\n\tAssertEqual(t, &buffer, \"true\")\n\n\tParseTest(&buffer, \"{{ 21 | divisibleby 4 }}\", \"\")\n\tAssertEqual(t, &buffer, \"false\")\n\n\tParseTest(&buffer, \"{{ 3.0 | divisibleby 3 }}\", \"\")\n\tAssertEqual(t, &buffer, \"true\")\n\n\tParseTest(&buffer, \"{{ 3.0 | divisibleby 1.5 }}\", \"\")\n\tAssertEqual(t, &buffer, \"true\")\n\n\tParseTest(&buffer, \"{{ . | divisibleby 1.5 }}\", uint(300))\n\tAssertEqual(t, &buffer, \"true\")\n\n\tParseTest(&buffer, \"{{ 12 | divisibleby . }}\", uint(3))\n\tAssertEqual(t, &buffer, \"true\")\n\n\tParseTest(&buffer, \"{{ 21 | divisibleby 4 }}\", \"\")\n\tAssertEqual(t, &buffer, \"false\")\n\n\tParseTest(&buffer, \"{{ false | divisibleby 3 }}\", \"\")\n\tAssertEqual(t, &buffer, \"false\")\n\n\tParseTest(&buffer, \"{{ 3 | divisibleby false }}\", \"\")\n\tAssertEqual(t, &buffer, \"false\")\n\n\tParseTest(&buffer, \"{{ \\\"Go\\\" | lengthis 2 }}\", \"\")\n\tAssertEqual(t, &buffer, \"true\")\n\n\tParseTest(&buffer, \"{{ \\\"안녕하세요.\\\" | lengthis 6 }}\", \"\")\n\tAssertEqual(t, &buffer, \"true\")\n\n\tParseTest(&buffer, \"{{ \\\"안녕하세요. Go!\\\" | lengthis 10 }}\", \"\")\n\tAssertEqual(t, &buffer, \"true\")\n\n\tParseTest(&buffer, \"{{ . | lengthis 3 }}\", []string{\"go\", \"python\", \"ruby\"})\n\tAssertEqual(t, &buffer, \"true\")\n\n\tParseTest(&buffer, \"{{ . | lengthis 3 }}\", false)\n\tAssertEqual(t, &buffer, \"false\")\n\n\tParseTest(&buffer, \"{{ \\\"       The Go Programming Language     \\\" | trim }}\", \"\")\n\tAssertEqual(t, &buffer, \"The Go Programming Language\")\n\n\tParseTest(&buffer, \"{{ \\\"the go programming language\\\" | capfirst }}\", \"\")\n\tAssertEqual(t, &buffer, \"The go programming language\")\n\n\tParseTest(&buffer, \"You have 0 message{{ 0 | pluralize \\\"s\\\" }}\", \"\")\n\tAssertEqual(t, &buffer, \"You have 0 messages\")\n\n\tParseTest(&buffer, \"You have 1 message{{ 1 | pluralize \\\"s\\\" }}\", \"\")\n\tAssertEqual(t, &buffer, \"You have 1 message\")\n\n\tParseTest(&buffer, \"0 cand{{ 0 | pluralize \\\"y,ies\\\" }}\", \"\")\n\tAssertEqual(t, &buffer, \"0 candies\")\n\n\tParseTest(&buffer, \"1 cand{{ 1 | pluralize \\\"y,ies\\\" }}\", \"\")\n\tAssertEqual(t, &buffer, \"1 candy\")\n\n\tParseTest(&buffer, \"2 cand{{ 2 | pluralize \\\"y,ies\\\" }}\", \"\")\n\tAssertEqual(t, &buffer, \"2 candies\")\n\n\tParseTest(&buffer, \"{{ 2 | pluralize \\\"y,ies,s\\\" }}\", \"\")\n\tAssertEqual(t, &buffer, \"\")\n\n\tParseTest(&buffer, \"2 cand{{ . | pluralize \\\"y,ies\\\" }}\", uint(2))\n\tAssertEqual(t, &buffer, \"2 candies\")\n\n\tParseTest(&buffer, \"1 cand{{ . | pluralize \\\"y,ies\\\" }}\", uint(1))\n\tAssertEqual(t, &buffer, \"1 candy\")\n\n\tParseTest(&buffer, \"{{ . | pluralize \\\"y,ies\\\" }}\", \"test\")\n\tAssertEqual(t, &buffer, \"\")\n\n\tParseTest(&buffer, \"{{ true | yesno \\\"yes~\\\" \\\"no~\\\" }}\", \"\")\n\tAssertEqual(t, &buffer, \"yes~\")\n\n\tParseTest(&buffer, \"{{ false | yesno \\\"yes~\\\" \\\"no~\\\" }}\", \"\")\n\tAssertEqual(t, &buffer, \"no~\")\n\n\tParseTest(&buffer, \"{{ \\\"Go\\\" | rjust 10 }}\", \"\")\n\tAssertEqual(t, &buffer, \"        Go\")\n\n\tParseTest(&buffer, \"{{ \\\"안녕하세요\\\" | rjust 10 }}\", \"\")\n\tAssertEqual(t, &buffer, \"     안녕하세요\")\n\n\tParseTest(&buffer, \"{{ \\\"Go\\\" | ljust 10 }}\", \"\")\n\tAssertEqual(t, &buffer, \"Go        \")\n\n\tParseTest(&buffer, \"{{ \\\"안녕하세요\\\" | ljust 10 }}\", \"\")\n\tAssertEqual(t, &buffer, \"안녕하세요     \")\n\n\tParseTest(&buffer, \"{{ \\\"Go\\\" | center 10 }}\", \"\")\n\tAssertEqual(t, &buffer, \"    Go    \")\n\n\tParseTest(&buffer, \"{{ \\\"안녕하세요\\\" | center 10 }}\", \"\")\n\tAssertEqual(t, &buffer, \"  안녕하세요   \")\n\n\tParseTest(&buffer, \"{{ 123456789 | filesizeformat }}\", \"\")\n\tAssertEqual(t, &buffer, \"117.7 MB\")\n\n\tParseTest(&buffer, \"{{ 234 | filesizeformat }}\", \"\")\n\tAssertEqual(t, &buffer, \"234 bytes\")\n\n\tParseTest(&buffer, \"{{ 12345 | filesizeformat }}\", \"\")\n\tAssertEqual(t, &buffer, \"12.1 KB\")\n\n\tParseTest(&buffer, \"{{ 554832114 | filesizeformat }}\", \"\")\n\tAssertEqual(t, &buffer, \"529.1 MB\")\n\n\tParseTest(&buffer, \"{{ 1048576 | filesizeformat }}\", \"\")\n\tAssertEqual(t, &buffer, \"1 MB\")\n\n\tParseTest(&buffer, \"{{ 14868735121 | filesizeformat }}\", \"\")\n\tAssertEqual(t, &buffer, \"13.8 GB\")\n\n\tParseTest(&buffer, \"{{ 14868735121365 | filesizeformat }}\", \"\")\n\tAssertEqual(t, &buffer, \"13.5 TB\")\n\n\tParseTest(&buffer, \"{{ 1486873512136523 | filesizeformat }}\", \"\")\n\tAssertEqual(t, &buffer, \"1.3 PB\")\n\n\tParseTest(&buffer, \"{{ 12345.35335 | filesizeformat }}\", \"\")\n\tAssertEqual(t, &buffer, \"12.1 KB\")\n\n\tParseTest(&buffer, \"{{ 4294967293 | filesizeformat }}\", \"\")\n\tAssertEqual(t, &buffer, \"4 GB\")\n\n\tParseTest(&buffer, \"{{ \\\"Go\\\" | filesizeformat }}\", \"\")\n\tAssertEqual(t, &buffer, \"\")\n\n\tParseTest(&buffer, \"{{ . | filesizeformat }}\", uint(500))\n\tAssertEqual(t, &buffer, \"500 bytes\")\n\n\tParseTest(&buffer, \"{{ . | apnumber }}\", uint(500))\n\tAssertEqual(t, &buffer, \"500\")\n\n\tParseTest(&buffer, \"{{ . | apnumber }}\", uint(7))\n\tAssertEqual(t, &buffer, \"seven\")\n\n\tParseTest(&buffer, \"{{ . | apnumber }}\", int8(3))\n\tAssertEqual(t, &buffer, \"three\")\n\n\tParseTest(&buffer, \"{{ 2 | apnumber }}\", \"\")\n\tAssertEqual(t, &buffer, \"two\")\n\n\tParseTest(&buffer, \"{{ 1000 | apnumber }}\", \"\")\n\tAssertEqual(t, &buffer, \"1000\")\n\n\tParseTest(&buffer, \"{{ 1000 | intcomma }}\", \"\")\n\tAssertEqual(t, &buffer, \"1,000\")\n\n\tParseTest(&buffer, \"{{ -1000 | intcomma }}\", \"\")\n\tAssertEqual(t, &buffer, \"-1,000\")\n\n\tParseTest(&buffer, \"{{ 1578652313 | intcomma }}\", \"\")\n\tAssertEqual(t, &buffer, \"1,578,652,313\")\n\n\tParseTest(&buffer, \"{{ . | intcomma }}\", uint64(12315358198))\n\tAssertEqual(t, &buffer, \"12,315,358,198\")\n\n\tParseTest(&buffer, \"{{ . | intcomma }}\", 25.352)\n\tAssertEqual(t, &buffer, \"\")\n\n\tParseTest(&buffer, \"{{ 1 | ordinal }}\", \"\")\n\tAssertEqual(t, &buffer, \"1st\")\n\n\tParseTest(&buffer, \"{{ 2 | ordinal }}\", \"\")\n\tAssertEqual(t, &buffer, \"2nd\")\n\n\tParseTest(&buffer, \"{{ 3 | ordinal }}\", \"\")\n\tAssertEqual(t, &buffer, \"3rd\")\n\n\tParseTest(&buffer, \"{{ 11 | ordinal }}\", \"\")\n\tAssertEqual(t, &buffer, \"11th\")\n\n\tParseTest(&buffer, \"{{ 12 | ordinal }}\", \"\")\n\tAssertEqual(t, &buffer, \"12th\")\n\n\tParseTest(&buffer, \"{{ 13 | ordinal }}\", \"\")\n\tAssertEqual(t, &buffer, \"13th\")\n\n\tParseTest(&buffer, \"{{ 14 | ordinal }}\", \"\")\n\tAssertEqual(t, &buffer, \"14th\")\n\n\tParseTest(&buffer, \"{{ -1 | ordinal }}\", \"\")\n\tAssertEqual(t, &buffer, \"\")\n\n\tParseTest(&buffer, \"{{ . | ordinal }}\", uint(14))\n\tAssertEqual(t, &buffer, \"14th\")\n\n\tParseTest(&buffer, \"{{ . | ordinal }}\", false)\n\tAssertEqual(t, &buffer, \"\")\n\n\tParseTest(&buffer, \"{{ . | first }}\", \"The go programming language\")\n\tAssertEqual(t, &buffer, \"T\")\n\n\tParseTest(&buffer, \"{{ . | first }}\", \"안녕하세요\")\n\tAssertEqual(t, &buffer, \"안\")\n\n\tParseTest(&buffer, \"{{ . | first }}\", []string{\"go\", \"python\", \"ruby\"})\n\tAssertEqual(t, &buffer, \"go\")\n\n\tParseTest(&buffer, \"{{ . | first }}\", [3]string{\"go\", \"python\", \"ruby\"})\n\tAssertEqual(t, &buffer, \"go\")\n\n\tParseTest(&buffer, \"{{ . | first }}\", false)\n\tAssertEqual(t, &buffer, \"\")\n\n\tParseTest(&buffer, \"{{ . | last }}\", \"The go programming language\")\n\tAssertEqual(t, &buffer, \"e\")\n\n\tParseTest(&buffer, \"{{ . | last }}\", \"안녕하세요\")\n\tAssertEqual(t, &buffer, \"요\")\n\n\tParseTest(&buffer, \"{{ . | last }}\", []string{\"go\", \"python\", \"ruby\"})\n\tAssertEqual(t, &buffer, \"ruby\")\n\n\tParseTest(&buffer, \"{{ . | last }}\", false)\n\tAssertEqual(t, &buffer, \"\")\n\n\tParseTest(&buffer, \"{{ . | join \\\" \\\" }}\", []string{\"go\", \"python\", \"ruby\"})\n\tAssertEqual(t, &buffer, \"go python ruby\")\n\n\tParseTest(&buffer, \"{{ . | slice 0 2 }}\", []string{\"go\", \"python\", \"ruby\"})\n\tAssertEqual(t, &buffer, \"[go python]\")\n\n\tParseTest(&buffer, \"{{ . | slice 0 6 }}\", \"The go programming language\")\n\tAssertEqual(t, &buffer, \"The go\")\n\n\tParseTest(&buffer, \"{{ . | slice 0 2 }}\", \"안녕하세요\")\n\tAssertEqual(t, &buffer, \"안녕\")\n\n\tParseTest(&buffer, \"{{ . | slice -10 10 }}\", \"안녕하세요\")\n\tAssertEqual(t, &buffer, \"안녕하세요\")\n\n\tParseTest(&buffer, \"{{ . | slice 0 2 }}\", false)\n\tAssertEqual(t, &buffer, \"\")\n\n\tParseTest(&buffer, \"{{ . | random }}\", \"T\")\n\tAssertEqual(t, &buffer, \"T\")\n\n\tParseTest(&buffer, \"{{ . | random }}\", \"안\")\n\tAssertEqual(t, &buffer, \"안\")\n\n\tParseTest(&buffer, \"{{ . | random }}\", []string{\"go\"})\n\tAssertEqual(t, &buffer, \"go\")\n\n\tParseTest(&buffer, \"{{ . | random }}\", [1]string{\"go\"})\n\tAssertEqual(t, &buffer, \"go\")\n\n\tParseTest(&buffer, \"{{ . | randomintrange 1 5 }}\", false)\n\tAssertIntInRange(t, &buffer, 1, 5)\n\n\tParseTest(&buffer, \"{{ . | random }}\", false)\n\tAssertEqual(t, &buffer, \"\")\n\n\tParseTest(&buffer, \"{{ . | striptags }}\", \"<strong>text</strong>\")\n\tAssertEqual(t, &buffer, \"text\")\n\n\tParseTest(&buffer, \"{{ . | striptags }}\", \"<strong><em>안녕하세요</em></strong>\")\n\tAssertEqual(t, &buffer, \"안녕하세요\")\n\n\tParseTest(&buffer, \"{{ . | striptags }}\", \"<a href=\\\"http://example.com/\\\">text <strong>안녕하세요</strong></a>\")\n\tAssertEqual(t, &buffer, \"text 안녕하세요\")\n}\n\nfunc TestInject(t *testing.T) {\n\tvar buffer bytes.Buffer\n\n\tvar originalFuncMap = template.FuncMap{\n\t\t// originalFuncMap is made for test purpose.\n\t\t// It tests that Inject function does not overwrite the original functions\n\t\t// which have same names.\n\t\t\"length\": func(value interface{}) int {\n\t\t\treturn -1\n\t\t},\n\t\t\"lower\": func(s string) string {\n\t\t\treturn \"foo\"\n\t\t},\n\t}\n\n\tCustomParseTest(originalFuncMap, &buffer, \"{{ \\\"The Go Programming Language\\\" | length }}\", \"\")\n\tAssertEqual(t, &buffer, \"-1\")\n\tCustomParseTest(originalFuncMap, &buffer, \"{{ \\\"The Go Programming Language\\\" | lower }}\", \"\")\n\tAssertEqual(t, &buffer, \"foo\")\n\n\tInject(originalFuncMap) // Inject!\n\n\t// Check if Inject function does not overwrite the original functions which have same names.\n\tCustomParseTest(originalFuncMap, &buffer, \"{{ \\\"The Go Programming Language\\\" | length }}\", \"\")\n\tAssertEqual(t, &buffer, \"-1\")\n\tCustomParseTest(originalFuncMap, &buffer, \"{{ \\\"The Go Programming Language\\\" | lower }}\", \"\")\n\tAssertEqual(t, &buffer, \"foo\")\n\n\t// Now, I can use gtf functions because they are injected into originalFuncMap.\n\tCustomParseTest(originalFuncMap, &buffer, \"{{ . | first }}\", []string{\"go\", \"python\", \"ruby\"})\n\tAssertEqual(t, &buffer, \"go\")\n\tCustomParseTest(originalFuncMap, &buffer, \"{{ . | slice 0 6 }}\", \"The go programming language\")\n\tAssertEqual(t, &buffer, \"The go\")\n\tCustomParseTest(originalFuncMap, &buffer, \"{{ 13 | ordinal }}\", \"\")\n\tAssertEqual(t, &buffer, \"13th\")\n}\n\nfunc TestForceInject(t *testing.T) {\n\tvar buffer bytes.Buffer\n\n\tvar originalFuncMap = template.FuncMap{\n\t\t\"length\": func(value interface{}) int {\n\t\t\treturn -1\n\t\t},\n\t\t\"lower\": func(s string) string {\n\t\t\treturn \"foo\"\n\t\t},\n\t}\n\n\tCustomParseTest(originalFuncMap, &buffer, \"{{ \\\"The Go Programming Language\\\" | length }}\", \"\")\n\tAssertEqual(t, &buffer, \"-1\")\n\tCustomParseTest(originalFuncMap, &buffer, \"{{ \\\"The Go Programming Language\\\" | lower }}\", \"\")\n\tAssertEqual(t, &buffer, \"foo\")\n\n\tForceInject(originalFuncMap) // ForceInject!\n\n\t// Check if ForceInject function overwrites the original functions which have same names.\n\tCustomParseTest(originalFuncMap, &buffer, \"{{ \\\"The Go Programming Language\\\" | length }}\", \"\")\n\tAssertEqual(t, &buffer, \"27\")\n\tCustomParseTest(originalFuncMap, &buffer, \"{{ \\\"The Go Programming Language\\\" | lower }}\", \"\")\n\tAssertEqual(t, &buffer, \"the go programming language\")\n\n\t// Now, I can use gtf functions because they are injected into originalFuncMap.\n\tCustomParseTest(originalFuncMap, &buffer, \"{{ . | first }}\", []string{\"go\", \"python\", \"ruby\"})\n\tAssertEqual(t, &buffer, \"go\")\n\tCustomParseTest(originalFuncMap, &buffer, \"{{ . | slice 0 6 }}\", \"The go programming language\")\n\tAssertEqual(t, &buffer, \"The go\")\n\tCustomParseTest(originalFuncMap, &buffer, \"{{ 13 | ordinal }}\", \"\")\n\tAssertEqual(t, &buffer, \"13th\")\n}\n\nfunc TestInjectWithPrefix(t *testing.T) {\n\tvar buffer bytes.Buffer\n\n\tvar originalFuncMap = template.FuncMap{\n\t\t\"length\": func(value interface{}) int {\n\t\t\treturn -1\n\t\t},\n\t\t\"lower\": func(s string) string {\n\t\t\treturn \"foo\"\n\t\t},\n\t}\n\n\tCustomParseTest(originalFuncMap, &buffer, \"{{ \\\"The Go Programming Language\\\" | length }}\", \"\")\n\tAssertEqual(t, &buffer, \"-1\")\n\tCustomParseTest(originalFuncMap, &buffer, \"{{ \\\"The Go Programming Language\\\" | lower }}\", \"\")\n\tAssertEqual(t, &buffer, \"foo\")\n\n\tInjectWithPrefix(originalFuncMap, \"gtf_\") // InjectWithPrefix! (prefix : gtf_)\n\n\t// Check if Inject function does not overwrite the original functions which have same names.\n\tCustomParseTest(originalFuncMap, &buffer, \"{{ \\\"The Go Programming Language\\\" | length }}\", \"\")\n\tAssertEqual(t, &buffer, \"-1\")\n\tCustomParseTest(originalFuncMap, &buffer, \"{{ \\\"The Go Programming Language\\\" | lower }}\", \"\")\n\tAssertEqual(t, &buffer, \"foo\")\n\n\t// Now, I can use gtf functions because they are injected into originalFuncMap.\n\tCustomParseTest(originalFuncMap, &buffer, \"{{ . | gtf_first }}\", []string{\"go\", \"python\", \"ruby\"})\n\tAssertEqual(t, &buffer, \"go\")\n\tCustomParseTest(originalFuncMap, &buffer, \"{{ . | gtf_slice 0 6 }}\", \"The go programming language\")\n\tAssertEqual(t, &buffer, \"The go\")\n\tCustomParseTest(originalFuncMap, &buffer, \"{{ 13 | gtf_ordinal }}\", \"\")\n\tAssertEqual(t, &buffer, \"13th\")\n}\n"
  },
  {
    "path": "gtf_text_test.go",
    "content": "package gtf\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\t\"text/template\"\n)\n\nfunc TextTemplateParseTest(buffer *bytes.Buffer, body string, data interface{}) {\n\ttpl := template.New(\"test\").Funcs(GtfTextFuncMap)\n\ttpl.Parse(body)\n\ttpl.Execute(buffer, data)\n}\n\nfunc TestTextTemplateGtfFuncMap(t *testing.T) {\n\tvar buffer bytes.Buffer\n\n\tTextTemplateParseTest(&buffer, \"{{ \\\"The Go Programming Language\\\" | replace \\\" \\\" }}\", \"\")\n\tAssertEqual(t, &buffer, \"TheGoProgrammingLanguage\")\n\n\tTextTemplateParseTest(&buffer, \"{{ \\\"The Go Programming Language\\\" | findreplace \\\" \\\" \\\"X\\\" }}\", \"\")\n\tAssertEqual(t, &buffer, \"TheXGoXProgrammingXLanguage\")\n\n\tTextTemplateParseTest(&buffer, \"{{ \\\"The Go Programming Language\\\" | length }}\", \"\")\n\tAssertEqual(t, &buffer, \"27\")\n\n\tTextTemplateParseTest(&buffer, \"{{ 21 | divisibleby 3 }}\", \"\")\n\tAssertEqual(t, &buffer, \"true\")\n}\n"
  }
]