mirror of
https://salsa.debian.org/mdosch/feed-to-muc.git
synced 2024-12-22 21:05:25 +01:00
parent
3d981156fe
commit
96fd8cf3f6
11 changed files with 754 additions and 160 deletions
14
vendor/github.com/chilts/sid/ReadMe.md
generated
vendored
14
vendor/github.com/chilts/sid/ReadMe.md
generated
vendored
|
@ -20,19 +20,13 @@ go get github.com/chilts/sid
|
|||
|
||||
```
|
||||
id1 := sid.Id()
|
||||
id2 := sid.IdHex()
|
||||
id3 := sid.IdBase32()
|
||||
id4 := sid.IdBase64()
|
||||
id2 := sid.Id()
|
||||
|
||||
fmt.Printf("id1 = %s\n", id1)
|
||||
fmt.Printf("id2 = %s\n", id2)
|
||||
fmt.Printf("id3 = %s\n", id3)
|
||||
fmt.Printf("id4 = %s\n", id4)
|
||||
|
||||
// -> "id1 = 1559872035903071353-1186579057231285506"
|
||||
// -> "id2 = 15a5cf57e7d2a837-6eaafe687e7b3ec3"
|
||||
// -> "id3 = 1b9efqnl51jj7-4u66ikpfq9ugm"
|
||||
// -> "id4 = 1IeSBAWW9kK-0cDG64GQgGJ"
|
||||
// -> "id1 = 1IeSBAWW83j-2wgJ4PUtlAr"
|
||||
// -> "id2 = 1IeSBAWW9kK-0cDG64GQgGJ"
|
||||
```
|
||||
|
||||
## Author
|
||||
|
@ -43,6 +37,6 @@ For [AppsAttic](https://appsattic.com/), [@AppsAttic](https://twitter.com/AppsAt
|
|||
|
||||
## License
|
||||
|
||||
[MIT](https://chilts.mit-license.org/2017/)
|
||||
[MIT](https://publish.li/mit-qLQqmVTO).
|
||||
|
||||
(Ends)
|
||||
|
|
3
vendor/github.com/chilts/sid/go.mod
generated
vendored
3
vendor/github.com/chilts/sid/go.mod
generated
vendored
|
@ -1,3 +0,0 @@
|
|||
module github.com/chilts/sid
|
||||
|
||||
go 1.12
|
93
vendor/github.com/chilts/sid/sid.go
generated
vendored
93
vendor/github.com/chilts/sid/sid.go
generated
vendored
|
@ -12,7 +12,6 @@
|
|||
package sid
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -33,39 +32,17 @@ var chars = make([]string, 11, 11)
|
|||
// 64 chars but ordered by ASCII
|
||||
const base64 string = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"
|
||||
|
||||
func toStr(n int64) string {
|
||||
func toStr(now int64) string {
|
||||
// now do the generation (backwards, so we just %64 then /64 along the way)
|
||||
for i := 10; i >= 0; i-- {
|
||||
index := n % 64
|
||||
index := now % 64
|
||||
chars[i] = string(base64[index])
|
||||
n = n / 64
|
||||
now = now / 64
|
||||
}
|
||||
|
||||
return strings.Join(chars, "")
|
||||
}
|
||||
|
||||
func toBase32(n int64) string {
|
||||
b32 := strconv.FormatInt(n, 32)
|
||||
|
||||
for len(b32) < 13 {
|
||||
b32 = "0" + b32
|
||||
}
|
||||
|
||||
// log.Printf("b32=%s\n", b32)
|
||||
|
||||
return b32
|
||||
}
|
||||
|
||||
func toHex(n int64) string {
|
||||
hex := fmt.Sprintf("%x", n)
|
||||
|
||||
for len(hex) < 16 {
|
||||
hex = "0" + hex
|
||||
}
|
||||
|
||||
return hex
|
||||
}
|
||||
|
||||
// IdBase64 returns a 23 char string based on timestamp and a random number. The format is "XXXXXXXXXXX-YYYYYYYYYYY"
|
||||
// where X is the timestamp and Y is the random number. If (by any chance) this is called in the same nanosecond, the
|
||||
// random number is incremented instead of a new one being generated. This makes sure that two consecutive Ids
|
||||
|
@ -94,66 +71,10 @@ func IdBase64() string {
|
|||
return toStr(now) + "-" + toStr(r)
|
||||
}
|
||||
|
||||
// IdBase32 returns a 27 char string based on timestamp and a random number. The format is
|
||||
// "XXXXXXXXXXXXX-YYYYYYYYYYYYY" where X is the timestamp and Y is the random number. If (by any chance) this is called
|
||||
// in the same nanosecond, the random number is incremented instead of a new one being generated. This makes sure that
|
||||
// two consecutive Ids generated in the same goroutine also ensure those Ids are also sortable.
|
||||
//
|
||||
// It is safe to call from different goroutines since it has it's own locking.
|
||||
func IdBase32() string {
|
||||
// lock for lastTime, lastRand, and chars
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
now := time.Now().UTC().UnixNano()
|
||||
var r int64
|
||||
|
||||
// if we have the same time, just inc lastRand, else create a new one
|
||||
if now == lastTime {
|
||||
lastRand++
|
||||
} else {
|
||||
lastRand = rand.Int63()
|
||||
}
|
||||
r = lastRand
|
||||
|
||||
// remember this for next time
|
||||
lastTime = now
|
||||
|
||||
return toBase32(now) + "-" + toBase32(r)
|
||||
}
|
||||
|
||||
// IdHex returns a char string based on timestamp and a random number. The format is
|
||||
// "XXXXXXXXXXXXXXXX-YYYYYYYYYYYYYYYY" where X is the timestamp and Y is the random number. If (by any chance) this is
|
||||
// called in the same nanosecond, the random number is incremented instead of a new one being generated. This makes
|
||||
// sure that two consecutive Ids generated in the same goroutine also ensure those Ids are also sortable.
|
||||
//
|
||||
// It is safe to call from different goroutines since it has it's own locking.
|
||||
func IdHex() string {
|
||||
// lock for lastTime, lastRand, and chars
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
now := time.Now().UTC().UnixNano()
|
||||
var r int64
|
||||
|
||||
// if we have the same time, just inc lastRand, else create a new one
|
||||
if now == lastTime {
|
||||
lastRand++
|
||||
} else {
|
||||
lastRand = rand.Int63()
|
||||
}
|
||||
r = lastRand
|
||||
|
||||
// remember this for next time
|
||||
lastTime = now
|
||||
|
||||
return toHex(now) + "-" + toHex(r)
|
||||
}
|
||||
|
||||
// Id returns a 39 char string based on timestamp and a random number. The format is
|
||||
// "XXXXXXXXXXXXXXXXXXX-YYYYYYYYYYYYYYYYYYY" where X is the timestamp and Y is the random number. If (by any chance)
|
||||
// this is called in the same nanosecond, the random number is incremented instead of a new one being generated. This
|
||||
// makes sure that two consecutive Ids generated in the same goroutine also ensure those Ids are also sortable.
|
||||
// Id returns a 23 char string based on timestamp and a random number. The format is "XXXXXXXXXXX-YYYYYYYYYYY" where X
|
||||
// is the timestamp and Y is the random number. If (by any chance) this is called in the same nanosecond, the random
|
||||
// number is incremented instead of a new one being generated. This makes sure that two consecutive Ids generated in
|
||||
// the same goroutine also ensure those Ids are also sortable.
|
||||
//
|
||||
// It is safe to call from different goroutines since it has it's own locking.
|
||||
func Id() string {
|
||||
|
|
2
vendor/github.com/olekukonko/tablewriter/README.md
generated
vendored
2
vendor/github.com/olekukonko/tablewriter/README.md
generated
vendored
|
@ -283,7 +283,7 @@ import (
|
|||
|
||||
func main() {
|
||||
tableString := &strings.Builder{}
|
||||
table := tablewriter.NewWriter(tableString)
|
||||
table := tablewriter.NewWriter(tableString)
|
||||
|
||||
/*
|
||||
* Code to fill the table
|
||||
|
|
8
vendor/github.com/olekukonko/tablewriter/go.mod
generated
vendored
8
vendor/github.com/olekukonko/tablewriter/go.mod
generated
vendored
|
@ -1,8 +0,0 @@
|
|||
module github.com/olekukonko/tablewriter
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/mattn/go-runewidth v0.0.4
|
||||
github.com/olekukonko/tablewriter v0.0.1
|
||||
)
|
4
vendor/github.com/olekukonko/tablewriter/go.sum
generated
vendored
4
vendor/github.com/olekukonko/tablewriter/go.sum
generated
vendored
|
@ -1,4 +0,0 @@
|
|||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88=
|
||||
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
4
vendor/github.com/olekukonko/tablewriter/table.go
generated
vendored
4
vendor/github.com/olekukonko/tablewriter/table.go
generated
vendored
|
@ -766,7 +766,7 @@ func (t *Table) printRowMergeCells(writer io.Writer, columns [][]string, rowIdx
|
|||
|
||||
if t.autoMergeCells {
|
||||
//Store the full line to merge mutli-lines cells
|
||||
fullLine := strings.TrimRight(strings.Join(columns[y], " "), " ")
|
||||
fullLine := strings.Join(columns[y], " ")
|
||||
if len(previousLine) > y && fullLine == previousLine[y] && fullLine != "" {
|
||||
// If this cell is identical to the one above but not empty, we don't display the border and keep the cell empty.
|
||||
displayCellBorder = append(displayCellBorder, false)
|
||||
|
@ -804,7 +804,7 @@ func (t *Table) printRowMergeCells(writer io.Writer, columns [][]string, rowIdx
|
|||
//The new previous line is the current one
|
||||
previousLine = make([]string, total)
|
||||
for y := 0; y < total; y++ {
|
||||
previousLine[y] = strings.TrimRight(strings.Join(columns[y], " ")," ") //Store the full line for multi-lines cells
|
||||
previousLine[y] = strings.Join(columns[y], " ") //Store the full line for multi-lines cells
|
||||
}
|
||||
//Returns the newly added line and wether or not a border should be displayed above.
|
||||
return previousLine, displayCellBorder
|
||||
|
|
22
vendor/jaytaylor.com/html2text/LICENSE
generated
vendored
Normal file
22
vendor/jaytaylor.com/html2text/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Jay Taylor
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
137
vendor/jaytaylor.com/html2text/README.md
generated
vendored
Normal file
137
vendor/jaytaylor.com/html2text/README.md
generated
vendored
Normal file
|
@ -0,0 +1,137 @@
|
|||
# html2text
|
||||
|
||||
[![Documentation](https://godoc.org/github.com/jaytaylor/html2text?status.svg)](https://godoc.org/github.com/jaytaylor/html2text)
|
||||
[![Build Status](https://travis-ci.org/jaytaylor/html2text.svg?branch=master)](https://travis-ci.org/jaytaylor/html2text)
|
||||
[![Report Card](https://goreportcard.com/badge/github.com/jaytaylor/html2text)](https://goreportcard.com/report/github.com/jaytaylor/html2text)
|
||||
|
||||
### Converts HTML into text of the markdown-flavored variety
|
||||
|
||||
|
||||
## Introduction
|
||||
|
||||
Ensure your emails are readable by all!
|
||||
|
||||
Turns HTML into raw text, useful for sending fancy HTML emails with an equivalently nicely formatted TXT document as a fallback (e.g. for people who don't allow HTML emails or have other display issues).
|
||||
|
||||
html2text is a simple golang package for rendering HTML into plaintext.
|
||||
|
||||
There are still lots of improvements to be had, but FWIW this has worked fine for my [basic] HTML-2-text needs.
|
||||
|
||||
It requires go 1.x or newer ;)
|
||||
|
||||
|
||||
## Download the package
|
||||
|
||||
```bash
|
||||
go get jaytaylor.com/html2text
|
||||
```
|
||||
|
||||
## Example usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"jaytaylor.com/html2text"
|
||||
)
|
||||
|
||||
func main() {
|
||||
inputHTML := `
|
||||
<html>
|
||||
<head>
|
||||
<title>My Mega Service</title>
|
||||
<link rel=\"stylesheet\" href=\"main.css\">
|
||||
<style type=\"text/css\">body { color: #fff; }</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="logo">
|
||||
<a href="http://jaytaylor.com/"><img src="/logo-image.jpg" alt="Mega Service"/></a>
|
||||
</div>
|
||||
|
||||
<h1>Welcome to your new account on my service!</h1>
|
||||
|
||||
<p>
|
||||
Here is some more information:
|
||||
|
||||
<ul>
|
||||
<li>Link 1: <a href="https://example.com">Example.com</a></li>
|
||||
<li>Link 2: <a href="https://example2.com">Example2.com</a></li>
|
||||
<li>Something else</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Header 1</th><th>Header 2</th></tr>
|
||||
</thead>
|
||||
<tfoot>
|
||||
<tr><td>Footer 1</td><td>Footer 2</td></tr>
|
||||
</tfoot>
|
||||
<tbody>
|
||||
<tr><td>Row 1 Col 1</td><td>Row 1 Col 2</td></tr>
|
||||
<tr><td>Row 2 Col 1</td><td>Row 2 Col 2</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
text, err := html2text.FromString(inputHTML, html2text.Options{PrettyTables: true})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(text)
|
||||
}
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
Mega Service ( http://jaytaylor.com/ )
|
||||
|
||||
******************************************
|
||||
Welcome to your new account on my service!
|
||||
******************************************
|
||||
|
||||
Here is some more information:
|
||||
|
||||
* Link 1: Example.com ( https://example.com )
|
||||
* Link 2: Example2.com ( https://example2.com )
|
||||
* Something else
|
||||
|
||||
+-------------+-------------+
|
||||
| HEADER 1 | HEADER 2 |
|
||||
+-------------+-------------+
|
||||
| Row 1 Col 1 | Row 1 Col 2 |
|
||||
| Row 2 Col 1 | Row 2 Col 2 |
|
||||
+-------------+-------------+
|
||||
| FOOTER 1 | FOOTER 2 |
|
||||
+-------------+-------------+
|
||||
```
|
||||
|
||||
|
||||
## Unit-tests
|
||||
|
||||
Running the unit-tests is straightforward and standard:
|
||||
|
||||
```bash
|
||||
go test
|
||||
```
|
||||
|
||||
|
||||
# License
|
||||
|
||||
Permissive MIT license.
|
||||
|
||||
|
||||
## Contact
|
||||
|
||||
You are more than welcome to open issues and send pull requests if you find a bug or want a new feature.
|
||||
|
||||
If you appreciate this library please feel free to drop me a line and tell me! It's always nice to hear from people who have benefitted from my work.
|
||||
|
||||
Email: jay at (my github username).com
|
||||
|
||||
Twitter: [@jtaylor](https://twitter.com/jtaylor)
|
||||
|
535
vendor/jaytaylor.com/html2text/html2text.go
generated
vendored
Normal file
535
vendor/jaytaylor.com/html2text/html2text.go
generated
vendored
Normal file
|
@ -0,0 +1,535 @@
|
|||
package html2text
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/ssor/bom"
|
||||
"golang.org/x/net/html"
|
||||
"golang.org/x/net/html/atom"
|
||||
)
|
||||
|
||||
// Options provide toggles and overrides to control specific rendering behaviors.
|
||||
type Options struct {
|
||||
PrettyTables bool // Turns on pretty ASCII rendering for table elements.
|
||||
PrettyTablesOptions *PrettyTablesOptions // Configures pretty ASCII rendering for table elements.
|
||||
OmitLinks bool // Turns on omitting links
|
||||
}
|
||||
|
||||
// PrettyTablesOptions overrides tablewriter behaviors
|
||||
type PrettyTablesOptions struct {
|
||||
AutoFormatHeader bool
|
||||
AutoWrapText bool
|
||||
ReflowDuringAutoWrap bool
|
||||
ColWidth int
|
||||
ColumnSeparator string
|
||||
RowSeparator string
|
||||
CenterSeparator string
|
||||
HeaderAlignment int
|
||||
FooterAlignment int
|
||||
Alignment int
|
||||
ColumnAlignment []int
|
||||
NewLine string
|
||||
HeaderLine bool
|
||||
RowLine bool
|
||||
AutoMergeCells bool
|
||||
Borders tablewriter.Border
|
||||
}
|
||||
|
||||
// NewPrettyTablesOptions creates PrettyTablesOptions with default settings
|
||||
func NewPrettyTablesOptions() *PrettyTablesOptions {
|
||||
return &PrettyTablesOptions{
|
||||
AutoFormatHeader: true,
|
||||
AutoWrapText: true,
|
||||
ReflowDuringAutoWrap: true,
|
||||
ColWidth: tablewriter.MAX_ROW_WIDTH,
|
||||
ColumnSeparator: tablewriter.COLUMN,
|
||||
RowSeparator: tablewriter.ROW,
|
||||
CenterSeparator: tablewriter.CENTER,
|
||||
HeaderAlignment: tablewriter.ALIGN_DEFAULT,
|
||||
FooterAlignment: tablewriter.ALIGN_DEFAULT,
|
||||
Alignment: tablewriter.ALIGN_DEFAULT,
|
||||
ColumnAlignment: []int{},
|
||||
NewLine: tablewriter.NEWLINE,
|
||||
HeaderLine: true,
|
||||
RowLine: false,
|
||||
AutoMergeCells: false,
|
||||
Borders: tablewriter.Border{Left: true, Right: true, Bottom: true, Top: true},
|
||||
}
|
||||
}
|
||||
|
||||
// FromHTMLNode renders text output from a pre-parsed HTML document.
|
||||
func FromHTMLNode(doc *html.Node, o ...Options) (string, error) {
|
||||
var options Options
|
||||
if len(o) > 0 {
|
||||
options = o[0]
|
||||
}
|
||||
|
||||
ctx := textifyTraverseContext{
|
||||
buf: bytes.Buffer{},
|
||||
options: options,
|
||||
}
|
||||
if err := ctx.traverse(doc); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
text := strings.TrimSpace(newlineRe.ReplaceAllString(
|
||||
strings.Replace(ctx.buf.String(), "\n ", "\n", -1), "\n\n"),
|
||||
)
|
||||
return text, nil
|
||||
}
|
||||
|
||||
// FromReader renders text output after parsing HTML for the specified
|
||||
// io.Reader.
|
||||
func FromReader(reader io.Reader, options ...Options) (string, error) {
|
||||
newReader, err := bom.NewReaderWithoutBom(reader)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
doc, err := html.Parse(newReader)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return FromHTMLNode(doc, options...)
|
||||
}
|
||||
|
||||
// FromString parses HTML from the input string, then renders the text form.
|
||||
func FromString(input string, options ...Options) (string, error) {
|
||||
bs := bom.CleanBom([]byte(input))
|
||||
text, err := FromReader(bytes.NewReader(bs), options...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return text, nil
|
||||
}
|
||||
|
||||
var (
|
||||
spacingRe = regexp.MustCompile(`[ \r\n\t]+`)
|
||||
newlineRe = regexp.MustCompile(`\n\n+`)
|
||||
)
|
||||
|
||||
// traverseTableCtx holds text-related context.
|
||||
type textifyTraverseContext struct {
|
||||
buf bytes.Buffer
|
||||
|
||||
prefix string
|
||||
tableCtx tableTraverseContext
|
||||
options Options
|
||||
endsWithSpace bool
|
||||
justClosedDiv bool
|
||||
blockquoteLevel int
|
||||
lineLength int
|
||||
isPre bool
|
||||
}
|
||||
|
||||
// tableTraverseContext holds table ASCII-form related context.
|
||||
type tableTraverseContext struct {
|
||||
header []string
|
||||
body [][]string
|
||||
footer []string
|
||||
tmpRow int
|
||||
isInFooter bool
|
||||
}
|
||||
|
||||
func (tableCtx *tableTraverseContext) init() {
|
||||
tableCtx.body = [][]string{}
|
||||
tableCtx.header = []string{}
|
||||
tableCtx.footer = []string{}
|
||||
tableCtx.isInFooter = false
|
||||
tableCtx.tmpRow = 0
|
||||
}
|
||||
|
||||
func (ctx *textifyTraverseContext) handleElement(node *html.Node) error {
|
||||
ctx.justClosedDiv = false
|
||||
|
||||
switch node.DataAtom {
|
||||
case atom.Br:
|
||||
return ctx.emit("\n")
|
||||
|
||||
case atom.H1, atom.H2, atom.H3:
|
||||
subCtx := textifyTraverseContext{}
|
||||
if err := subCtx.traverseChildren(node); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
str := subCtx.buf.String()
|
||||
dividerLen := 0
|
||||
for _, line := range strings.Split(str, "\n") {
|
||||
if lineLen := len([]rune(line)); lineLen-1 > dividerLen {
|
||||
dividerLen = lineLen - 1
|
||||
}
|
||||
}
|
||||
var divider string
|
||||
if node.DataAtom == atom.H1 {
|
||||
divider = strings.Repeat("*", dividerLen)
|
||||
} else {
|
||||
divider = strings.Repeat("-", dividerLen)
|
||||
}
|
||||
|
||||
if node.DataAtom == atom.H3 {
|
||||
return ctx.emit("\n\n" + str + "\n" + divider + "\n\n")
|
||||
}
|
||||
return ctx.emit("\n\n" + divider + "\n" + str + "\n" + divider + "\n\n")
|
||||
|
||||
case atom.Blockquote:
|
||||
ctx.blockquoteLevel++
|
||||
ctx.prefix = strings.Repeat(">", ctx.blockquoteLevel) + " "
|
||||
if err := ctx.emit("\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
if ctx.blockquoteLevel == 1 {
|
||||
if err := ctx.emit("\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := ctx.traverseChildren(node); err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.blockquoteLevel--
|
||||
ctx.prefix = strings.Repeat(">", ctx.blockquoteLevel)
|
||||
if ctx.blockquoteLevel > 0 {
|
||||
ctx.prefix += " "
|
||||
}
|
||||
return ctx.emit("\n\n")
|
||||
|
||||
case atom.Div:
|
||||
if ctx.lineLength > 0 {
|
||||
if err := ctx.emit("\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := ctx.traverseChildren(node); err != nil {
|
||||
return err
|
||||
}
|
||||
var err error
|
||||
if !ctx.justClosedDiv {
|
||||
err = ctx.emit("\n")
|
||||
}
|
||||
ctx.justClosedDiv = true
|
||||
return err
|
||||
|
||||
case atom.Li:
|
||||
if err := ctx.emit("* "); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ctx.traverseChildren(node); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.emit("\n")
|
||||
|
||||
case atom.B, atom.Strong:
|
||||
subCtx := textifyTraverseContext{}
|
||||
subCtx.endsWithSpace = true
|
||||
if err := subCtx.traverseChildren(node); err != nil {
|
||||
return err
|
||||
}
|
||||
str := subCtx.buf.String()
|
||||
return ctx.emit("*" + str + "*")
|
||||
|
||||
case atom.A:
|
||||
linkText := ""
|
||||
// For simple link element content with single text node only, peek at the link text.
|
||||
if node.FirstChild != nil && node.FirstChild.NextSibling == nil && node.FirstChild.Type == html.TextNode {
|
||||
linkText = node.FirstChild.Data
|
||||
}
|
||||
|
||||
// If image is the only child, take its alt text as the link text.
|
||||
if img := node.FirstChild; img != nil && node.LastChild == img && img.DataAtom == atom.Img {
|
||||
if altText := getAttrVal(img, "alt"); altText != "" {
|
||||
if err := ctx.emit(altText); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if err := ctx.traverseChildren(node); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hrefLink := ""
|
||||
if attrVal := getAttrVal(node, "href"); attrVal != "" {
|
||||
attrVal = ctx.normalizeHrefLink(attrVal)
|
||||
// Don't print link href if it matches link element content or if the link is empty.
|
||||
if !ctx.options.OmitLinks && attrVal != "" && linkText != attrVal {
|
||||
hrefLink = "( " + attrVal + " )"
|
||||
}
|
||||
}
|
||||
|
||||
return ctx.emit(hrefLink)
|
||||
|
||||
case atom.P, atom.Ul:
|
||||
return ctx.paragraphHandler(node)
|
||||
|
||||
case atom.Table, atom.Tfoot, atom.Th, atom.Tr, atom.Td:
|
||||
if ctx.options.PrettyTables {
|
||||
return ctx.handleTableElement(node)
|
||||
} else if node.DataAtom == atom.Table {
|
||||
return ctx.paragraphHandler(node)
|
||||
}
|
||||
return ctx.traverseChildren(node)
|
||||
|
||||
case atom.Pre:
|
||||
ctx.isPre = true
|
||||
err := ctx.traverseChildren(node)
|
||||
ctx.isPre = false
|
||||
return err
|
||||
|
||||
case atom.Style, atom.Script, atom.Head:
|
||||
// Ignore the subtree.
|
||||
return nil
|
||||
|
||||
default:
|
||||
return ctx.traverseChildren(node)
|
||||
}
|
||||
}
|
||||
|
||||
// paragraphHandler renders node children surrounded by double newlines.
|
||||
func (ctx *textifyTraverseContext) paragraphHandler(node *html.Node) error {
|
||||
if err := ctx.emit("\n\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ctx.traverseChildren(node); err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.emit("\n\n")
|
||||
}
|
||||
|
||||
// handleTableElement is only to be invoked when options.PrettyTables is active.
|
||||
func (ctx *textifyTraverseContext) handleTableElement(node *html.Node) error {
|
||||
if !ctx.options.PrettyTables {
|
||||
panic("handleTableElement invoked when PrettyTables not active")
|
||||
}
|
||||
|
||||
switch node.DataAtom {
|
||||
case atom.Table:
|
||||
if err := ctx.emit("\n\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Re-intialize all table context.
|
||||
ctx.tableCtx.init()
|
||||
|
||||
// Browse children, enriching context with table data.
|
||||
if err := ctx.traverseChildren(node); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
table := tablewriter.NewWriter(buf)
|
||||
if ctx.options.PrettyTablesOptions != nil {
|
||||
options := ctx.options.PrettyTablesOptions
|
||||
table.SetAutoFormatHeaders(options.AutoFormatHeader)
|
||||
table.SetAutoWrapText(options.AutoWrapText)
|
||||
table.SetReflowDuringAutoWrap(options.ReflowDuringAutoWrap)
|
||||
table.SetColWidth(options.ColWidth)
|
||||
table.SetColumnSeparator(options.ColumnSeparator)
|
||||
table.SetRowSeparator(options.RowSeparator)
|
||||
table.SetCenterSeparator(options.CenterSeparator)
|
||||
table.SetHeaderAlignment(options.HeaderAlignment)
|
||||
table.SetFooterAlignment(options.FooterAlignment)
|
||||
table.SetAlignment(options.Alignment)
|
||||
table.SetColumnAlignment(options.ColumnAlignment)
|
||||
table.SetNewLine(options.NewLine)
|
||||
table.SetHeaderLine(options.HeaderLine)
|
||||
table.SetRowLine(options.RowLine)
|
||||
table.SetAutoMergeCells(options.AutoMergeCells)
|
||||
table.SetBorders(options.Borders)
|
||||
}
|
||||
table.SetHeader(ctx.tableCtx.header)
|
||||
table.SetFooter(ctx.tableCtx.footer)
|
||||
table.AppendBulk(ctx.tableCtx.body)
|
||||
|
||||
// Render the table using ASCII.
|
||||
table.Render()
|
||||
if err := ctx.emit(buf.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.emit("\n\n")
|
||||
|
||||
case atom.Tfoot:
|
||||
ctx.tableCtx.isInFooter = true
|
||||
if err := ctx.traverseChildren(node); err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.tableCtx.isInFooter = false
|
||||
|
||||
case atom.Tr:
|
||||
ctx.tableCtx.body = append(ctx.tableCtx.body, []string{})
|
||||
if err := ctx.traverseChildren(node); err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.tableCtx.tmpRow++
|
||||
|
||||
case atom.Th:
|
||||
res, err := ctx.renderEachChild(node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx.tableCtx.header = append(ctx.tableCtx.header, res)
|
||||
|
||||
case atom.Td:
|
||||
res, err := ctx.renderEachChild(node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ctx.tableCtx.isInFooter {
|
||||
ctx.tableCtx.footer = append(ctx.tableCtx.footer, res)
|
||||
} else {
|
||||
ctx.tableCtx.body[ctx.tableCtx.tmpRow] = append(ctx.tableCtx.body[ctx.tableCtx.tmpRow], res)
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctx *textifyTraverseContext) traverse(node *html.Node) error {
|
||||
switch node.Type {
|
||||
default:
|
||||
return ctx.traverseChildren(node)
|
||||
|
||||
case html.TextNode:
|
||||
var data string
|
||||
if ctx.isPre {
|
||||
data = node.Data
|
||||
} else {
|
||||
data = strings.TrimSpace(spacingRe.ReplaceAllString(node.Data, " "))
|
||||
}
|
||||
return ctx.emit(data)
|
||||
|
||||
case html.ElementNode:
|
||||
return ctx.handleElement(node)
|
||||
}
|
||||
}
|
||||
|
||||
func (ctx *textifyTraverseContext) traverseChildren(node *html.Node) error {
|
||||
for c := node.FirstChild; c != nil; c = c.NextSibling {
|
||||
if err := ctx.traverse(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctx *textifyTraverseContext) emit(data string) error {
|
||||
if data == "" {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
lines = ctx.breakLongLines(data)
|
||||
err error
|
||||
)
|
||||
for _, line := range lines {
|
||||
runes := []rune(line)
|
||||
startsWithSpace := unicode.IsSpace(runes[0])
|
||||
if !startsWithSpace && !ctx.endsWithSpace && !strings.HasPrefix(data, ".") {
|
||||
if err = ctx.buf.WriteByte(' '); err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.lineLength++
|
||||
}
|
||||
ctx.endsWithSpace = unicode.IsSpace(runes[len(runes)-1])
|
||||
for _, c := range line {
|
||||
if _, err = ctx.buf.WriteString(string(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.lineLength++
|
||||
if c == '\n' {
|
||||
ctx.lineLength = 0
|
||||
if ctx.prefix != "" {
|
||||
if _, err = ctx.buf.WriteString(ctx.prefix); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const maxLineLen = 74
|
||||
|
||||
func (ctx *textifyTraverseContext) breakLongLines(data string) []string {
|
||||
// Only break lines when in blockquotes.
|
||||
if ctx.blockquoteLevel == 0 {
|
||||
return []string{data}
|
||||
}
|
||||
var (
|
||||
ret = []string{}
|
||||
runes = []rune(data)
|
||||
l = len(runes)
|
||||
existing = ctx.lineLength
|
||||
)
|
||||
if existing >= maxLineLen {
|
||||
ret = append(ret, "\n")
|
||||
existing = 0
|
||||
}
|
||||
for l+existing > maxLineLen {
|
||||
i := maxLineLen - existing
|
||||
for i >= 0 && !unicode.IsSpace(runes[i]) {
|
||||
i--
|
||||
}
|
||||
if i == -1 {
|
||||
// No spaces, so go the other way.
|
||||
i = maxLineLen - existing
|
||||
for i < l && !unicode.IsSpace(runes[i]) {
|
||||
i++
|
||||
}
|
||||
}
|
||||
ret = append(ret, string(runes[:i])+"\n")
|
||||
for i < l && unicode.IsSpace(runes[i]) {
|
||||
i++
|
||||
}
|
||||
runes = runes[i:]
|
||||
l = len(runes)
|
||||
existing = 0
|
||||
}
|
||||
if len(runes) > 0 {
|
||||
ret = append(ret, string(runes))
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (ctx *textifyTraverseContext) normalizeHrefLink(link string) string {
|
||||
link = strings.TrimSpace(link)
|
||||
link = strings.TrimPrefix(link, "mailto:")
|
||||
return link
|
||||
}
|
||||
|
||||
// renderEachChild visits each direct child of a node and collects the sequence of
|
||||
// textuual representaitons separated by a single newline.
|
||||
func (ctx *textifyTraverseContext) renderEachChild(node *html.Node) (string, error) {
|
||||
buf := &bytes.Buffer{}
|
||||
for c := node.FirstChild; c != nil; c = c.NextSibling {
|
||||
s, err := FromHTMLNode(c, ctx.options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err = buf.WriteString(s); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if c.NextSibling != nil {
|
||||
if err = buf.WriteByte('\n'); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func getAttrVal(node *html.Node, attrName string) string {
|
||||
for _, attr := range node.Attr {
|
||||
if attr.Key == attrName {
|
||||
return attr.Val
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
92
vendor/vendor.json
vendored
92
vendor/vendor.json
vendored
|
@ -15,10 +15,10 @@
|
|||
"revisionTime": "2018-10-12T15:44:24Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "WoatyvsqqryIUsNP+2fXlbVtAVM=",
|
||||
"checksumSHA1": "5UWAX/5F5bqC7tKtDb2QFFRqwAs=",
|
||||
"path": "github.com/chilts/sid",
|
||||
"revision": "660e94789ec9b45634f588d40881e81b56de92a0",
|
||||
"revisionTime": "2019-06-07T04:24:30Z"
|
||||
"revision": "250d10e55bf450834d37cb13b7dae8816ada9b28",
|
||||
"revisionTime": "2018-09-28T23:21:30Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "qR7D38Zmn9TPUvGn64k+r0+Kq1c=",
|
||||
|
@ -69,10 +69,10 @@
|
|||
"revisionTime": "2018-10-12T15:49:47Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "C2jnBks3LctebwDmLMoq/zYZex0=",
|
||||
"checksumSHA1": "HZJ2dhzXoMi8n+iY80A9vsnyQUk=",
|
||||
"path": "github.com/olekukonko/tablewriter",
|
||||
"revision": "cc27d85e17cec9768d2ac401ea5d619a9628f16d",
|
||||
"revisionTime": "2019-06-18T03:32:46Z"
|
||||
"revision": "1c0837c15a0bac7871496dfce5dcdd308e0a330f",
|
||||
"revisionTime": "2019-05-08T01:39:46Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "qErubHtC7DAFBnEQkMTuKDtfFTU=",
|
||||
|
@ -83,122 +83,122 @@
|
|||
{
|
||||
"checksumSHA1": "bONEZcbkYKiPyABrecOLzHomjPU=",
|
||||
"path": "golang.org/x/net/html",
|
||||
"revision": "ba9fcec4b297b415637633c5a6e8fa592e4a16c3",
|
||||
"revisionTime": "2019-08-26T16:14:39Z"
|
||||
"revision": "f3200d17e092c607f615320ecaad13d87ad9a2b3",
|
||||
"revisionTime": "2019-05-22T15:39:15Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "xwhqe/igHQrY3IhqDwzo6j7qpm8=",
|
||||
"path": "golang.org/x/net/html/atom",
|
||||
"revision": "ba9fcec4b297b415637633c5a6e8fa592e4a16c3",
|
||||
"revisionTime": "2019-08-26T16:14:39Z"
|
||||
"revision": "f3200d17e092c607f615320ecaad13d87ad9a2b3",
|
||||
"revisionTime": "2019-05-22T15:39:15Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "barUU39reQ7LdgYLA323hQ/UGy4=",
|
||||
"path": "golang.org/x/net/html/charset",
|
||||
"revision": "ba9fcec4b297b415637633c5a6e8fa592e4a16c3",
|
||||
"revisionTime": "2019-08-26T16:14:39Z"
|
||||
"revision": "f3200d17e092c607f615320ecaad13d87ad9a2b3",
|
||||
"revisionTime": "2019-05-22T15:39:15Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "tqqo7DEeFCclb58XbN44WwdpWww=",
|
||||
"path": "golang.org/x/text/encoding",
|
||||
"revision": "3d0f7978add91030e5e8976ff65ccdd828286cba",
|
||||
"revisionTime": "2019-08-29T15:11:34Z"
|
||||
"revision": "342b2e1fbaa52c93f31447ad2c6abc048c63e475",
|
||||
"revisionTime": "2018-12-15T17:52:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "DSdlK4MKI/a3U8Zaee2XKBe01Fo=",
|
||||
"path": "golang.org/x/text/encoding/charmap",
|
||||
"revision": "3d0f7978add91030e5e8976ff65ccdd828286cba",
|
||||
"revisionTime": "2019-08-29T15:11:34Z"
|
||||
"revision": "342b2e1fbaa52c93f31447ad2c6abc048c63e475",
|
||||
"revisionTime": "2018-12-15T17:52:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "SbJkfe5G/5tji96Pa15/ePDOCtk=",
|
||||
"path": "golang.org/x/text/encoding/htmlindex",
|
||||
"revision": "3d0f7978add91030e5e8976ff65ccdd828286cba",
|
||||
"revisionTime": "2019-08-29T15:11:34Z"
|
||||
"revision": "342b2e1fbaa52c93f31447ad2c6abc048c63e475",
|
||||
"revisionTime": "2018-12-15T17:52:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "zeHyHebIZl1tGuwGllIhjfci+wI=",
|
||||
"path": "golang.org/x/text/encoding/internal",
|
||||
"revision": "3d0f7978add91030e5e8976ff65ccdd828286cba",
|
||||
"revisionTime": "2019-08-29T15:11:34Z"
|
||||
"revision": "342b2e1fbaa52c93f31447ad2c6abc048c63e475",
|
||||
"revisionTime": "2018-12-15T17:52:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "hT7VaIBlkm2YpKulgnjqXNdicGQ=",
|
||||
"path": "golang.org/x/text/encoding/internal/identifier",
|
||||
"revision": "3d0f7978add91030e5e8976ff65ccdd828286cba",
|
||||
"revisionTime": "2019-08-29T15:11:34Z"
|
||||
"revision": "342b2e1fbaa52c93f31447ad2c6abc048c63e475",
|
||||
"revisionTime": "2018-12-15T17:52:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "2YqVpmvjWGEBATyUphTP1MS34JE=",
|
||||
"path": "golang.org/x/text/encoding/japanese",
|
||||
"revision": "3d0f7978add91030e5e8976ff65ccdd828286cba",
|
||||
"revisionTime": "2019-08-29T15:11:34Z"
|
||||
"revision": "342b2e1fbaa52c93f31447ad2c6abc048c63e475",
|
||||
"revisionTime": "2018-12-15T17:52:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "+ErWCAdaMwO4PLtrk9D/Hh+7oQM=",
|
||||
"path": "golang.org/x/text/encoding/korean",
|
||||
"revision": "3d0f7978add91030e5e8976ff65ccdd828286cba",
|
||||
"revisionTime": "2019-08-29T15:11:34Z"
|
||||
"revision": "342b2e1fbaa52c93f31447ad2c6abc048c63e475",
|
||||
"revisionTime": "2018-12-15T17:52:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "mTuZi5urYwgDIO8+Gfql2pv8Vwg=",
|
||||
"path": "golang.org/x/text/encoding/simplifiedchinese",
|
||||
"revision": "3d0f7978add91030e5e8976ff65ccdd828286cba",
|
||||
"revisionTime": "2019-08-29T15:11:34Z"
|
||||
"revision": "342b2e1fbaa52c93f31447ad2c6abc048c63e475",
|
||||
"revisionTime": "2018-12-15T17:52:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "D+VI4j0Wjzr8SeupWdOB5KBdFOw=",
|
||||
"path": "golang.org/x/text/encoding/traditionalchinese",
|
||||
"revision": "3d0f7978add91030e5e8976ff65ccdd828286cba",
|
||||
"revisionTime": "2019-08-29T15:11:34Z"
|
||||
"revision": "342b2e1fbaa52c93f31447ad2c6abc048c63e475",
|
||||
"revisionTime": "2018-12-15T17:52:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "bAJTZJ3IGJdNmN/PSlRMRxWtxec=",
|
||||
"path": "golang.org/x/text/encoding/unicode",
|
||||
"revision": "3d0f7978add91030e5e8976ff65ccdd828286cba",
|
||||
"revisionTime": "2019-08-29T15:11:34Z"
|
||||
"revision": "342b2e1fbaa52c93f31447ad2c6abc048c63e475",
|
||||
"revisionTime": "2018-12-15T17:52:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "ybE4kAPmNPV/dvShuG86AmLbhdE=",
|
||||
"path": "golang.org/x/text/internal/language",
|
||||
"revision": "3d0f7978add91030e5e8976ff65ccdd828286cba",
|
||||
"revisionTime": "2019-08-29T15:11:34Z"
|
||||
"revision": "342b2e1fbaa52c93f31447ad2c6abc048c63e475",
|
||||
"revisionTime": "2018-12-15T17:52:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "VDwNSsZP6KShjTSwGUQUGJVrs1I=",
|
||||
"path": "golang.org/x/text/internal/language/compact",
|
||||
"revision": "3d0f7978add91030e5e8976ff65ccdd828286cba",
|
||||
"revisionTime": "2019-08-29T15:11:34Z"
|
||||
"revision": "342b2e1fbaa52c93f31447ad2c6abc048c63e475",
|
||||
"revisionTime": "2018-12-15T17:52:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "hyNCcTwMQnV6/MK8uUW9E5H0J0M=",
|
||||
"path": "golang.org/x/text/internal/tag",
|
||||
"revision": "3d0f7978add91030e5e8976ff65ccdd828286cba",
|
||||
"revisionTime": "2019-08-29T15:11:34Z"
|
||||
"revision": "342b2e1fbaa52c93f31447ad2c6abc048c63e475",
|
||||
"revisionTime": "2018-12-15T17:52:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Qk7dljcrEK1BJkAEZguxAbG9dSo=",
|
||||
"path": "golang.org/x/text/internal/utf8internal",
|
||||
"revision": "3d0f7978add91030e5e8976ff65ccdd828286cba",
|
||||
"revisionTime": "2019-08-29T15:11:34Z"
|
||||
"revision": "342b2e1fbaa52c93f31447ad2c6abc048c63e475",
|
||||
"revisionTime": "2018-12-15T17:52:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "oYNlkS+0TimKOScUz3Hn9QWyz6w=",
|
||||
"path": "golang.org/x/text/language",
|
||||
"revision": "3d0f7978add91030e5e8976ff65ccdd828286cba",
|
||||
"revisionTime": "2019-08-29T15:11:34Z"
|
||||
"revision": "342b2e1fbaa52c93f31447ad2c6abc048c63e475",
|
||||
"revisionTime": "2018-12-15T17:52:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "IV4MN7KGBSocu/5NR3le3sxup4Y=",
|
||||
"path": "golang.org/x/text/runes",
|
||||
"revision": "3d0f7978add91030e5e8976ff65ccdd828286cba",
|
||||
"revisionTime": "2019-08-29T15:11:34Z"
|
||||
"revision": "342b2e1fbaa52c93f31447ad2c6abc048c63e475",
|
||||
"revisionTime": "2018-12-15T17:52:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "R9iBDY+aPnT+8pyRcqGjXq5QixA=",
|
||||
"path": "golang.org/x/text/transform",
|
||||
"revision": "3d0f7978add91030e5e8976ff65ccdd828286cba",
|
||||
"revisionTime": "2019-08-29T15:11:34Z"
|
||||
"revision": "342b2e1fbaa52c93f31447ad2c6abc048c63e475",
|
||||
"revisionTime": "2018-12-15T17:52:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "2/9hMw7Y4I42L/PTobKqVldWUAU=",
|
||||
|
|
Loading…
Reference in a new issue