URI: 
       content.go - hugo - [fork] hugo port for 9front
  HTML git clone https://git.drkhsh.at/hugo.git
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
   DIR README
   DIR LICENSE
       ---
       content.go (5403B)
       ---
            1 // Copyright 2019 The Hugo Authors. All rights reserved.
            2 //
            3 // Licensed under the Apache License, Version 2.0 (the "License");
            4 // you may not use this file except in compliance with the License.
            5 // You may obtain a copy of the License at
            6 // http://www.apache.org/licenses/LICENSE-2.0
            7 //
            8 // Unless required by applicable law or agreed to in writing, software
            9 // distributed under the License is distributed on an "AS IS" BASIS,
           10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
           11 // See the License for the specific language governing permissions and
           12 // limitations under the License.
           13 
           14 // Package helpers implements general utility functions that work with
           15 // and on content.  The helper functions defined here lay down the
           16 // foundation of how Hugo works with files and filepaths, and perform
           17 // string operations on content.
           18 package helpers
           19 
           20 import (
           21         "bytes"
           22         "html/template"
           23         "strings"
           24         "unicode"
           25 
           26         "github.com/gohugoio/hugo/common/hexec"
           27         "github.com/gohugoio/hugo/common/loggers"
           28         "github.com/gohugoio/hugo/media"
           29 
           30         "github.com/spf13/afero"
           31 
           32         "github.com/gohugoio/hugo/markup/converter"
           33 
           34         "github.com/gohugoio/hugo/markup"
           35 
           36         "github.com/gohugoio/hugo/config"
           37 )
           38 
           39 // ContentSpec provides functionality to render markdown content.
           40 type ContentSpec struct {
           41         Converters          markup.ConverterProvider
           42         anchorNameSanitizer converter.AnchorNameSanitizer
           43         Cfg                 config.AllProvider
           44 }
           45 
           46 // NewContentSpec returns a ContentSpec initialized
           47 // with the appropriate fields from the given config.Provider.
           48 func NewContentSpec(cfg config.AllProvider, logger loggers.Logger, contentFs afero.Fs, ex *hexec.Exec) (*ContentSpec, error) {
           49         spec := &ContentSpec{
           50                 Cfg: cfg,
           51         }
           52 
           53         converterProvider, err := markup.NewConverterProvider(converter.ProviderConfig{
           54                 Conf:      cfg,
           55                 ContentFs: contentFs,
           56                 Logger:    logger,
           57                 Exec:      ex,
           58         })
           59         if err != nil {
           60                 return nil, err
           61         }
           62 
           63         spec.Converters = converterProvider
           64         p := converterProvider.Get("markdown")
           65         conv, err := p.New(converter.DocumentContext{})
           66         if err != nil {
           67                 return nil, err
           68         }
           69         if as, ok := conv.(converter.AnchorNameSanitizer); ok {
           70                 spec.anchorNameSanitizer = as
           71         } else {
           72                 // Use Goldmark's sanitizer
           73                 p := converterProvider.Get("goldmark")
           74                 conv, err := p.New(converter.DocumentContext{})
           75                 if err != nil {
           76                         return nil, err
           77                 }
           78                 spec.anchorNameSanitizer = conv.(converter.AnchorNameSanitizer)
           79         }
           80 
           81         return spec, nil
           82 }
           83 
           84 // stripEmptyNav strips out empty <nav> tags from content.
           85 func stripEmptyNav(in []byte) []byte {
           86         return bytes.Replace(in, []byte("<nav>\n</nav>\n\n"), []byte(``), -1)
           87 }
           88 
           89 // BytesToHTML converts bytes to type template.HTML.
           90 func BytesToHTML(b []byte) template.HTML {
           91         return template.HTML(string(b))
           92 }
           93 
           94 // ExtractTOC extracts Table of Contents from content.
           95 func ExtractTOC(content []byte) (newcontent []byte, toc []byte) {
           96         if !bytes.Contains(content, []byte("<nav>")) {
           97                 return content, nil
           98         }
           99         origContent := make([]byte, len(content))
          100         copy(origContent, content)
          101         first := []byte(`<nav>
          102 <ul>`)
          103 
          104         last := []byte(`</ul>
          105 </nav>`)
          106 
          107         replacement := []byte(`<nav id="TableOfContents">
          108 <ul>`)
          109 
          110         startOfTOC := bytes.Index(content, first)
          111 
          112         peekEnd := min(len(content), 70+startOfTOC)
          113 
          114         if startOfTOC < 0 {
          115                 return stripEmptyNav(content), toc
          116         }
          117         // Need to peek ahead to see if this nav element is actually the right one.
          118         correctNav := bytes.Index(content[startOfTOC:peekEnd], []byte(`<li><a href="#`))
          119         if correctNav < 0 { // no match found
          120                 return content, toc
          121         }
          122         lengthOfTOC := bytes.Index(content[startOfTOC:], last) + len(last)
          123         endOfTOC := startOfTOC + lengthOfTOC
          124 
          125         newcontent = append(content[:startOfTOC], content[endOfTOC:]...)
          126         toc = append(replacement, origContent[startOfTOC+len(first):endOfTOC]...)
          127         return
          128 }
          129 
          130 func (c *ContentSpec) SanitizeAnchorName(s string) string {
          131         return c.anchorNameSanitizer.SanitizeAnchorName(s)
          132 }
          133 
          134 func (c *ContentSpec) ResolveMarkup(in string) string {
          135         in = strings.ToLower(in)
          136 
          137         if mediaType, found := c.Cfg.ContentTypes().(media.ContentTypes).Types().GetBestMatch(markup.ResolveMarkup(in)); found {
          138                 return mediaType.SubType
          139         }
          140 
          141         if conv := c.Converters.Get(in); conv != nil {
          142                 return markup.ResolveMarkup(conv.Name())
          143         }
          144 
          145         return ""
          146 }
          147 
          148 // TotalWords counts instance of one or more consecutive white space
          149 // characters, as defined by unicode.IsSpace, in s.
          150 // This is a cheaper way of word counting than the obvious len(strings.Fields(s)).
          151 func TotalWords(s string) int {
          152         n := 0
          153         inWord := false
          154         for _, r := range s {
          155                 wasInWord := inWord
          156                 inWord = !unicode.IsSpace(r)
          157                 if inWord && !wasInWord {
          158                         n++
          159                 }
          160         }
          161         return n
          162 }
          163 
          164 // TrimShortHTML removes the outer tags from HTML input where (a) the opening
          165 // tag is present only once with the input, and (b) the opening and closing
          166 // tags wrap the input after white space removal.
          167 func (c *ContentSpec) TrimShortHTML(input []byte, markup string) []byte {
          168         openingTag := []byte("<p>")
          169         closingTag := []byte("</p>")
          170 
          171         if markup == media.DefaultContentTypes.AsciiDoc.SubType {
          172                 openingTag = []byte("<div class=\"paragraph\">\n<p>")
          173                 closingTag = []byte("</p>\n</div>")
          174         }
          175 
          176         if bytes.Count(input, openingTag) == 1 {
          177                 input = bytes.TrimSpace(input)
          178                 if bytes.HasPrefix(input, openingTag) && bytes.HasSuffix(input, closingTag) {
          179                         input = bytes.TrimPrefix(input, openingTag)
          180                         input = bytes.TrimSuffix(input, closingTag)
          181                         input = bytes.TrimSpace(input)
          182                 }
          183         }
          184         return input
          185 }