summaryrefslogtreecommitdiff
path: root/vendor/github.com/gosimple/slug/slug.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/gosimple/slug/slug.go')
-rw-r--r--vendor/github.com/gosimple/slug/slug.go196
1 files changed, 196 insertions, 0 deletions
diff --git a/vendor/github.com/gosimple/slug/slug.go b/vendor/github.com/gosimple/slug/slug.go
new file mode 100644
index 0000000..f93f636
--- /dev/null
+++ b/vendor/github.com/gosimple/slug/slug.go
@@ -0,0 +1,196 @@
+// Copyright 2013 by Dobrosław Żybort. All rights reserved.
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package slug
+
+import (
+ "bytes"
+ "regexp"
+ "sort"
+ "strings"
+
+ "github.com/gosimple/unidecode"
+)
+
+var (
+ // CustomSub stores custom substitution map
+ CustomSub map[string]string
+ // CustomRuneSub stores custom rune substitution map
+ CustomRuneSub map[rune]string
+
+ // MaxLength stores maximum slug length.
+ // By default slugs aren't shortened.
+ // If MaxLength is smaller than length of the first word, then returned
+ // slug will contain only substring from the first word truncated
+ // after MaxLength.
+ MaxLength int
+
+ // EnableSmartTruncate defines if cutting with MaxLength is smart.
+ // Smart algorithm will cat slug after full word.
+ // Default is true.
+ EnableSmartTruncate = true
+
+ // Lowercase defines if the resulting slug is transformed to lowercase.
+ // Default is true.
+ Lowercase = true
+
+ regexpNonAuthorizedChars = regexp.MustCompile("[^a-zA-Z0-9-_]")
+ regexpMultipleDashes = regexp.MustCompile("-+")
+)
+
+//=============================================================================
+
+// Make returns slug generated from provided string. Will use "en" as language
+// substitution.
+func Make(s string) (slug string) {
+ return MakeLang(s, "en")
+}
+
+// MakeLang returns slug generated from provided string and will use provided
+// language for chars substitution.
+func MakeLang(s string, lang string) (slug string) {
+ slug = strings.TrimSpace(s)
+
+ // Custom substitutions
+ // Always substitute runes first
+ slug = SubstituteRune(slug, CustomRuneSub)
+ slug = Substitute(slug, CustomSub)
+
+ // Process string with selected substitution language.
+ // Catch ISO 3166-1, ISO 639-1:2002 and ISO 639-3:2007.
+ switch strings.ToLower(lang) {
+ case "bg", "bgr":
+ slug = SubstituteRune(slug, bgSub)
+ case "cs", "ces":
+ slug = SubstituteRune(slug, csSub)
+ case "de", "deu":
+ slug = SubstituteRune(slug, deSub)
+ case "en", "eng":
+ slug = SubstituteRune(slug, enSub)
+ case "es", "spa":
+ slug = SubstituteRune(slug, esSub)
+ case "fi", "fin":
+ slug = SubstituteRune(slug, fiSub)
+ case "fr", "fra":
+ slug = SubstituteRune(slug, frSub)
+ case "gr", "el", "ell":
+ slug = SubstituteRune(slug, grSub)
+ case "hu", "hun":
+ slug = SubstituteRune(slug, huSub)
+ case "id", "idn", "ind":
+ slug = SubstituteRune(slug, idSub)
+ case "it", "ita":
+ slug = SubstituteRune(slug, itSub)
+ case "kz", "kk", "kaz":
+ slug = SubstituteRune(slug, kkSub)
+ case "nb", "nob":
+ slug = SubstituteRune(slug, nbSub)
+ case "nl", "nld":
+ slug = SubstituteRune(slug, nlSub)
+ case "nn", "nno":
+ slug = SubstituteRune(slug, nnSub)
+ case "pl", "pol":
+ slug = SubstituteRune(slug, plSub)
+ case "ro", "rou":
+ slug = SubstituteRune(slug, roSub)
+ case "sl", "slv":
+ slug = SubstituteRune(slug, slSub)
+ case "sv", "swe":
+ slug = SubstituteRune(slug, svSub)
+ case "tr", "tur":
+ slug = SubstituteRune(slug, trSub)
+ default: // fallback to "en" if lang not found
+ slug = SubstituteRune(slug, enSub)
+ }
+
+ // Process all non ASCII symbols
+ slug = unidecode.Unidecode(slug)
+
+ if Lowercase {
+ slug = strings.ToLower(slug)
+ }
+
+ if !EnableSmartTruncate && len(slug) >= MaxLength {
+ slug = slug[:MaxLength]
+ }
+
+ // Process all remaining symbols
+ slug = regexpNonAuthorizedChars.ReplaceAllString(slug, "-")
+ slug = regexpMultipleDashes.ReplaceAllString(slug, "-")
+ slug = strings.Trim(slug, "-_")
+
+ if MaxLength > 0 && EnableSmartTruncate {
+ slug = smartTruncate(slug)
+ }
+
+ return slug
+}
+
+// Substitute returns string with superseded all substrings from
+// provided substitution map. Substitution map will be applied in alphabetic
+// order. Many passes, on one substitution another one could apply.
+func Substitute(s string, sub map[string]string) (buf string) {
+ buf = s
+ var keys []string
+ for k := range sub {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+
+ for _, key := range keys {
+ buf = strings.Replace(buf, key, sub[key], -1)
+ }
+ return
+}
+
+// SubstituteRune substitutes string chars with provided rune
+// substitution map. One pass.
+func SubstituteRune(s string, sub map[rune]string) string {
+ var buf bytes.Buffer
+ for _, c := range s {
+ if d, ok := sub[c]; ok {
+ buf.WriteString(d)
+ } else {
+ buf.WriteRune(c)
+ }
+ }
+ return buf.String()
+}
+
+func smartTruncate(text string) string {
+ if len(text) <= MaxLength {
+ return text
+ }
+
+ // If slug is too long, we need to find the last '-' before MaxLength, and
+ // we cut there.
+ // If we don't find any, we have only one word, and we cut at MaxLength.
+ for i := MaxLength; i >= 0; i-- {
+ if text[i] == '-' {
+ return text[:i]
+ }
+ }
+ return text[:MaxLength]
+}
+
+// IsSlug returns True if provided text does not contain white characters,
+// punctuation, all letters are lower case and only from ASCII range.
+// It could contain `-` and `_` but not at the beginning or end of the text.
+// It should be in range of the MaxLength var if specified.
+// All output from slug.Make(text) should pass this test.
+func IsSlug(text string) bool {
+ if text == "" ||
+ (MaxLength > 0 && len(text) > MaxLength) ||
+ text[0] == '-' || text[0] == '_' ||
+ text[len(text)-1] == '-' || text[len(text)-1] == '_' {
+ return false
+ }
+ for _, c := range text {
+ if (c < 'a' || c > 'z') && c != '-' && c != '_' && (c < '0' || c > '9') {
+ return false
+ }
+ }
+ return true
+}