That's some text with a footnote.
//= = = = = = = = = = = = = = = = = = = = = = = =//
3
//- - - - - - - - -//
[^000]:0 [^]:
//- - - - - - - - -//
//= = = = = = = = = = = = = = = = = = = = = = = =//
4
//- - - - - - - - -//
This[^3] is[^1] text with footnotes[^2].
[^1]: Footnote one
[^2]: Footnote two
[^3]: Footnote three
//- - - - - - - - -//
www.commonmark.org/he <lp
//= = = = = = = = = = = = = = = = = = = = = = = =//
7
//- - - - - - - - -//
http://commonmark.org
(Visit https://encrypted.google.com/search?q=Markup+(business))
Anonymous FTP is available at ftp://foo.bar.baz.
//- - - - - - - - -//
http://commonmark.org
(Visit https://encrypted.google.com/search?q=Markup+(business) )
Anonymous FTP is available at ftp://foo.bar.baz .
//= = = = = = = = = = = = = = = = = = = = = = = =//
8
//- - - - - - - - -//
foo@bar.baz
//- - - - - - - - -//
foo@bar.baz
//= = = = = = = = = = = = = = = = = = = = = = = =//
9
//- - - - - - - - -//
hello@mail+xyz.example isn't valid, but hello+xyz@mail.example is.
//- - - - - - - - -//
hello@mail+xyz.example isn't valid, but hello+xyz@mail.example is.
//= = = = = = = = = = = = = = = = = = = = = = = =//
10
//- - - - - - - - -//
a.b-c_d@a.b
a.b-c_d@a.b.
a.b-c_d@a.b-
a.b-c_d@a.b_
//- - - - - - - - -//
a.b-c_d@a.b
a.b-c_d@a.b .
a.b-c_d@a.b-
a.b-c_d@a.b_
//= = = = = = = = = = = = = = = = = = = = = = = =//
11
//- - - - - - - - -//
https://github.com#sun,mon
//- - - - - - - - -//
https://github.com#sun,mon
//= = = = = = = = = = = = = = = = = = = = = = = =//
12
//- - - - - - - - -//
https://github.com/sunday's
//- - - - - - - - -//
https://github.com/sunday's
//= = = = = = = = = = = = = = = = = = = = = = = =//
13
//- - - - - - - - -//
https://github.com?q=stars:>1
//- - - - - - - - -//
https://github.com?q=stars:>1
//= = = = = = = = = = = = = = = = = = = = = = = =//
14
//- - - - - - - - -//
[https://google.com](https://google.com)
//- - - - - - - - -//
https://google.com
//= = = = = = = = = = = = = = = = = = = = = = = =//
15
//- - - - - - - - -//
This is a `git@github.com:vim/vim`
//- - - - - - - - -//
This is a git@github.com:vim/vim
//= = = = = = = = = = = = = = = = = = = = = = = =//
16
//- - - - - - - - -//
https://nic.college
//- - - - - - - - -//
https://nic.college
//= = = = = = = = = = = = = = = = = = = = = = = =//
17
//- - - - - - - - -//
http://server.intranet.acme.com:1313
//- - - - - - - - -//
http://server.intranet.acme.com:1313
//= = = = = = = = = = = = = = = = = = = = = = = =//
18
//- - - - - - - - -//
https://g.page/foo
//- - - - - - - - -//
https://g.page/foo
//= = = = = = = = = = = = = = = = = = = = = = = =//
19: Trailing punctuation (specifically, ?, !, ., ,, :, *, _, and ~) will not be considered part of the autolink
//- - - - - - - - -//
__http://test.com/~/a__
__http://test.com/~/__
__http://test.com/~__
__http://test.com/a/~__
//- - - - - - - - -//
http://test.com/~/a
http://test.com/~/
http://test.com/ ~
http://test.com/a/ ~
//= = = = = = = = = = = = = = = = = = = = = = = =//
yuin-goldmark-9a7f4c9/extension/_test/strikethrough.txt 0000664 0000000 0000000 00000001664 15037761217 0023541 0 ustar 00root root 0000000 0000000 1
//- - - - - - - - -//
~~Hi~~ Hello, world!
//- - - - - - - - -//
Hi Hello, world!
//= = = = = = = = = = = = = = = = = = = = = = = =//
2
//- - - - - - - - -//
This ~~has a
new paragraph~~.
//- - - - - - - - -//
This ~~has a
new paragraph~~.
//= = = = = = = = = = = = = = = = = = = = = = = =//
3
//- - - - - - - - -//
~Hi~ Hello, world!
//- - - - - - - - -//
Hi Hello, world!
//= = = = = = = = = = = = = = = = = = = = = = = =//
4: Three or more tildes do not create a strikethrough
//- - - - - - - - -//
This will ~~~not~~~ strike.
//- - - - - - - - -//
This will ~~~not~~~ strike.
//= = = = = = = = = = = = = = = = = = = = = = = =//
5: Leading three or more tildes do not create a strikethrough, create a code block
//- - - - - - - - -//
~~~Hi~~~ Hello, world!
//- - - - - - - - -//
//= = = = = = = = = = = = = = = = = = = = = = = =//
yuin-goldmark-9a7f4c9/extension/_test/table.txt 0000664 0000000 0000000 00000006715 15037761217 0021730 0 ustar 00root root 0000000 0000000 1
//- - - - - - - - -//
| foo | bar |
| --- | --- |
| baz | bim |
//- - - - - - - - -//
//= = = = = = = = = = = = = = = = = = = = = = = =//
2
//- - - - - - - - -//
| abc | defghi |
:-: | -----------:
bar | baz
//- - - - - - - - -//
//= = = = = = = = = = = = = = = = = = = = = = = =//
3
//- - - - - - - - -//
| f\|oo |
| ------ |
| b `\|` az |
| b **\|** im |
//- - - - - - - - -//
//= = = = = = = = = = = = = = = = = = = = = = = =//
4
//- - - - - - - - -//
| abc | def |
| --- | --- |
| bar | baz |
> bar
//- - - - - - - - -//
bar
//= = = = = = = = = = = = = = = = = = = = = = = =//
5
//- - - - - - - - -//
| abc | def |
| --- | --- |
| bar | baz |
bar
bar
//- - - - - - - - -//
bar
//= = = = = = = = = = = = = = = = = = = = = = = =//
6
//- - - - - - - - -//
| abc | def |
| --- |
| bar |
//- - - - - - - - -//
| abc | def |
| --- |
| bar |
//= = = = = = = = = = = = = = = = = = = = = = = =//
7
//- - - - - - - - -//
| abc | def |
| --- | --- |
| bar |
| bar | baz | boo |
//- - - - - - - - -//
//= = = = = = = = = = = = = = = = = = = = = = = =//
8
//- - - - - - - - -//
| abc | def |
| --- | --- |
//- - - - - - - - -//
//= = = = = = = = = = = = = = = = = = = = = = = =//
9
//- - - - - - - - -//
Foo|Bar
---|---
`Yoyo`|Dyne
//- - - - - - - - -//
//= = = = = = = = = = = = = = = = = = = = = = = =//
10
//- - - - - - - - -//
foo|bar
---|---
`\` | second column
//- - - - - - - - -//
//= = = = = = = = = = = = = = = = = = = = = = = =//
11: Tables can interrupt paragraph
//- - - - - - - - -//
**xxx**
| hello | hi |
| :----: | :----:|
//- - - - - - - - -//
xxx
//= = = = = = = = = = = = = = = = = = = = = = = =//
12: A delimiter can not start with more than 3 spaces
//- - - - - - - - -//
Foo
---
//- - - - - - - - -//
Foo
---
//= = = = = = = = = = = = = = = = = = = = = = = =//
13: A delimiter can not start with more than 3 spaces(w/ tabs)
OPTIONS: {"enableEscape": true}
//- - - - - - - - -//
- aaa
Foo
\t\t---
//- - - - - - - - -//
//= = = = = = = = = = = = = = = = = = = = = = = =//
yuin-goldmark-9a7f4c9/extension/_test/tasklist.txt 0000664 0000000 0000000 00000001670 15037761217 0022472 0 ustar 00root root 0000000 0000000 1
//- - - - - - - - -//
- [ ] foo
- [x] bar
//- - - - - - - - -//
//= = = = = = = = = = = = = = = = = = = = = = = =//
2
//- - - - - - - - -//
- [x] foo
- [ ] bar
- [x] baz
- [ ] bim
//- - - - - - - - -//
//= = = = = = = = = = = = = = = = = = = = = = = =//
3
//- - - - - - - - -//
- test[x]=[x]
//- - - - - - - - -//
//= = = = = = = = = = = = = = = = = = = = = = = =//
4
//- - - - - - - - -//
+ [x] [x]
//- - - - - - - - -//
//= = = = = = = = = = = = = = = = = = = = = = = =//
yuin-goldmark-9a7f4c9/extension/_test/typographer.txt 0000664 0000000 0000000 00000012325 15037761217 0023177 0 ustar 00root root 0000000 0000000 1
//- - - - - - - - -//
This should 'be' replaced
//- - - - - - - - -//
This should ‘be’ replaced
//= = = = = = = = = = = = = = = = = = = = = = = =//
2
//- - - - - - - - -//
This should "be" replaced
//- - - - - - - - -//
This should “be” replaced
//= = = = = = = = = = = = = = = = = = = = = = = =//
3
//- - - - - - - - -//
**--** *---* a...<< b>>
//- - - - - - - - -//
– — a…« b»
//= = = = = = = = = = = = = = = = = = = = = = = =//
4
//- - - - - - - - -//
Some say '90s, others say 90's, but I can't say which is best.
//- - - - - - - - -//
Some say ’90s, others say 90’s, but I can’t say which is best.
//= = = = = = = = = = = = = = = = = = = = = = = =//
5: contractions
//- - - - - - - - -//
Alice's, I'm ,Don't, You'd
I've, I'll, You're
[Cat][]'s Pajamas
Yahoo!'s
[Cat]: http://example.com
//- - - - - - - - -//
Alice’s, I’m ,Don’t, You’d
I’ve, I’ll, You’re
Cat ’s Pajamas
Yahoo!’s
//= = = = = = = = = = = = = = = = = = = = = = = =//
6: "" after digits are an inch
//- - - - - - - - -//
My height is 5'6"".
//- - - - - - - - -//
My height is 5'6"".
//= = = = = = = = = = = = = = = = = = = = = = = =//
7: quote followed by ,.?! and spaces maybe a closer
//- - - - - - - - -//
reported "issue 1 (IE-only)", "issue 2", 'issue3 (FF-only)', 'issue4'
//- - - - - - - - -//
reported “issue 1 (IE-only)”, “issue 2”, ‘issue3 (FF-only)’, ‘issue4’
//= = = = = = = = = = = = = = = = = = = = = = = =//
8: handle inches in qoutes
//- - - - - - - - -//
"Monitor 21"" and "Monitor""
//- - - - - - - - -//
“Monitor 21"” and “Monitor”"
//= = = = = = = = = = = = = = = = = = = = = = = =//
9: Closing quotation marks within italics
//- - - - - - - - -//
*"At first, things were not clear."*
//- - - - - - - - -//
“At first, things were not clear.”
//= = = = = = = = = = = = = = = = = = = = = = = =//
10: Closing quotation marks within boldfacing
//- - - - - - - - -//
**"At first, things were not clear."**
//- - - - - - - - -//
“At first, things were not clear.”
//= = = = = = = = = = = = = = = = = = = = = = = =//
11: Closing quotation marks within boldfacing and italics
//- - - - - - - - -//
***"At first, things were not clear."***
//- - - - - - - - -//
“At first, things were not clear.”
//= = = = = = = = = = = = = = = = = = = = = = = =//
12: Closing quotation marks within boldfacing and italics
//- - - - - - - - -//
***"At first, things were not clear."***
//- - - - - - - - -//
“At first, things were not clear.”
//= = = = = = = = = = = = = = = = = = = = = = = =//
13: Plural possessives
//- - - - - - - - -//
John's dog is named Sam. The Smiths' dog is named Rover.
//- - - - - - - - -//
John’s dog is named Sam. The Smiths’ dog is named Rover.
//= = = = = = = = = = = = = = = = = = = = = = = =//
14: Links within quotation marks and parenthetical phrases
//- - - - - - - - -//
This is not difficult (see "[Introduction to Hugo Templating](https://gohugo.io/templates/introduction/)").
//- - - - - - - - -//
This is not difficult (see “Introduction to Hugo Templating ”).
//= = = = = = = = = = = = = = = = = = = = = = = =//
15: Quotation marks within links
//- - - - - - - - -//
Apple's early Cairo font gave us ["moof" and the "dogcow."](https://www.macworld.com/article/2926184/we-miss-you-clarus-the-dogcow.html)
//- - - - - - - - -//
Apple’s early Cairo font gave us “moof” and the “dogcow.”
//= = = = = = = = = = = = = = = = = = = = = = = =//
16: Single closing quotation marks with slang/informalities
//- - - - - - - - -//
"I'm not doin' that," Bill said with emphasis.
//- - - - - - - - -//
“I’m not doin’ that,” Bill said with emphasis.
//= = = = = = = = = = = = = = = = = = = = = = = =//
17: Closing single quotation marks in quotations-within-quotations
//- - - - - - - - -//
Janet said, "When everything is 'breaking news,' nothing is 'breaking news.'"
//- - - - - - - - -//
Janet said, “When everything is ‘breaking news,’ nothing is ‘breaking news.’”
//= = = = = = = = = = = = = = = = = = = = = = = =//
18: Opening single quotation marks for abbreviations
//- - - - - - - - -//
We're talking about the internet --- 'net for short. Let's rock 'n roll!
//- - - - - - - - -//
We’re talking about the internet — ’net for short. Let’s rock ’n roll!
//= = = = = = = = = = = = = = = = = = = = = = = =//
19: Quotes in alt text
//- - - - - - - - -//

//- - - - - - - - -//
//= = = = = = = = = = = = = = = = = = = = = = = =//
yuin-goldmark-9a7f4c9/extension/ast/ 0000775 0000000 0000000 00000000000 15037761217 0017540 5 ustar 00root root 0000000 0000000 yuin-goldmark-9a7f4c9/extension/ast/definition_list.go 0000664 0000000 0000000 00000004331 15037761217 0023253 0 ustar 00root root 0000000 0000000 package ast
import (
gast "github.com/yuin/goldmark/ast"
)
// A DefinitionList struct represents a definition list of Markdown
// (PHPMarkdownExtra) text.
type DefinitionList struct {
gast.BaseBlock
Offset int
TemporaryParagraph *gast.Paragraph
}
// Dump implements Node.Dump.
func (n *DefinitionList) Dump(source []byte, level int) {
gast.DumpHelper(n, source, level, nil, nil)
}
// KindDefinitionList is a NodeKind of the DefinitionList node.
var KindDefinitionList = gast.NewNodeKind("DefinitionList")
// Kind implements Node.Kind.
func (n *DefinitionList) Kind() gast.NodeKind {
return KindDefinitionList
}
// NewDefinitionList returns a new DefinitionList node.
func NewDefinitionList(offset int, para *gast.Paragraph) *DefinitionList {
return &DefinitionList{
Offset: offset,
TemporaryParagraph: para,
}
}
// A DefinitionTerm struct represents a definition list term of Markdown
// (PHPMarkdownExtra) text.
type DefinitionTerm struct {
gast.BaseBlock
}
// Dump implements Node.Dump.
func (n *DefinitionTerm) Dump(source []byte, level int) {
gast.DumpHelper(n, source, level, nil, nil)
}
// KindDefinitionTerm is a NodeKind of the DefinitionTerm node.
var KindDefinitionTerm = gast.NewNodeKind("DefinitionTerm")
// Kind implements Node.Kind.
func (n *DefinitionTerm) Kind() gast.NodeKind {
return KindDefinitionTerm
}
// NewDefinitionTerm returns a new DefinitionTerm node.
func NewDefinitionTerm() *DefinitionTerm {
return &DefinitionTerm{}
}
// A DefinitionDescription struct represents a definition list description of Markdown
// (PHPMarkdownExtra) text.
type DefinitionDescription struct {
gast.BaseBlock
IsTight bool
}
// Dump implements Node.Dump.
func (n *DefinitionDescription) Dump(source []byte, level int) {
gast.DumpHelper(n, source, level, nil, nil)
}
// KindDefinitionDescription is a NodeKind of the DefinitionDescription node.
var KindDefinitionDescription = gast.NewNodeKind("DefinitionDescription")
// Kind implements Node.Kind.
func (n *DefinitionDescription) Kind() gast.NodeKind {
return KindDefinitionDescription
}
// NewDefinitionDescription returns a new DefinitionDescription node.
func NewDefinitionDescription() *DefinitionDescription {
return &DefinitionDescription{}
}
yuin-goldmark-9a7f4c9/extension/ast/footnote.go 0000664 0000000 0000000 00000006331 15037761217 0021727 0 ustar 00root root 0000000 0000000 package ast
import (
"fmt"
gast "github.com/yuin/goldmark/ast"
)
// A FootnoteLink struct represents a link to a footnote of Markdown
// (PHP Markdown Extra) text.
type FootnoteLink struct {
gast.BaseInline
Index int
RefCount int
RefIndex int
}
// Dump implements Node.Dump.
func (n *FootnoteLink) Dump(source []byte, level int) {
m := map[string]string{}
m["Index"] = fmt.Sprintf("%v", n.Index)
m["RefCount"] = fmt.Sprintf("%v", n.RefCount)
m["RefIndex"] = fmt.Sprintf("%v", n.RefIndex)
gast.DumpHelper(n, source, level, m, nil)
}
// KindFootnoteLink is a NodeKind of the FootnoteLink node.
var KindFootnoteLink = gast.NewNodeKind("FootnoteLink")
// Kind implements Node.Kind.
func (n *FootnoteLink) Kind() gast.NodeKind {
return KindFootnoteLink
}
// NewFootnoteLink returns a new FootnoteLink node.
func NewFootnoteLink(index int) *FootnoteLink {
return &FootnoteLink{
Index: index,
RefCount: 0,
RefIndex: 0,
}
}
// A FootnoteBacklink struct represents a link to a footnote of Markdown
// (PHP Markdown Extra) text.
type FootnoteBacklink struct {
gast.BaseInline
Index int
RefCount int
RefIndex int
}
// Dump implements Node.Dump.
func (n *FootnoteBacklink) Dump(source []byte, level int) {
m := map[string]string{}
m["Index"] = fmt.Sprintf("%v", n.Index)
m["RefCount"] = fmt.Sprintf("%v", n.RefCount)
m["RefIndex"] = fmt.Sprintf("%v", n.RefIndex)
gast.DumpHelper(n, source, level, m, nil)
}
// KindFootnoteBacklink is a NodeKind of the FootnoteBacklink node.
var KindFootnoteBacklink = gast.NewNodeKind("FootnoteBacklink")
// Kind implements Node.Kind.
func (n *FootnoteBacklink) Kind() gast.NodeKind {
return KindFootnoteBacklink
}
// NewFootnoteBacklink returns a new FootnoteBacklink node.
func NewFootnoteBacklink(index int) *FootnoteBacklink {
return &FootnoteBacklink{
Index: index,
RefCount: 0,
RefIndex: 0,
}
}
// A Footnote struct represents a footnote of Markdown
// (PHP Markdown Extra) text.
type Footnote struct {
gast.BaseBlock
Ref []byte
Index int
}
// Dump implements Node.Dump.
func (n *Footnote) Dump(source []byte, level int) {
m := map[string]string{}
m["Index"] = fmt.Sprintf("%v", n.Index)
m["Ref"] = string(n.Ref)
gast.DumpHelper(n, source, level, m, nil)
}
// KindFootnote is a NodeKind of the Footnote node.
var KindFootnote = gast.NewNodeKind("Footnote")
// Kind implements Node.Kind.
func (n *Footnote) Kind() gast.NodeKind {
return KindFootnote
}
// NewFootnote returns a new Footnote node.
func NewFootnote(ref []byte) *Footnote {
return &Footnote{
Ref: ref,
Index: -1,
}
}
// A FootnoteList struct represents footnotes of Markdown
// (PHP Markdown Extra) text.
type FootnoteList struct {
gast.BaseBlock
Count int
}
// Dump implements Node.Dump.
func (n *FootnoteList) Dump(source []byte, level int) {
m := map[string]string{}
m["Count"] = fmt.Sprintf("%v", n.Count)
gast.DumpHelper(n, source, level, m, nil)
}
// KindFootnoteList is a NodeKind of the FootnoteList node.
var KindFootnoteList = gast.NewNodeKind("FootnoteList")
// Kind implements Node.Kind.
func (n *FootnoteList) Kind() gast.NodeKind {
return KindFootnoteList
}
// NewFootnoteList returns a new FootnoteList node.
func NewFootnoteList() *FootnoteList {
return &FootnoteList{
Count: 0,
}
}
yuin-goldmark-9a7f4c9/extension/ast/strikethrough.go 0000664 0000000 0000000 00000001332 15037761217 0022770 0 ustar 00root root 0000000 0000000 // Package ast defines AST nodes that represents extension's elements
package ast
import (
gast "github.com/yuin/goldmark/ast"
)
// A Strikethrough struct represents a strikethrough of GFM text.
type Strikethrough struct {
gast.BaseInline
}
// Dump implements Node.Dump.
func (n *Strikethrough) Dump(source []byte, level int) {
gast.DumpHelper(n, source, level, nil, nil)
}
// KindStrikethrough is a NodeKind of the Strikethrough node.
var KindStrikethrough = gast.NewNodeKind("Strikethrough")
// Kind implements Node.Kind.
func (n *Strikethrough) Kind() gast.NodeKind {
return KindStrikethrough
}
// NewStrikethrough returns a new Strikethrough node.
func NewStrikethrough() *Strikethrough {
return &Strikethrough{}
}
yuin-goldmark-9a7f4c9/extension/ast/table.go 0000664 0000000 0000000 00000006676 15037761217 0021175 0 ustar 00root root 0000000 0000000 package ast
import (
"fmt"
"strings"
gast "github.com/yuin/goldmark/ast"
)
// Alignment is a text alignment of table cells.
type Alignment int
const (
// AlignLeft indicates text should be left justified.
AlignLeft Alignment = iota + 1
// AlignRight indicates text should be right justified.
AlignRight
// AlignCenter indicates text should be centered.
AlignCenter
// AlignNone indicates text should be aligned by default manner.
AlignNone
)
func (a Alignment) String() string {
switch a {
case AlignLeft:
return "left"
case AlignRight:
return "right"
case AlignCenter:
return "center"
case AlignNone:
return "none"
}
return ""
}
// A Table struct represents a table of Markdown(GFM) text.
type Table struct {
gast.BaseBlock
// Alignments returns alignments of the columns.
Alignments []Alignment
}
// Dump implements Node.Dump.
func (n *Table) Dump(source []byte, level int) {
gast.DumpHelper(n, source, level, nil, func(level int) {
indent := strings.Repeat(" ", level)
fmt.Printf("%sAlignments {\n", indent)
for i, alignment := range n.Alignments {
indent2 := strings.Repeat(" ", level+1)
fmt.Printf("%s%s", indent2, alignment.String())
if i != len(n.Alignments)-1 {
fmt.Println("")
}
}
fmt.Printf("\n%s}\n", indent)
})
}
// KindTable is a NodeKind of the Table node.
var KindTable = gast.NewNodeKind("Table")
// Kind implements Node.Kind.
func (n *Table) Kind() gast.NodeKind {
return KindTable
}
// NewTable returns a new Table node.
func NewTable() *Table {
return &Table{
Alignments: []Alignment{},
}
}
// A TableRow struct represents a table row of Markdown(GFM) text.
type TableRow struct {
gast.BaseBlock
Alignments []Alignment
}
// Dump implements Node.Dump.
func (n *TableRow) Dump(source []byte, level int) {
gast.DumpHelper(n, source, level, nil, nil)
}
// KindTableRow is a NodeKind of the TableRow node.
var KindTableRow = gast.NewNodeKind("TableRow")
// Kind implements Node.Kind.
func (n *TableRow) Kind() gast.NodeKind {
return KindTableRow
}
// NewTableRow returns a new TableRow node.
func NewTableRow(alignments []Alignment) *TableRow {
return &TableRow{Alignments: alignments}
}
// A TableHeader struct represents a table header of Markdown(GFM) text.
type TableHeader struct {
gast.BaseBlock
Alignments []Alignment
}
// KindTableHeader is a NodeKind of the TableHeader node.
var KindTableHeader = gast.NewNodeKind("TableHeader")
// Kind implements Node.Kind.
func (n *TableHeader) Kind() gast.NodeKind {
return KindTableHeader
}
// Dump implements Node.Dump.
func (n *TableHeader) Dump(source []byte, level int) {
gast.DumpHelper(n, source, level, nil, nil)
}
// NewTableHeader returns a new TableHeader node.
func NewTableHeader(row *TableRow) *TableHeader {
n := &TableHeader{}
for c := row.FirstChild(); c != nil; {
next := c.NextSibling()
n.AppendChild(n, c)
c = next
}
return n
}
// A TableCell struct represents a table cell of a Markdown(GFM) text.
type TableCell struct {
gast.BaseBlock
Alignment Alignment
}
// Dump implements Node.Dump.
func (n *TableCell) Dump(source []byte, level int) {
gast.DumpHelper(n, source, level, nil, nil)
}
// KindTableCell is a NodeKind of the TableCell node.
var KindTableCell = gast.NewNodeKind("TableCell")
// Kind implements Node.Kind.
func (n *TableCell) Kind() gast.NodeKind {
return KindTableCell
}
// NewTableCell returns a new TableCell node.
func NewTableCell() *TableCell {
return &TableCell{
Alignment: AlignNone,
}
}
yuin-goldmark-9a7f4c9/extension/ast/tasklist.go 0000664 0000000 0000000 00000001406 15037761217 0021726 0 ustar 00root root 0000000 0000000 package ast
import (
"fmt"
gast "github.com/yuin/goldmark/ast"
)
// A TaskCheckBox struct represents a checkbox of a task list.
type TaskCheckBox struct {
gast.BaseInline
IsChecked bool
}
// Dump implements Node.Dump.
func (n *TaskCheckBox) Dump(source []byte, level int) {
m := map[string]string{
"Checked": fmt.Sprintf("%v", n.IsChecked),
}
gast.DumpHelper(n, source, level, m, nil)
}
// KindTaskCheckBox is a NodeKind of the TaskCheckBox node.
var KindTaskCheckBox = gast.NewNodeKind("TaskCheckBox")
// Kind implements Node.Kind.
func (n *TaskCheckBox) Kind() gast.NodeKind {
return KindTaskCheckBox
}
// NewTaskCheckBox returns a new TaskCheckBox node.
func NewTaskCheckBox(checked bool) *TaskCheckBox {
return &TaskCheckBox{
IsChecked: checked,
}
}
yuin-goldmark-9a7f4c9/extension/ast_test.go 0000664 0000000 0000000 00000004204 15037761217 0021126 0 ustar 00root root 0000000 0000000 package extension
import (
"bytes"
"testing"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/testutil"
"github.com/yuin/goldmark/text"
)
func TestASTBlockNodeText(t *testing.T) {
var cases = []struct {
Name string
Source string
T1 string
T2 string
C bool
}{
{
Name: "DefinitionList",
Source: `c1
: c2
c3
a
c4
: c5
c6`,
T1: `c1c2
c3`,
T2: `c4c5
c6`,
},
{
Name: "Table",
Source: `| h1 | h2 |
| -- | -- |
| c1 | c2 |
a
| h3 | h4 |
| -- | -- |
| c3 | c4 |`,
T1: `h1h2c1c2`,
T2: `h3h4c3c4`,
},
}
for _, cs := range cases {
t.Run(cs.Name, func(t *testing.T) {
s := []byte(cs.Source)
md := goldmark.New(
goldmark.WithRendererOptions(
html.WithUnsafe(),
),
goldmark.WithExtensions(
DefinitionList,
Table,
),
)
n := md.Parser().Parse(text.NewReader(s))
c1 := n.FirstChild()
c2 := c1.NextSibling().NextSibling()
if cs.C {
c1 = c1.FirstChild()
c2 = c2.FirstChild()
}
if !bytes.Equal(c1.Text(s), []byte(cs.T1)) { // nolint: staticcheck
t.Errorf("%s unmatch:\n%s", cs.Name, testutil.DiffPretty(c1.Text(s), []byte(cs.T1))) // nolint: staticcheck
}
if !bytes.Equal(c2.Text(s), []byte(cs.T2)) { // nolint: staticcheck
t.Errorf("%s(EOF) unmatch: %s", cs.Name, testutil.DiffPretty(c2.Text(s), []byte(cs.T2))) // nolint: staticcheck
}
})
}
}
func TestASTInlineNodeText(t *testing.T) {
var cases = []struct {
Name string
Source string
T1 string
}{
{
Name: "Strikethrough",
Source: `~c1 *c2*~`,
T1: `c1 c2`,
},
}
for _, cs := range cases {
t.Run(cs.Name, func(t *testing.T) {
s := []byte(cs.Source)
md := goldmark.New(
goldmark.WithRendererOptions(
html.WithUnsafe(),
),
goldmark.WithExtensions(
Strikethrough,
),
)
n := md.Parser().Parse(text.NewReader(s))
c1 := n.FirstChild().FirstChild()
if !bytes.Equal(c1.Text(s), []byte(cs.T1)) { // nolint: staticcheck
t.Errorf("%s unmatch:\n%s", cs.Name, testutil.DiffPretty(c1.Text(s), []byte(cs.T1))) // nolint: staticcheck
}
})
}
}
yuin-goldmark-9a7f4c9/extension/cjk.go 0000664 0000000 0000000 00000004222 15037761217 0020047 0 ustar 00root root 0000000 0000000 package extension
import (
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer/html"
)
// A CJKOption sets options for CJK support mostly for HTML based renderers.
type CJKOption func(*cjk)
// A EastAsianLineBreaks is a style of east asian line breaks.
type EastAsianLineBreaks int
const (
//EastAsianLineBreaksNone renders line breaks as it is.
EastAsianLineBreaksNone EastAsianLineBreaks = iota
// EastAsianLineBreaksSimple is a style where soft line breaks are ignored
// if both sides of the break are east asian wide characters.
EastAsianLineBreaksSimple
// EastAsianLineBreaksCSS3Draft is a style where soft line breaks are ignored
// even if only one side of the break is an east asian wide character.
EastAsianLineBreaksCSS3Draft
)
// WithEastAsianLineBreaks is a functional option that indicates whether softline breaks
// between east asian wide characters should be ignored.
// style defauts to [EastAsianLineBreaksSimple] .
func WithEastAsianLineBreaks(style ...EastAsianLineBreaks) CJKOption {
return func(c *cjk) {
if len(style) == 0 {
c.EastAsianLineBreaks = EastAsianLineBreaksSimple
return
}
c.EastAsianLineBreaks = style[0]
}
}
// WithEscapedSpace is a functional option that indicates that a '\' escaped half-space(0x20) should not be rendered.
func WithEscapedSpace() CJKOption {
return func(c *cjk) {
c.EscapedSpace = true
}
}
type cjk struct {
EastAsianLineBreaks EastAsianLineBreaks
EscapedSpace bool
}
// CJK is a goldmark extension that provides functionalities for CJK languages.
var CJK = NewCJK(WithEastAsianLineBreaks(), WithEscapedSpace())
// NewCJK returns a new extension with given options.
func NewCJK(opts ...CJKOption) goldmark.Extender {
e := &cjk{
EastAsianLineBreaks: EastAsianLineBreaksNone,
}
for _, opt := range opts {
opt(e)
}
return e
}
func (e *cjk) Extend(m goldmark.Markdown) {
m.Renderer().AddOptions(html.WithEastAsianLineBreaks(
html.EastAsianLineBreaks(e.EastAsianLineBreaks)))
if e.EscapedSpace {
m.Renderer().AddOptions(html.WithWriter(html.NewWriter(html.WithEscapedSpace())))
m.Parser().AddOptions(parser.WithEscapedSpace())
}
}
yuin-goldmark-9a7f4c9/extension/cjk_test.go 0000664 0000000 0000000 00000020355 15037761217 0021113 0 ustar 00root root 0000000 0000000 package extension
import (
"testing"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/testutil"
)
func TestEscapedSpace(t *testing.T) {
markdown := goldmark.New(goldmark.WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
))
no := 1
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: no,
Description: "Without spaces around an emphasis started with east asian punctuations, it is not interpreted as an emphasis(as defined in CommonMark spec)",
Markdown: "太郎は**「こんにちわ」**と言った\nんです",
Expected: "太郎は**「こんにちわ」**と言った\nんです
",
},
t,
)
no = 2
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: no,
Description: "With spaces around an emphasis started with east asian punctuations, it is interpreted as an emphasis(but remains unnecessary spaces)",
Markdown: "太郎は **「こんにちわ」** と言った\nんです",
Expected: "太郎は 「こんにちわ」 と言った\nんです
",
},
t,
)
// Enables EscapedSpace
markdown = goldmark.New(goldmark.WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
),
goldmark.WithExtensions(NewCJK(WithEscapedSpace())),
)
no = 3
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: no,
Description: "With spaces around an emphasis started with east asian punctuations,it is interpreted as an emphasis",
Markdown: "太郎は\\ **「こんにちわ」**\\ と言った\nんです",
Expected: "太郎は「こんにちわ」 と言った\nんです
",
},
t,
)
// ' ' triggers Linkify extension inline parser.
// Escaped spaces should not trigger the inline parser.
markdown = goldmark.New(goldmark.WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
),
goldmark.WithExtensions(
NewCJK(WithEscapedSpace()),
Linkify,
),
)
no = 4
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: no,
Description: "Escaped space and linkfy extension",
Markdown: "太郎は\\ **「こんにちわ」**\\ と言った\nんです",
Expected: "太郎は「こんにちわ」 と言った\nんです
",
},
t,
)
}
func TestEastAsianLineBreaks(t *testing.T) {
markdown := goldmark.New(goldmark.WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
))
no := 1
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: no,
Description: "Soft line breaks are rendered as a newline, so some asian users will see it as an unnecessary space",
Markdown: "太郎は\\ **「こんにちわ」**\\ と言った\nんです",
Expected: "太郎は\\ 「こんにちわ」 \\ と言った\nんです
",
},
t,
)
// Enables EastAsianLineBreaks
markdown = goldmark.New(goldmark.WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
),
goldmark.WithExtensions(NewCJK(WithEastAsianLineBreaks())),
)
no = 2
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: no,
Description: "Soft line breaks between east asian wide characters are ignored",
Markdown: "太郎は\\ **「こんにちわ」**\\ と言った\nんです",
Expected: "太郎は\\ 「こんにちわ」 \\ と言ったんです
",
},
t,
)
no = 3
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: no,
Description: "Soft line breaks between western characters are rendered as a newline",
Markdown: "太郎は\\ **「こんにちわ」**\\ と言ったa\nbんです",
Expected: "太郎は\\ 「こんにちわ」 \\ と言ったa\nbんです
",
},
t,
)
no = 4
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: no,
Description: "Soft line breaks between a western character and an east asian wide character are rendered as a newline",
Markdown: "太郎は\\ **「こんにちわ」**\\ と言ったa\nんです",
Expected: "太郎は\\ 「こんにちわ」 \\ と言ったa\nんです
",
},
t,
)
no = 5
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: no,
Description: "Soft line breaks between an east asian wide character and a western character are rendered as a newline",
Markdown: "太郎は\\ **「こんにちわ」**\\ と言った\nbんです",
Expected: "太郎は\\ 「こんにちわ」 \\ と言った\nbんです
",
},
t,
)
// WithHardWraps take precedence over WithEastAsianLineBreaks
markdown = goldmark.New(goldmark.WithRendererOptions(
html.WithHardWraps(),
html.WithXHTML(),
html.WithUnsafe(),
),
goldmark.WithExtensions(NewCJK(WithEastAsianLineBreaks())),
)
no = 6
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: no,
Description: "WithHardWraps take precedence over WithEastAsianLineBreaks",
Markdown: "太郎は\\ **「こんにちわ」**\\ と言った\nんです",
Expected: "太郎は\\ 「こんにちわ」 \\ と言った \nんです
",
},
t,
)
// Tests with EastAsianLineBreaksStyleSimple
markdown = goldmark.New(goldmark.WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
),
goldmark.WithExtensions(
NewCJK(WithEastAsianLineBreaks()),
Linkify,
),
)
no = 7
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: no,
Description: "WithEastAsianLineBreaks and linkfy extension",
Markdown: "太郎は\\ **「こんにちわ」**\\ と言った\r\nんです",
Expected: "太郎は\\ 「こんにちわ」 \\ と言ったんです
",
},
t,
)
no = 8
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: no,
Description: "Soft line breaks between east asian wide characters or punctuations are ignored",
Markdown: "太郎は\\ **「こんにちわ」**\\ と、\r\n言った\r\nんです",
Expected: "太郎は\\ 「こんにちわ」 \\ と、言ったんです
",
},
t,
)
no = 9
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: no,
Description: "Soft line breaks between an east asian wide character and a western character are ignored",
Markdown: "私はプログラマーです。\n東京の会社に勤めています。\nGoでWebアプリケーションを開発しています。",
Expected: "私はプログラマーです。東京の会社に勤めています。\nGoでWebアプリケーションを開発しています。
",
},
t,
)
// Tests with EastAsianLineBreaksCSS3Draft
markdown = goldmark.New(goldmark.WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
),
goldmark.WithExtensions(
NewCJK(WithEastAsianLineBreaks(EastAsianLineBreaksCSS3Draft)),
),
)
no = 10
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: no,
Description: "Soft line breaks between a western character and an east asian wide character are ignored",
Markdown: "太郎は\\ **「こんにちわ」**\\ と言ったa\nんです",
Expected: "太郎は\\ 「こんにちわ」 \\ と言ったaんです
",
},
t,
)
no = 11
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: no,
Description: "Soft line breaks between an east asian wide character and a western character are ignored",
Markdown: "太郎は\\ **「こんにちわ」**\\ と言った\nbんです",
Expected: "太郎は\\ 「こんにちわ」 \\ と言ったbんです
",
},
t,
)
no = 12
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: no,
Description: "Soft line breaks between an east asian wide character and a western character are ignored",
Markdown: "私はプログラマーです。\n東京の会社に勤めています。\nGoでWebアプリケーションを開発しています。",
Expected: "私はプログラマーです。東京の会社に勤めています。GoでWebアプリケーションを開発しています。
",
},
t,
)
}
yuin-goldmark-9a7f4c9/extension/definition_list.go 0000664 0000000 0000000 00000017734 15037761217 0022477 0 ustar 00root root 0000000 0000000 package extension
import (
"github.com/yuin/goldmark"
gast "github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/extension/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
)
type definitionListParser struct {
}
var defaultDefinitionListParser = &definitionListParser{}
// NewDefinitionListParser return a new parser.BlockParser that
// can parse PHP Markdown Extra Definition lists.
func NewDefinitionListParser() parser.BlockParser {
return defaultDefinitionListParser
}
func (b *definitionListParser) Trigger() []byte {
return []byte{':'}
}
func (b *definitionListParser) Open(parent gast.Node, reader text.Reader, pc parser.Context) (gast.Node, parser.State) {
if _, ok := parent.(*ast.DefinitionList); ok {
return nil, parser.NoChildren
}
line, _ := reader.PeekLine()
pos := pc.BlockOffset()
indent := pc.BlockIndent()
if pos < 0 || line[pos] != ':' || indent != 0 {
return nil, parser.NoChildren
}
last := parent.LastChild()
// need 1 or more spaces after ':'
w, _ := util.IndentWidth(line[pos+1:], pos+1)
if w < 1 {
return nil, parser.NoChildren
}
if w >= 8 { // starts with indented code
w = 5
}
w += pos + 1 /* 1 = ':' */
para, lastIsParagraph := last.(*gast.Paragraph)
var list *ast.DefinitionList
status := parser.HasChildren
var ok bool
if lastIsParagraph {
list, ok = last.PreviousSibling().(*ast.DefinitionList)
if ok { // is not first item
list.Offset = w
list.TemporaryParagraph = para
} else { // is first item
list = ast.NewDefinitionList(w, para)
status |= parser.RequireParagraph
}
} else if list, ok = last.(*ast.DefinitionList); ok { // multiple description
list.Offset = w
list.TemporaryParagraph = nil
} else {
return nil, parser.NoChildren
}
return list, status
}
func (b *definitionListParser) Continue(node gast.Node, reader text.Reader, pc parser.Context) parser.State {
line, _ := reader.PeekLine()
if util.IsBlank(line) {
return parser.Continue | parser.HasChildren
}
list, _ := node.(*ast.DefinitionList)
w, _ := util.IndentWidth(line, reader.LineOffset())
if w < list.Offset {
return parser.Close
}
pos, padding := util.IndentPosition(line, reader.LineOffset(), list.Offset)
reader.AdvanceAndSetPadding(pos, padding)
return parser.Continue | parser.HasChildren
}
func (b *definitionListParser) Close(node gast.Node, reader text.Reader, pc parser.Context) {
// nothing to do
}
func (b *definitionListParser) CanInterruptParagraph() bool {
return true
}
func (b *definitionListParser) CanAcceptIndentedLine() bool {
return false
}
type definitionDescriptionParser struct {
}
var defaultDefinitionDescriptionParser = &definitionDescriptionParser{}
// NewDefinitionDescriptionParser return a new parser.BlockParser that
// can parse definition description starts with ':'.
func NewDefinitionDescriptionParser() parser.BlockParser {
return defaultDefinitionDescriptionParser
}
func (b *definitionDescriptionParser) Trigger() []byte {
return []byte{':'}
}
func (b *definitionDescriptionParser) Open(
parent gast.Node, reader text.Reader, pc parser.Context) (gast.Node, parser.State) {
line, _ := reader.PeekLine()
pos := pc.BlockOffset()
indent := pc.BlockIndent()
if pos < 0 || line[pos] != ':' || indent != 0 {
return nil, parser.NoChildren
}
list, _ := parent.(*ast.DefinitionList)
if list == nil {
return nil, parser.NoChildren
}
para := list.TemporaryParagraph
list.TemporaryParagraph = nil
if para != nil {
lines := para.Lines()
l := lines.Len()
for i := 0; i < l; i++ {
term := ast.NewDefinitionTerm()
segment := lines.At(i)
term.Lines().Append(segment.TrimRightSpace(reader.Source()))
list.AppendChild(list, term)
}
para.Parent().RemoveChild(para.Parent(), para)
}
cpos, padding := util.IndentPosition(line[pos+1:], pos+1, list.Offset-pos-1)
reader.AdvanceAndSetPadding(cpos+1, padding)
return ast.NewDefinitionDescription(), parser.HasChildren
}
func (b *definitionDescriptionParser) Continue(node gast.Node, reader text.Reader, pc parser.Context) parser.State {
// definitionListParser detects end of the description.
// so this method will never be called.
return parser.Continue | parser.HasChildren
}
func (b *definitionDescriptionParser) Close(node gast.Node, reader text.Reader, pc parser.Context) {
desc := node.(*ast.DefinitionDescription)
desc.IsTight = !desc.HasBlankPreviousLines()
if desc.IsTight {
for gc := desc.FirstChild(); gc != nil; gc = gc.NextSibling() {
paragraph, ok := gc.(*gast.Paragraph)
if ok {
textBlock := gast.NewTextBlock()
textBlock.SetLines(paragraph.Lines())
desc.ReplaceChild(desc, paragraph, textBlock)
}
}
}
}
func (b *definitionDescriptionParser) CanInterruptParagraph() bool {
return true
}
func (b *definitionDescriptionParser) CanAcceptIndentedLine() bool {
return false
}
// DefinitionListHTMLRenderer is a renderer.NodeRenderer implementation that
// renders DefinitionList nodes.
type DefinitionListHTMLRenderer struct {
html.Config
}
// NewDefinitionListHTMLRenderer returns a new DefinitionListHTMLRenderer.
func NewDefinitionListHTMLRenderer(opts ...html.Option) renderer.NodeRenderer {
r := &DefinitionListHTMLRenderer{
Config: html.NewConfig(),
}
for _, opt := range opts {
opt.SetHTMLOption(&r.Config)
}
return r
}
// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs.
func (r *DefinitionListHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(ast.KindDefinitionList, r.renderDefinitionList)
reg.Register(ast.KindDefinitionTerm, r.renderDefinitionTerm)
reg.Register(ast.KindDefinitionDescription, r.renderDefinitionDescription)
}
// DefinitionListAttributeFilter defines attribute names which dl elements can have.
var DefinitionListAttributeFilter = html.GlobalAttributeFilter
func (r *DefinitionListHTMLRenderer) renderDefinitionList(
w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) {
if entering {
if n.Attributes() != nil {
_, _ = w.WriteString("\n")
} else {
_, _ = w.WriteString("\n")
}
} else {
_, _ = w.WriteString(" \n")
}
return gast.WalkContinue, nil
}
// DefinitionTermAttributeFilter defines attribute names which dd elements can have.
var DefinitionTermAttributeFilter = html.GlobalAttributeFilter
func (r *DefinitionListHTMLRenderer) renderDefinitionTerm(
w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) {
if entering {
if n.Attributes() != nil {
_, _ = w.WriteString("')
} else {
_, _ = w.WriteString("")
}
} else {
_, _ = w.WriteString(" \n")
}
return gast.WalkContinue, nil
}
// DefinitionDescriptionAttributeFilter defines attribute names which dd elements can have.
var DefinitionDescriptionAttributeFilter = html.GlobalAttributeFilter
func (r *DefinitionListHTMLRenderer) renderDefinitionDescription(
w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
if entering {
n := node.(*ast.DefinitionDescription)
_, _ = w.WriteString(" ")
} else {
_, _ = w.WriteString(">\n")
}
} else {
_, _ = w.WriteString(" \n")
}
return gast.WalkContinue, nil
}
type definitionList struct {
}
// DefinitionList is an extension that allow you to use PHP Markdown Extra Definition lists.
var DefinitionList = &definitionList{}
func (e *definitionList) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(parser.WithBlockParsers(
util.Prioritized(NewDefinitionListParser(), 101),
util.Prioritized(NewDefinitionDescriptionParser(), 102),
))
m.Renderer().AddOptions(renderer.WithNodeRenderers(
util.Prioritized(NewDefinitionListHTMLRenderer(), 500),
))
}
yuin-goldmark-9a7f4c9/extension/definition_list_test.go 0000664 0000000 0000000 00000000656 15037761217 0023531 0 ustar 00root root 0000000 0000000 package extension
import (
"testing"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/testutil"
)
func TestDefinitionList(t *testing.T) {
markdown := goldmark.New(
goldmark.WithRendererOptions(
html.WithUnsafe(),
),
goldmark.WithExtensions(
DefinitionList,
),
)
testutil.DoTestCaseFile(markdown, "_test/definition_list.txt", t, testutil.ParseCliCaseArg()...)
}
yuin-goldmark-9a7f4c9/extension/footnote.go 0000664 0000000 0000000 00000045712 15037761217 0021146 0 ustar 00root root 0000000 0000000 package extension
import (
"bytes"
"fmt"
"strconv"
"github.com/yuin/goldmark"
gast "github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/extension/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
)
var footnoteListKey = parser.NewContextKey()
var footnoteLinkListKey = parser.NewContextKey()
type footnoteBlockParser struct {
}
var defaultFootnoteBlockParser = &footnoteBlockParser{}
// NewFootnoteBlockParser returns a new parser.BlockParser that can parse
// footnotes of the Markdown(PHP Markdown Extra) text.
func NewFootnoteBlockParser() parser.BlockParser {
return defaultFootnoteBlockParser
}
func (b *footnoteBlockParser) Trigger() []byte {
return []byte{'['}
}
func (b *footnoteBlockParser) Open(parent gast.Node, reader text.Reader, pc parser.Context) (gast.Node, parser.State) {
line, segment := reader.PeekLine()
pos := pc.BlockOffset()
if pos < 0 || line[pos] != '[' {
return nil, parser.NoChildren
}
pos++
if pos > len(line)-1 || line[pos] != '^' {
return nil, parser.NoChildren
}
open := pos + 1
var closes int
closure := util.FindClosure(line[pos+1:], '[', ']', false, false) //nolint:staticcheck
closes = pos + 1 + closure
next := closes + 1
if closure > -1 {
if next >= len(line) || line[next] != ':' {
return nil, parser.NoChildren
}
} else {
return nil, parser.NoChildren
}
padding := segment.Padding
label := reader.Value(text.NewSegment(segment.Start+open-padding, segment.Start+closes-padding))
if util.IsBlank(label) {
return nil, parser.NoChildren
}
item := ast.NewFootnote(label)
pos = next + 1 - padding
if pos >= len(line) {
reader.Advance(pos)
return item, parser.NoChildren
}
reader.AdvanceAndSetPadding(pos, padding)
return item, parser.HasChildren
}
func (b *footnoteBlockParser) Continue(node gast.Node, reader text.Reader, pc parser.Context) parser.State {
line, _ := reader.PeekLine()
if util.IsBlank(line) {
return parser.Continue | parser.HasChildren
}
childpos, padding := util.IndentPosition(line, reader.LineOffset(), 4)
if childpos < 0 {
return parser.Close
}
reader.AdvanceAndSetPadding(childpos, padding)
return parser.Continue | parser.HasChildren
}
func (b *footnoteBlockParser) Close(node gast.Node, reader text.Reader, pc parser.Context) {
var list *ast.FootnoteList
if tlist := pc.Get(footnoteListKey); tlist != nil {
list = tlist.(*ast.FootnoteList)
} else {
list = ast.NewFootnoteList()
pc.Set(footnoteListKey, list)
node.Parent().InsertBefore(node.Parent(), node, list)
}
node.Parent().RemoveChild(node.Parent(), node)
list.AppendChild(list, node)
}
func (b *footnoteBlockParser) CanInterruptParagraph() bool {
return true
}
func (b *footnoteBlockParser) CanAcceptIndentedLine() bool {
return false
}
type footnoteParser struct {
}
var defaultFootnoteParser = &footnoteParser{}
// NewFootnoteParser returns a new parser.InlineParser that can parse
// footnote links of the Markdown(PHP Markdown Extra) text.
func NewFootnoteParser() parser.InlineParser {
return defaultFootnoteParser
}
func (s *footnoteParser) Trigger() []byte {
// footnote syntax probably conflict with the image syntax.
// So we need trigger this parser with '!'.
return []byte{'!', '['}
}
func (s *footnoteParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) gast.Node {
line, segment := block.PeekLine()
pos := 1
if len(line) > 0 && line[0] == '!' {
pos++
}
if pos >= len(line) || line[pos] != '^' {
return nil
}
pos++
if pos >= len(line) {
return nil
}
open := pos
closure := util.FindClosure(line[pos:], '[', ']', false, false) //nolint:staticcheck
if closure < 0 {
return nil
}
closes := pos + closure
value := block.Value(text.NewSegment(segment.Start+open, segment.Start+closes))
block.Advance(closes + 1)
var list *ast.FootnoteList
if tlist := pc.Get(footnoteListKey); tlist != nil {
list = tlist.(*ast.FootnoteList)
}
if list == nil {
return nil
}
index := 0
for def := list.FirstChild(); def != nil; def = def.NextSibling() {
d := def.(*ast.Footnote)
if bytes.Equal(d.Ref, value) {
if d.Index < 0 {
list.Count++
d.Index = list.Count
}
index = d.Index
break
}
}
if index == 0 {
return nil
}
fnlink := ast.NewFootnoteLink(index)
var fnlist []*ast.FootnoteLink
if tmp := pc.Get(footnoteLinkListKey); tmp != nil {
fnlist = tmp.([]*ast.FootnoteLink)
} else {
fnlist = []*ast.FootnoteLink{}
pc.Set(footnoteLinkListKey, fnlist)
}
pc.Set(footnoteLinkListKey, append(fnlist, fnlink))
if line[0] == '!' {
parent.AppendChild(parent, gast.NewTextSegment(text.NewSegment(segment.Start, segment.Start+1)))
}
return fnlink
}
type footnoteASTTransformer struct {
}
var defaultFootnoteASTTransformer = &footnoteASTTransformer{}
// NewFootnoteASTTransformer returns a new parser.ASTTransformer that
// insert a footnote list to the last of the document.
func NewFootnoteASTTransformer() parser.ASTTransformer {
return defaultFootnoteASTTransformer
}
func (a *footnoteASTTransformer) Transform(node *gast.Document, reader text.Reader, pc parser.Context) {
var list *ast.FootnoteList
var fnlist []*ast.FootnoteLink
if tmp := pc.Get(footnoteListKey); tmp != nil {
list = tmp.(*ast.FootnoteList)
}
if tmp := pc.Get(footnoteLinkListKey); tmp != nil {
fnlist = tmp.([]*ast.FootnoteLink)
}
pc.Set(footnoteListKey, nil)
pc.Set(footnoteLinkListKey, nil)
if list == nil {
return
}
counter := map[int]int{}
if fnlist != nil {
for _, fnlink := range fnlist {
if fnlink.Index >= 0 {
counter[fnlink.Index]++
}
}
refCounter := map[int]int{}
for _, fnlink := range fnlist {
fnlink.RefCount = counter[fnlink.Index]
if _, ok := refCounter[fnlink.Index]; !ok {
refCounter[fnlink.Index] = 0
}
fnlink.RefIndex = refCounter[fnlink.Index]
refCounter[fnlink.Index]++
}
}
for footnote := list.FirstChild(); footnote != nil; {
var container gast.Node = footnote
next := footnote.NextSibling()
if fc := container.LastChild(); fc != nil && gast.IsParagraph(fc) {
container = fc
}
fn := footnote.(*ast.Footnote)
index := fn.Index
if index < 0 {
list.RemoveChild(list, footnote)
} else {
refCount := counter[index]
backLink := ast.NewFootnoteBacklink(index)
backLink.RefCount = refCount
backLink.RefIndex = 0
container.AppendChild(container, backLink)
if refCount > 1 {
for i := 1; i < refCount; i++ {
backLink := ast.NewFootnoteBacklink(index)
backLink.RefCount = refCount
backLink.RefIndex = i
container.AppendChild(container, backLink)
}
}
}
footnote = next
}
list.SortChildren(func(n1, n2 gast.Node) int {
if n1.(*ast.Footnote).Index < n2.(*ast.Footnote).Index {
return -1
}
return 1
})
if list.Count <= 0 {
list.Parent().RemoveChild(list.Parent(), list)
return
}
node.AppendChild(node, list)
}
// FootnoteConfig holds configuration values for the footnote extension.
//
// Link* and Backlink* configurations have some variables:
// Occurrences of “^^” in the string will be replaced by the
// corresponding footnote number in the HTML output.
// Occurrences of “%%” will be replaced by a number for the
// reference (footnotes can have multiple references).
type FootnoteConfig struct {
html.Config
// IDPrefix is a prefix for the id attributes generated by footnotes.
IDPrefix []byte
// IDPrefix is a function that determines the id attribute for given Node.
IDPrefixFunction func(gast.Node) []byte
// LinkTitle is an optional title attribute for footnote links.
LinkTitle []byte
// BacklinkTitle is an optional title attribute for footnote backlinks.
BacklinkTitle []byte
// LinkClass is a class for footnote links.
LinkClass []byte
// BacklinkClass is a class for footnote backlinks.
BacklinkClass []byte
// BacklinkHTML is an HTML content for footnote backlinks.
BacklinkHTML []byte
}
// FootnoteOption interface is a functional option interface for the extension.
type FootnoteOption interface {
renderer.Option
// SetFootnoteOption sets given option to the extension.
SetFootnoteOption(*FootnoteConfig)
}
// NewFootnoteConfig returns a new Config with defaults.
func NewFootnoteConfig() FootnoteConfig {
return FootnoteConfig{
Config: html.NewConfig(),
LinkTitle: []byte(""),
BacklinkTitle: []byte(""),
LinkClass: []byte("footnote-ref"),
BacklinkClass: []byte("footnote-backref"),
BacklinkHTML: []byte("↩︎"),
}
}
// SetOption implements renderer.SetOptioner.
func (c *FootnoteConfig) SetOption(name renderer.OptionName, value interface{}) {
switch name {
case optFootnoteIDPrefixFunction:
c.IDPrefixFunction = value.(func(gast.Node) []byte)
case optFootnoteIDPrefix:
c.IDPrefix = value.([]byte)
case optFootnoteLinkTitle:
c.LinkTitle = value.([]byte)
case optFootnoteBacklinkTitle:
c.BacklinkTitle = value.([]byte)
case optFootnoteLinkClass:
c.LinkClass = value.([]byte)
case optFootnoteBacklinkClass:
c.BacklinkClass = value.([]byte)
case optFootnoteBacklinkHTML:
c.BacklinkHTML = value.([]byte)
default:
c.Config.SetOption(name, value)
}
}
type withFootnoteHTMLOptions struct {
value []html.Option
}
func (o *withFootnoteHTMLOptions) SetConfig(c *renderer.Config) {
if o.value != nil {
for _, v := range o.value {
v.(renderer.Option).SetConfig(c)
}
}
}
func (o *withFootnoteHTMLOptions) SetFootnoteOption(c *FootnoteConfig) {
if o.value != nil {
for _, v := range o.value {
v.SetHTMLOption(&c.Config)
}
}
}
// WithFootnoteHTMLOptions is functional option that wraps goldmark HTMLRenderer options.
func WithFootnoteHTMLOptions(opts ...html.Option) FootnoteOption {
return &withFootnoteHTMLOptions{opts}
}
const optFootnoteIDPrefix renderer.OptionName = "FootnoteIDPrefix"
type withFootnoteIDPrefix struct {
value []byte
}
func (o *withFootnoteIDPrefix) SetConfig(c *renderer.Config) {
c.Options[optFootnoteIDPrefix] = o.value
}
func (o *withFootnoteIDPrefix) SetFootnoteOption(c *FootnoteConfig) {
c.IDPrefix = o.value
}
// WithFootnoteIDPrefix is a functional option that is a prefix for the id attributes generated by footnotes.
func WithFootnoteIDPrefix[T []byte | string](a T) FootnoteOption {
return &withFootnoteIDPrefix{[]byte(a)}
}
const optFootnoteIDPrefixFunction renderer.OptionName = "FootnoteIDPrefixFunction"
type withFootnoteIDPrefixFunction struct {
value func(gast.Node) []byte
}
func (o *withFootnoteIDPrefixFunction) SetConfig(c *renderer.Config) {
c.Options[optFootnoteIDPrefixFunction] = o.value
}
func (o *withFootnoteIDPrefixFunction) SetFootnoteOption(c *FootnoteConfig) {
c.IDPrefixFunction = o.value
}
// WithFootnoteIDPrefixFunction is a functional option that is a prefix for the id attributes generated by footnotes.
func WithFootnoteIDPrefixFunction(a func(gast.Node) []byte) FootnoteOption {
return &withFootnoteIDPrefixFunction{a}
}
const optFootnoteLinkTitle renderer.OptionName = "FootnoteLinkTitle"
type withFootnoteLinkTitle struct {
value []byte
}
func (o *withFootnoteLinkTitle) SetConfig(c *renderer.Config) {
c.Options[optFootnoteLinkTitle] = o.value
}
func (o *withFootnoteLinkTitle) SetFootnoteOption(c *FootnoteConfig) {
c.LinkTitle = o.value
}
// WithFootnoteLinkTitle is a functional option that is an optional title attribute for footnote links.
func WithFootnoteLinkTitle[T []byte | string](a T) FootnoteOption {
return &withFootnoteLinkTitle{[]byte(a)}
}
const optFootnoteBacklinkTitle renderer.OptionName = "FootnoteBacklinkTitle"
type withFootnoteBacklinkTitle struct {
value []byte
}
func (o *withFootnoteBacklinkTitle) SetConfig(c *renderer.Config) {
c.Options[optFootnoteBacklinkTitle] = o.value
}
func (o *withFootnoteBacklinkTitle) SetFootnoteOption(c *FootnoteConfig) {
c.BacklinkTitle = o.value
}
// WithFootnoteBacklinkTitle is a functional option that is an optional title attribute for footnote backlinks.
func WithFootnoteBacklinkTitle[T []byte | string](a T) FootnoteOption {
return &withFootnoteBacklinkTitle{[]byte(a)}
}
const optFootnoteLinkClass renderer.OptionName = "FootnoteLinkClass"
type withFootnoteLinkClass struct {
value []byte
}
func (o *withFootnoteLinkClass) SetConfig(c *renderer.Config) {
c.Options[optFootnoteLinkClass] = o.value
}
func (o *withFootnoteLinkClass) SetFootnoteOption(c *FootnoteConfig) {
c.LinkClass = o.value
}
// WithFootnoteLinkClass is a functional option that is a class for footnote links.
func WithFootnoteLinkClass[T []byte | string](a T) FootnoteOption {
return &withFootnoteLinkClass{[]byte(a)}
}
const optFootnoteBacklinkClass renderer.OptionName = "FootnoteBacklinkClass"
type withFootnoteBacklinkClass struct {
value []byte
}
func (o *withFootnoteBacklinkClass) SetConfig(c *renderer.Config) {
c.Options[optFootnoteBacklinkClass] = o.value
}
func (o *withFootnoteBacklinkClass) SetFootnoteOption(c *FootnoteConfig) {
c.BacklinkClass = o.value
}
// WithFootnoteBacklinkClass is a functional option that is a class for footnote backlinks.
func WithFootnoteBacklinkClass[T []byte | string](a T) FootnoteOption {
return &withFootnoteBacklinkClass{[]byte(a)}
}
const optFootnoteBacklinkHTML renderer.OptionName = "FootnoteBacklinkHTML"
type withFootnoteBacklinkHTML struct {
value []byte
}
func (o *withFootnoteBacklinkHTML) SetConfig(c *renderer.Config) {
c.Options[optFootnoteBacklinkHTML] = o.value
}
func (o *withFootnoteBacklinkHTML) SetFootnoteOption(c *FootnoteConfig) {
c.BacklinkHTML = o.value
}
// WithFootnoteBacklinkHTML is an HTML content for footnote backlinks.
func WithFootnoteBacklinkHTML[T []byte | string](a T) FootnoteOption {
return &withFootnoteBacklinkHTML{[]byte(a)}
}
// FootnoteHTMLRenderer is a renderer.NodeRenderer implementation that
// renders FootnoteLink nodes.
type FootnoteHTMLRenderer struct {
FootnoteConfig
}
// NewFootnoteHTMLRenderer returns a new FootnoteHTMLRenderer.
func NewFootnoteHTMLRenderer(opts ...FootnoteOption) renderer.NodeRenderer {
r := &FootnoteHTMLRenderer{
FootnoteConfig: NewFootnoteConfig(),
}
for _, opt := range opts {
opt.SetFootnoteOption(&r.FootnoteConfig)
}
return r
}
// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs.
func (r *FootnoteHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(ast.KindFootnoteLink, r.renderFootnoteLink)
reg.Register(ast.KindFootnoteBacklink, r.renderFootnoteBacklink)
reg.Register(ast.KindFootnote, r.renderFootnote)
reg.Register(ast.KindFootnoteList, r.renderFootnoteList)
}
func (r *FootnoteHTMLRenderer) renderFootnoteLink(
w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
if entering {
n := node.(*ast.FootnoteLink)
is := strconv.Itoa(n.Index)
_, _ = w.WriteString(` `)
}
return gast.WalkContinue, nil
}
func (r *FootnoteHTMLRenderer) renderFootnoteBacklink(
w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
if entering {
n := node.(*ast.FootnoteBacklink)
is := strconv.Itoa(n.Index)
_, _ = w.WriteString(` `)
}
return gast.WalkContinue, nil
}
func (r *FootnoteHTMLRenderer) renderFootnote(
w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
n := node.(*ast.Footnote)
is := strconv.Itoa(n.Index)
if entering {
_, _ = w.WriteString(` \n")
} else {
_, _ = w.WriteString(" \n")
}
return gast.WalkContinue, nil
}
func (r *FootnoteHTMLRenderer) renderFootnoteList(
w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
if entering {
_, _ = w.WriteString(`\n")
}
return gast.WalkContinue, nil
}
func (r *FootnoteHTMLRenderer) idPrefix(node gast.Node) []byte {
if r.FootnoteConfig.IDPrefix != nil {
return r.FootnoteConfig.IDPrefix
}
if r.FootnoteConfig.IDPrefixFunction != nil {
return r.FootnoteConfig.IDPrefixFunction(node)
}
return []byte("")
}
func applyFootnoteTemplate(b []byte, index, refCount int) []byte {
fast := true
for i, c := range b {
if i != 0 {
if b[i-1] == '^' && c == '^' {
fast = false
break
}
if b[i-1] == '%' && c == '%' {
fast = false
break
}
}
}
if fast {
return b
}
is := []byte(strconv.Itoa(index))
rs := []byte(strconv.Itoa(refCount))
ret := bytes.Replace(b, []byte("^^"), is, -1)
return bytes.Replace(ret, []byte("%%"), rs, -1)
}
type footnote struct {
options []FootnoteOption
}
// Footnote is an extension that allow you to use PHP Markdown Extra Footnotes.
var Footnote = &footnote{
options: []FootnoteOption{},
}
// NewFootnote returns a new extension with given options.
func NewFootnote(opts ...FootnoteOption) goldmark.Extender {
return &footnote{
options: opts,
}
}
func (e *footnote) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(
parser.WithBlockParsers(
util.Prioritized(NewFootnoteBlockParser(), 999),
),
parser.WithInlineParsers(
util.Prioritized(NewFootnoteParser(), 101),
),
parser.WithASTTransformers(
util.Prioritized(NewFootnoteASTTransformer(), 999),
),
)
m.Renderer().AddOptions(renderer.WithNodeRenderers(
util.Prioritized(NewFootnoteHTMLRenderer(e.options...), 500),
))
}
yuin-goldmark-9a7f4c9/extension/footnote_test.go 0000664 0000000 0000000 00000010544 15037761217 0022200 0 ustar 00root root 0000000 0000000 package extension
import (
"testing"
"github.com/yuin/goldmark"
gast "github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/testutil"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
)
func TestFootnote(t *testing.T) {
markdown := goldmark.New(
goldmark.WithRendererOptions(
html.WithUnsafe(),
),
goldmark.WithExtensions(
Footnote,
),
)
testutil.DoTestCaseFile(markdown, "_test/footnote.txt", t, testutil.ParseCliCaseArg()...)
}
type footnoteID struct {
}
func (a *footnoteID) Transform(node *gast.Document, reader text.Reader, pc parser.Context) {
node.Meta()["footnote-prefix"] = "article12-"
}
func TestFootnoteOptions(t *testing.T) {
markdown := goldmark.New(
goldmark.WithRendererOptions(
html.WithUnsafe(),
),
goldmark.WithExtensions(
NewFootnote(
WithFootnoteIDPrefix("article12-"),
WithFootnoteLinkClass("link-class"),
WithFootnoteBacklinkClass("backlink-class"),
WithFootnoteLinkTitle("link-title-%%-^^"),
WithFootnoteBacklinkTitle("backlink-title"),
WithFootnoteBacklinkHTML("^"),
),
),
)
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: 1,
Description: "Footnote with options",
Markdown: `That's some text with a footnote.[^1]
Same footnote.[^1]
Another one.[^2]
[^1]: And that's the footnote.
[^2]: Another footnote.
`,
Expected: `That's some text with a footnote.1
Same footnote.1
Another one.2
`,
},
t,
)
markdown = goldmark.New(
goldmark.WithParserOptions(
parser.WithASTTransformers(
util.Prioritized(&footnoteID{}, 100),
),
),
goldmark.WithRendererOptions(
html.WithUnsafe(),
),
goldmark.WithExtensions(
NewFootnote(
WithFootnoteIDPrefixFunction(func(n gast.Node) []byte {
v, ok := n.OwnerDocument().Meta()["footnote-prefix"]
if ok {
return util.StringToReadOnlyBytes(v.(string))
}
return nil
}),
WithFootnoteLinkClass([]byte("link-class")),
WithFootnoteBacklinkClass([]byte("backlink-class")),
WithFootnoteLinkTitle([]byte("link-title-%%-^^")),
WithFootnoteBacklinkTitle([]byte("backlink-title")),
WithFootnoteBacklinkHTML([]byte("^")),
),
),
)
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: 2,
Description: "Footnote with an id prefix function",
Markdown: `That's some text with a footnote.[^1]
Same footnote.[^1]
Another one.[^2]
[^1]: And that's the footnote.
[^2]: Another footnote.
`,
Expected: `That's some text with a footnote.1
Same footnote.1
Another one.2
`,
},
t,
)
}
yuin-goldmark-9a7f4c9/extension/gfm.go 0000664 0000000 0000000 00000000460 15037761217 0020051 0 ustar 00root root 0000000 0000000 package extension
import (
"github.com/yuin/goldmark"
)
type gfm struct {
}
// GFM is an extension that provides Github Flavored markdown functionalities.
var GFM = &gfm{}
func (e *gfm) Extend(m goldmark.Markdown) {
Linkify.Extend(m)
Table.Extend(m)
Strikethrough.Extend(m)
TaskList.Extend(m)
}
yuin-goldmark-9a7f4c9/extension/linkify.go 0000664 0000000 0000000 00000017354 15037761217 0020757 0 ustar 00root root 0000000 0000000 package extension
import (
"bytes"
"regexp"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
)
var wwwURLRegxp = regexp.MustCompile(`^www\.[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-z]+(?:[/#?][-a-zA-Z0-9@:%_\+.~#!?&/=\(\);,'">\^{}\[\]` + "`" + `]*)?`) //nolint:golint,lll
var urlRegexp = regexp.MustCompile(`^(?:http|https|ftp)://[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-z]+(?::\d+)?(?:[/#?][-a-zA-Z0-9@:%_+.~#$!?&/=\(\);,'">\^{}\[\]` + "`" + `]*)?`) //nolint:golint,lll
// An LinkifyConfig struct is a data structure that holds configuration of the
// Linkify extension.
type LinkifyConfig struct {
AllowedProtocols [][]byte
URLRegexp *regexp.Regexp
WWWRegexp *regexp.Regexp
EmailRegexp *regexp.Regexp
}
const (
optLinkifyAllowedProtocols parser.OptionName = "LinkifyAllowedProtocols"
optLinkifyURLRegexp parser.OptionName = "LinkifyURLRegexp"
optLinkifyWWWRegexp parser.OptionName = "LinkifyWWWRegexp"
optLinkifyEmailRegexp parser.OptionName = "LinkifyEmailRegexp"
)
// SetOption implements SetOptioner.
func (c *LinkifyConfig) SetOption(name parser.OptionName, value interface{}) {
switch name {
case optLinkifyAllowedProtocols:
c.AllowedProtocols = value.([][]byte)
case optLinkifyURLRegexp:
c.URLRegexp = value.(*regexp.Regexp)
case optLinkifyWWWRegexp:
c.WWWRegexp = value.(*regexp.Regexp)
case optLinkifyEmailRegexp:
c.EmailRegexp = value.(*regexp.Regexp)
}
}
// A LinkifyOption interface sets options for the LinkifyOption.
type LinkifyOption interface {
parser.Option
SetLinkifyOption(*LinkifyConfig)
}
type withLinkifyAllowedProtocols struct {
value [][]byte
}
func (o *withLinkifyAllowedProtocols) SetParserOption(c *parser.Config) {
c.Options[optLinkifyAllowedProtocols] = o.value
}
func (o *withLinkifyAllowedProtocols) SetLinkifyOption(p *LinkifyConfig) {
p.AllowedProtocols = o.value
}
// WithLinkifyAllowedProtocols is a functional option that specify allowed
// protocols in autolinks. Each protocol must end with ':' like
// 'http:' .
func WithLinkifyAllowedProtocols[T []byte | string](value []T) LinkifyOption {
opt := &withLinkifyAllowedProtocols{}
for _, v := range value {
opt.value = append(opt.value, []byte(v))
}
return opt
}
type withLinkifyURLRegexp struct {
value *regexp.Regexp
}
func (o *withLinkifyURLRegexp) SetParserOption(c *parser.Config) {
c.Options[optLinkifyURLRegexp] = o.value
}
func (o *withLinkifyURLRegexp) SetLinkifyOption(p *LinkifyConfig) {
p.URLRegexp = o.value
}
// WithLinkifyURLRegexp is a functional option that specify
// a pattern of the URL including a protocol.
func WithLinkifyURLRegexp(value *regexp.Regexp) LinkifyOption {
return &withLinkifyURLRegexp{
value: value,
}
}
type withLinkifyWWWRegexp struct {
value *regexp.Regexp
}
func (o *withLinkifyWWWRegexp) SetParserOption(c *parser.Config) {
c.Options[optLinkifyWWWRegexp] = o.value
}
func (o *withLinkifyWWWRegexp) SetLinkifyOption(p *LinkifyConfig) {
p.WWWRegexp = o.value
}
// WithLinkifyWWWRegexp is a functional option that specify
// a pattern of the URL without a protocol.
// This pattern must start with 'www.' .
func WithLinkifyWWWRegexp(value *regexp.Regexp) LinkifyOption {
return &withLinkifyWWWRegexp{
value: value,
}
}
type withLinkifyEmailRegexp struct {
value *regexp.Regexp
}
func (o *withLinkifyEmailRegexp) SetParserOption(c *parser.Config) {
c.Options[optLinkifyEmailRegexp] = o.value
}
func (o *withLinkifyEmailRegexp) SetLinkifyOption(p *LinkifyConfig) {
p.EmailRegexp = o.value
}
// WithLinkifyEmailRegexp is a functional otpion that specify
// a pattern of the email address.
func WithLinkifyEmailRegexp(value *regexp.Regexp) LinkifyOption {
return &withLinkifyEmailRegexp{
value: value,
}
}
type linkifyParser struct {
LinkifyConfig
}
// NewLinkifyParser return a new InlineParser can parse
// text that seems like a URL.
func NewLinkifyParser(opts ...LinkifyOption) parser.InlineParser {
p := &linkifyParser{
LinkifyConfig: LinkifyConfig{
AllowedProtocols: nil,
URLRegexp: urlRegexp,
WWWRegexp: wwwURLRegxp,
},
}
for _, o := range opts {
o.SetLinkifyOption(&p.LinkifyConfig)
}
return p
}
func (s *linkifyParser) Trigger() []byte {
// ' ' indicates any white spaces and a line head
return []byte{' ', '*', '_', '~', '('}
}
var (
protoHTTP = []byte("http:")
protoHTTPS = []byte("https:")
protoFTP = []byte("ftp:")
domainWWW = []byte("www.")
)
func (s *linkifyParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
if pc.IsInLinkLabel() {
return nil
}
line, segment := block.PeekLine()
consumes := 0
start := segment.Start
c := line[0]
// advance if current position is not a line head.
if c == ' ' || c == '*' || c == '_' || c == '~' || c == '(' {
consumes++
start++
line = line[1:]
}
var m []int
var protocol []byte
var typ ast.AutoLinkType = ast.AutoLinkURL
if s.LinkifyConfig.AllowedProtocols == nil {
if bytes.HasPrefix(line, protoHTTP) || bytes.HasPrefix(line, protoHTTPS) || bytes.HasPrefix(line, protoFTP) {
m = s.LinkifyConfig.URLRegexp.FindSubmatchIndex(line)
}
} else {
for _, prefix := range s.LinkifyConfig.AllowedProtocols {
if bytes.HasPrefix(line, prefix) {
m = s.LinkifyConfig.URLRegexp.FindSubmatchIndex(line)
break
}
}
}
if m == nil && bytes.HasPrefix(line, domainWWW) {
m = s.LinkifyConfig.WWWRegexp.FindSubmatchIndex(line)
protocol = []byte("http")
}
if m != nil && m[0] != 0 {
m = nil
}
if m != nil && m[0] == 0 {
lastChar := line[m[1]-1]
if lastChar == '.' {
m[1]--
} else if lastChar == ')' {
closing := 0
for i := m[1] - 1; i >= m[0]; i-- {
if line[i] == ')' {
closing++
} else if line[i] == '(' {
closing--
}
}
if closing > 0 {
m[1] -= closing
}
} else if lastChar == ';' {
i := m[1] - 2
for ; i >= m[0]; i-- {
if util.IsAlphaNumeric(line[i]) {
continue
}
break
}
if i != m[1]-2 {
if line[i] == '&' {
m[1] -= m[1] - i
}
}
}
}
if m == nil {
if len(line) > 0 && util.IsPunct(line[0]) {
return nil
}
typ = ast.AutoLinkEmail
stop := -1
if s.LinkifyConfig.EmailRegexp == nil {
stop = util.FindEmailIndex(line)
} else {
m := s.LinkifyConfig.EmailRegexp.FindSubmatchIndex(line)
if m != nil && m[0] == 0 {
stop = m[1]
}
}
if stop < 0 {
return nil
}
at := bytes.IndexByte(line, '@')
m = []int{0, stop, at, stop - 1}
if m == nil || bytes.IndexByte(line[m[2]:m[3]], '.') < 0 {
return nil
}
lastChar := line[m[1]-1]
if lastChar == '.' {
m[1]--
}
if m[1] < len(line) {
nextChar := line[m[1]]
if nextChar == '-' || nextChar == '_' {
return nil
}
}
}
if m == nil {
return nil
}
if consumes != 0 {
s := segment.WithStop(segment.Start + 1)
ast.MergeOrAppendTextSegment(parent, s)
}
i := m[1] - 1
for ; i > 0; i-- {
c := line[i]
switch c {
case '?', '!', '.', ',', ':', '*', '_', '~':
default:
goto endfor
}
}
endfor:
i++
consumes += i
block.Advance(consumes)
n := ast.NewTextSegment(text.NewSegment(start, start+i))
link := ast.NewAutoLink(typ, n)
link.Protocol = protocol
return link
}
func (s *linkifyParser) CloseBlock(parent ast.Node, pc parser.Context) {
// nothing to do
}
type linkify struct {
options []LinkifyOption
}
// Linkify is an extension that allow you to parse text that seems like a URL.
var Linkify = &linkify{}
// NewLinkify creates a new [goldmark.Extender] that
// allow you to parse text that seems like a URL.
func NewLinkify(opts ...LinkifyOption) goldmark.Extender {
return &linkify{
options: opts,
}
}
func (e *linkify) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(
parser.WithInlineParsers(
util.Prioritized(NewLinkifyParser(e.options...), 999),
),
)
}
yuin-goldmark-9a7f4c9/extension/linkify_test.go 0000664 0000000 0000000 00000004044 15037761217 0022006 0 ustar 00root root 0000000 0000000 package extension
import (
"regexp"
"testing"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/testutil"
)
func TestLinkify(t *testing.T) {
markdown := goldmark.New(
goldmark.WithRendererOptions(
html.WithUnsafe(),
),
goldmark.WithExtensions(
Linkify,
),
)
testutil.DoTestCaseFile(markdown, "_test/linkify.txt", t, testutil.ParseCliCaseArg()...)
}
func TestLinkifyWithAllowedProtocols(t *testing.T) {
markdown := goldmark.New(
goldmark.WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
),
goldmark.WithExtensions(
NewLinkify(
WithLinkifyAllowedProtocols([]string{
"ssh:",
}),
WithLinkifyURLRegexp(
regexp.MustCompile(`\w+://[^\s]+`),
),
),
),
)
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: 1,
Markdown: `hoge ssh://user@hoge.com. http://example.com/`,
Expected: `hoge ssh://user@hoge.com . http://example.com/
`,
},
t,
)
}
func TestLinkifyWithWWWRegexp(t *testing.T) {
markdown := goldmark.New(
goldmark.WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
),
goldmark.WithExtensions(
NewLinkify(
WithLinkifyWWWRegexp(
regexp.MustCompile(`www\.example\.com`),
),
),
),
)
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: 1,
Markdown: `www.google.com www.example.com`,
Expected: `www.google.com www.example.com
`,
},
t,
)
}
func TestLinkifyWithEmailRegexp(t *testing.T) {
markdown := goldmark.New(
goldmark.WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
),
goldmark.WithExtensions(
NewLinkify(
WithLinkifyEmailRegexp(
regexp.MustCompile(`user@example\.com`),
),
),
),
)
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: 1,
Markdown: `hoge@example.com user@example.com`,
Expected: `hoge@example.com user@example.com
`,
},
t,
)
}
yuin-goldmark-9a7f4c9/extension/package.go 0000664 0000000 0000000 00000000116 15037761217 0020671 0 ustar 00root root 0000000 0000000 // Package extension is a collection of builtin extensions.
package extension
yuin-goldmark-9a7f4c9/extension/strikethrough.go 0000664 0000000 0000000 00000006537 15037761217 0022215 0 ustar 00root root 0000000 0000000 package extension
import (
"github.com/yuin/goldmark"
gast "github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/extension/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
)
type strikethroughDelimiterProcessor struct {
}
func (p *strikethroughDelimiterProcessor) IsDelimiter(b byte) bool {
return b == '~'
}
func (p *strikethroughDelimiterProcessor) CanOpenCloser(opener, closer *parser.Delimiter) bool {
return opener.Char == closer.Char
}
func (p *strikethroughDelimiterProcessor) OnMatch(consumes int) gast.Node {
return ast.NewStrikethrough()
}
var defaultStrikethroughDelimiterProcessor = &strikethroughDelimiterProcessor{}
type strikethroughParser struct {
}
var defaultStrikethroughParser = &strikethroughParser{}
// NewStrikethroughParser return a new InlineParser that parses
// strikethrough expressions.
func NewStrikethroughParser() parser.InlineParser {
return defaultStrikethroughParser
}
func (s *strikethroughParser) Trigger() []byte {
return []byte{'~'}
}
func (s *strikethroughParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) gast.Node {
before := block.PrecendingCharacter()
line, segment := block.PeekLine()
node := parser.ScanDelimiter(line, before, 1, defaultStrikethroughDelimiterProcessor)
if node == nil || node.OriginalLength > 2 || before == '~' {
return nil
}
node.Segment = segment.WithStop(segment.Start + node.OriginalLength)
block.Advance(node.OriginalLength)
pc.PushDelimiter(node)
return node
}
func (s *strikethroughParser) CloseBlock(parent gast.Node, pc parser.Context) {
// nothing to do
}
// StrikethroughHTMLRenderer is a renderer.NodeRenderer implementation that
// renders Strikethrough nodes.
type StrikethroughHTMLRenderer struct {
html.Config
}
// NewStrikethroughHTMLRenderer returns a new StrikethroughHTMLRenderer.
func NewStrikethroughHTMLRenderer(opts ...html.Option) renderer.NodeRenderer {
r := &StrikethroughHTMLRenderer{
Config: html.NewConfig(),
}
for _, opt := range opts {
opt.SetHTMLOption(&r.Config)
}
return r
}
// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs.
func (r *StrikethroughHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(ast.KindStrikethrough, r.renderStrikethrough)
}
// StrikethroughAttributeFilter defines attribute names which dd elements can have.
var StrikethroughAttributeFilter = html.GlobalAttributeFilter
func (r *StrikethroughHTMLRenderer) renderStrikethrough(
w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) {
if entering {
if n.Attributes() != nil {
_, _ = w.WriteString("')
} else {
_, _ = w.WriteString("")
}
} else {
_, _ = w.WriteString("")
}
return gast.WalkContinue, nil
}
type strikethrough struct {
}
// Strikethrough is an extension that allow you to use strikethrough expression like '~~text~~' .
var Strikethrough = &strikethrough{}
func (e *strikethrough) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(parser.WithInlineParsers(
util.Prioritized(NewStrikethroughParser(), 500),
))
m.Renderer().AddOptions(renderer.WithNodeRenderers(
util.Prioritized(NewStrikethroughHTMLRenderer(), 500),
))
}
yuin-goldmark-9a7f4c9/extension/strikethrough_test.go 0000664 0000000 0000000 00000000652 15037761217 0023244 0 ustar 00root root 0000000 0000000 package extension
import (
"testing"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/testutil"
)
func TestStrikethrough(t *testing.T) {
markdown := goldmark.New(
goldmark.WithRendererOptions(
html.WithUnsafe(),
),
goldmark.WithExtensions(
Strikethrough,
),
)
testutil.DoTestCaseFile(markdown, "_test/strikethrough.txt", t, testutil.ParseCliCaseArg()...)
}
yuin-goldmark-9a7f4c9/extension/table.go 0000664 0000000 0000000 00000040654 15037761217 0020400 0 ustar 00root root 0000000 0000000 package extension
import (
"bytes"
"fmt"
"regexp"
"github.com/yuin/goldmark"
gast "github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/extension/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
)
var escapedPipeCellListKey = parser.NewContextKey()
type escapedPipeCell struct {
Cell *ast.TableCell
Pos []int
Transformed bool
}
// TableCellAlignMethod indicates how are table cells aligned in HTML format.
type TableCellAlignMethod int
const (
// TableCellAlignDefault renders alignments by default method.
// With XHTML, alignments are rendered as an align attribute.
// With HTML5, alignments are rendered as a style attribute.
TableCellAlignDefault TableCellAlignMethod = iota
// TableCellAlignAttribute renders alignments as an align attribute.
TableCellAlignAttribute
// TableCellAlignStyle renders alignments as a style attribute.
TableCellAlignStyle
// TableCellAlignNone does not care about alignments.
// If you using classes or other styles, you can add these attributes
// in an ASTTransformer.
TableCellAlignNone
)
// TableConfig struct holds options for the extension.
type TableConfig struct {
html.Config
// TableCellAlignMethod indicates how are table celss aligned.
TableCellAlignMethod TableCellAlignMethod
}
// TableOption interface is a functional option interface for the extension.
type TableOption interface {
renderer.Option
// SetTableOption sets given option to the extension.
SetTableOption(*TableConfig)
}
// NewTableConfig returns a new Config with defaults.
func NewTableConfig() TableConfig {
return TableConfig{
Config: html.NewConfig(),
TableCellAlignMethod: TableCellAlignDefault,
}
}
// SetOption implements renderer.SetOptioner.
func (c *TableConfig) SetOption(name renderer.OptionName, value interface{}) {
switch name {
case optTableCellAlignMethod:
c.TableCellAlignMethod = value.(TableCellAlignMethod)
default:
c.Config.SetOption(name, value)
}
}
type withTableHTMLOptions struct {
value []html.Option
}
func (o *withTableHTMLOptions) SetConfig(c *renderer.Config) {
if o.value != nil {
for _, v := range o.value {
v.(renderer.Option).SetConfig(c)
}
}
}
func (o *withTableHTMLOptions) SetTableOption(c *TableConfig) {
if o.value != nil {
for _, v := range o.value {
v.SetHTMLOption(&c.Config)
}
}
}
// WithTableHTMLOptions is functional option that wraps goldmark HTMLRenderer options.
func WithTableHTMLOptions(opts ...html.Option) TableOption {
return &withTableHTMLOptions{opts}
}
const optTableCellAlignMethod renderer.OptionName = "TableTableCellAlignMethod"
type withTableCellAlignMethod struct {
value TableCellAlignMethod
}
func (o *withTableCellAlignMethod) SetConfig(c *renderer.Config) {
c.Options[optTableCellAlignMethod] = o.value
}
func (o *withTableCellAlignMethod) SetTableOption(c *TableConfig) {
c.TableCellAlignMethod = o.value
}
// WithTableCellAlignMethod is a functional option that indicates how are table cells aligned in HTML format.
func WithTableCellAlignMethod(a TableCellAlignMethod) TableOption {
return &withTableCellAlignMethod{a}
}
func isTableDelim(bs []byte) bool {
if w, _ := util.IndentWidth(bs, 0); w > 3 {
return false
}
for _, b := range bs {
if !(util.IsSpace(b) || b == '-' || b == '|' || b == ':') {
return false
}
}
return true
}
var tableDelimLeft = regexp.MustCompile(`^\s*\:\-+\s*$`)
var tableDelimRight = regexp.MustCompile(`^\s*\-+\:\s*$`)
var tableDelimCenter = regexp.MustCompile(`^\s*\:\-+\:\s*$`)
var tableDelimNone = regexp.MustCompile(`^\s*\-+\s*$`)
type tableParagraphTransformer struct {
}
var defaultTableParagraphTransformer = &tableParagraphTransformer{}
// NewTableParagraphTransformer returns a new ParagraphTransformer
// that can transform paragraphs into tables.
func NewTableParagraphTransformer() parser.ParagraphTransformer {
return defaultTableParagraphTransformer
}
func (b *tableParagraphTransformer) Transform(node *gast.Paragraph, reader text.Reader, pc parser.Context) {
lines := node.Lines()
if lines.Len() < 2 {
return
}
for i := 1; i < lines.Len(); i++ {
alignments := b.parseDelimiter(lines.At(i), reader)
if alignments == nil {
continue
}
header := b.parseRow(lines.At(i-1), alignments, true, reader, pc)
if header == nil || len(alignments) != header.ChildCount() {
return
}
table := ast.NewTable()
table.Alignments = alignments
table.AppendChild(table, ast.NewTableHeader(header))
for j := i + 1; j < lines.Len(); j++ {
table.AppendChild(table, b.parseRow(lines.At(j), alignments, false, reader, pc))
}
node.Lines().SetSliced(0, i-1)
node.Parent().InsertAfter(node.Parent(), node, table)
if node.Lines().Len() == 0 {
node.Parent().RemoveChild(node.Parent(), node)
} else {
last := node.Lines().At(i - 2)
last.Stop = last.Stop - 1 // trim last newline(\n)
node.Lines().Set(i-2, last)
}
}
}
func (b *tableParagraphTransformer) parseRow(segment text.Segment,
alignments []ast.Alignment, isHeader bool, reader text.Reader, pc parser.Context) *ast.TableRow {
source := reader.Source()
segment = segment.TrimLeftSpace(source)
segment = segment.TrimRightSpace(source)
line := segment.Value(source)
pos := 0
limit := len(line)
row := ast.NewTableRow(alignments)
if len(line) > 0 && line[pos] == '|' {
pos++
}
if len(line) > 0 && line[limit-1] == '|' {
limit--
}
i := 0
for ; pos < limit; i++ {
alignment := ast.AlignNone
if i >= len(alignments) {
if !isHeader {
return row
}
} else {
alignment = alignments[i]
}
var escapedCell *escapedPipeCell
node := ast.NewTableCell()
node.Alignment = alignment
hasBacktick := false
closure := pos
for ; closure < limit; closure++ {
if line[closure] == '`' {
hasBacktick = true
}
if line[closure] == '|' {
if closure == 0 || line[closure-1] != '\\' {
break
} else if hasBacktick {
if escapedCell == nil {
escapedCell = &escapedPipeCell{node, []int{}, false}
escapedList := pc.ComputeIfAbsent(escapedPipeCellListKey,
func() interface{} {
return []*escapedPipeCell{}
}).([]*escapedPipeCell)
escapedList = append(escapedList, escapedCell)
pc.Set(escapedPipeCellListKey, escapedList)
}
escapedCell.Pos = append(escapedCell.Pos, segment.Start+closure-1)
}
}
}
seg := text.NewSegment(segment.Start+pos, segment.Start+closure)
seg = seg.TrimLeftSpace(source)
seg = seg.TrimRightSpace(source)
node.Lines().Append(seg)
row.AppendChild(row, node)
pos = closure + 1
}
for ; i < len(alignments); i++ {
row.AppendChild(row, ast.NewTableCell())
}
return row
}
func (b *tableParagraphTransformer) parseDelimiter(segment text.Segment, reader text.Reader) []ast.Alignment {
line := segment.Value(reader.Source())
if !isTableDelim(line) {
return nil
}
cols := bytes.Split(line, []byte{'|'})
if util.IsBlank(cols[0]) {
cols = cols[1:]
}
if len(cols) > 0 && util.IsBlank(cols[len(cols)-1]) {
cols = cols[:len(cols)-1]
}
var alignments []ast.Alignment
for _, col := range cols {
if tableDelimLeft.Match(col) {
alignments = append(alignments, ast.AlignLeft)
} else if tableDelimRight.Match(col) {
alignments = append(alignments, ast.AlignRight)
} else if tableDelimCenter.Match(col) {
alignments = append(alignments, ast.AlignCenter)
} else if tableDelimNone.Match(col) {
alignments = append(alignments, ast.AlignNone)
} else {
return nil
}
}
return alignments
}
type tableASTTransformer struct {
}
var defaultTableASTTransformer = &tableASTTransformer{}
// NewTableASTTransformer returns a parser.ASTTransformer for tables.
func NewTableASTTransformer() parser.ASTTransformer {
return defaultTableASTTransformer
}
func (a *tableASTTransformer) Transform(node *gast.Document, reader text.Reader, pc parser.Context) {
lst := pc.Get(escapedPipeCellListKey)
if lst == nil {
return
}
pc.Set(escapedPipeCellListKey, nil)
for _, v := range lst.([]*escapedPipeCell) {
if v.Transformed {
continue
}
_ = gast.Walk(v.Cell, func(n gast.Node, entering bool) (gast.WalkStatus, error) {
if !entering || n.Kind() != gast.KindCodeSpan {
return gast.WalkContinue, nil
}
for c := n.FirstChild(); c != nil; {
next := c.NextSibling()
if c.Kind() != gast.KindText {
c = next
continue
}
parent := c.Parent()
ts := &c.(*gast.Text).Segment
n := c
for _, v := range lst.([]*escapedPipeCell) {
for _, pos := range v.Pos {
if ts.Start <= pos && pos < ts.Stop {
segment := n.(*gast.Text).Segment
n1 := gast.NewRawTextSegment(segment.WithStop(pos))
n2 := gast.NewRawTextSegment(segment.WithStart(pos + 1))
parent.InsertAfter(parent, n, n1)
parent.InsertAfter(parent, n1, n2)
parent.RemoveChild(parent, n)
n = n2
v.Transformed = true
}
}
}
c = next
}
return gast.WalkContinue, nil
})
}
}
// TableHTMLRenderer is a renderer.NodeRenderer implementation that
// renders Table nodes.
type TableHTMLRenderer struct {
TableConfig
}
// NewTableHTMLRenderer returns a new TableHTMLRenderer.
func NewTableHTMLRenderer(opts ...TableOption) renderer.NodeRenderer {
r := &TableHTMLRenderer{
TableConfig: NewTableConfig(),
}
for _, opt := range opts {
opt.SetTableOption(&r.TableConfig)
}
return r
}
// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs.
func (r *TableHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(ast.KindTable, r.renderTable)
reg.Register(ast.KindTableHeader, r.renderTableHeader)
reg.Register(ast.KindTableRow, r.renderTableRow)
reg.Register(ast.KindTableCell, r.renderTableCell)
}
// TableAttributeFilter defines attribute names which table elements can have.
//
// - align: Deprecated
// - bgcolor: Deprecated
// - border: Deprecated
// - cellpadding: Deprecated
// - cellspacing: Deprecated
// - frame: Deprecated
// - rules: Deprecated
// - summary: Deprecated
// - width: Deprecated.
var TableAttributeFilter = html.GlobalAttributeFilter.ExtendString(`align,bgcolor,border,cellpadding,cellspacing,frame,rules,summary,width`) // nolint: lll
func (r *TableHTMLRenderer) renderTable(
w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) {
if entering {
_, _ = w.WriteString("\n")
} else {
_, _ = w.WriteString("
\n")
}
return gast.WalkContinue, nil
}
// TableHeaderAttributeFilter defines attribute names which elements can have.
//
// - align: Deprecated since HTML4, Obsolete since HTML5
// - bgcolor: Not Standardized
// - char: Deprecated since HTML4, Obsolete since HTML5
// - charoff: Deprecated since HTML4, Obsolete since HTML5
// - valign: Deprecated since HTML4, Obsolete since HTML5.
var TableHeaderAttributeFilter = html.GlobalAttributeFilter.ExtendString(`align,bgcolor,char,charoff,valign`)
func (r *TableHTMLRenderer) renderTableHeader(
w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) {
if entering {
_, _ = w.WriteString("\n")
_, _ = w.WriteString("\n") // Header has no separate handle
} else {
_, _ = w.WriteString(" \n")
_, _ = w.WriteString(" \n")
if n.NextSibling() != nil {
_, _ = w.WriteString(" \n")
}
}
return gast.WalkContinue, nil
}
// TableRowAttributeFilter defines attribute names which elements can have.
//
// - align: Obsolete since HTML5
// - bgcolor: Obsolete since HTML5
// - char: Obsolete since HTML5
// - charoff: Obsolete since HTML5
// - valign: Obsolete since HTML5.
var TableRowAttributeFilter = html.GlobalAttributeFilter.ExtendString(`align,bgcolor,char,charoff,valign`)
func (r *TableHTMLRenderer) renderTableRow(
w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) {
if entering {
_, _ = w.WriteString(" \n")
} else {
_, _ = w.WriteString(" \n")
if n.Parent().LastChild() == n {
_, _ = w.WriteString(" \n")
}
}
return gast.WalkContinue, nil
}
// TableThCellAttributeFilter defines attribute names which table cells can have.
//
// - abbr: [OK] Contains a short abbreviated description of the cell's content [NOT OK in ]
// - align: Obsolete since HTML5
// - axis: Obsolete since HTML5
// - bgcolor: Not Standardized
// - char: Obsolete since HTML5
// - charoff: Obsolete since HTML5
// - colspan: [OK] Number of columns that the cell is to span
// - headers: [OK] This attribute contains a list of space-separated strings,
// each corresponding to the id attribute of the elements that apply to this element
// - height: Deprecated since HTML4. Obsolete since HTML5
// - rowspan: [OK] Number of rows that the cell is to span
// - scope: [OK] This enumerated attribute defines the cells that the header
// (defined in the ) element relates to [NOT OK in ]
// - valign: Obsolete since HTML5
// - width: Deprecated since HTML4. Obsolete since HTML5.
var TableThCellAttributeFilter = html.GlobalAttributeFilter.ExtendString(`abbr,align,axis,bgcolor,char,charoff,colspan,headers,height,rowspan,scope,valign,width`) // nolint:lll
// TableTdCellAttributeFilter defines attribute names which table cells can have.
//
// - abbr: Obsolete since HTML5. [OK in ]
// - align: Obsolete since HTML5
// - axis: Obsolete since HTML5
// - bgcolor: Not Standardized
// - char: Obsolete since HTML5
// - charoff: Obsolete since HTML5
// - colspan: [OK] Number of columns that the cell is to span
// - headers: [OK] This attribute contains a list of space-separated strings, each corresponding
// to the id attribute of the elements that apply to this element
// - height: Deprecated since HTML4. Obsolete since HTML5
// - rowspan: [OK] Number of rows that the cell is to span
// - scope: Obsolete since HTML5. [OK in ]
// - valign: Obsolete since HTML5
// - width: Deprecated since HTML4. Obsolete since HTML5.
var TableTdCellAttributeFilter = html.GlobalAttributeFilter.ExtendString(`abbr,align,axis,bgcolor,char,charoff,colspan,headers,height,rowspan,scope,valign,width`) // nolint: lll
func (r *TableHTMLRenderer) renderTableCell(
w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
n := node.(*ast.TableCell)
tag := "td"
if n.Parent().Kind() == ast.KindTableHeader {
tag = "th"
}
if entering {
_, _ = fmt.Fprintf(w, "<%s", tag)
if n.Alignment != ast.AlignNone {
amethod := r.TableConfig.TableCellAlignMethod
if amethod == TableCellAlignDefault {
if r.Config.XHTML {
amethod = TableCellAlignAttribute
} else {
amethod = TableCellAlignStyle
}
}
switch amethod {
case TableCellAlignAttribute:
if _, ok := n.AttributeString("align"); !ok { // Skip align render if overridden
_, _ = fmt.Fprintf(w, ` align="%s"`, n.Alignment.String())
}
case TableCellAlignStyle:
v, ok := n.AttributeString("style")
var cob util.CopyOnWriteBuffer
if ok {
cob = util.NewCopyOnWriteBuffer(v.([]byte))
cob.AppendByte(';')
}
style := fmt.Sprintf("text-align:%s", n.Alignment.String())
cob.AppendString(style)
n.SetAttributeString("style", cob.Bytes())
}
}
if n.Attributes() != nil {
if tag == "td" {
html.RenderAttributes(w, n, TableTdCellAttributeFilter) //
} else {
html.RenderAttributes(w, n, TableThCellAttributeFilter) //
}
}
_ = w.WriteByte('>')
} else {
_, _ = fmt.Fprintf(w, "%s>\n", tag)
}
return gast.WalkContinue, nil
}
type table struct {
options []TableOption
}
// Table is an extension that allow you to use GFM tables .
var Table = &table{
options: []TableOption{},
}
// NewTable returns a new extension with given options.
func NewTable(opts ...TableOption) goldmark.Extender {
return &table{
options: opts,
}
}
func (e *table) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(
parser.WithParagraphTransformers(
util.Prioritized(NewTableParagraphTransformer(), 200),
),
parser.WithASTTransformers(
util.Prioritized(defaultTableASTTransformer, 0),
),
)
m.Renderer().AddOptions(renderer.WithNodeRenderers(
util.Prioritized(NewTableHTMLRenderer(e.options...), 500),
))
}
yuin-goldmark-9a7f4c9/extension/table_test.go 0000664 0000000 0000000 00000015715 15037761217 0021437 0 ustar 00root root 0000000 0000000 package extension
import (
"testing"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/ast"
east "github.com/yuin/goldmark/extension/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/testutil"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
)
func TestTable(t *testing.T) {
markdown := goldmark.New(
goldmark.WithRendererOptions(
html.WithUnsafe(),
html.WithXHTML(),
),
goldmark.WithExtensions(
Table,
),
)
testutil.DoTestCaseFile(markdown, "_test/table.txt", t, testutil.ParseCliCaseArg()...)
}
func TestTableWithAlignDefault(t *testing.T) {
markdown := goldmark.New(
goldmark.WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
),
goldmark.WithExtensions(
NewTable(
WithTableCellAlignMethod(TableCellAlignDefault),
),
),
)
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: 1,
Description: "Cell with TableCellAlignDefault and XHTML should be rendered as an align attribute",
Markdown: `
| abc | defghi |
:-: | -----------:
bar | baz
`,
Expected: ``,
},
t,
)
markdown = goldmark.New(
goldmark.WithRendererOptions(
html.WithUnsafe(),
),
goldmark.WithExtensions(
NewTable(
WithTableCellAlignMethod(TableCellAlignDefault),
),
),
)
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: 2,
Description: "Cell with TableCellAlignDefault and HTML5 should be rendered as a style attribute",
Markdown: `
| abc | defghi |
:-: | -----------:
bar | baz
`,
Expected: ``,
},
t,
)
}
func TestTableWithAlignAttribute(t *testing.T) {
markdown := goldmark.New(
goldmark.WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
),
goldmark.WithExtensions(
NewTable(
WithTableCellAlignMethod(TableCellAlignAttribute),
),
),
)
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: 1,
Description: "Cell with TableCellAlignAttribute and XHTML should be rendered as an align attribute",
Markdown: `
| abc | defghi |
:-: | -----------:
bar | baz
`,
Expected: ``,
},
t,
)
markdown = goldmark.New(
goldmark.WithRendererOptions(
html.WithUnsafe(),
),
goldmark.WithExtensions(
NewTable(
WithTableCellAlignMethod(TableCellAlignAttribute),
),
),
)
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: 2,
Description: "Cell with TableCellAlignAttribute and HTML5 should be rendered as an align attribute",
Markdown: `
| abc | defghi |
:-: | -----------:
bar | baz
`,
Expected: ``,
},
t,
)
}
type tableStyleTransformer struct {
}
func (a *tableStyleTransformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) {
cell := node.FirstChild().FirstChild().FirstChild().(*east.TableCell)
cell.SetAttributeString("style", []byte("font-size:1em"))
}
func TestTableWithAlignStyle(t *testing.T) {
markdown := goldmark.New(
goldmark.WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
),
goldmark.WithExtensions(
NewTable(
WithTableCellAlignMethod(TableCellAlignStyle),
),
),
)
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: 1,
Description: "Cell with TableCellAlignStyle and XHTML should be rendered as a style attribute",
Markdown: `
| abc | defghi |
:-: | -----------:
bar | baz
`,
Expected: ``,
},
t,
)
markdown = goldmark.New(
goldmark.WithRendererOptions(
html.WithUnsafe(),
),
goldmark.WithExtensions(
NewTable(
WithTableCellAlignMethod(TableCellAlignStyle),
),
),
)
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: 2,
Description: "Cell with TableCellAlignStyle and HTML5 should be rendered as a style attribute",
Markdown: `
| abc | defghi |
:-: | -----------:
bar | baz
`,
Expected: ``,
},
t,
)
markdown = goldmark.New(
goldmark.WithParserOptions(
parser.WithASTTransformers(
util.Prioritized(&tableStyleTransformer{}, 0),
),
),
goldmark.WithRendererOptions(
html.WithUnsafe(),
),
goldmark.WithExtensions(
NewTable(
WithTableCellAlignMethod(TableCellAlignStyle),
),
),
)
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: 3,
Description: "Styled cell should not be broken the style by the alignments",
Markdown: `
| abc | defghi |
:-: | -----------:
bar | baz
`,
Expected: ``,
},
t,
)
}
func TestTableWithAlignNone(t *testing.T) {
markdown := goldmark.New(
goldmark.WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
),
goldmark.WithExtensions(
NewTable(
WithTableCellAlignMethod(TableCellAlignNone),
),
),
)
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: 1,
Description: "Cell with TableCellAlignStyle and XHTML should not be rendered",
Markdown: `
| abc | defghi |
:-: | -----------:
bar | baz
`,
Expected: ``,
},
t,
)
}
func TestTableFuzzedPanics(t *testing.T) {
markdown := goldmark.New(
goldmark.WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
),
goldmark.WithExtensions(
NewTable(),
),
)
testutil.DoTestCase(
markdown,
testutil.MarkdownTestCase{
No: 1,
Description: "This should not panic",
Markdown: "* 0\n-|\n\t0",
Expected: ``,
},
t,
)
}
yuin-goldmark-9a7f4c9/extension/tasklist.go 0000664 0000000 0000000 00000006131 15037761217 0021137 0 ustar 00root root 0000000 0000000 package extension
import (
"regexp"
"github.com/yuin/goldmark"
gast "github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/extension/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
)
var taskListRegexp = regexp.MustCompile(`^\[([\sxX])\]\s*`)
type taskCheckBoxParser struct {
}
var defaultTaskCheckBoxParser = &taskCheckBoxParser{}
// NewTaskCheckBoxParser returns a new InlineParser that can parse
// checkboxes in list items.
// This parser must take precedence over the parser.LinkParser.
func NewTaskCheckBoxParser() parser.InlineParser {
return defaultTaskCheckBoxParser
}
func (s *taskCheckBoxParser) Trigger() []byte {
return []byte{'['}
}
func (s *taskCheckBoxParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) gast.Node {
// Given AST structure must be like
// - List
// - ListItem : parent.Parent
// - TextBlock : parent
// (current line)
if parent.Parent() == nil || parent.Parent().FirstChild() != parent {
return nil
}
if parent.HasChildren() {
return nil
}
if _, ok := parent.Parent().(*gast.ListItem); !ok {
return nil
}
line, _ := block.PeekLine()
m := taskListRegexp.FindSubmatchIndex(line)
if m == nil {
return nil
}
value := line[m[2]:m[3]][0]
block.Advance(m[1])
checked := value == 'x' || value == 'X'
return ast.NewTaskCheckBox(checked)
}
func (s *taskCheckBoxParser) CloseBlock(parent gast.Node, pc parser.Context) {
// nothing to do
}
// TaskCheckBoxHTMLRenderer is a renderer.NodeRenderer implementation that
// renders checkboxes in list items.
type TaskCheckBoxHTMLRenderer struct {
html.Config
}
// NewTaskCheckBoxHTMLRenderer returns a new TaskCheckBoxHTMLRenderer.
func NewTaskCheckBoxHTMLRenderer(opts ...html.Option) renderer.NodeRenderer {
r := &TaskCheckBoxHTMLRenderer{
Config: html.NewConfig(),
}
for _, opt := range opts {
opt.SetHTMLOption(&r.Config)
}
return r
}
// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs.
func (r *TaskCheckBoxHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(ast.KindTaskCheckBox, r.renderTaskCheckBox)
}
func (r *TaskCheckBoxHTMLRenderer) renderTaskCheckBox(
w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
if !entering {
return gast.WalkContinue, nil
}
n := node.(*ast.TaskCheckBox)
if n.IsChecked {
_, _ = w.WriteString(` ")
} else {
_, _ = w.WriteString("> ")
}
return gast.WalkContinue, nil
}
type taskList struct {
}
// TaskList is an extension that allow you to use GFM task lists.
var TaskList = &taskList{}
func (e *taskList) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(parser.WithInlineParsers(
util.Prioritized(NewTaskCheckBoxParser(), 0),
))
m.Renderer().AddOptions(renderer.WithNodeRenderers(
util.Prioritized(NewTaskCheckBoxHTMLRenderer(), 500),
))
}
yuin-goldmark-9a7f4c9/extension/tasklist_test.go 0000664 0000000 0000000 00000000633 15037761217 0022177 0 ustar 00root root 0000000 0000000 package extension
import (
"testing"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/testutil"
)
func TestTaskList(t *testing.T) {
markdown := goldmark.New(
goldmark.WithRendererOptions(
html.WithUnsafe(),
),
goldmark.WithExtensions(
TaskList,
),
)
testutil.DoTestCaseFile(markdown, "_test/tasklist.txt", t, testutil.ParseCliCaseArg()...)
}
yuin-goldmark-9a7f4c9/extension/typographer.go 0000664 0000000 0000000 00000024122 15037761217 0021645 0 ustar 00root root 0000000 0000000 package extension
import (
"unicode"
"github.com/yuin/goldmark"
gast "github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
)
var uncloseCounterKey = parser.NewContextKey()
type unclosedCounter struct {
Single int
Double int
}
func (u *unclosedCounter) Reset() {
u.Single = 0
u.Double = 0
}
func getUnclosedCounter(pc parser.Context) *unclosedCounter {
v := pc.Get(uncloseCounterKey)
if v == nil {
v = &unclosedCounter{}
pc.Set(uncloseCounterKey, v)
}
return v.(*unclosedCounter)
}
// TypographicPunctuation is a key of the punctuations that can be replaced with
// typographic entities.
type TypographicPunctuation int
const (
// LeftSingleQuote is ' .
LeftSingleQuote TypographicPunctuation = iota + 1
// RightSingleQuote is ' .
RightSingleQuote
// LeftDoubleQuote is " .
LeftDoubleQuote
// RightDoubleQuote is " .
RightDoubleQuote
// EnDash is -- .
EnDash
// EmDash is --- .
EmDash
// Ellipsis is ... .
Ellipsis
// LeftAngleQuote is << .
LeftAngleQuote
// RightAngleQuote is >> .
RightAngleQuote
// Apostrophe is ' .
Apostrophe
typographicPunctuationMax
)
// An TypographerConfig struct is a data structure that holds configuration of the
// Typographer extension.
type TypographerConfig struct {
Substitutions [][]byte
}
func newDefaultSubstitutions() [][]byte {
replacements := make([][]byte, typographicPunctuationMax)
replacements[LeftSingleQuote] = []byte("‘")
replacements[RightSingleQuote] = []byte("’")
replacements[LeftDoubleQuote] = []byte("“")
replacements[RightDoubleQuote] = []byte("”")
replacements[EnDash] = []byte("–")
replacements[EmDash] = []byte("—")
replacements[Ellipsis] = []byte("…")
replacements[LeftAngleQuote] = []byte("«")
replacements[RightAngleQuote] = []byte("»")
replacements[Apostrophe] = []byte("’")
return replacements
}
// SetOption implements SetOptioner.
func (b *TypographerConfig) SetOption(name parser.OptionName, value interface{}) {
switch name {
case optTypographicSubstitutions:
b.Substitutions = value.([][]byte)
}
}
// A TypographerOption interface sets options for the TypographerParser.
type TypographerOption interface {
parser.Option
SetTypographerOption(*TypographerConfig)
}
const optTypographicSubstitutions parser.OptionName = "TypographicSubstitutions"
// TypographicSubstitutions is a list of the substitutions for the Typographer extension.
type TypographicSubstitutions map[TypographicPunctuation][]byte
type withTypographicSubstitutions struct {
value [][]byte
}
func (o *withTypographicSubstitutions) SetParserOption(c *parser.Config) {
c.Options[optTypographicSubstitutions] = o.value
}
func (o *withTypographicSubstitutions) SetTypographerOption(p *TypographerConfig) {
p.Substitutions = o.value
}
// WithTypographicSubstitutions is a functional otpion that specify replacement text
// for punctuations.
func WithTypographicSubstitutions[T []byte | string](values map[TypographicPunctuation]T) TypographerOption {
replacements := newDefaultSubstitutions()
for k, v := range values {
replacements[k] = []byte(v)
}
return &withTypographicSubstitutions{replacements}
}
type typographerDelimiterProcessor struct {
}
func (p *typographerDelimiterProcessor) IsDelimiter(b byte) bool {
return b == '\'' || b == '"'
}
func (p *typographerDelimiterProcessor) CanOpenCloser(opener, closer *parser.Delimiter) bool {
return opener.Char == closer.Char
}
func (p *typographerDelimiterProcessor) OnMatch(consumes int) gast.Node {
return nil
}
var defaultTypographerDelimiterProcessor = &typographerDelimiterProcessor{}
type typographerParser struct {
TypographerConfig
}
// NewTypographerParser return a new InlineParser that parses
// typographer expressions.
func NewTypographerParser(opts ...TypographerOption) parser.InlineParser {
p := &typographerParser{
TypographerConfig: TypographerConfig{
Substitutions: newDefaultSubstitutions(),
},
}
for _, o := range opts {
o.SetTypographerOption(&p.TypographerConfig)
}
return p
}
func (s *typographerParser) Trigger() []byte {
return []byte{'\'', '"', '-', '.', ',', '<', '>', '*', '['}
}
func (s *typographerParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) gast.Node {
line, _ := block.PeekLine()
c := line[0]
if len(line) > 2 {
if c == '-' {
if s.Substitutions[EmDash] != nil && line[1] == '-' && line[2] == '-' { // ---
node := gast.NewString(s.Substitutions[EmDash])
node.SetCode(true)
block.Advance(3)
return node
}
} else if c == '.' {
if s.Substitutions[Ellipsis] != nil && line[1] == '.' && line[2] == '.' { // ...
node := gast.NewString(s.Substitutions[Ellipsis])
node.SetCode(true)
block.Advance(3)
return node
}
return nil
}
}
if len(line) > 1 {
if c == '<' {
if s.Substitutions[LeftAngleQuote] != nil && line[1] == '<' { // <<
node := gast.NewString(s.Substitutions[LeftAngleQuote])
node.SetCode(true)
block.Advance(2)
return node
}
return nil
} else if c == '>' {
if s.Substitutions[RightAngleQuote] != nil && line[1] == '>' { // >>
node := gast.NewString(s.Substitutions[RightAngleQuote])
node.SetCode(true)
block.Advance(2)
return node
}
return nil
} else if s.Substitutions[EnDash] != nil && c == '-' && line[1] == '-' { // --
node := gast.NewString(s.Substitutions[EnDash])
node.SetCode(true)
block.Advance(2)
return node
}
}
if c == '\'' || c == '"' {
before := block.PrecendingCharacter()
d := parser.ScanDelimiter(line, before, 1, defaultTypographerDelimiterProcessor)
if d == nil {
return nil
}
counter := getUnclosedCounter(pc)
if c == '\'' {
if s.Substitutions[Apostrophe] != nil {
// Handle decade abbrevations such as '90s
if d.CanOpen && !d.CanClose && len(line) > 3 &&
util.IsNumeric(line[1]) && util.IsNumeric(line[2]) && line[3] == 's' {
after := rune(' ')
if len(line) > 4 {
after = util.ToRune(line, 4)
}
if len(line) == 3 || util.IsSpaceRune(after) || util.IsPunctRune(after) {
node := gast.NewString(s.Substitutions[Apostrophe])
node.SetCode(true)
block.Advance(1)
return node
}
}
// special cases: 'twas, 'em, 'net
if len(line) > 1 && (unicode.IsPunct(before) || unicode.IsSpace(before)) &&
(line[1] == 't' || line[1] == 'e' || line[1] == 'n' || line[1] == 'l') {
node := gast.NewString(s.Substitutions[Apostrophe])
node.SetCode(true)
block.Advance(1)
return node
}
// Convert normal apostrophes. This is probably more flexible than necessary but
// converts any apostrophe in between two alphanumerics.
if len(line) > 1 && (unicode.IsDigit(before) || unicode.IsLetter(before)) &&
(unicode.IsLetter(util.ToRune(line, 1))) {
node := gast.NewString(s.Substitutions[Apostrophe])
node.SetCode(true)
block.Advance(1)
return node
}
}
if s.Substitutions[LeftSingleQuote] != nil && d.CanOpen && !d.CanClose {
nt := LeftSingleQuote
// special cases: Alice's, I'm, Don't, You'd
if len(line) > 1 && (line[1] == 's' || line[1] == 'm' || line[1] == 't' || line[1] == 'd') &&
(len(line) < 3 || util.IsPunct(line[2]) || util.IsSpace(line[2])) {
nt = RightSingleQuote
}
// special cases: I've, I'll, You're
if len(line) > 2 && ((line[1] == 'v' && line[2] == 'e') ||
(line[1] == 'l' && line[2] == 'l') || (line[1] == 'r' && line[2] == 'e')) &&
(len(line) < 4 || util.IsPunct(line[3]) || util.IsSpace(line[3])) {
nt = RightSingleQuote
}
if nt == LeftSingleQuote {
counter.Single++
}
node := gast.NewString(s.Substitutions[nt])
node.SetCode(true)
block.Advance(1)
return node
}
if s.Substitutions[RightSingleQuote] != nil {
// plural possesive and abbreviations: Smiths', doin'
if len(line) > 1 && unicode.IsSpace(util.ToRune(line, 0)) || unicode.IsPunct(util.ToRune(line, 0)) &&
(len(line) > 2 && !unicode.IsDigit(util.ToRune(line, 1))) {
node := gast.NewString(s.Substitutions[RightSingleQuote])
node.SetCode(true)
block.Advance(1)
return node
}
}
if s.Substitutions[RightSingleQuote] != nil && counter.Single > 0 {
isClose := d.CanClose && !d.CanOpen
maybeClose := d.CanClose && d.CanOpen && len(line) > 1 && unicode.IsPunct(util.ToRune(line, 1)) &&
(len(line) == 2 || (len(line) > 2 && util.IsPunct(line[2]) || util.IsSpace(line[2])))
if isClose || maybeClose {
node := gast.NewString(s.Substitutions[RightSingleQuote])
node.SetCode(true)
block.Advance(1)
counter.Single--
return node
}
}
}
if c == '"' {
if s.Substitutions[LeftDoubleQuote] != nil && d.CanOpen && !d.CanClose {
node := gast.NewString(s.Substitutions[LeftDoubleQuote])
node.SetCode(true)
block.Advance(1)
counter.Double++
return node
}
if s.Substitutions[RightDoubleQuote] != nil && counter.Double > 0 {
isClose := d.CanClose && !d.CanOpen
maybeClose := d.CanClose && d.CanOpen && len(line) > 1 && (unicode.IsPunct(util.ToRune(line, 1))) &&
(len(line) == 2 || (len(line) > 2 && util.IsPunct(line[2]) || util.IsSpace(line[2])))
if isClose || maybeClose {
// special case: "Monitor 21""
if len(line) > 1 && line[1] == '"' && unicode.IsDigit(before) {
return nil
}
node := gast.NewString(s.Substitutions[RightDoubleQuote])
node.SetCode(true)
block.Advance(1)
counter.Double--
return node
}
}
}
}
return nil
}
func (s *typographerParser) CloseBlock(parent gast.Node, pc parser.Context) {
getUnclosedCounter(pc).Reset()
}
type typographer struct {
options []TypographerOption
}
// Typographer is an extension that replaces punctuations with typographic entities.
var Typographer = &typographer{}
// NewTypographer returns a new Extender that replaces punctuations with typographic entities.
func NewTypographer(opts ...TypographerOption) goldmark.Extender {
return &typographer{
options: opts,
}
}
func (e *typographer) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(parser.WithInlineParsers(
util.Prioritized(NewTypographerParser(e.options...), 9999),
))
}
yuin-goldmark-9a7f4c9/extension/typographer_test.go 0000664 0000000 0000000 00000000644 15037761217 0022707 0 ustar 00root root 0000000 0000000 package extension
import (
"testing"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/testutil"
)
func TestTypographer(t *testing.T) {
markdown := goldmark.New(
goldmark.WithRendererOptions(
html.WithUnsafe(),
),
goldmark.WithExtensions(
Typographer,
),
)
testutil.DoTestCaseFile(markdown, "_test/typographer.txt", t, testutil.ParseCliCaseArg()...)
}
yuin-goldmark-9a7f4c9/extra_test.go 0000664 0000000 0000000 00000012340 15037761217 0017446 0 ustar 00root root 0000000 0000000 package goldmark_test
import (
"bytes"
"os"
"strconv"
"strings"
"testing"
"time"
. "github.com/yuin/goldmark"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/testutil"
)
var testTimeoutMultiplier = 1.0
func init() {
m, err := strconv.ParseFloat(os.Getenv("GOLDMARK_TEST_TIMEOUT_MULTIPLIER"), 64)
if err == nil {
testTimeoutMultiplier = m
}
}
func TestExtras(t *testing.T) {
markdown := New(WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
))
testutil.DoTestCaseFile(markdown, "_test/extra.txt", t, testutil.ParseCliCaseArg()...)
}
func TestEndsWithNonSpaceCharacters(t *testing.T) {
markdown := New(WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
))
source := []byte("```\na\n```")
var b bytes.Buffer
err := markdown.Convert(source, &b)
if err != nil {
t.Error(err.Error())
}
if b.String() != "a\n \n" {
t.Errorf("%s \n---------\n %s", source, b.String())
}
}
func TestWindowsNewLine(t *testing.T) {
markdown := New(WithRendererOptions(
html.WithXHTML(),
))
source := []byte("a \r\nb\n")
var b bytes.Buffer
err := markdown.Convert(source, &b)
if err != nil {
t.Error(err.Error())
}
if b.String() != "a \nb
\n" {
t.Errorf("%s\n---------\n%s", source, b.String())
}
source = []byte("a\\\r\nb\r\n")
var b2 bytes.Buffer
err = markdown.Convert(source, &b2)
if err != nil {
t.Error(err.Error())
}
if b2.String() != "a \nb
\n" {
t.Errorf("\n%s\n---------\n%s", source, b2.String())
}
}
type myIDs struct {
}
func (s *myIDs) Generate(value []byte, kind ast.NodeKind) []byte {
return []byte("my-id")
}
func (s *myIDs) Put(value []byte) {
}
func TestAutogeneratedIDs(t *testing.T) {
ctx := parser.NewContext(parser.WithIDs(&myIDs{}))
markdown := New(WithParserOptions(parser.WithAutoHeadingID()))
source := []byte("# Title1\n## Title2")
var b bytes.Buffer
err := markdown.Convert(source, &b, parser.WithContext(ctx))
if err != nil {
t.Error(err.Error())
}
if b.String() != `Title1
Title2
` {
t.Errorf("%s\n---------\n%s", source, b.String())
}
}
func nowMillis() int64 {
// TODO: replace UnixNano to UnixMillis(drops Go1.16 support)
return time.Now().UnixNano() / 1000000
}
func TestDeepNestedLabelPerformance(t *testing.T) {
if testing.Short() {
t.Skip("skipping performance test in short mode")
}
markdown := New(WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
))
started := nowMillis()
n := 50000
source := []byte(strings.Repeat("[", n) + strings.Repeat("]", n))
var b bytes.Buffer
_ = markdown.Convert(source, &b)
finished := nowMillis()
if (finished - started) > int64(5000*testTimeoutMultiplier) {
t.Error("Parsing deep nested labels took too long")
}
}
func TestManyProcessingInstructionPerformance(t *testing.T) {
if testing.Short() {
t.Skip("skipping performance test in short mode")
}
markdown := New(WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
))
started := nowMillis()
n := 50000
source := []byte("a " + strings.Repeat("", n))
var b bytes.Buffer
_ = markdown.Convert(source, &b)
finished := nowMillis()
if (finished - started) > int64(5000*testTimeoutMultiplier) {
t.Error("Parsing processing instructions took too long")
}
}
func TestManyCDATAPerformance(t *testing.T) {
if testing.Short() {
t.Skip("skipping performance test in short mode")
}
markdown := New(WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
))
started := nowMillis()
n := 50000
source := []byte(strings.Repeat("a int64(5000*testTimeoutMultiplier) {
t.Error("Parsing processing instructions took too long")
}
}
func TestManyDeclPerformance(t *testing.T) {
if testing.Short() {
t.Skip("skipping performance test in short mode")
}
markdown := New(WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
))
started := nowMillis()
n := 50000
source := []byte(strings.Repeat("a int64(5000*testTimeoutMultiplier) {
t.Error("Parsing processing instructions took too long")
}
}
func TestManyCommentPerformance(t *testing.T) {
if testing.Short() {
t.Skip("skipping performance test in short mode")
}
markdown := New(WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
))
started := nowMillis()
n := 50000
source := []byte(strings.Repeat("a ")
var emptyComment2 = []byte("")
var openComment = []byte("")
func (s *rawHTMLParser) parseComment(block text.Reader, pc Context) ast.Node {
savedLine, savedSegment := block.Position()
node := ast.NewRawHTML()
line, segment := block.PeekLine()
if bytes.HasPrefix(line, emptyComment1) {
node.Segments.Append(segment.WithStop(segment.Start + len(emptyComment1)))
block.Advance(len(emptyComment1))
return node
}
if bytes.HasPrefix(line, emptyComment2) {
node.Segments.Append(segment.WithStop(segment.Start + len(emptyComment2)))
block.Advance(len(emptyComment2))
return node
}
offset := len(openComment)
line = line[offset:]
for {
index := bytes.Index(line, closeComment)
if index > -1 {
node.Segments.Append(segment.WithStop(segment.Start + offset + index + len(closeComment)))
block.Advance(offset + index + len(closeComment))
return node
}
offset = 0
node.Segments.Append(segment)
block.AdvanceLine()
line, segment = block.PeekLine()
if line == nil {
break
}
}
block.SetPosition(savedLine, savedSegment)
return nil
}
func (s *rawHTMLParser) parseUntil(block text.Reader, closer []byte, pc Context) ast.Node {
savedLine, savedSegment := block.Position()
node := ast.NewRawHTML()
for {
line, segment := block.PeekLine()
if line == nil {
break
}
index := bytes.Index(line, closer)
if index > -1 {
node.Segments.Append(segment.WithStop(segment.Start + index + len(closer)))
block.Advance(index + len(closer))
return node
}
node.Segments.Append(segment)
block.AdvanceLine()
}
block.SetPosition(savedLine, savedSegment)
return nil
}
func (s *rawHTMLParser) parseMultiLineRegexp(reg *regexp.Regexp, block text.Reader, pc Context) ast.Node {
sline, ssegment := block.Position()
if block.Match(reg) {
node := ast.NewRawHTML()
eline, esegment := block.Position()
block.SetPosition(sline, ssegment)
for {
line, segment := block.PeekLine()
if line == nil {
break
}
l, _ := block.Position()
start := segment.Start
if l == sline {
start = ssegment.Start
}
end := segment.Stop
if l == eline {
end = esegment.Start
}
node.Segments.Append(text.NewSegment(start, end))
if l == eline {
block.Advance(end - start)
break
}
block.AdvanceLine()
}
return node
}
return nil
}
yuin-goldmark-9a7f4c9/parser/setext_headings.go 0000664 0000000 0000000 00000005774 15037761217 0021753 0 ustar 00root root 0000000 0000000 package parser
import (
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
)
var temporaryParagraphKey = NewContextKey()
type setextHeadingParser struct {
HeadingConfig
}
func matchesSetextHeadingBar(line []byte) (byte, bool) {
start := 0
end := len(line)
space := util.TrimLeftLength(line, []byte{' '})
if space > 3 {
return 0, false
}
start += space
level1 := util.TrimLeftLength(line[start:end], []byte{'='})
c := byte('=')
var level2 int
if level1 == 0 {
level2 = util.TrimLeftLength(line[start:end], []byte{'-'})
c = '-'
}
if util.IsSpace(line[end-1]) {
end -= util.TrimRightSpaceLength(line[start:end])
}
if !((level1 > 0 && start+level1 == end) || (level2 > 0 && start+level2 == end)) {
return 0, false
}
return c, true
}
// NewSetextHeadingParser return a new BlockParser that can parse Setext headings.
func NewSetextHeadingParser(opts ...HeadingOption) BlockParser {
p := &setextHeadingParser{}
for _, o := range opts {
o.SetHeadingOption(&p.HeadingConfig)
}
return p
}
func (b *setextHeadingParser) Trigger() []byte {
return []byte{'-', '='}
}
func (b *setextHeadingParser) Open(parent ast.Node, reader text.Reader, pc Context) (ast.Node, State) {
last := pc.LastOpenedBlock().Node
if last == nil {
return nil, NoChildren
}
paragraph, ok := last.(*ast.Paragraph)
if !ok || paragraph.Parent() != parent {
return nil, NoChildren
}
line, segment := reader.PeekLine()
c, ok := matchesSetextHeadingBar(line)
if !ok {
return nil, NoChildren
}
level := 1
if c == '-' {
level = 2
}
node := ast.NewHeading(level)
node.Lines().Append(segment)
pc.Set(temporaryParagraphKey, last)
return node, NoChildren | RequireParagraph
}
func (b *setextHeadingParser) Continue(node ast.Node, reader text.Reader, pc Context) State {
return Close
}
func (b *setextHeadingParser) Close(node ast.Node, reader text.Reader, pc Context) {
heading := node.(*ast.Heading)
segment := node.Lines().At(0)
heading.Lines().Clear()
tmp := pc.Get(temporaryParagraphKey).(*ast.Paragraph)
pc.Set(temporaryParagraphKey, nil)
if tmp.Lines().Len() == 0 {
next := heading.NextSibling()
segment = segment.TrimLeftSpace(reader.Source())
if next == nil || !ast.IsParagraph(next) {
para := ast.NewParagraph()
para.Lines().Append(segment)
heading.Parent().InsertAfter(heading.Parent(), heading, para)
} else {
next.Lines().Unshift(segment)
}
heading.Parent().RemoveChild(heading.Parent(), heading)
} else {
heading.SetLines(tmp.Lines())
heading.SetBlankPreviousLines(tmp.HasBlankPreviousLines())
tp := tmp.Parent()
if tp != nil {
tp.RemoveChild(tp, tmp)
}
}
if b.Attribute {
parseLastLineAttributes(node, reader, pc)
}
if b.AutoHeadingID {
id, ok := node.AttributeString("id")
if !ok {
generateAutoHeadingID(heading, reader, pc)
} else {
pc.IDs().Put(id.([]byte))
}
}
}
func (b *setextHeadingParser) CanInterruptParagraph() bool {
return true
}
func (b *setextHeadingParser) CanAcceptIndentedLine() bool {
return false
}
yuin-goldmark-9a7f4c9/parser/thematic_break.go 0000664 0000000 0000000 00000003035 15037761217 0021523 0 ustar 00root root 0000000 0000000 package parser
import (
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
)
type thematicBreakPraser struct {
}
var defaultThematicBreakPraser = &thematicBreakPraser{}
// NewThematicBreakParser returns a new BlockParser that
// parses thematic breaks.
func NewThematicBreakParser() BlockParser {
return defaultThematicBreakPraser
}
func isThematicBreak(line []byte, offset int) bool {
w, pos := util.IndentWidth(line, offset)
if w > 3 {
return false
}
mark := byte(0)
count := 0
for i := pos; i < len(line); i++ {
c := line[i]
if util.IsSpace(c) {
continue
}
if mark == 0 {
mark = c
count = 1
if mark == '*' || mark == '-' || mark == '_' {
continue
}
return false
}
if c != mark {
return false
}
count++
}
return count > 2
}
func (b *thematicBreakPraser) Trigger() []byte {
return []byte{'-', '*', '_'}
}
func (b *thematicBreakPraser) Open(parent ast.Node, reader text.Reader, pc Context) (ast.Node, State) {
line, _ := reader.PeekLine()
if isThematicBreak(line, reader.LineOffset()) {
reader.AdvanceToEOL()
return ast.NewThematicBreak(), NoChildren
}
return nil, NoChildren
}
func (b *thematicBreakPraser) Continue(node ast.Node, reader text.Reader, pc Context) State {
return Close
}
func (b *thematicBreakPraser) Close(node ast.Node, reader text.Reader, pc Context) {
// nothing to do
}
func (b *thematicBreakPraser) CanInterruptParagraph() bool {
return true
}
func (b *thematicBreakPraser) CanAcceptIndentedLine() bool {
return false
}
yuin-goldmark-9a7f4c9/renderer/ 0000775 0000000 0000000 00000000000 15037761217 0016543 5 ustar 00root root 0000000 0000000 yuin-goldmark-9a7f4c9/renderer/html/ 0000775 0000000 0000000 00000000000 15037761217 0017507 5 ustar 00root root 0000000 0000000 yuin-goldmark-9a7f4c9/renderer/html/html.go 0000664 0000000 0000000 00000065067 15037761217 0021020 0 ustar 00root root 0000000 0000000 // Package html implements renderer that outputs HTMLs.
package html
import (
"bytes"
"fmt"
"strconv"
"unicode"
"unicode/utf8"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/util"
)
// A Config struct has configurations for the HTML based renderers.
type Config struct {
Writer Writer
HardWraps bool
EastAsianLineBreaks EastAsianLineBreaks
XHTML bool
Unsafe bool
}
// NewConfig returns a new Config with defaults.
func NewConfig() Config {
return Config{
Writer: DefaultWriter,
HardWraps: false,
EastAsianLineBreaks: EastAsianLineBreaksNone,
XHTML: false,
Unsafe: false,
}
}
// SetOption implements renderer.NodeRenderer.SetOption.
func (c *Config) SetOption(name renderer.OptionName, value interface{}) {
switch name {
case optHardWraps:
c.HardWraps = value.(bool)
case optEastAsianLineBreaks:
c.EastAsianLineBreaks = value.(EastAsianLineBreaks)
case optXHTML:
c.XHTML = value.(bool)
case optUnsafe:
c.Unsafe = value.(bool)
case optTextWriter:
c.Writer = value.(Writer)
}
}
// An Option interface sets options for HTML based renderers.
type Option interface {
SetHTMLOption(*Config)
}
// TextWriter is an option name used in WithWriter.
const optTextWriter renderer.OptionName = "Writer"
type withWriter struct {
value Writer
}
func (o *withWriter) SetConfig(c *renderer.Config) {
c.Options[optTextWriter] = o.value
}
func (o *withWriter) SetHTMLOption(c *Config) {
c.Writer = o.value
}
// WithWriter is a functional option that allow you to set the given writer to
// the renderer.
func WithWriter(writer Writer) interface {
renderer.Option
Option
} {
return &withWriter{writer}
}
// HardWraps is an option name used in WithHardWraps.
const optHardWraps renderer.OptionName = "HardWraps"
type withHardWraps struct {
}
func (o *withHardWraps) SetConfig(c *renderer.Config) {
c.Options[optHardWraps] = true
}
func (o *withHardWraps) SetHTMLOption(c *Config) {
c.HardWraps = true
}
// WithHardWraps is a functional option that indicates whether softline breaks
// should be rendered as ' '.
func WithHardWraps() interface {
renderer.Option
Option
} {
return &withHardWraps{}
}
// EastAsianLineBreaks is an option name used in WithEastAsianLineBreaks.
const optEastAsianLineBreaks renderer.OptionName = "EastAsianLineBreaks"
// A EastAsianLineBreaks is a style of east asian line breaks.
type EastAsianLineBreaks int
const (
//EastAsianLineBreaksNone renders line breaks as it is.
EastAsianLineBreaksNone EastAsianLineBreaks = iota
// EastAsianLineBreaksSimple follows east_asian_line_breaks in Pandoc.
EastAsianLineBreaksSimple
// EastAsianLineBreaksCSS3Draft follows CSS text level3 "Segment Break Transformation Rules" with some enhancements.
EastAsianLineBreaksCSS3Draft
)
func (b EastAsianLineBreaks) softLineBreak(thisLastRune rune, siblingFirstRune rune) bool {
switch b {
case EastAsianLineBreaksNone:
return false
case EastAsianLineBreaksSimple:
return !(util.IsEastAsianWideRune(thisLastRune) && util.IsEastAsianWideRune(siblingFirstRune))
case EastAsianLineBreaksCSS3Draft:
return eastAsianLineBreaksCSS3DraftSoftLineBreak(thisLastRune, siblingFirstRune)
}
return false
}
func eastAsianLineBreaksCSS3DraftSoftLineBreak(thisLastRune rune, siblingFirstRune rune) bool {
// Implements CSS text level3 Segment Break Transformation Rules with some enhancements.
// References:
// - https://www.w3.org/TR/2020/WD-css-text-3-20200429/#line-break-transform
// - https://github.com/w3c/csswg-drafts/issues/5086
// Rule1:
// If the character immediately before or immediately after the segment break is
// the zero-width space character (U+200B), then the break is removed, leaving behind the zero-width space.
if thisLastRune == '\u200B' || siblingFirstRune == '\u200B' {
return false
}
// Rule2:
// Otherwise, if the East Asian Width property of both the character before and after the segment break is
// F, W, or H (not A), and neither side is Hangul, then the segment break is removed.
thisLastRuneEastAsianWidth := util.EastAsianWidth(thisLastRune)
siblingFirstRuneEastAsianWidth := util.EastAsianWidth(siblingFirstRune)
if (thisLastRuneEastAsianWidth == "F" ||
thisLastRuneEastAsianWidth == "W" ||
thisLastRuneEastAsianWidth == "H") &&
(siblingFirstRuneEastAsianWidth == "F" ||
siblingFirstRuneEastAsianWidth == "W" ||
siblingFirstRuneEastAsianWidth == "H") {
return unicode.Is(unicode.Hangul, thisLastRune) || unicode.Is(unicode.Hangul, siblingFirstRune)
}
// Rule3:
// Otherwise, if either the character before or after the segment break belongs to
// the space-discarding character set and it is a Unicode Punctuation (P*) or U+3000,
// then the segment break is removed.
if util.IsSpaceDiscardingUnicodeRune(thisLastRune) ||
unicode.IsPunct(thisLastRune) ||
thisLastRune == '\u3000' ||
util.IsSpaceDiscardingUnicodeRune(siblingFirstRune) ||
unicode.IsPunct(siblingFirstRune) ||
siblingFirstRune == '\u3000' {
return false
}
// Rule4:
// Otherwise, the segment break is converted to a space (U+0020).
return true
}
type withEastAsianLineBreaks struct {
eastAsianLineBreaksStyle EastAsianLineBreaks
}
func (o *withEastAsianLineBreaks) SetConfig(c *renderer.Config) {
c.Options[optEastAsianLineBreaks] = o.eastAsianLineBreaksStyle
}
func (o *withEastAsianLineBreaks) SetHTMLOption(c *Config) {
c.EastAsianLineBreaks = o.eastAsianLineBreaksStyle
}
// WithEastAsianLineBreaks is a functional option that indicates whether softline breaks
// between east asian wide characters should be ignored.
func WithEastAsianLineBreaks(e EastAsianLineBreaks) interface {
renderer.Option
Option
} {
return &withEastAsianLineBreaks{e}
}
// XHTML is an option name used in WithXHTML.
const optXHTML renderer.OptionName = "XHTML"
type withXHTML struct {
}
func (o *withXHTML) SetConfig(c *renderer.Config) {
c.Options[optXHTML] = true
}
func (o *withXHTML) SetHTMLOption(c *Config) {
c.XHTML = true
}
// WithXHTML is a functional option indicates that nodes should be rendered in
// xhtml instead of HTML5.
func WithXHTML() interface {
Option
renderer.Option
} {
return &withXHTML{}
}
// Unsafe is an option name used in WithUnsafe.
const optUnsafe renderer.OptionName = "Unsafe"
type withUnsafe struct {
}
func (o *withUnsafe) SetConfig(c *renderer.Config) {
c.Options[optUnsafe] = true
}
func (o *withUnsafe) SetHTMLOption(c *Config) {
c.Unsafe = true
}
// WithUnsafe is a functional option that renders dangerous contents
// (raw htmls and potentially dangerous links) as it is.
func WithUnsafe() interface {
renderer.Option
Option
} {
return &withUnsafe{}
}
// A Renderer struct is an implementation of renderer.NodeRenderer that renders
// nodes as (X)HTML.
type Renderer struct {
Config
}
// NewRenderer returns a new Renderer with given options.
func NewRenderer(opts ...Option) renderer.NodeRenderer {
r := &Renderer{
Config: NewConfig(),
}
for _, opt := range opts {
opt.SetHTMLOption(&r.Config)
}
return r
}
// RegisterFuncs implements NodeRenderer.RegisterFuncs .
func (r *Renderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
// blocks
reg.Register(ast.KindDocument, r.renderDocument)
reg.Register(ast.KindHeading, r.renderHeading)
reg.Register(ast.KindBlockquote, r.renderBlockquote)
reg.Register(ast.KindCodeBlock, r.renderCodeBlock)
reg.Register(ast.KindFencedCodeBlock, r.renderFencedCodeBlock)
reg.Register(ast.KindHTMLBlock, r.renderHTMLBlock)
reg.Register(ast.KindList, r.renderList)
reg.Register(ast.KindListItem, r.renderListItem)
reg.Register(ast.KindParagraph, r.renderParagraph)
reg.Register(ast.KindTextBlock, r.renderTextBlock)
reg.Register(ast.KindThematicBreak, r.renderThematicBreak)
// inlines
reg.Register(ast.KindAutoLink, r.renderAutoLink)
reg.Register(ast.KindCodeSpan, r.renderCodeSpan)
reg.Register(ast.KindEmphasis, r.renderEmphasis)
reg.Register(ast.KindImage, r.renderImage)
reg.Register(ast.KindLink, r.renderLink)
reg.Register(ast.KindRawHTML, r.renderRawHTML)
reg.Register(ast.KindText, r.renderText)
reg.Register(ast.KindString, r.renderString)
}
func (r *Renderer) writeLines(w util.BufWriter, source []byte, n ast.Node) {
l := n.Lines().Len()
for i := 0; i < l; i++ {
line := n.Lines().At(i)
r.Writer.RawWrite(w, line.Value(source))
}
}
// GlobalAttributeFilter defines attribute names which any elements can have.
var GlobalAttributeFilter = util.NewBytesFilterString(`accesskey,autocapitalize,autofocus,class,contenteditable,dir,draggable,enterkeyhint,hidden,id,inert,inputmode,is,itemid,itemprop,itemref,itemscope,itemtype,lang,part,role,slot,spellcheck,style,tabindex,title,translate`) // nolint:lll
func (r *Renderer) renderDocument(
w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
// nothing to do
return ast.WalkContinue, nil
}
// HeadingAttributeFilter defines attribute names which heading elements can have.
var HeadingAttributeFilter = GlobalAttributeFilter
func (r *Renderer) renderHeading(
w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.Heading)
if entering {
_, _ = w.WriteString("')
} else {
_, _ = w.WriteString(" \n")
}
return ast.WalkContinue, nil
}
// BlockquoteAttributeFilter defines attribute names which blockquote elements can have.
var BlockquoteAttributeFilter = GlobalAttributeFilter.ExtendString(`cite`)
func (r *Renderer) renderBlockquote(
w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
if n.Attributes() != nil {
_, _ = w.WriteString("')
} else {
_, _ = w.WriteString("\n")
}
} else {
_, _ = w.WriteString(" \n")
}
return ast.WalkContinue, nil
}
func (r *Renderer) renderCodeBlock(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
_, _ = w.WriteString("")
r.writeLines(w, source, n)
} else {
_, _ = w.WriteString(" \n")
}
return ast.WalkContinue, nil
}
func (r *Renderer) renderFencedCodeBlock(
w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.FencedCodeBlock)
if entering {
_, _ = w.WriteString("')
r.writeLines(w, source, n)
} else {
_, _ = w.WriteString(" \n")
}
return ast.WalkContinue, nil
}
func (r *Renderer) renderHTMLBlock(
w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.HTMLBlock)
if entering {
if r.Unsafe {
l := n.Lines().Len()
for i := 0; i < l; i++ {
line := n.Lines().At(i)
r.Writer.SecureWrite(w, line.Value(source))
}
} else {
_, _ = w.WriteString("\n")
}
} else {
if n.HasClosure() {
if r.Unsafe {
closure := n.ClosureLine
r.Writer.SecureWrite(w, closure.Value(source))
} else {
_, _ = w.WriteString("\n")
}
}
}
return ast.WalkContinue, nil
}
// ListAttributeFilter defines attribute names which list elements can have.
var ListAttributeFilter = GlobalAttributeFilter.ExtendString(`start,reversed,type`)
func (r *Renderer) renderList(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.List)
tag := "ul"
if n.IsOrdered() {
tag = "ol"
}
if entering {
_ = w.WriteByte('<')
_, _ = w.WriteString(tag)
if n.IsOrdered() && n.Start != 1 {
_, _ = fmt.Fprintf(w, " start=\"%d\"", n.Start)
}
if n.Attributes() != nil {
RenderAttributes(w, n, ListAttributeFilter)
}
_, _ = w.WriteString(">\n")
} else {
_, _ = w.WriteString("")
_, _ = w.WriteString(tag)
_, _ = w.WriteString(">\n")
}
return ast.WalkContinue, nil
}
// ListItemAttributeFilter defines attribute names which list item elements can have.
var ListItemAttributeFilter = GlobalAttributeFilter.ExtendString(`value`)
func (r *Renderer) renderListItem(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
if n.Attributes() != nil {
_, _ = w.WriteString("')
} else {
_, _ = w.WriteString(" ")
}
fc := n.FirstChild()
if fc != nil {
if _, ok := fc.(*ast.TextBlock); !ok {
_ = w.WriteByte('\n')
}
}
} else {
_, _ = w.WriteString(" \n")
}
return ast.WalkContinue, nil
}
// ParagraphAttributeFilter defines attribute names which paragraph elements can have.
var ParagraphAttributeFilter = GlobalAttributeFilter
func (r *Renderer) renderParagraph(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
if n.Attributes() != nil {
_, _ = w.WriteString("')
} else {
_, _ = w.WriteString("
")
}
} else {
_, _ = w.WriteString("
\n")
}
return ast.WalkContinue, nil
}
func (r *Renderer) renderTextBlock(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
if n.NextSibling() != nil && n.FirstChild() != nil {
_ = w.WriteByte('\n')
}
}
return ast.WalkContinue, nil
}
// ThematicAttributeFilter defines attribute names which hr elements can have.
var ThematicAttributeFilter = GlobalAttributeFilter.ExtendString(`align,color,noshade,size,width`)
func (r *Renderer) renderThematicBreak(
w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
return ast.WalkContinue, nil
}
_, _ = w.WriteString(" \n")
} else {
_, _ = w.WriteString(">\n")
}
return ast.WalkContinue, nil
}
// LinkAttributeFilter defines attribute names which link elements can have.
var LinkAttributeFilter = GlobalAttributeFilter.ExtendString(`download,hreflang,media,ping,referrerpolicy,rel,shape,target`) // nolint:lll
func (r *Renderer) renderAutoLink(
w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.AutoLink)
if !entering {
return ast.WalkContinue, nil
}
_, _ = w.WriteString(`')
} else {
_, _ = w.WriteString(`">`)
}
_, _ = w.Write(util.EscapeHTML(label))
_, _ = w.WriteString(` `)
return ast.WalkContinue, nil
}
// CodeAttributeFilter defines attribute names which code elements can have.
var CodeAttributeFilter = GlobalAttributeFilter
func (r *Renderer) renderCodeSpan(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
if n.Attributes() != nil {
_, _ = w.WriteString("')
} else {
_, _ = w.WriteString("")
}
for c := n.FirstChild(); c != nil; c = c.NextSibling() {
segment := c.(*ast.Text).Segment
value := segment.Value(source)
if bytes.HasSuffix(value, []byte("\n")) {
r.Writer.RawWrite(w, value[:len(value)-1])
r.Writer.RawWrite(w, []byte(" "))
} else {
r.Writer.RawWrite(w, value)
}
}
return ast.WalkSkipChildren, nil
}
_, _ = w.WriteString("")
return ast.WalkContinue, nil
}
// EmphasisAttributeFilter defines attribute names which emphasis elements can have.
var EmphasisAttributeFilter = GlobalAttributeFilter
func (r *Renderer) renderEmphasis(
w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.Emphasis)
tag := "em"
if n.Level == 2 {
tag = "strong"
}
if entering {
_ = w.WriteByte('<')
_, _ = w.WriteString(tag)
if n.Attributes() != nil {
RenderAttributes(w, n, EmphasisAttributeFilter)
}
_ = w.WriteByte('>')
} else {
_, _ = w.WriteString("")
_, _ = w.WriteString(tag)
_ = w.WriteByte('>')
}
return ast.WalkContinue, nil
}
func (r *Renderer) renderLink(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.Link)
if entering {
_, _ = w.WriteString("')
} else {
_, _ = w.WriteString(" ")
}
return ast.WalkContinue, nil
}
// ImageAttributeFilter defines attribute names which image elements can have.
var ImageAttributeFilter = GlobalAttributeFilter.ExtendString(`align,border,crossorigin,decoding,height,importance,intrinsicsize,ismap,loading,referrerpolicy,sizes,srcset,usemap,width`) // nolint: lll
func (r *Renderer) renderImage(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
return ast.WalkContinue, nil
}
n := node.(*ast.Image)
_, _ = w.WriteString(" ")
} else {
_, _ = w.WriteString(">")
}
return ast.WalkSkipChildren, nil
}
func (r *Renderer) renderRawHTML(
w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
return ast.WalkSkipChildren, nil
}
if r.Unsafe {
n := node.(*ast.RawHTML)
l := n.Segments.Len()
for i := 0; i < l; i++ {
segment := n.Segments.At(i)
_, _ = w.Write(segment.Value(source))
}
return ast.WalkSkipChildren, nil
}
_, _ = w.WriteString("")
return ast.WalkSkipChildren, nil
}
func (r *Renderer) renderText(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
return ast.WalkContinue, nil
}
n := node.(*ast.Text)
segment := n.Segment
if n.IsRaw() {
r.Writer.RawWrite(w, segment.Value(source))
} else {
value := segment.Value(source)
r.Writer.Write(w, value)
if n.HardLineBreak() || (n.SoftLineBreak() && r.HardWraps) {
if r.XHTML {
_, _ = w.WriteString(" \n")
} else {
_, _ = w.WriteString(" \n")
}
} else if n.SoftLineBreak() {
if r.EastAsianLineBreaks != EastAsianLineBreaksNone && len(value) != 0 {
sibling := node.NextSibling()
if sibling != nil && sibling.Kind() == ast.KindText {
if siblingText := sibling.(*ast.Text).Value(source); len(siblingText) != 0 {
thisLastRune := util.ToRune(value, len(value)-1)
siblingFirstRune, _ := utf8.DecodeRune(siblingText)
if r.EastAsianLineBreaks.softLineBreak(thisLastRune, siblingFirstRune) {
_ = w.WriteByte('\n')
}
}
}
} else {
_ = w.WriteByte('\n')
}
}
}
return ast.WalkContinue, nil
}
func (r *Renderer) renderString(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
return ast.WalkContinue, nil
}
n := node.(*ast.String)
if n.IsCode() {
_, _ = w.Write(n.Value)
} else {
if n.IsRaw() {
r.Writer.RawWrite(w, n.Value)
} else {
r.Writer.Write(w, n.Value)
}
}
return ast.WalkContinue, nil
}
func (r *Renderer) renderTexts(w util.BufWriter, source []byte, n ast.Node) {
for c := n.FirstChild(); c != nil; c = c.NextSibling() {
if s, ok := c.(*ast.String); ok {
_, _ = r.renderString(w, source, s, true)
} else if t, ok := c.(*ast.Text); ok {
_, _ = r.renderText(w, source, t, true)
} else {
r.renderTexts(w, source, c)
}
}
}
var dataPrefix = []byte("data-")
// RenderAttributes renders given node's attributes.
// You can specify attribute names to render by the filter.
// If filter is nil, RenderAttributes renders all attributes.
func RenderAttributes(w util.BufWriter, node ast.Node, filter util.BytesFilter) {
for _, attr := range node.Attributes() {
if filter != nil && !filter.Contains(attr.Name) {
if !bytes.HasPrefix(attr.Name, dataPrefix) {
continue
}
}
_, _ = w.WriteString(" ")
_, _ = w.Write(attr.Name)
_, _ = w.WriteString(`="`)
// TODO: convert numeric values to strings
var value []byte
switch typed := attr.Value.(type) {
case []byte:
value = typed
case string:
value = util.StringToReadOnlyBytes(typed)
}
_, _ = w.Write(util.EscapeHTML(value))
_ = w.WriteByte('"')
}
}
// A Writer interface writes textual contents to a writer.
type Writer interface {
// Write writes the given source to writer with resolving references and unescaping
// backslash escaped characters.
Write(writer util.BufWriter, source []byte)
// RawWrite writes the given source to writer without resolving references and
// unescaping backslash escaped characters.
RawWrite(writer util.BufWriter, source []byte)
// SecureWrite writes the given source to writer with replacing insecure characters.
SecureWrite(writer util.BufWriter, source []byte)
}
var replacementCharacter = []byte("\ufffd")
// A WriterConfig struct has configurations for the HTML based writers.
type WriterConfig struct {
// EscapedSpace is an option that indicates that a '\' escaped half-space(0x20) should not be rendered.
EscapedSpace bool
}
// A WriterOption interface sets options for HTML based writers.
type WriterOption func(*WriterConfig)
// WithEscapedSpace is a WriterOption indicates that a '\' escaped half-space(0x20) should not be rendered.
func WithEscapedSpace() WriterOption {
return func(c *WriterConfig) {
c.EscapedSpace = true
}
}
type defaultWriter struct {
WriterConfig
}
// NewWriter returns a new Writer.
func NewWriter(opts ...WriterOption) Writer {
w := &defaultWriter{}
for _, opt := range opts {
opt(&w.WriterConfig)
}
return w
}
func escapeRune(writer util.BufWriter, r rune) {
if r < 256 {
v := util.EscapeHTMLByte(byte(r))
if v != nil {
_, _ = writer.Write(v)
return
}
}
_, _ = writer.WriteRune(util.ToValidRune(r))
}
func (d *defaultWriter) SecureWrite(writer util.BufWriter, source []byte) {
n := 0
l := len(source)
for i := 0; i < l; i++ {
if source[i] == '\u0000' {
_, _ = writer.Write(source[i-n : i])
n = 0
_, _ = writer.Write(replacementCharacter)
continue
}
n++
}
if n != 0 {
_, _ = writer.Write(source[l-n:])
}
}
func (d *defaultWriter) RawWrite(writer util.BufWriter, source []byte) {
n := 0
l := len(source)
for i := 0; i < l; i++ {
v := util.EscapeHTMLByte(source[i])
if v != nil {
_, _ = writer.Write(source[i-n : i])
n = 0
_, _ = writer.Write(v)
continue
}
n++
}
if n != 0 {
_, _ = writer.Write(source[l-n:])
}
}
func (d *defaultWriter) Write(writer util.BufWriter, source []byte) {
escaped := false
var ok bool
limit := len(source)
n := 0
for i := 0; i < limit; i++ {
c := source[i]
if escaped {
if util.IsPunct(c) {
d.RawWrite(writer, source[n:i-1])
n = i
escaped = false
continue
}
if d.EscapedSpace && c == ' ' {
d.RawWrite(writer, source[n:i-1])
n = i + 1
escaped = false
continue
}
}
if c == '\x00' {
d.RawWrite(writer, source[n:i])
d.RawWrite(writer, replacementCharacter)
n = i + 1
escaped = false
continue
}
if c == '&' {
pos := i
next := i + 1
if next < limit && source[next] == '#' {
nnext := next + 1
if nnext < limit {
nc := source[nnext]
// code point like #x22;
if nnext < limit && nc == 'x' || nc == 'X' {
start := nnext + 1
i, ok = util.ReadWhile(source, [2]int{start, limit}, util.IsHexDecimal)
if ok && i < limit && source[i] == ';' && i-start < 7 {
v, _ := strconv.ParseUint(util.BytesToReadOnlyString(source[start:i]), 16, 32)
d.RawWrite(writer, source[n:pos])
n = i + 1
escapeRune(writer, rune(v))
continue
}
// code point like #1234;
} else if nc >= '0' && nc <= '9' {
start := nnext
i, ok = util.ReadWhile(source, [2]int{start, limit}, util.IsNumeric)
if ok && i < limit && i-start < 8 && source[i] == ';' {
v, _ := strconv.ParseUint(util.BytesToReadOnlyString(source[start:i]), 10, 32)
d.RawWrite(writer, source[n:pos])
n = i + 1
escapeRune(writer, rune(v))
continue
}
}
}
} else {
start := next
i, ok = util.ReadWhile(source, [2]int{start, limit}, util.IsAlphaNumeric)
// entity reference
if ok && i < limit && source[i] == ';' {
name := util.BytesToReadOnlyString(source[start:i])
entity, ok := util.LookUpHTML5EntityByName(name)
if ok {
d.RawWrite(writer, source[n:pos])
n = i + 1
d.RawWrite(writer, entity.Characters)
continue
}
}
}
i = next - 1
}
if c == '\\' {
escaped = true
continue
}
escaped = false
}
d.RawWrite(writer, source[n:])
}
// DefaultWriter is a default instance of the Writer.
var DefaultWriter = NewWriter()
var bDataImage = []byte("data:image/")
var bPng = []byte("png;")
var bGif = []byte("gif;")
var bJpeg = []byte("jpeg;")
var bWebp = []byte("webp;")
var bSvg = []byte("svg+xml;")
var bJs = []byte("javascript:")
var bVb = []byte("vbscript:")
var bFile = []byte("file:")
var bData = []byte("data:")
func hasPrefix(s, prefix []byte) bool {
return len(s) >= len(prefix) && bytes.Equal(bytes.ToLower(s[0:len(prefix)]), bytes.ToLower(prefix))
}
// IsDangerousURL returns true if the given url seems a potentially dangerous url,
// otherwise false.
func IsDangerousURL(url []byte) bool {
if hasPrefix(url, bDataImage) && len(url) >= 11 {
v := url[11:]
if hasPrefix(v, bPng) || hasPrefix(v, bGif) ||
hasPrefix(v, bJpeg) || hasPrefix(v, bWebp) ||
hasPrefix(v, bSvg) {
return false
}
return true
}
return hasPrefix(url, bJs) || hasPrefix(url, bVb) ||
hasPrefix(url, bFile) || hasPrefix(url, bData)
}
yuin-goldmark-9a7f4c9/renderer/renderer.go 0000664 0000000 0000000 00000010773 15037761217 0020710 0 ustar 00root root 0000000 0000000 // Package renderer renders the given AST to certain formats.
package renderer
import (
"bufio"
"io"
"sync"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/util"
)
// A Config struct is a data structure that holds configuration of the Renderer.
type Config struct {
Options map[OptionName]interface{}
NodeRenderers util.PrioritizedSlice
}
// NewConfig returns a new Config.
func NewConfig() *Config {
return &Config{
Options: map[OptionName]interface{}{},
NodeRenderers: util.PrioritizedSlice{},
}
}
// An OptionName is a name of the option.
type OptionName string
// An Option interface is a functional option type for the Renderer.
type Option interface {
SetConfig(*Config)
}
type withNodeRenderers struct {
value []util.PrioritizedValue
}
func (o *withNodeRenderers) SetConfig(c *Config) {
c.NodeRenderers = append(c.NodeRenderers, o.value...)
}
// WithNodeRenderers is a functional option that allow you to add
// NodeRenderers to the renderer.
func WithNodeRenderers(ps ...util.PrioritizedValue) Option {
return &withNodeRenderers{ps}
}
type withOption struct {
name OptionName
value interface{}
}
func (o *withOption) SetConfig(c *Config) {
c.Options[o.name] = o.value
}
// WithOption is a functional option that allow you to set
// an arbitrary option to the parser.
func WithOption(name OptionName, value interface{}) Option {
return &withOption{name, value}
}
// A SetOptioner interface sets given option to the object.
type SetOptioner interface {
// SetOption sets given option to the object.
// Unacceptable options may be passed.
// Thus implementations must ignore unacceptable options.
SetOption(name OptionName, value interface{})
}
// NodeRendererFunc is a function that renders a given node.
type NodeRendererFunc func(writer util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error)
// A NodeRenderer interface offers NodeRendererFuncs.
type NodeRenderer interface {
// RendererFuncs registers NodeRendererFuncs to given NodeRendererFuncRegisterer.
RegisterFuncs(NodeRendererFuncRegisterer)
}
// A NodeRendererFuncRegisterer registers given NodeRendererFunc to this object.
type NodeRendererFuncRegisterer interface {
// Register registers given NodeRendererFunc to this object.
Register(ast.NodeKind, NodeRendererFunc)
}
// A Renderer interface renders given AST node to given
// writer with given Renderer.
type Renderer interface {
Render(w io.Writer, source []byte, n ast.Node) error
// AddOptions adds given option to this renderer.
AddOptions(...Option)
}
type renderer struct {
config *Config
options map[OptionName]interface{}
nodeRendererFuncsTmp map[ast.NodeKind]NodeRendererFunc
maxKind int
nodeRendererFuncs []NodeRendererFunc
initSync sync.Once
}
// NewRenderer returns a new Renderer with given options.
func NewRenderer(options ...Option) Renderer {
config := NewConfig()
for _, opt := range options {
opt.SetConfig(config)
}
r := &renderer{
options: map[OptionName]interface{}{},
config: config,
nodeRendererFuncsTmp: map[ast.NodeKind]NodeRendererFunc{},
}
return r
}
func (r *renderer) AddOptions(opts ...Option) {
for _, opt := range opts {
opt.SetConfig(r.config)
}
}
func (r *renderer) Register(kind ast.NodeKind, v NodeRendererFunc) {
r.nodeRendererFuncsTmp[kind] = v
if int(kind) > r.maxKind {
r.maxKind = int(kind)
}
}
// Render renders the given AST node to the given writer with the given Renderer.
func (r *renderer) Render(w io.Writer, source []byte, n ast.Node) error {
r.initSync.Do(func() {
r.options = r.config.Options
r.config.NodeRenderers.Sort()
l := len(r.config.NodeRenderers)
for i := l - 1; i >= 0; i-- {
v := r.config.NodeRenderers[i]
nr, _ := v.Value.(NodeRenderer)
if se, ok := v.Value.(SetOptioner); ok {
for oname, ovalue := range r.options {
se.SetOption(oname, ovalue)
}
}
nr.RegisterFuncs(r)
}
r.nodeRendererFuncs = make([]NodeRendererFunc, r.maxKind+1)
for kind, nr := range r.nodeRendererFuncsTmp {
r.nodeRendererFuncs[kind] = nr
}
r.config = nil
r.nodeRendererFuncsTmp = nil
})
writer, ok := w.(util.BufWriter)
if !ok {
writer = bufio.NewWriter(w)
}
err := ast.Walk(n, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
s := ast.WalkStatus(ast.WalkContinue)
var err error
f := r.nodeRendererFuncs[n.Kind()]
if f != nil {
s, err = f(writer, source, n, entering)
}
return s, err
})
if err != nil {
return err
}
return writer.Flush()
}
yuin-goldmark-9a7f4c9/testutil/ 0000775 0000000 0000000 00000000000 15037761217 0016612 5 ustar 00root root 0000000 0000000 yuin-goldmark-9a7f4c9/testutil/testutil.go 0000664 0000000 0000000 00000021407 15037761217 0021022 0 ustar 00root root 0000000 0000000 // Package testutil provides utilities for unit tests.
package testutil
import (
"bufio"
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"regexp"
"runtime/debug"
"strconv"
"strings"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/util"
)
// TestingT is a subset of the functionality provided by testing.T.
type TestingT interface {
Logf(string, ...interface{})
Skipf(string, ...interface{})
Errorf(string, ...interface{})
FailNow()
}
// MarkdownTestCase represents a test case.
type MarkdownTestCase struct {
No int
Description string
Options MarkdownTestCaseOptions
Markdown string
Expected string
}
func source(t *MarkdownTestCase) string {
ret := t.Markdown
if t.Options.Trim {
ret = strings.TrimSpace(ret)
}
if t.Options.EnableEscape {
return string(applyEscapeSequence([]byte(ret)))
}
return ret
}
func expected(t *MarkdownTestCase) string {
ret := t.Expected
if t.Options.Trim {
ret = strings.TrimSpace(ret)
}
if t.Options.EnableEscape {
return string(applyEscapeSequence([]byte(ret)))
}
return ret
}
// MarkdownTestCaseOptions represents options for each test case.
type MarkdownTestCaseOptions struct {
EnableEscape bool
Trim bool
}
const attributeSeparator = "//- - - - - - - - -//"
const caseSeparator = "//= = = = = = = = = = = = = = = = = = = = = = = =//"
var optionsRegexp = regexp.MustCompile(`(?i)\s*options:(.*)`)
// ParseCliCaseArg parses -case command line args.
func ParseCliCaseArg() []int {
ret := []int{}
for _, a := range os.Args {
if strings.HasPrefix(a, "case=") {
parts := strings.Split(a, "=")
for _, cas := range strings.Split(parts[1], ",") {
value, err := strconv.Atoi(strings.TrimSpace(cas))
if err == nil {
ret = append(ret, value)
}
}
}
}
return ret
}
// DoTestCaseFile runs test cases in a given file.
func DoTestCaseFile(m goldmark.Markdown, filename string, t TestingT, no ...int) {
fp, err := os.Open(filename)
if err != nil {
panic(err)
}
defer func() {
_ = fp.Close()
}()
scanner := bufio.NewScanner(fp)
c := MarkdownTestCase{
No: -1,
Description: "",
Options: MarkdownTestCaseOptions{},
Markdown: "",
Expected: "",
}
cases := []MarkdownTestCase{}
line := 0
for scanner.Scan() {
line++
if util.IsBlank([]byte(scanner.Text())) {
continue
}
header := scanner.Text()
c.Description = ""
if strings.Contains(header, ":") {
parts := strings.Split(header, ":")
c.No, err = strconv.Atoi(strings.TrimSpace(parts[0]))
c.Description = strings.Join(parts[1:], ":")
} else {
c.No, err = strconv.Atoi(scanner.Text())
}
if err != nil {
panic(fmt.Sprintf("%s: invalid case No at line %d", filename, line))
}
if !scanner.Scan() {
panic(fmt.Sprintf("%s: invalid case at line %d", filename, line))
}
line++
matches := optionsRegexp.FindAllStringSubmatch(scanner.Text(), -1)
if len(matches) != 0 {
err = json.Unmarshal([]byte(matches[0][1]), &c.Options)
if err != nil {
panic(fmt.Sprintf("%s: invalid options at line %d", filename, line))
}
scanner.Scan()
line++
}
if scanner.Text() != attributeSeparator {
panic(fmt.Sprintf("%s: invalid separator '%s' at line %d", filename, scanner.Text(), line))
}
buf := []string{}
for scanner.Scan() {
line++
text := scanner.Text()
if text == attributeSeparator {
break
}
buf = append(buf, text)
}
c.Markdown = strings.Join(buf, "\n")
buf = []string{}
for scanner.Scan() {
line++
text := scanner.Text()
if text == caseSeparator {
break
}
buf = append(buf, text)
}
c.Expected = strings.Join(buf, "\n")
if len(c.Expected) != 0 {
c.Expected = c.Expected + "\n"
}
shouldAdd := len(no) == 0
if !shouldAdd {
for _, n := range no {
if n == c.No {
shouldAdd = true
break
}
}
}
if shouldAdd {
cases = append(cases, c)
}
}
DoTestCases(m, cases, t)
}
// DoTestCases runs a set of test cases.
func DoTestCases(m goldmark.Markdown, cases []MarkdownTestCase, t TestingT, opts ...parser.ParseOption) {
for _, testCase := range cases {
DoTestCase(m, testCase, t, opts...)
}
}
// DoTestCase runs a test case.
func DoTestCase(m goldmark.Markdown, testCase MarkdownTestCase, t TestingT, opts ...parser.ParseOption) {
var ok bool
var out bytes.Buffer
defer func() {
description := ""
if len(testCase.Description) != 0 {
description = ": " + testCase.Description
}
if err := recover(); err != nil {
format := `============= case %d%s ================
Markdown:
-----------
%s
Expected:
----------
%s
Actual
---------
%v
%s
`
t.Errorf(format, testCase.No, description, source(&testCase), expected(&testCase), err, debug.Stack())
} else if !ok {
format := `============= case %d%s ================
Markdown:
-----------
%s
Expected:
----------
%s
Actual
---------
%s
Diff
---------
%s
`
t.Errorf(format, testCase.No, description, source(&testCase), expected(&testCase), out.Bytes(),
DiffPretty([]byte(expected(&testCase)), out.Bytes()))
}
}()
if err := m.Convert([]byte(source(&testCase)), &out, opts...); err != nil {
panic(err)
}
ok = bytes.Equal(bytes.TrimSpace(out.Bytes()), bytes.TrimSpace([]byte(expected(&testCase))))
}
type diffType int
const (
diffRemoved diffType = iota
diffAdded
diffNone
)
type diff struct {
Type diffType
Lines [][]byte
}
func simpleDiff(v1, v2 []byte) []diff {
return simpleDiffAux(
bytes.Split(v1, []byte("\n")),
bytes.Split(v2, []byte("\n")))
}
func simpleDiffAux(v1lines, v2lines [][]byte) []diff {
v1index := map[string][]int{}
for i, line := range v1lines {
key := util.BytesToReadOnlyString(line)
if _, ok := v1index[key]; !ok {
v1index[key] = []int{}
}
v1index[key] = append(v1index[key], i)
}
overlap := map[int]int{}
v1start := 0
v2start := 0
length := 0
for v2pos, line := range v2lines {
newOverlap := map[int]int{}
key := util.BytesToReadOnlyString(line)
if _, ok := v1index[key]; !ok {
v1index[key] = []int{}
}
for _, v1pos := range v1index[key] {
value := 0
if v1pos != 0 {
if v, ok := overlap[v1pos-1]; ok {
value = v
}
}
newOverlap[v1pos] = value + 1
if newOverlap[v1pos] > length {
length = newOverlap[v1pos]
v1start = v1pos - length + 1
v2start = v2pos - length + 1
}
}
overlap = newOverlap
}
if length == 0 {
diffs := []diff{}
if len(v1lines) != 0 {
diffs = append(diffs, diff{diffRemoved, v1lines})
}
if len(v2lines) != 0 {
diffs = append(diffs, diff{diffAdded, v2lines})
}
return diffs
}
diffs := simpleDiffAux(v1lines[:v1start], v2lines[:v2start])
diffs = append(diffs, diff{diffNone, v2lines[v2start : v2start+length]})
diffs = append(diffs, simpleDiffAux(v1lines[v1start+length:],
v2lines[v2start+length:])...)
return diffs
}
// DiffPretty returns pretty formatted diff between given bytes.
func DiffPretty(v1, v2 []byte) []byte {
var b bytes.Buffer
diffs := simpleDiff(v1, v2)
for _, diff := range diffs {
c := " "
switch diff.Type {
case diffAdded:
c = "+"
case diffRemoved:
c = "-"
case diffNone:
c = " "
}
for _, line := range diff.Lines {
if c != " " {
b.WriteString(fmt.Sprintf("%s | %s\n", c, util.VisualizeSpaces(line)))
} else {
b.WriteString(fmt.Sprintf("%s | %s\n", c, line))
}
}
}
return b.Bytes()
}
func applyEscapeSequence(b []byte) []byte {
result := make([]byte, 0, len(b))
for i := 0; i < len(b); i++ {
if b[i] == '\\' && i != len(b)-1 {
switch b[i+1] {
case 'a':
result = append(result, '\a')
i++
continue
case 'b':
result = append(result, '\b')
i++
continue
case 'f':
result = append(result, '\f')
i++
continue
case 'n':
result = append(result, '\n')
i++
continue
case 'r':
result = append(result, '\r')
i++
continue
case 't':
result = append(result, '\t')
i++
continue
case 'v':
result = append(result, '\v')
i++
continue
case '\\':
result = append(result, '\\')
i++
continue
case 'x':
if len(b) >= i+3 && util.IsHexDecimal(b[i+2]) && util.IsHexDecimal(b[i+3]) {
v, _ := hex.DecodeString(string(b[i+2 : i+4]))
result = append(result, v[0])
i += 3
continue
}
case 'u', 'U':
if len(b) > i+2 {
num := []byte{}
for j := i + 2; j < len(b); j++ {
if util.IsHexDecimal(b[j]) {
num = append(num, b[j])
continue
}
break
}
if len(num) >= 4 && len(num) < 8 {
v, _ := strconv.ParseInt(string(num[:4]), 16, 32)
result = append(result, []byte(string(rune(v)))...)
i += 5
continue
}
if len(num) >= 8 {
v, _ := strconv.ParseInt(string(num[:8]), 16, 32)
result = append(result, []byte(string(rune(v)))...)
i += 9
continue
}
}
}
}
result = append(result, b[i])
}
return result
}
yuin-goldmark-9a7f4c9/testutil/testutil_test.go 0000664 0000000 0000000 00000000270 15037761217 0022054 0 ustar 00root root 0000000 0000000 package testutil
import "testing"
// This will fail to compile if the TestingT interface is changed in a way
// that doesn't conform to testing.T.
var _ TestingT = (*testing.T)(nil)
yuin-goldmark-9a7f4c9/text/ 0000775 0000000 0000000 00000000000 15037761217 0015721 5 ustar 00root root 0000000 0000000 yuin-goldmark-9a7f4c9/text/package.go 0000664 0000000 0000000 00000000113 15037761217 0017636 0 ustar 00root root 0000000 0000000 // Package text provides functionalities to manipulate texts.
package text
yuin-goldmark-9a7f4c9/text/reader.go 0000664 0000000 0000000 00000034277 15037761217 0017527 0 ustar 00root root 0000000 0000000 package text
import (
"bytes"
"io"
"regexp"
"unicode/utf8"
"github.com/yuin/goldmark/util"
)
const invalidValue = -1
// EOF indicates the end of file.
const EOF = byte(0xff)
// A Reader interface provides abstracted method for reading text.
type Reader interface {
io.RuneReader
// Source returns a source of the reader.
Source() []byte
// ResetPosition resets positions.
ResetPosition()
// Peek returns a byte at current position without advancing the internal pointer.
Peek() byte
// PeekLine returns the current line without advancing the internal pointer.
PeekLine() ([]byte, Segment)
// PrecendingCharacter returns a character just before current internal pointer.
PrecendingCharacter() rune
// Value returns a value of the given segment.
Value(Segment) []byte
// LineOffset returns a distance from the line head to current position.
LineOffset() int
// Position returns current line number and position.
Position() (int, Segment)
// SetPosition sets current line number and position.
SetPosition(int, Segment)
// SetPadding sets padding to the reader.
SetPadding(int)
// Advance advances the internal pointer.
Advance(int)
// AdvanceAndSetPadding advances the internal pointer and add padding to the
// reader.
AdvanceAndSetPadding(int, int)
// AdvanceToEOL advances the internal pointer to the end of line.
// If the line ends with a newline, it will be included in the segment.
// If the line ends with EOF, it will not be included in the segment.
AdvanceToEOL()
// AdvanceLine advances the internal pointer to the next line head.
AdvanceLine()
// SkipSpaces skips space characters and returns a non-blank line.
// If it reaches EOF, returns false.
SkipSpaces() (Segment, int, bool)
// SkipSpaces skips blank lines and returns a non-blank line.
// If it reaches EOF, returns false.
SkipBlankLines() (Segment, int, bool)
// Match performs regular expression matching to current line.
Match(reg *regexp.Regexp) bool
// Match performs regular expression searching to current line.
FindSubMatch(reg *regexp.Regexp) [][]byte
// FindClosure finds corresponding closure.
FindClosure(opener, closer byte, options FindClosureOptions) (*Segments, bool)
}
// FindClosureOptions is options for Reader.FindClosure.
type FindClosureOptions struct {
// CodeSpan is a flag for the FindClosure. If this is set to true,
// FindClosure ignores closers in codespans.
CodeSpan bool
// Nesting is a flag for the FindClosure. If this is set to true,
// FindClosure allows nesting.
Nesting bool
// Newline is a flag for the FindClosure. If this is set to true,
// FindClosure searches for a closer over multiple lines.
Newline bool
// Advance is a flag for the FindClosure. If this is set to true,
// FindClosure advances pointers when closer is found.
Advance bool
}
type reader struct {
source []byte
sourceLength int
line int
peekedLine []byte
pos Segment
head int
lineOffset int
}
// NewReader return a new Reader that can read UTF-8 bytes .
func NewReader(source []byte) Reader {
r := &reader{
source: source,
sourceLength: len(source),
}
r.ResetPosition()
return r
}
func (r *reader) FindClosure(opener, closer byte, options FindClosureOptions) (*Segments, bool) {
return findClosureReader(r, opener, closer, options)
}
func (r *reader) ResetPosition() {
r.line = -1
r.head = 0
r.lineOffset = -1
r.AdvanceLine()
}
func (r *reader) Source() []byte {
return r.source
}
func (r *reader) Value(seg Segment) []byte {
return seg.Value(r.source)
}
func (r *reader) Peek() byte {
if r.pos.Start >= 0 && r.pos.Start < r.sourceLength {
if r.pos.Padding != 0 {
return space[0]
}
return r.source[r.pos.Start]
}
return EOF
}
func (r *reader) PeekLine() ([]byte, Segment) {
if r.pos.Start >= 0 && r.pos.Start < r.sourceLength {
if r.peekedLine == nil {
r.peekedLine = r.pos.Value(r.Source())
}
return r.peekedLine, r.pos
}
return nil, r.pos
}
// io.RuneReader interface.
func (r *reader) ReadRune() (rune, int, error) {
return readRuneReader(r)
}
func (r *reader) LineOffset() int {
if r.lineOffset < 0 {
v := 0
for i := r.head; i < r.pos.Start; i++ {
if r.source[i] == '\t' {
v += util.TabWidth(v)
} else {
v++
}
}
r.lineOffset = v - r.pos.Padding
}
return r.lineOffset
}
func (r *reader) PrecendingCharacter() rune {
if r.pos.Start <= 0 {
if r.pos.Padding != 0 {
return rune(' ')
}
return rune('\n')
}
i := r.pos.Start - 1
for ; i >= 0; i-- {
if utf8.RuneStart(r.source[i]) {
break
}
}
rn, _ := utf8.DecodeRune(r.source[i:])
return rn
}
func (r *reader) Advance(n int) {
r.lineOffset = -1
if n < len(r.peekedLine) && r.pos.Padding == 0 {
r.pos.Start += n
r.peekedLine = nil
return
}
r.peekedLine = nil
l := r.sourceLength
for ; n > 0 && r.pos.Start < l; n-- {
if r.pos.Padding != 0 {
r.pos.Padding--
continue
}
if r.source[r.pos.Start] == '\n' {
r.AdvanceLine()
continue
}
r.pos.Start++
}
}
func (r *reader) AdvanceAndSetPadding(n, padding int) {
r.Advance(n)
if padding > r.pos.Padding {
r.SetPadding(padding)
}
}
func (r *reader) AdvanceToEOL() {
if r.pos.Start >= r.sourceLength {
return
}
r.lineOffset = -1
i := -1
if r.peekedLine != nil {
r.pos.Start += len(r.peekedLine) - r.pos.Padding - 1
if r.source[r.pos.Start] == '\n' {
i = 0
}
}
if i == -1 {
i = bytes.IndexByte(r.source[r.pos.Start:], '\n')
}
r.peekedLine = nil
if i != -1 {
r.pos.Start += i
} else {
r.pos.Start = r.sourceLength
}
r.pos.Padding = 0
}
func (r *reader) AdvanceLine() {
r.lineOffset = -1
r.peekedLine = nil
r.pos.Start = r.pos.Stop
r.head = r.pos.Start
if r.pos.Start < 0 || r.pos.Start >= r.sourceLength {
return
}
r.pos.Stop = r.sourceLength
i := 0
if r.source[r.pos.Start] != '\n' {
i = bytes.IndexByte(r.source[r.pos.Start:], '\n')
}
if i != -1 {
r.pos.Stop = r.pos.Start + i + 1
}
r.line++
r.pos.Padding = 0
}
func (r *reader) Position() (int, Segment) {
return r.line, r.pos
}
func (r *reader) SetPosition(line int, pos Segment) {
r.lineOffset = -1
r.line = line
r.pos = pos
}
func (r *reader) SetPadding(v int) {
r.pos.Padding = v
}
func (r *reader) SkipSpaces() (Segment, int, bool) {
return skipSpacesReader(r)
}
func (r *reader) SkipBlankLines() (Segment, int, bool) {
return skipBlankLinesReader(r)
}
func (r *reader) Match(reg *regexp.Regexp) bool {
return matchReader(r, reg)
}
func (r *reader) FindSubMatch(reg *regexp.Regexp) [][]byte {
return findSubMatchReader(r, reg)
}
// A BlockReader interface is a reader that is optimized for Blocks.
type BlockReader interface {
Reader
// Reset resets current state and sets new segments to the reader.
Reset(segment *Segments)
}
type blockReader struct {
source []byte
segments *Segments
segmentsLength int
line int
pos Segment
head int
last int
lineOffset int
}
// NewBlockReader returns a new BlockReader.
func NewBlockReader(source []byte, segments *Segments) BlockReader {
r := &blockReader{
source: source,
}
if segments != nil {
r.Reset(segments)
}
return r
}
func (r *blockReader) FindClosure(opener, closer byte, options FindClosureOptions) (*Segments, bool) {
return findClosureReader(r, opener, closer, options)
}
func (r *blockReader) ResetPosition() {
r.line = -1
r.head = 0
r.last = 0
r.lineOffset = -1
r.pos.Start = -1
r.pos.Stop = -1
r.pos.Padding = 0
if r.segmentsLength > 0 {
last := r.segments.At(r.segmentsLength - 1)
r.last = last.Stop
}
r.AdvanceLine()
}
func (r *blockReader) Reset(segments *Segments) {
r.segments = segments
r.segmentsLength = segments.Len()
r.ResetPosition()
}
func (r *blockReader) Source() []byte {
return r.source
}
func (r *blockReader) Value(seg Segment) []byte {
line := r.segmentsLength - 1
ret := make([]byte, 0, seg.Stop-seg.Start+1)
for ; line >= 0; line-- {
if seg.Start >= r.segments.At(line).Start {
break
}
}
i := seg.Start
for ; line < r.segmentsLength; line++ {
s := r.segments.At(line)
if i < 0 {
i = s.Start
}
ret = s.ConcatPadding(ret)
for ; i < seg.Stop && i < s.Stop; i++ {
ret = append(ret, r.source[i])
}
i = -1
if s.Stop > seg.Stop {
break
}
}
return ret
}
// io.RuneReader interface.
func (r *blockReader) ReadRune() (rune, int, error) {
return readRuneReader(r)
}
func (r *blockReader) PrecendingCharacter() rune {
if r.pos.Padding != 0 {
return rune(' ')
}
if r.segments.Len() < 1 {
return rune('\n')
}
firstSegment := r.segments.At(0)
if r.line == 0 && r.pos.Start <= firstSegment.Start {
return rune('\n')
}
l := len(r.source)
i := r.pos.Start - 1
for ; i < l && i >= 0; i-- {
if utf8.RuneStart(r.source[i]) {
break
}
}
if i < 0 || i >= l {
return rune('\n')
}
rn, _ := utf8.DecodeRune(r.source[i:])
return rn
}
func (r *blockReader) LineOffset() int {
if r.lineOffset < 0 {
v := 0
for i := r.head; i < r.pos.Start; i++ {
if r.source[i] == '\t' {
v += util.TabWidth(v)
} else {
v++
}
}
r.lineOffset = v - r.pos.Padding
}
return r.lineOffset
}
func (r *blockReader) Peek() byte {
if r.line < r.segmentsLength && r.pos.Start >= 0 && r.pos.Start < r.last {
if r.pos.Padding != 0 {
return space[0]
}
return r.source[r.pos.Start]
}
return EOF
}
func (r *blockReader) PeekLine() ([]byte, Segment) {
if r.line < r.segmentsLength && r.pos.Start >= 0 && r.pos.Start < r.last {
return r.pos.Value(r.source), r.pos
}
return nil, r.pos
}
func (r *blockReader) Advance(n int) {
r.lineOffset = -1
if n < r.pos.Stop-r.pos.Start && r.pos.Padding == 0 {
r.pos.Start += n
return
}
for ; n > 0; n-- {
if r.pos.Padding != 0 {
r.pos.Padding--
continue
}
if r.pos.Start >= r.pos.Stop-1 && r.pos.Stop < r.last {
r.AdvanceLine()
continue
}
r.pos.Start++
}
}
func (r *blockReader) AdvanceAndSetPadding(n, padding int) {
r.Advance(n)
if padding > r.pos.Padding {
r.SetPadding(padding)
}
}
func (r *blockReader) AdvanceToEOL() {
r.lineOffset = -1
r.pos.Padding = 0
c := r.source[r.pos.Stop-1]
if c == '\n' {
r.pos.Start = r.pos.Stop - 1
} else {
r.pos.Start = r.pos.Stop
}
}
func (r *blockReader) AdvanceLine() {
r.SetPosition(r.line+1, NewSegment(invalidValue, invalidValue))
r.head = r.pos.Start
}
func (r *blockReader) Position() (int, Segment) {
return r.line, r.pos
}
func (r *blockReader) SetPosition(line int, pos Segment) {
r.lineOffset = -1
r.line = line
if pos.Start == invalidValue {
if r.line < r.segmentsLength {
s := r.segments.At(line)
r.head = s.Start
r.pos = s
}
} else {
r.pos = pos
if r.line < r.segmentsLength {
s := r.segments.At(line)
r.head = s.Start
}
}
}
func (r *blockReader) SetPadding(v int) {
r.lineOffset = -1
r.pos.Padding = v
}
func (r *blockReader) SkipSpaces() (Segment, int, bool) {
return skipSpacesReader(r)
}
func (r *blockReader) SkipBlankLines() (Segment, int, bool) {
return skipBlankLinesReader(r)
}
func (r *blockReader) Match(reg *regexp.Regexp) bool {
return matchReader(r, reg)
}
func (r *blockReader) FindSubMatch(reg *regexp.Regexp) [][]byte {
return findSubMatchReader(r, reg)
}
func skipBlankLinesReader(r Reader) (Segment, int, bool) {
lines := 0
for {
line, seg := r.PeekLine()
if line == nil {
return seg, lines, false
}
if util.IsBlank(line) {
lines++
r.AdvanceLine()
} else {
return seg, lines, true
}
}
}
func skipSpacesReader(r Reader) (Segment, int, bool) {
chars := 0
for {
line, segment := r.PeekLine()
if line == nil {
return segment, chars, false
}
for i, c := range line {
if util.IsSpace(c) {
chars++
r.Advance(1)
continue
}
return segment.WithStart(segment.Start + i + 1), chars, true
}
}
}
func matchReader(r Reader, reg *regexp.Regexp) bool {
oldline, oldseg := r.Position()
match := reg.FindReaderSubmatchIndex(r)
r.SetPosition(oldline, oldseg)
if match == nil {
return false
}
r.Advance(match[1] - match[0])
return true
}
func findSubMatchReader(r Reader, reg *regexp.Regexp) [][]byte {
oldLine, oldSeg := r.Position()
match := reg.FindReaderSubmatchIndex(r)
r.SetPosition(oldLine, oldSeg)
if match == nil {
return nil
}
var bb bytes.Buffer
bb.Grow(match[1] - match[0])
for i := 0; i < match[1]; {
r, size, _ := readRuneReader(r)
i += size
bb.WriteRune(r)
}
bs := bb.Bytes()
var result [][]byte
for i := 0; i < len(match); i += 2 {
if match[i] < 0 {
result = append(result, []byte{})
continue
}
result = append(result, bs[match[i]:match[i+1]])
}
r.SetPosition(oldLine, oldSeg)
r.Advance(match[1] - match[0])
return result
}
func readRuneReader(r Reader) (rune, int, error) {
line, _ := r.PeekLine()
if line == nil {
return 0, 0, io.EOF
}
rn, size := utf8.DecodeRune(line)
if rn == utf8.RuneError {
return 0, 0, io.EOF
}
r.Advance(size)
return rn, size, nil
}
func findClosureReader(r Reader, opener, closer byte, opts FindClosureOptions) (*Segments, bool) {
opened := 1
codeSpanOpener := 0
closed := false
orgline, orgpos := r.Position()
var ret *Segments
for {
bs, seg := r.PeekLine()
if bs == nil {
goto end
}
i := 0
for i < len(bs) {
c := bs[i]
if opts.CodeSpan && codeSpanOpener != 0 && c == '`' {
codeSpanCloser := 0
for ; i < len(bs); i++ {
if bs[i] == '`' {
codeSpanCloser++
} else {
i--
break
}
}
if codeSpanCloser == codeSpanOpener {
codeSpanOpener = 0
}
} else if codeSpanOpener == 0 && c == '\\' && i < len(bs)-1 && util.IsPunct(bs[i+1]) {
i += 2
continue
} else if opts.CodeSpan && codeSpanOpener == 0 && c == '`' {
for ; i < len(bs); i++ {
if bs[i] == '`' {
codeSpanOpener++
} else {
i--
break
}
}
} else if (opts.CodeSpan && codeSpanOpener == 0) || !opts.CodeSpan {
if c == closer {
opened--
if opened == 0 {
if ret == nil {
ret = NewSegments()
}
ret.Append(seg.WithStop(seg.Start + i))
r.Advance(i + 1)
closed = true
goto end
}
} else if c == opener {
if !opts.Nesting {
goto end
}
opened++
}
}
i++
}
if !opts.Newline {
goto end
}
r.AdvanceLine()
if ret == nil {
ret = NewSegments()
}
ret.Append(seg)
}
end:
if !opts.Advance {
r.SetPosition(orgline, orgpos)
}
if closed {
return ret, true
}
return nil, false
}
yuin-goldmark-9a7f4c9/text/reader_test.go 0000664 0000000 0000000 00000000446 15037761217 0020555 0 ustar 00root root 0000000 0000000 package text
import (
"regexp"
"testing"
)
func TestFindSubMatchReader(t *testing.T) {
s := "微笑"
r := NewReader([]byte(":" + s + ":"))
reg := regexp.MustCompile(`:(\p{L}+):`)
match := r.FindSubMatch(reg)
if len(match) != 2 || string(match[1]) != s {
t.Fatal("no match cjk")
}
}
yuin-goldmark-9a7f4c9/text/segment.go 0000664 0000000 0000000 00000013012 15037761217 0017707 0 ustar 00root root 0000000 0000000 package text
import (
"bytes"
"github.com/yuin/goldmark/util"
)
var space = []byte(" ")
// A Segment struct holds information about source positions.
type Segment struct {
// Start is a start position of the segment.
Start int
// Stop is a stop position of the segment.
// This value should be excluded.
Stop int
// Padding is a padding length of the segment.
Padding int
// ForceNewline is true if the segment should be ended with a newline.
// Some elements(i.e. CodeBlock, FencedCodeBlock) does not trim trailing
// newlines. Spec defines that EOF is treated as a newline, so we need to
// add a newline to the end of the segment if it is not empty.
//
// i.e.:
//
// ```go
// const test = "test"
//
// This code does not close the code block and ends with EOF. In this case,
// we need to add a newline to the end of the last line like `const test = "test"\n`.
ForceNewline bool
}
// NewSegment return a new Segment.
func NewSegment(start, stop int) Segment {
return Segment{
Start: start,
Stop: stop,
Padding: 0,
}
}
// NewSegmentPadding returns a new Segment with the given padding.
func NewSegmentPadding(start, stop, n int) Segment {
return Segment{
Start: start,
Stop: stop,
Padding: n,
}
}
// Value returns a value of the segment.
func (t *Segment) Value(buffer []byte) []byte {
var result []byte
if t.Padding == 0 {
result = buffer[t.Start:t.Stop]
} else {
result = make([]byte, 0, t.Padding+t.Stop-t.Start+1)
result = append(result, bytes.Repeat(space, t.Padding)...)
result = append(result, buffer[t.Start:t.Stop]...)
}
if t.ForceNewline && len(result) > 0 && result[len(result)-1] != '\n' {
result = append(result, '\n')
}
return result
}
// Len returns a length of the segment.
func (t *Segment) Len() int {
return t.Stop - t.Start + t.Padding
}
// Between returns a segment between this segment and the given segment.
func (t *Segment) Between(other Segment) Segment {
if t.Stop != other.Stop {
panic("invalid state")
}
return NewSegmentPadding(
t.Start,
other.Start,
t.Padding-other.Padding,
)
}
// IsEmpty returns true if this segment is empty, otherwise false.
func (t *Segment) IsEmpty() bool {
return t.Start >= t.Stop && t.Padding == 0
}
// TrimRightSpace returns a new segment by slicing off all trailing
// space characters.
func (t *Segment) TrimRightSpace(buffer []byte) Segment {
v := buffer[t.Start:t.Stop]
l := util.TrimRightSpaceLength(v)
if l == len(v) {
return NewSegment(t.Start, t.Start)
}
return NewSegmentPadding(t.Start, t.Stop-l, t.Padding)
}
// TrimLeftSpace returns a new segment by slicing off all leading
// space characters including padding.
func (t *Segment) TrimLeftSpace(buffer []byte) Segment {
v := buffer[t.Start:t.Stop]
l := util.TrimLeftSpaceLength(v)
return NewSegment(t.Start+l, t.Stop)
}
// TrimLeftSpaceWidth returns a new segment by slicing off leading space
// characters until the given width.
func (t *Segment) TrimLeftSpaceWidth(width int, buffer []byte) Segment {
padding := t.Padding
for ; width > 0; width-- {
if padding == 0 {
break
}
padding--
}
if width == 0 {
return NewSegmentPadding(t.Start, t.Stop, padding)
}
text := buffer[t.Start:t.Stop]
start := t.Start
for _, c := range text {
if start >= t.Stop-1 || width <= 0 {
break
}
if c == ' ' {
width--
} else if c == '\t' {
width -= 4
} else {
break
}
start++
}
if width < 0 {
padding = width * -1
}
return NewSegmentPadding(start, t.Stop, padding)
}
// WithStart returns a new Segment with same value except Start.
func (t *Segment) WithStart(v int) Segment {
return NewSegmentPadding(v, t.Stop, t.Padding)
}
// WithStop returns a new Segment with same value except Stop.
func (t *Segment) WithStop(v int) Segment {
return NewSegmentPadding(t.Start, v, t.Padding)
}
// ConcatPadding concats the padding to the given slice.
func (t *Segment) ConcatPadding(v []byte) []byte {
if t.Padding > 0 {
return append(v, bytes.Repeat(space, t.Padding)...)
}
return v
}
// Segments is a collection of the Segment.
type Segments struct {
values []Segment
}
// NewSegments return a new Segments.
func NewSegments() *Segments {
return &Segments{
values: nil,
}
}
// Append appends the given segment after the tail of the collection.
func (s *Segments) Append(t Segment) {
s.values = append(s.values, t)
}
// AppendAll appends all elements of given segments after the tail of the collection.
func (s *Segments) AppendAll(t []Segment) {
s.values = append(s.values, t...)
}
// Len returns the length of the collection.
func (s *Segments) Len() int {
if s.values == nil {
return 0
}
return len(s.values)
}
// At returns a segment at the given index.
func (s *Segments) At(i int) Segment {
return s.values[i]
}
// Set sets the given Segment.
func (s *Segments) Set(i int, v Segment) {
s.values[i] = v
}
// SetSliced replace the collection with a subsliced value.
func (s *Segments) SetSliced(lo, hi int) {
s.values = s.values[lo:hi]
}
// Sliced returns a subslice of the collection.
func (s *Segments) Sliced(lo, hi int) []Segment {
return s.values[lo:hi]
}
// Clear delete all element of the collection.
func (s *Segments) Clear() {
s.values = nil
}
// Unshift insert the given Segment to head of the collection.
func (s *Segments) Unshift(v Segment) {
s.values = append(s.values[0:1], s.values[0:]...)
s.values[0] = v
}
// Value returns a string value of the collection.
func (s *Segments) Value(buffer []byte) []byte {
var result []byte
for _, v := range s.values {
result = append(result, v.Value(buffer)...)
}
return result
}
yuin-goldmark-9a7f4c9/util/ 0000775 0000000 0000000 00000000000 15037761217 0015712 5 ustar 00root root 0000000 0000000 yuin-goldmark-9a7f4c9/util/html5entities.gen.go 0000664 0000000 0000000 00000260576 15037761217 0021627 0 ustar 00root root 0000000 0000000 // Code generated by _tools; DO NOT EDIT.
package util
const _html5entitiesLength = 2124
const _html5entitiesName string = "AEligAMPAacuteAcircAcyAfrAgraveAlphaAmacrAndAogonAopfApplyFunctionAringAscrAssignAtildeAumlBackslashBarvBarwedBcyBecauseBernoullisBetaBfrBopfBreveBscrBumpeqCHcyCOPYCacuteCapCapitalDifferentialDCayleysCcaronCcedilCcircCconintCdotCedillaCenterDotCfrChiCircleDotCircleMinusCirclePlusCircleTimesClockwiseContourIntegralCloseCurlyDoubleQuoteCloseCurlyQuoteColonColoneCongruentConintContourIntegralCopfCoproductCounterClockwiseContourIntegralCrossCscrCupCupCapDDDDotrahdDJcyDScyDZcyDaggerDarrDashvDcaronDcyDelDeltaDfrDiacriticalAcuteDiacriticalDotDiacriticalDoubleAcuteDiacriticalGraveDiacriticalTildeDiamondDifferentialDDopfDotDotDotDotEqualDoubleContourIntegralDoubleDotDoubleDownArrowDoubleLeftArrowDoubleLeftRightArrowDoubleLeftTeeDoubleLongLeftArrowDoubleLongLeftRightArrowDoubleLongRightArrowDoubleRightArrowDoubleRightTeeDoubleUpArrowDoubleUpDownArrowDoubleVerticalBarDownArrowDownArrowBarDownArrowUpArrowDownBreveDownLeftRightVectorDownLeftTeeVectorDownLeftVectorDownLeftVectorBarDownRightTeeVectorDownRightVectorDownRightVectorBarDownTeeDownTeeArrowDownarrowDscrDstrokENGETHEacuteEcaronEcircEcyEdotEfrEgraveElementEmacrEmptySmallSquareEmptyVerySmallSquareEogonEopfEpsilonEqualEqualTildeEquilibriumEscrEsimEtaEumlExistsExponentialEFcyFfrFilledSmallSquareFilledVerySmallSquareFopfForAllFouriertrfFscrGJcyGTGammaGammadGbreveGcedilGcircGcyGdotGfrGgGopfGreaterEqualGreaterEqualLessGreaterFullEqualGreaterGreaterGreaterLessGreaterSlantEqualGreaterTildeGscrGtHARDcyHacekHatHcircHfrHilbertSpaceHopfHorizontalLineHscrHstrokHumpDownHumpHumpEqualIEcyIJligIOcyIacuteIcircIcyIdotIfrIgraveImImacrImaginaryIImpliesIntIntegralIntersectionInvisibleCommaInvisibleTimesIogonIopfIotaIscrItildeIukcyIumlJcircJcyJfrJopfJscrJsercyJukcyKHcyKJcyKappaKcedilKcyKfrKopfKscrLJcyLTLacuteLambdaLangLaplacetrfLarrLcaronLcedilLcyLeftAngleBracketLeftArrowLeftArrowBarLeftArrowRightArrowLeftCeilingLeftDoubleBracketLeftDownTeeVectorLeftDownVectorLeftDownVectorBarLeftFloorLeftRightArrowLeftRightVectorLeftTeeLeftTeeArrowLeftTeeVectorLeftTriangleLeftTriangleBarLeftTriangleEqualLeftUpDownVectorLeftUpTeeVectorLeftUpVectorLeftUpVectorBarLeftVectorLeftVectorBarLeftarrowLeftrightarrowLessEqualGreaterLessFullEqualLessGreaterLessLessLessSlantEqualLessTildeLfrLlLleftarrowLmidotLongLeftArrowLongLeftRightArrowLongRightArrowLongleftarrowLongleftrightarrowLongrightarrowLopfLowerLeftArrowLowerRightArrowLscrLshLstrokLtMapMcyMediumSpaceMellintrfMfrMinusPlusMopfMscrMuNJcyNacuteNcaronNcedilNcyNegativeMediumSpaceNegativeThickSpaceNegativeThinSpaceNegativeVeryThinSpaceNestedGreaterGreaterNestedLessLessNewLineNfrNoBreakNonBreakingSpaceNopfNotNotCongruentNotCupCapNotDoubleVerticalBarNotElementNotEqualNotEqualTildeNotExistsNotGreaterNotGreaterEqualNotGreaterFullEqualNotGreaterGreaterNotGreaterLessNotGreaterSlantEqualNotGreaterTildeNotHumpDownHumpNotHumpEqualNotLeftTriangleNotLeftTriangleBarNotLeftTriangleEqualNotLessNotLessEqualNotLessGreaterNotLessLessNotLessSlantEqualNotLessTildeNotNestedGreaterGreaterNotNestedLessLessNotPrecedesNotPrecedesEqualNotPrecedesSlantEqualNotReverseElementNotRightTriangleNotRightTriangleBarNotRightTriangleEqualNotSquareSubsetNotSquareSubsetEqualNotSquareSupersetNotSquareSupersetEqualNotSubsetNotSubsetEqualNotSucceedsNotSucceedsEqualNotSucceedsSlantEqualNotSucceedsTildeNotSupersetNotSupersetEqualNotTildeNotTildeEqualNotTildeFullEqualNotTildeTildeNotVerticalBarNscrNtildeNuOEligOacuteOcircOcyOdblacOfrOgraveOmacrOmegaOmicronOopfOpenCurlyDoubleQuoteOpenCurlyQuoteOrOscrOslashOtildeOtimesOumlOverBarOverBraceOverBracketOverParenthesisPartialDPcyPfrPhiPiPlusMinusPoincareplanePopfPrPrecedesPrecedesEqualPrecedesSlantEqualPrecedesTildePrimeProductProportionProportionalPscrPsiQUOTQfrQopfQscrRBarrREGRacuteRangRarrRarrtlRcaronRcedilRcyReReverseElementReverseEquilibriumReverseUpEquilibriumRfrRhoRightAngleBracketRightArrowRightArrowBarRightArrowLeftArrowRightCeilingRightDoubleBracketRightDownTeeVectorRightDownVectorRightDownVectorBarRightFloorRightTeeRightTeeArrowRightTeeVectorRightTriangleRightTriangleBarRightTriangleEqualRightUpDownVectorRightUpTeeVectorRightUpVectorRightUpVectorBarRightVectorRightVectorBarRightarrowRopfRoundImpliesRrightarrowRscrRshRuleDelayedSHCHcySHcySOFTcySacuteScScaronScedilScircScySfrShortDownArrowShortLeftArrowShortRightArrowShortUpArrowSigmaSmallCircleSopfSqrtSquareSquareIntersectionSquareSubsetSquareSubsetEqualSquareSupersetSquareSupersetEqualSquareUnionSscrStarSubSubsetSubsetEqualSucceedsSucceedsEqualSucceedsSlantEqualSucceedsTildeSuchThatSumSupSupersetSupersetEqualSupsetTHORNTRADETSHcyTScyTabTauTcaronTcedilTcyTfrThereforeThetaThickSpaceThinSpaceTildeTildeEqualTildeFullEqualTildeTildeTopfTripleDotTscrTstrokUacuteUarrUarrocirUbrcyUbreveUcircUcyUdblacUfrUgraveUmacrUnderBarUnderBraceUnderBracketUnderParenthesisUnionUnionPlusUogonUopfUpArrowUpArrowBarUpArrowDownArrowUpDownArrowUpEquilibriumUpTeeUpTeeArrowUparrowUpdownarrowUpperLeftArrowUpperRightArrowUpsiUpsilonUringUscrUtildeUumlVDashVbarVcyVdashVdashlVeeVerbarVertVerticalBarVerticalLineVerticalSeparatorVerticalTildeVeryThinSpaceVfrVopfVscrVvdashWcircWedgeWfrWopfWscrXfrXiXopfXscrYAcyYIcyYUcyYacuteYcircYcyYfrYopfYscrYumlZHcyZacuteZcaronZcyZdotZeroWidthSpaceZetaZfrZopfZscraacuteabreveacacEacdacircacuteacyaeligafafragravealefsymalephalphaamacramalgampandandandanddandslopeandvangangeangleangmsdangmsdaaangmsdabangmsdacangmsdadangmsdaeangmsdafangmsdagangmsdahangrtangrtvbangrtvbdangsphangstangzarraogonaopfapapEapacirapeapidaposapproxapproxeqaringascrastasympasympeqatildeaumlawconintawintbNotbackcongbackepsilonbackprimebacksimbacksimeqbarveebarwedbarwedgebbrkbbrktbrkbcongbcybdquobecausbecausebemptyvbepsibernoubetabethbetweenbfrbigcapbigcircbigcupbigodotbigoplusbigotimesbigsqcupbigstarbigtriangledownbigtriangleupbiguplusbigveebigwedgebkarowblacklozengeblacksquareblacktriangleblacktriangledownblacktriangleleftblacktrianglerightblankblk12blk14blk34blockbnebnequivbnotbopfbotbottombowtieboxDLboxDRboxDlboxDrboxHboxHDboxHUboxHdboxHuboxULboxURboxUlboxUrboxVboxVHboxVLboxVRboxVhboxVlboxVrboxboxboxdLboxdRboxdlboxdrboxhboxhDboxhUboxhdboxhuboxminusboxplusboxtimesboxuLboxuRboxulboxurboxvboxvHboxvLboxvRboxvhboxvlboxvrbprimebrevebrvbarbscrbsemibsimbsimebsolbsolbbsolhsubbullbulletbumpbumpEbumpebumpeqcacutecapcapandcapbrcupcapcapcapcupcapdotcapscaretcaronccapsccaronccedilccircccupsccupssmcdotcedilcemptyvcentcenterdotcfrchcycheckcheckmarkchicircirEcirccirceqcirclearrowleftcirclearrowrightcircledRcircledScircledastcircledcirccircleddashcirecirfnintcirmidcirscirclubsclubsuitcoloncolonecoloneqcommacommatcompcompfncomplementcomplexescongcongdotconintcopfcoprodcopycopysrcrarrcrosscscrcsubcsubecsupcsupectdotcudarrlcudarrrcueprcuesccularrcularrpcupcupbrcapcupcapcupcupcupdotcuporcupscurarrcurarrmcurlyeqpreccurlyeqsucccurlyveecurlywedgecurrencurvearrowleftcurvearrowrightcuveecuwedcwconintcwintcylctydArrdHardaggerdalethdarrdashdashvdbkarowdblacdcarondcyddddaggerddarrddotseqdegdeltademptyvdfishtdfrdharldharrdiamdiamonddiamondsuitdiamsdiedigammadisindivdividedivideontimesdivonxdjcydlcorndlcropdollardopfdotdoteqdoteqdotdotminusdotplusdotsquaredoublebarwedgedownarrowdowndownarrowsdownharpoonleftdownharpoonrightdrbkarowdrcorndrcropdscrdscydsoldstrokdtdotdtridtrifduarrduhardwangledzcydzigrarreDDoteDoteacuteeasterecaronecirecircecolonecyedoteeefDotefregegraveegsegsdotelelintersellelselsdotemacremptyemptysetemptyvemspemsp13emsp14engenspeogoneopfepareparsleplusepsiepsilonepsiveqcirceqcoloneqsimeqslantgtreqslantlessequalsequestequivequivDDeqvparslerDoterarrescresdotesimetaetheumleuroexclexistexpectationexponentialefallingdotseqfcyfemaleffiligffligfflligffrfiligfjligflatflligfltnsfnoffopfforallforkforkvfpartintfrac12frac13frac14frac15frac16frac18frac23frac25frac34frac35frac38frac45frac56frac58frac78fraslfrownfscrgEgElgacutegammagammadgapgbrevegcircgcygdotgegelgeqgeqqgeqslantgesgesccgesdotgesdotogesdotolgeslgeslesgfrggggggimelgjcyglglEglagljgnEgnapgnapproxgnegneqgneqqgnsimgopfgravegscrgsimgsimegsimlgtgtccgtcirgtdotgtlPargtquestgtrapproxgtrarrgtrdotgtreqlessgtreqqlessgtrlessgtrsimgvertneqqgvnEhArrhairsphalfhamilthardcyharrharrcirharrwhbarhcircheartsheartsuithellipherconhfrhksearowhkswarowhoarrhomththookleftarrowhookrightarrowhopfhorbarhscrhslashhstrokhybullhypheniacuteicicircicyiecyiexcliffifrigraveiiiiiintiiintiinfiniiotaijligimacrimageimaglineimagpartimathimofimpedinincareinfininfintieinodotintintcalintegersintercalintlarhkintprodiocyiogoniopfiotaiprodiquestiscrisinisinEisindotisinsisinsvisinvititildeiukcyiumljcircjcyjfrjmathjopfjscrjsercyjukcykappakappavkcedilkcykfrkgreenkhcykjcykopfkscrlAarrlArrlAtaillBarrlElEglHarlacutelaemptyvlagranlambdalanglangdlanglelaplaquolarrlarrblarrbfslarrfslarrhklarrlplarrpllarrsimlarrtllatlataillatelateslbarrlbbrklbracelbracklbrkelbrksldlbrkslulcaronlcedillceillcublcyldcaldquoldquorldrdharldrusharldshleleftarrowleftarrowtailleftharpoondownleftharpoonupleftleftarrowsleftrightarrowleftrightarrowsleftrightharpoonsleftrightsquigarrowleftthreetimeslegleqleqqleqslantleslescclesdotlesdotolesdotorlesglesgeslessapproxlessdotlesseqgtrlesseqqgtrlessgtrlesssimlfishtlfloorlfrlglgElhardlharulharullhblkljcyllllarrllcornerllhardlltrilmidotlmoustlmoustachelnElnaplnapproxlnelneqlneqqlnsimloangloarrlobrklongleftarrowlongleftrightarrowlongmapstolongrightarrowlooparrowleftlooparrowrightloparlopflopluslotimeslowastlowbarlozlozengelozflparlparltlrarrlrcornerlrharlrhardlrmlrtrilsaquolscrlshlsimlsimelsimglsqblsquolsquorlstrokltltccltcirltdotlthreeltimesltlarrltquestltrParltriltrieltriflurdsharluruharlvertneqqlvnEmDDotmacrmalemaltmaltesemapmapstomapstodownmapstoleftmapstoupmarkermcommamcymdashmeasuredanglemfrmhomicromidmidastmidcirmiddotminusminusbminusdminusdumlcpmldrmnplusmodelsmopfmpmscrmstposmumultimapmumapnGgnGtnGtvnLeftarrownLeftrightarrownLlnLtnLtvnRightarrownVDashnVdashnablanacutenangnapnapEnapidnaposnapproxnaturnaturalnaturalsnbspnbumpnbumpencapncaronncedilncongncongdotncupncyndashneneArrnearhknearrnearrownedotnequivnesearnesimnexistnexistsnfrngEngengeqngeqqngeqslantngesngsimngtngtrnhArrnharrnhparninisnisdnivnjcynlArrnlEnlarrnldrnlenleftarrownleftrightarrownleqnleqqnleqslantnlesnlessnlsimnltnltrinltrienmidnopfnotnotinnotinEnotindotnotinvanotinvbnotinvcnotninotnivanotnivbnotnivcnparnparallelnparslnpartnpolintnprnprcuenprenprecnpreceqnrArrnrarrnrarrcnrarrwnrightarrownrtrinrtrienscnsccuenscenscrnshortmidnshortparallelnsimnsimensimeqnsmidnsparnsqsubensqsupensubnsubEnsubensubsetnsubseteqnsubseteqqnsuccnsucceqnsupnsupEnsupensupsetnsupseteqnsupseteqqntglntildentlgntriangleleftntrianglelefteqntrianglerightntrianglerighteqnunumnumeronumspnvDashnvHarrnvapnvdashnvgenvgtnvinfinnvlArrnvlenvltnvltrienvrArrnvrtrienvsimnwArrnwarhknwarrnwarrownwnearoSoacuteoastocirocircocyodashodblacodivodotodsoldoeligofcirofrogonograveogtohbarohmointolarrolcirolcrossolineoltomacromegaomicronomidominusoopfoparoperpoplusororarrordorderorderofordfordmorigoforororslopeorvoscroslashosolotildeotimesotimesasoumlovbarparparaparallelparsimparslpartpcypercntperiodpermilperppertenkpfrphiphivphmmatphonepipitchforkpivplanckplanckhplankvplusplusacirplusbpluscirplusdoplusdupluseplusmnplussimplustwopmpointintpopfpoundprprEprapprcuepreprecprecapproxpreccurlyeqpreceqprecnapproxprecneqqprecnsimprecsimprimeprimesprnEprnapprnsimprodprofalarproflineprofsurfpropproptoprsimprurelpscrpsipuncspqfrqintqopfqprimeqscrquaternionsquatintquestquesteqquotrAarrrArrrAtailrBarrrHarraceracuteradicraemptyvrangrangdrangerangleraquorarrrarraprarrbrarrbfsrarrcrarrfsrarrhkrarrlprarrplrarrsimrarrtlrarrwratailratiorationalsrbarrrbbrkrbracerbrackrbrkerbrksldrbrkslurcaronrcedilrceilrcubrcyrdcardldharrdquordquorrdshrealrealinerealpartrealsrectregrfishtrfloorrfrrhardrharurharulrhorhovrightarrowrightarrowtailrightharpoondownrightharpoonuprightleftarrowsrightleftharpoonsrightrightarrowsrightsquigarrowrightthreetimesringrisingdotseqrlarrrlharrlmrmoustrmoustachernmidroangroarrrobrkroparropfroplusrotimesrparrpargtrppolintrrarrrsaquorscrrshrsqbrsquorsquorrthreertimesrtrirtriertrifrtriltriruluharrxsacutesbquoscscEscapscaronsccuescescedilscircscnEscnapscnsimscpolintscsimscysdotsdotbsdoteseArrsearhksearrsearrowsectsemiseswarsetminussetmnsextsfrsfrownsharpshchcyshcyshortmidshortparallelshysigmasigmafsigmavsimsimdotsimesimeqsimgsimgEsimlsimlEsimnesimplussimrarrslarrsmallsetminussmashpsmeparslsmidsmilesmtsmtesmtessoftcysolsolbsolbarsopfspadesspadesuitsparsqcapsqcapssqcupsqcupssqsubsqsubesqsubsetsqsubseteqsqsupsqsupesqsupsetsqsupseteqsqusquaresquarfsqufsrarrsscrssetmnssmilesstarfstarstarfstraightepsilonstraightphistrnssubsubEsubdotsubesubedotsubmultsubnEsubnesubplussubrarrsubsetsubseteqsubseteqqsubsetneqsubsetneqqsubsimsubsubsubsupsuccsuccapproxsucccurlyeqsucceqsuccnapproxsuccneqqsuccnsimsuccsimsumsungsupsup1sup2sup3supEsupdotsupdsubsupesupedotsuphsolsuphsubsuplarrsupmultsupnEsupnesupplussupsetsupseteqsupseteqqsupsetneqsupsetneqqsupsimsupsubsupsupswArrswarhkswarrswarrowswnwarszligtargettautbrktcarontcediltcytdottelrectfrthere4thereforethetathetasymthetavthickapproxthicksimthinspthkapthksimthorntildetimestimesbtimesbartimesdtinttoeatoptopbottopcirtopftopforktosatprimetradetriangletriangledowntrianglelefttrianglelefteqtriangleqtrianglerighttrianglerighteqtridottrietriminustriplustrisbtritimetrpeziumtscrtscytshcytstroktwixttwoheadleftarrowtwoheadrightarrowuArruHaruacuteuarrubrcyubreveucircucyudarrudblacudharufishtufrugraveuharluharruhblkulcornulcornerulcropultriumacrumluogonuopfuparrowupdownarrowupharpoonleftupharpoonrightuplusupsiupsihupsilonupuparrowsurcornurcornerurcropuringurtriuscrutdotutildeutriutrifuuarruumluwanglevArrvBarvBarvvDashvangrtvarepsilonvarkappavarnothingvarphivarpivarproptovarrvarrhovarsigmavarsubsetneqvarsubsetneqqvarsupsetneqvarsupsetneqqvarthetavartriangleleftvartrianglerightvcyvdashveeveebarveeeqvellipverbarvertvfrvltrivnsubvnsupvopfvpropvrtrivscrvsubnEvsubnevsupnEvsupnevzigzagwcircwedbarwedgewedgeqweierpwfrwopfwpwrwreathwscrxcapxcircxcupxdtrixfrxhArrxharrxixlArrxlarrxmapxnisxodotxopfxoplusxotimexrArrxrarrxscrxsqcupxuplusxutrixveexwedgeyacuteyacyycircycyyenyfryicyyopfyscryucyyumlzacutezcaronzcyzdotzeetrfzetazfrzhcyzigrarrzopfzscrzwjzwnj"
const _html5entitiesNameIndex = "\x05\x03\x06\x05\x03\x03\x06\x05\x05\x03\x05\x04\x0d\x05\x04\x06\x06\x04\x09\x04\x06\x03\x07\x0a\x04\x03\x04\x05\x04\x06\x04\x04\x06\x03\x14\x07\x06\x06\x05\x07\x04\x07\x09\x03\x03\x09\x0b\x0a\x0b\x18\x15\x0f\x05\x06\x09\x06\x0f\x04\x09\x1f\x05\x04\x03\x06\x02\x08\x04\x04\x04\x06\x04\x05\x06\x03\x03\x05\x03\x10\x0e\x16\x10\x10\x07\x0d\x04\x03\x06\x08\x15\x09\x0f\x0f\x14\x0d\x13\x18\x14\x10\x0e\x0d\x11\x11\x09\x0c\x10\x09\x13\x11\x0e\x11\x12\x0f\x12\x07\x0c\x09\x04\x06\x03\x03\x06\x06\x05\x03\x04\x03\x06\x07\x05\x10\x14\x05\x04\x07\x05\x0a\x0b\x04\x04\x03\x04\x06\x0c\x03\x03\x11\x15\x04\x06\x0a\x04\x04\x02\x05\x06\x06\x06\x05\x03\x04\x03\x02\x04\x0c\x10\x10\x0e\x0b\x11\x0c\x04\x02\x06\x05\x03\x05\x03\x0c\x04\x0e\x04\x06\x0c\x09\x04\x05\x04\x06\x05\x03\x04\x03\x06\x02\x05\x0a\x07\x03\x08\x0c\x0e\x0e\x05\x04\x04\x04\x06\x05\x04\x05\x03\x03\x04\x04\x06\x05\x04\x04\x05\x06\x03\x03\x04\x04\x04\x02\x06\x06\x04\x0a\x04\x06\x06\x03\x10\x09\x0c\x13\x0b\x11\x11\x0e\x11\x09\x0e\x0f\x07\x0c\x0d\x0c\x0f\x11\x10\x0f\x0c\x0f\x0a\x0d\x09\x0e\x10\x0d\x0b\x08\x0e\x09\x03\x02\x0a\x06\x0d\x12\x0e\x0d\x12\x0e\x04\x0e\x0f\x04\x03\x06\x02\x03\x03\x0b\x09\x03\x09\x04\x04\x02\x04\x06\x06\x06\x03\x13\x12\x11\x15\x14\x0e\x07\x03\x07\x10\x04\x03\x0c\x09\x14\x0a\x08\x0d\x09\x0a\x0f\x13\x11\x0e\x14\x0f\x0f\x0c\x0f\x12\x14\x07\x0c\x0e\x0b\x11\x0c\x17\x11\x0b\x10\x15\x11\x10\x13\x15\x0f\x14\x11\x16\x09\x0e\x0b\x10\x15\x10\x0b\x10\x08\x0d\x11\x0d\x0e\x04\x06\x02\x05\x06\x05\x03\x06\x03\x06\x05\x05\x07\x04\x14\x0e\x02\x04\x06\x06\x06\x04\x07\x09\x0b\x0f\x08\x03\x03\x03\x02\x09\x0d\x04\x02\x08\x0d\x12\x0d\x05\x07\x0a\x0c\x04\x03\x04\x03\x04\x04\x05\x03\x06\x04\x04\x06\x06\x06\x03\x02\x0e\x12\x14\x03\x03\x11\x0a\x0d\x13\x0c\x12\x12\x0f\x12\x0a\x08\x0d\x0e\x0d\x10\x12\x11\x10\x0d\x10\x0b\x0e\x0a\x04\x0c\x0b\x04\x03\x0b\x06\x04\x06\x06\x02\x06\x06\x05\x03\x03\x0e\x0e\x0f\x0c\x05\x0b\x04\x04\x06\x12\x0c\x11\x0e\x13\x0b\x04\x04\x03\x06\x0b\x08\x0d\x12\x0d\x08\x03\x03\x08\x0d\x06\x05\x05\x05\x04\x03\x03\x06\x06\x03\x03\x09\x05\x0a\x09\x05\x0a\x0e\x0a\x04\x09\x04\x06\x06\x04\x08\x05\x06\x05\x03\x06\x03\x06\x05\x08\x0a\x0c\x10\x05\x09\x05\x04\x07\x0a\x10\x0b\x0d\x05\x0a\x07\x0b\x0e\x0f\x04\x07\x05\x04\x06\x04\x05\x04\x03\x05\x06\x03\x06\x04\x0b\x0c\x11\x0d\x0d\x03\x04\x04\x06\x05\x05\x03\x04\x04\x03\x02\x04\x04\x04\x04\x04\x06\x05\x03\x03\x04\x04\x04\x04\x06\x06\x03\x04\x0e\x04\x03\x04\x04\x06\x06\x02\x03\x03\x05\x05\x03\x05\x02\x03\x06\x07\x05\x05\x05\x05\x03\x03\x06\x04\x08\x04\x03\x04\x05\x06\x08\x08\x08\x08\x08\x08\x08\x08\x05\x07\x08\x06\x05\x07\x05\x04\x02\x03\x06\x03\x04\x04\x06\x08\x05\x04\x03\x05\x07\x06\x04\x08\x05\x04\x08\x0b\x09\x07\x09\x06\x06\x08\x04\x08\x05\x03\x05\x06\x07\x07\x05\x06\x04\x04\x07\x03\x06\x07\x06\x07\x08\x09\x08\x07\x0f\x0d\x08\x06\x08\x06\x0c\x0b\x0d\x11\x11\x12\x05\x05\x05\x05\x05\x03\x07\x04\x04\x03\x06\x06\x05\x05\x05\x05\x04\x05\x05\x05\x05\x05\x05\x05\x05\x04\x05\x05\x05\x05\x05\x05\x06\x05\x05\x05\x05\x04\x05\x05\x05\x05\x08\x07\x08\x05\x05\x05\x05\x04\x05\x05\x05\x05\x05\x05\x06\x05\x06\x04\x05\x04\x05\x04\x05\x08\x04\x06\x04\x05\x05\x06\x06\x03\x06\x08\x06\x06\x06\x04\x05\x05\x05\x06\x06\x05\x05\x07\x04\x05\x07\x04\x09\x03\x04\x05\x09\x03\x03\x04\x04\x06\x0f\x10\x08\x08\x0a\x0b\x0b\x04\x08\x06\x07\x05\x08\x05\x06\x07\x05\x06\x04\x06\x0a\x09\x04\x07\x06\x04\x06\x04\x06\x05\x05\x04\x04\x05\x04\x05\x05\x07\x07\x05\x05\x06\x07\x03\x08\x06\x06\x06\x05\x04\x06\x07\x0b\x0b\x08\x0a\x06\x0e\x0f\x05\x05\x08\x05\x06\x04\x04\x06\x06\x04\x04\x05\x07\x05\x06\x03\x02\x07\x05\x07\x03\x05\x07\x06\x03\x05\x05\x04\x07\x0b\x05\x03\x07\x05\x03\x06\x0d\x06\x04\x06\x06\x06\x04\x03\x05\x08\x08\x07\x09\x0e\x09\x0e\x0f\x10\x08\x06\x06\x04\x04\x04\x06\x05\x04\x05\x05\x05\x07\x04\x08\x05\x04\x06\x06\x06\x04\x05\x06\x03\x04\x02\x05\x03\x02\x06\x03\x06\x02\x08\x03\x03\x06\x05\x05\x08\x06\x04\x06\x06\x03\x04\x05\x04\x04\x06\x05\x04\x07\x05\x06\x07\x05\x0a\x0b\x06\x06\x05\x07\x08\x05\x05\x04\x05\x04\x03\x03\x04\x04\x04\x05\x0b\x0c\x0d\x03\x06\x06\x05\x06\x03\x05\x05\x04\x05\x05\x04\x04\x06\x04\x05\x08\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x05\x05\x04\x02\x03\x06\x05\x06\x03\x06\x05\x03\x04\x02\x03\x03\x04\x08\x03\x05\x06\x07\x08\x04\x06\x03\x02\x03\x05\x04\x02\x03\x03\x03\x03\x04\x08\x03\x04\x05\x05\x04\x05\x04\x04\x05\x05\x02\x04\x05\x05\x06\x07\x09\x06\x06\x09\x0a\x07\x06\x09\x04\x04\x06\x04\x06\x06\x04\x07\x05\x04\x05\x06\x09\x06\x06\x03\x08\x08\x05\x06\x0d\x0e\x04\x06\x04\x06\x06\x06\x06\x06\x02\x05\x03\x04\x05\x03\x03\x06\x02\x06\x05\x06\x05\x05\x05\x05\x08\x08\x05\x04\x05\x02\x06\x05\x08\x06\x03\x06\x08\x08\x08\x07\x04\x05\x04\x04\x05\x06\x04\x04\x05\x07\x05\x06\x05\x02\x06\x05\x04\x05\x03\x03\x05\x04\x04\x06\x05\x05\x06\x06\x03\x03\x06\x04\x04\x04\x04\x05\x04\x06\x05\x02\x03\x04\x06\x08\x06\x06\x04\x05\x06\x03\x05\x04\x05\x07\x06\x06\x06\x06\x07\x06\x03\x06\x04\x05\x05\x05\x06\x06\x05\x07\x07\x06\x06\x05\x04\x03\x04\x05\x06\x07\x08\x04\x02\x09\x0d\x0f\x0d\x0e\x0e\x0f\x11\x13\x0e\x03\x03\x04\x08\x03\x05\x06\x07\x08\x04\x06\x0a\x07\x09\x0a\x07\x07\x06\x06\x03\x02\x03\x05\x05\x06\x05\x04\x02\x05\x08\x06\x05\x06\x06\x0a\x03\x04\x08\x03\x04\x05\x05\x05\x05\x05\x0d\x12\x0a\x0e\x0d\x0e\x05\x04\x06\x07\x06\x06\x03\x07\x04\x04\x06\x05\x08\x05\x06\x03\x05\x06\x04\x03\x04\x05\x05\x04\x05\x06\x06\x02\x04\x05\x05\x06\x06\x06\x07\x06\x04\x05\x05\x08\x07\x09\x04\x05\x04\x04\x04\x07\x03\x06\x0a\x0a\x08\x06\x06\x03\x05\x0d\x03\x03\x05\x03\x06\x06\x06\x05\x06\x06\x07\x04\x04\x06\x06\x04\x02\x04\x06\x02\x08\x05\x03\x03\x04\x0a\x0f\x03\x03\x04\x0b\x06\x06\x05\x06\x04\x03\x04\x05\x05\x07\x05\x07\x08\x04\x05\x06\x04\x06\x06\x05\x08\x04\x03\x05\x02\x05\x06\x05\x07\x05\x06\x06\x05\x06\x07\x03\x03\x03\x04\x05\x09\x04\x05\x03\x04\x05\x05\x05\x02\x03\x04\x03\x04\x05\x03\x05\x04\x03\x0a\x0f\x04\x05\x09\x04\x05\x05\x03\x05\x06\x04\x04\x03\x05\x06\x08\x07\x07\x07\x05\x07\x07\x07\x04\x09\x06\x05\x07\x03\x06\x04\x05\x07\x05\x05\x06\x06\x0b\x05\x06\x03\x06\x04\x04\x09\x0e\x04\x05\x06\x05\x05\x07\x07\x04\x05\x05\x07\x09\x0a\x05\x07\x04\x05\x05\x07\x09\x0a\x04\x06\x04\x0d\x0f\x0e\x10\x02\x03\x06\x05\x06\x06\x04\x06\x04\x04\x07\x06\x04\x04\x07\x06\x07\x05\x05\x06\x05\x07\x06\x02\x06\x04\x04\x05\x03\x05\x06\x04\x04\x06\x05\x05\x03\x04\x06\x03\x05\x03\x04\x05\x05\x07\x05\x03\x05\x05\x07\x04\x06\x04\x04\x05\x05\x02\x05\x03\x05\x07\x04\x04\x06\x04\x07\x03\x04\x06\x04\x06\x06\x08\x04\x05\x03\x04\x08\x06\x05\x04\x03\x06\x06\x06\x04\x07\x03\x03\x04\x06\x05\x02\x09\x03\x06\x07\x06\x04\x08\x05\x07\x06\x06\x05\x06\x07\x07\x02\x08\x04\x05\x02\x03\x04\x05\x03\x04\x0a\x0b\x06\x0b\x08\x08\x07\x05\x06\x04\x05\x06\x04\x08\x08\x08\x04\x06\x05\x06\x04\x03\x06\x03\x04\x04\x06\x04\x0b\x07\x05\x07\x04\x05\x04\x06\x05\x04\x04\x06\x05\x08\x04\x05\x05\x06\x05\x04\x06\x05\x07\x05\x06\x06\x06\x06\x07\x06\x05\x06\x05\x09\x05\x05\x06\x06\x05\x07\x07\x06\x06\x05\x04\x03\x04\x07\x05\x06\x04\x04\x07\x08\x05\x04\x03\x06\x06\x03\x05\x05\x06\x03\x04\x0a\x0e\x10\x0e\x0f\x11\x10\x0f\x0f\x04\x0c\x05\x05\x03\x06\x0a\x05\x05\x05\x05\x05\x04\x06\x07\x04\x06\x08\x05\x06\x04\x03\x04\x05\x06\x06\x06\x04\x05\x05\x08\x07\x02\x06\x05\x02\x03\x04\x06\x05\x03\x06\x05\x04\x05\x06\x08\x05\x03\x04\x05\x05\x05\x06\x05\x07\x04\x04\x06\x08\x05\x04\x03\x06\x05\x06\x04\x08\x0d\x03\x05\x06\x06\x03\x06\x04\x05\x04\x05\x04\x05\x05\x07\x07\x05\x0d\x06\x08\x04\x05\x03\x04\x05\x06\x03\x04\x06\x04\x06\x09\x04\x05\x06\x05\x06\x05\x06\x08\x0a\x05\x06\x08\x0a\x03\x06\x06\x04\x05\x04\x06\x06\x06\x04\x05\x0f\x0b\x05\x03\x04\x06\x04\x07\x07\x05\x05\x07\x07\x06\x08\x09\x09\x0a\x06\x06\x06\x04\x0a\x0b\x06\x0b\x08\x08\x07\x03\x04\x03\x04\x04\x04\x04\x06\x07\x04\x07\x07\x07\x07\x07\x05\x05\x07\x06\x08\x09\x09\x0a\x06\x06\x06\x05\x06\x05\x07\x06\x05\x06\x03\x04\x06\x06\x03\x04\x06\x03\x06\x09\x05\x08\x06\x0b\x08\x06\x05\x06\x05\x05\x05\x06\x08\x06\x04\x04\x03\x06\x06\x04\x07\x04\x06\x05\x08\x0c\x0c\x0e\x09\x0d\x0f\x06\x04\x08\x07\x05\x07\x08\x04\x04\x05\x06\x05\x10\x11\x04\x04\x06\x04\x05\x06\x05\x03\x05\x06\x05\x06\x03\x06\x05\x05\x05\x06\x08\x06\x05\x05\x03\x05\x04\x07\x0b\x0d\x0e\x05\x04\x05\x07\x0a\x06\x08\x06\x05\x05\x04\x05\x06\x04\x05\x05\x04\x07\x04\x04\x05\x05\x06\x0a\x08\x0a\x06\x05\x09\x04\x06\x08\x0c\x0d\x0c\x0d\x08\x0f\x10\x03\x05\x03\x06\x05\x06\x06\x04\x03\x05\x05\x05\x04\x05\x05\x04\x06\x06\x06\x06\x07\x05\x06\x05\x06\x06\x03\x04\x02\x02\x06\x04\x04\x05\x04\x05\x03\x05\x05\x02\x05\x05\x04\x04\x05\x04\x06\x06\x05\x05\x04\x06\x06\x05\x04\x06\x06\x04\x05\x03\x03\x03\x04\x04\x04\x04\x04\x06\x06\x03\x04\x06\x04\x03\x04\x07\x04\x04\x03\x04"
var _html5entitiesCodePoints = [...]int{198, 38, 193, 194, 1040, 120068, 192, 913, 256, 10835, 260, 120120, 8289, 197, 119964, 8788, 195, 196, 8726, 10983, 8966, 1041, 8757, 8492, 914, 120069, 120121, 728, 8492, 8782, 1063, 169, 262, 8914, 8517, 8493, 268, 199, 264, 8752, 266, 184, 183, 8493, 935, 8857, 8854, 8853, 8855, 8754, 8221, 8217, 8759, 10868, 8801, 8751, 8750, 8450, 8720, 8755, 10799, 119966, 8915, 8781, 8517, 10513, 1026, 1029, 1039, 8225, 8609, 10980, 270, 1044, 8711, 916, 120071, 180, 729, 733, 96, 732, 8900, 8518, 120123, 168, 8412, 8784, 8751, 168, 8659, 8656, 8660, 10980, 10232, 10234, 10233, 8658, 8872, 8657, 8661, 8741, 8595, 10515, 8693, 785, 10576, 10590, 8637, 10582, 10591, 8641, 10583, 8868, 8615, 8659, 119967, 272, 330, 208, 201, 282, 202, 1069, 278, 120072, 200, 8712, 274, 9723, 9643, 280, 120124, 917, 10869, 8770, 8652, 8496, 10867, 919, 203, 8707, 8519, 1060, 120073, 9724, 9642, 120125, 8704, 8497, 8497, 1027, 62, 915, 988, 286, 290, 284, 1043, 288, 120074, 8921, 120126, 8805, 8923, 8807, 10914, 8823, 10878, 8819, 119970, 8811, 1066, 711, 94, 292, 8460, 8459, 8461, 9472, 8459, 294, 8782, 8783, 1045, 306, 1025, 205, 206, 1048, 304, 8465, 204, 8465, 298, 8520, 8658, 8748, 8747, 8898, 8291, 8290, 302, 120128, 921, 8464, 296, 1030, 207, 308, 1049, 120077, 120129, 119973, 1032, 1028, 1061, 1036, 922, 310, 1050, 120078, 120130, 119974, 1033, 60, 313, 923, 10218, 8466, 8606, 317, 315, 1051, 10216, 8592, 8676, 8646, 8968, 10214, 10593, 8643, 10585, 8970, 8596, 10574, 8867, 8612, 10586, 8882, 10703, 8884, 10577, 10592, 8639, 10584, 8636, 10578, 8656, 8660, 8922, 8806, 8822, 10913, 10877, 8818, 120079, 8920, 8666, 319, 10229, 10231, 10230, 10232, 10234, 10233, 120131, 8601, 8600, 8466, 8624, 321, 8810, 10501, 1052, 8287, 8499, 120080, 8723, 120132, 8499, 924, 1034, 323, 327, 325, 1053, 8203, 8203, 8203, 8203, 8811, 8810, 10, 120081, 8288, 160, 8469, 10988, 8802, 8813, 8742, 8713, 8800, 877, 24, 8708, 8815, 8817, 880, 24, 881, 24, 8825, 1087, 24, 8821, 878, 24, 878, 24, 8938, 1070, 24, 8940, 8814, 8816, 8824, 881, 24, 1087, 24, 8820, 1091, 24, 1091, 24, 8832, 1092, 24, 8928, 8716, 8939, 1070, 24, 8941, 884, 24, 8930, 884, 24, 8931, 883, 402, 8840, 8833, 1092, 24, 8929, 883, 24, 883, 402, 8841, 8769, 8772, 8775, 8777, 8740, 119977, 209, 925, 338, 211, 212, 1054, 336, 120082, 210, 332, 937, 927, 120134, 8220, 8216, 10836, 119978, 216, 213, 10807, 214, 8254, 9182, 9140, 9180, 8706, 1055, 120083, 934, 928, 177, 8460, 8473, 10939, 8826, 10927, 8828, 8830, 8243, 8719, 8759, 8733, 119979, 936, 34, 120084, 8474, 119980, 10512, 174, 340, 10219, 8608, 10518, 344, 342, 1056, 8476, 8715, 8651, 10607, 8476, 929, 10217, 8594, 8677, 8644, 8969, 10215, 10589, 8642, 10581, 8971, 8866, 8614, 10587, 8883, 10704, 8885, 10575, 10588, 8638, 10580, 8640, 10579, 8658, 8477, 10608, 8667, 8475, 8625, 10740, 1065, 1064, 1068, 346, 10940, 352, 350, 348, 1057, 120086, 8595, 8592, 8594, 8593, 931, 8728, 120138, 8730, 9633, 8851, 8847, 8849, 8848, 8850, 8852, 119982, 8902, 8912, 8912, 8838, 8827, 10928, 8829, 8831, 8715, 8721, 8913, 8835, 8839, 8913, 222, 8482, 1035, 1062, 9, 932, 356, 354, 1058, 120087, 8756, 920, 828, 202, 8201, 8764, 8771, 8773, 8776, 120139, 8411, 119983, 358, 218, 8607, 10569, 1038, 364, 219, 1059, 368, 120088, 217, 362, 95, 9183, 9141, 9181, 8899, 8846, 370, 120140, 8593, 10514, 8645, 8597, 10606, 8869, 8613, 8657, 8661, 8598, 8599, 978, 933, 366, 119984, 360, 220, 8875, 10987, 1042, 8873, 10982, 8897, 8214, 8214, 8739, 124, 10072, 8768, 8202, 120089, 120141, 119985, 8874, 372, 8896, 120090, 120142, 119986, 120091, 926, 120143, 119987, 1071, 1031, 1070, 221, 374, 1067, 120092, 120144, 119988, 376, 1046, 377, 381, 1047, 379, 8203, 918, 8488, 8484, 119989, 225, 259, 8766, 876, 19, 8767, 226, 180, 1072, 230, 8289, 120094, 224, 8501, 8501, 945, 257, 10815, 38, 8743, 10837, 10844, 10840, 10842, 8736, 10660, 8736, 8737, 10664, 10665, 10666, 10667, 10668, 10669, 10670, 10671, 8735, 8894, 10653, 8738, 197, 9084, 261, 120146, 8776, 10864, 10863, 8778, 8779, 39, 8776, 8778, 229, 119990, 42, 8776, 8781, 227, 228, 8755, 10769, 10989, 8780, 1014, 8245, 8765, 8909, 8893, 8965, 8965, 9141, 9142, 8780, 1073, 8222, 8757, 8757, 10672, 1014, 8492, 946, 8502, 8812, 120095, 8898, 9711, 8899, 10752, 10753, 10754, 10758, 9733, 9661, 9651, 10756, 8897, 8896, 10509, 10731, 9642, 9652, 9662, 9666, 9656, 9251, 9618, 9617, 9619, 9608, 6, 421, 880, 421, 8976, 120147, 8869, 8869, 8904, 9559, 9556, 9558, 9555, 9552, 9574, 9577, 9572, 9575, 9565, 9562, 9564, 9561, 9553, 9580, 9571, 9568, 9579, 9570, 9567, 10697, 9557, 9554, 9488, 9484, 9472, 9573, 9576, 9516, 9524, 8863, 8862, 8864, 9563, 9560, 9496, 9492, 9474, 9578, 9569, 9566, 9532, 9508, 9500, 8245, 728, 166, 119991, 8271, 8765, 8909, 92, 10693, 10184, 8226, 8226, 8782, 10926, 8783, 8783, 263, 8745, 10820, 10825, 10827, 10823, 10816, 874, 5024, 8257, 711, 10829, 269, 231, 265, 10828, 10832, 267, 184, 10674, 162, 183, 120096, 1095, 10003, 10003, 967, 9675, 10691, 710, 8791, 8634, 8635, 174, 9416, 8859, 8858, 8861, 8791, 10768, 10991, 10690, 9827, 9827, 58, 8788, 8788, 44, 64, 8705, 8728, 8705, 8450, 8773, 10861, 8750, 120148, 8720, 169, 8471, 8629, 10007, 119992, 10959, 10961, 10960, 10962, 8943, 10552, 10549, 8926, 8927, 8630, 10557, 8746, 10824, 10822, 10826, 8845, 10821, 874, 5024, 8631, 10556, 8926, 8927, 8910, 8911, 164, 8630, 8631, 8910, 8911, 8754, 8753, 9005, 8659, 10597, 8224, 8504, 8595, 8208, 8867, 10511, 733, 271, 1076, 8518, 8225, 8650, 10871, 176, 948, 10673, 10623, 120097, 8643, 8642, 8900, 8900, 9830, 9830, 168, 989, 8946, 247, 247, 8903, 8903, 1106, 8990, 8973, 36, 120149, 729, 8784, 8785, 8760, 8724, 8865, 8966, 8595, 8650, 8643, 8642, 10512, 8991, 8972, 119993, 1109, 10742, 273, 8945, 9663, 9662, 8693, 10607, 10662, 1119, 10239, 10871, 8785, 233, 10862, 283, 8790, 234, 8789, 1101, 279, 8519, 8786, 120098, 10906, 232, 10902, 10904, 10905, 9191, 8467, 10901, 10903, 275, 8709, 8709, 8709, 8195, 8196, 8197, 331, 8194, 281, 120150, 8917, 10723, 10865, 949, 949, 1013, 8790, 8789, 8770, 10902, 10901, 61, 8799, 8801, 10872, 10725, 8787, 10609, 8495, 8784, 8770, 951, 240, 235, 8364, 33, 8707, 8496, 8519, 8786, 1092, 9792, 64259, 64256, 64260, 120099, 64257, 10, 06, 9837, 64258, 9649, 402, 120151, 8704, 8916, 10969, 10765, 189, 8531, 188, 8533, 8537, 8539, 8532, 8534, 190, 8535, 8540, 8536, 8538, 8541, 8542, 8260, 8994, 119995, 8807, 10892, 501, 947, 989, 10886, 287, 285, 1075, 289, 8805, 8923, 8805, 8807, 10878, 10878, 10921, 10880, 10882, 10884, 892, 5024, 10900, 120100, 8811, 8921, 8503, 1107, 8823, 10898, 10917, 10916, 8809, 10890, 10890, 10888, 10888, 8809, 8935, 120152, 96, 8458, 8819, 10894, 10896, 62, 10919, 10874, 8919, 10645, 10876, 10886, 10616, 8919, 8923, 10892, 8823, 8819, 880, 5024, 880, 5024, 8660, 8202, 189, 8459, 1098, 8596, 10568, 8621, 8463, 293, 9829, 9829, 8230, 8889, 120101, 10533, 10534, 8703, 8763, 8617, 8618, 120153, 8213, 119997, 8463, 295, 8259, 8208, 237, 8291, 238, 1080, 1077, 161, 8660, 120102, 236, 8520, 10764, 8749, 10716, 8489, 307, 299, 8465, 8464, 8465, 305, 8887, 437, 8712, 8453, 8734, 10717, 305, 8747, 8890, 8484, 8890, 10775, 10812, 1105, 303, 120154, 953, 10812, 191, 119998, 8712, 8953, 8949, 8948, 8947, 8712, 8290, 297, 1110, 239, 309, 1081, 120103, 567, 120155, 119999, 1112, 1108, 954, 1008, 311, 1082, 120104, 312, 1093, 1116, 120156, 120000, 8666, 8656, 10523, 10510, 8806, 10891, 10594, 314, 10676, 8466, 955, 10216, 10641, 10216, 10885, 171, 8592, 8676, 10527, 10525, 8617, 8619, 10553, 10611, 8610, 10923, 10521, 10925, 1092, 5024, 10508, 10098, 123, 91, 10635, 10639, 10637, 318, 316, 8968, 123, 1083, 10550, 8220, 8222, 10599, 10571, 8626, 8804, 8592, 8610, 8637, 8636, 8647, 8596, 8646, 8651, 8621, 8907, 8922, 8804, 8806, 10877, 10877, 10920, 10879, 10881, 10883, 892, 5024, 10899, 10885, 8918, 8922, 10891, 8822, 8818, 10620, 8970, 120105, 8822, 10897, 8637, 8636, 10602, 9604, 1113, 8810, 8647, 8990, 10603, 9722, 320, 9136, 9136, 8808, 10889, 10889, 10887, 10887, 8808, 8934, 10220, 8701, 10214, 10229, 10231, 10236, 10230, 8619, 8620, 10629, 120157, 10797, 10804, 8727, 95, 9674, 9674, 10731, 40, 10643, 8646, 8991, 8651, 10605, 8206, 8895, 8249, 120001, 8624, 8818, 10893, 10895, 91, 8216, 8218, 322, 60, 10918, 10873, 8918, 8907, 8905, 10614, 10875, 10646, 9667, 8884, 9666, 10570, 10598, 880, 5024, 880, 5024, 8762, 175, 9794, 10016, 10016, 8614, 8614, 8615, 8612, 8613, 9646, 10793, 1084, 8212, 8737, 120106, 8487, 181, 8739, 42, 10992, 183, 8722, 8863, 8760, 10794, 10971, 8230, 8723, 8871, 120158, 8723, 120002, 8766, 956, 8888, 8888, 892, 24, 881, 402, 881, 24, 8653, 8654, 892, 24, 881, 402, 881, 24, 8655, 8879, 8878, 8711, 324, 873, 402, 8777, 1086, 24, 877, 24, 329, 8777, 9838, 9838, 8469, 160, 878, 24, 878, 24, 10819, 328, 326, 8775, 1086, 24, 10818, 1085, 8211, 8800, 8663, 10532, 8599, 8599, 878, 24, 8802, 10536, 877, 24, 8708, 8708, 120107, 880, 24, 8817, 8817, 880, 24, 1087, 24, 1087, 24, 8821, 8815, 8815, 8654, 8622, 10994, 8715, 8956, 8954, 8715, 1114, 8653, 880, 24, 8602, 8229, 8816, 8602, 8622, 8816, 880, 24, 1087, 24, 1087, 24, 8814, 8820, 8814, 8938, 8940, 8740, 120159, 172, 8713, 895, 24, 894, 24, 8713, 8951, 8950, 8716, 8716, 8958, 8957, 8742, 8742, 1100, 421, 870, 24, 10772, 8832, 8928, 1092, 24, 8832, 1092, 24, 8655, 8603, 1054, 24, 860, 24, 8603, 8939, 8941, 8833, 8929, 1092, 24, 120003, 8740, 8742, 8769, 8772, 8772, 8740, 8742, 8930, 8931, 8836, 1094, 24, 8840, 883, 402, 8840, 1094, 24, 8833, 1092, 24, 8837, 1095, 24, 8841, 883, 402, 8841, 1095, 24, 8825, 241, 8824, 8938, 8940, 8939, 8941, 957, 35, 8470, 8199, 8877, 10500, 878, 402, 8876, 880, 402, 6, 402, 10718, 10498, 880, 402, 6, 402, 888, 402, 10499, 888, 402, 876, 402, 8662, 10531, 8598, 8598, 10535, 9416, 243, 8859, 8858, 244, 1086, 8861, 337, 10808, 8857, 10684, 339, 10687, 120108, 731, 242, 10689, 10677, 937, 8750, 8634, 10686, 10683, 8254, 10688, 333, 969, 959, 10678, 8854, 120160, 10679, 10681, 8853, 8744, 8635, 10845, 8500, 8500, 170, 186, 8886, 10838, 10839, 10843, 8500, 248, 8856, 245, 8855, 10806, 246, 9021, 8741, 182, 8741, 10995, 11005, 8706, 1087, 37, 46, 8240, 8869, 8241, 120109, 966, 981, 8499, 9742, 960, 8916, 982, 8463, 8462, 8463, 43, 10787, 8862, 10786, 8724, 10789, 10866, 177, 10790, 10791, 177, 10773, 120161, 163, 8826, 10931, 10935, 8828, 10927, 8826, 10935, 8828, 10927, 10937, 10933, 8936, 8830, 8242, 8473, 10933, 10937, 8936, 8719, 9006, 8978, 8979, 8733, 8733, 8830, 8880, 120005, 968, 8200, 120110, 10764, 120162, 8279, 120006, 8461, 10774, 63, 8799, 34, 8667, 8658, 10524, 10511, 10596, 876, 17, 341, 8730, 10675, 10217, 10642, 10661, 10217, 187, 8594, 10613, 8677, 10528, 10547, 10526, 8618, 8620, 10565, 10612, 8611, 8605, 10522, 8758, 8474, 10509, 10099, 125, 93, 10636, 10638, 10640, 345, 343, 8969, 125, 1088, 10551, 10601, 8221, 8221, 8627, 8476, 8475, 8476, 8477, 9645, 174, 10621, 8971, 120111, 8641, 8640, 10604, 961, 1009, 8594, 8611, 8641, 8640, 8644, 8652, 8649, 8605, 8908, 730, 8787, 8644, 8652, 8207, 9137, 9137, 10990, 10221, 8702, 10215, 10630, 120163, 10798, 10805, 41, 10644, 10770, 8649, 8250, 120007, 8625, 93, 8217, 8217, 8908, 8906, 9657, 8885, 9656, 10702, 10600, 8478, 347, 8218, 8827, 10932, 10936, 353, 8829, 10928, 351, 349, 10934, 10938, 8937, 10771, 8831, 1089, 8901, 8865, 10854, 8664, 10533, 8600, 8600, 167, 59, 10537, 8726, 8726, 10038, 120112, 8994, 9839, 1097, 1096, 8739, 8741, 173, 963, 962, 962, 8764, 10858, 8771, 8771, 10910, 10912, 10909, 10911, 8774, 10788, 10610, 8592, 8726, 10803, 10724, 8739, 8995, 10922, 10924, 1092, 5024, 1100, 47, 10692, 9023, 120164, 9824, 9824, 8741, 8851, 885, 5024, 8852, 885, 5024, 8847, 8849, 8847, 8849, 8848, 8850, 8848, 8850, 9633, 9633, 9642, 9642, 8594, 120008, 8726, 8995, 8902, 9734, 9733, 1013, 981, 175, 8834, 10949, 10941, 8838, 10947, 10945, 10955, 8842, 10943, 10617, 8834, 8838, 10949, 8842, 10955, 10951, 10965, 10963, 8827, 10936, 8829, 10928, 10938, 10934, 8937, 8831, 8721, 9834, 8835, 185, 178, 179, 10950, 10942, 10968, 8839, 10948, 10185, 10967, 10619, 10946, 10956, 8843, 10944, 8835, 8839, 10950, 8843, 10956, 10952, 10964, 10966, 8665, 10534, 8601, 8601, 10538, 223, 8982, 964, 9140, 357, 355, 1090, 8411, 8981, 120113, 8756, 8756, 952, 977, 977, 8776, 8764, 8201, 8776, 8764, 254, 732, 215, 8864, 10801, 10800, 8749, 10536, 8868, 9014, 10993, 120165, 10970, 10537, 8244, 8482, 9653, 9663, 9667, 8884, 8796, 9657, 8885, 9708, 8796, 10810, 10809, 10701, 10811, 9186, 120009, 1094, 1115, 359, 8812, 8606, 8608, 8657, 10595, 250, 8593, 1118, 365, 251, 1091, 8645, 369, 10606, 10622, 120114, 249, 8639, 8638, 9600, 8988, 8988, 8975, 9720, 363, 168, 371, 120166, 8593, 8597, 8639, 8638, 8846, 965, 978, 965, 8648, 8989, 8989, 8974, 367, 9721, 120010, 8944, 361, 9653, 9652, 8648, 252, 10663, 8661, 10984, 10985, 8872, 10652, 1013, 1008, 8709, 981, 982, 8733, 8597, 1009, 962, 884, 5024, 1095, 5024, 884, 5024, 1095, 5024, 977, 8882, 8883, 1074, 8866, 8744, 8891, 8794, 8942, 124, 124, 120115, 8882, 883, 402, 883, 402, 120167, 8733, 8883, 120011, 1095, 5024, 884, 5024, 1095, 5024, 884, 5024, 10650, 373, 10847, 8743, 8793, 8472, 120116, 120168, 8472, 8768, 8768, 120012, 8898, 9711, 8899, 9661, 120117, 10234, 10231, 958, 10232, 10229, 10236, 8955, 10752, 120169, 10753, 10754, 10233, 10230, 120013, 10758, 10756, 9651, 8897, 8896, 253, 1103, 375, 1099, 165, 120118, 1111, 120170, 120014, 1102, 255, 378, 382, 1079, 380, 8488, 950, 120119, 1078, 8669, 120171, 120015, 8205, 8204}
var _html5entitiesCodePointsIndex = "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x01\x01\x01\x02\x02\x01\x02\x01\x02\x02\x01\x02\x01\x01\x01\x01\x02\x02\x01\x02\x02\x01\x02\x01\x01\x01\x02\x01\x02\x01\x02\x01\x02\x01\x01\x02\x01\x02\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x02\x02\x01\x01\x02\x02\x02\x01\x01\x01\x01\x01\x02\x01\x02\x02\x01\x01\x01\x01\x01\x01\x02\x02\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x02\x01\x01\x02\x01\x01\x01\x02\x01\x01\x02\x02\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x02\x02\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x02\x01\x01\x01\x02\x01\x02\x01\x01\x02\x02\x01\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x01\x02\x02\x01\x01\x02\x02\x02\x01\x02\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x02\x02\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x02\x01\x01\x01\x01\x02\x02\x02\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
var _html5entitiesCharacters = [...]byte{0xc3, 0x86, 0x26, 0xc3, 0x81, 0xc3, 0x82, 0xd0, 0x90, 0xf0, 0x9d, 0x94, 0x84, 0xc3, 0x80, 0xce, 0x91, 0xc4, 0x80, 0xe2, 0xa9, 0x93, 0xc4, 0x84, 0xf0, 0x9d, 0x94, 0xb8, 0xe2, 0x81, 0xa1, 0xc3, 0x85, 0xf0, 0x9d, 0x92, 0x9c, 0xe2, 0x89, 0x94, 0xc3, 0x83, 0xc3, 0x84, 0xe2, 0x88, 0x96, 0xe2, 0xab, 0xa7, 0xe2, 0x8c, 0x86, 0xd0, 0x91, 0xe2, 0x88, 0xb5, 0xe2, 0x84, 0xac, 0xce, 0x92, 0xf0, 0x9d, 0x94, 0x85, 0xf0, 0x9d, 0x94, 0xb9, 0xcb, 0x98, 0xe2, 0x84, 0xac, 0xe2, 0x89, 0x8e, 0xd0, 0xa7, 0xc2, 0xa9, 0xc4, 0x86, 0xe2, 0x8b, 0x92, 0xe2, 0x85, 0x85, 0xe2, 0x84, 0xad, 0xc4, 0x8c, 0xc3, 0x87, 0xc4, 0x88, 0xe2, 0x88, 0xb0, 0xc4, 0x8a, 0xc2, 0xb8, 0xc2, 0xb7, 0xe2, 0x84, 0xad, 0xce, 0xa7, 0xe2, 0x8a, 0x99, 0xe2, 0x8a, 0x96, 0xe2, 0x8a, 0x95, 0xe2, 0x8a, 0x97, 0xe2, 0x88, 0xb2, 0xe2, 0x80, 0x9d, 0xe2, 0x80, 0x99, 0xe2, 0x88, 0xb7, 0xe2, 0xa9, 0xb4, 0xe2, 0x89, 0xa1, 0xe2, 0x88, 0xaf, 0xe2, 0x88, 0xae, 0xe2, 0x84, 0x82, 0xe2, 0x88, 0x90, 0xe2, 0x88, 0xb3, 0xe2, 0xa8, 0xaf, 0xf0, 0x9d, 0x92, 0x9e, 0xe2, 0x8b, 0x93, 0xe2, 0x89, 0x8d, 0xe2, 0x85, 0x85, 0xe2, 0xa4, 0x91, 0xd0, 0x82, 0xd0, 0x85, 0xd0, 0x8f, 0xe2, 0x80, 0xa1, 0xe2, 0x86, 0xa1, 0xe2, 0xab, 0xa4, 0xc4, 0x8e, 0xd0, 0x94, 0xe2, 0x88, 0x87, 0xce, 0x94, 0xf0, 0x9d, 0x94, 0x87, 0xc2, 0xb4, 0xcb, 0x99, 0xcb, 0x9d, 0x60, 0xcb, 0x9c, 0xe2, 0x8b, 0x84, 0xe2, 0x85, 0x86, 0xf0, 0x9d, 0x94, 0xbb, 0xc2, 0xa8, 0xe2, 0x83, 0x9c, 0xe2, 0x89, 0x90, 0xe2, 0x88, 0xaf, 0xc2, 0xa8, 0xe2, 0x87, 0x93, 0xe2, 0x87, 0x90, 0xe2, 0x87, 0x94, 0xe2, 0xab, 0xa4, 0xe2, 0x9f, 0xb8, 0xe2, 0x9f, 0xba, 0xe2, 0x9f, 0xb9, 0xe2, 0x87, 0x92, 0xe2, 0x8a, 0xa8, 0xe2, 0x87, 0x91, 0xe2, 0x87, 0x95, 0xe2, 0x88, 0xa5, 0xe2, 0x86, 0x93, 0xe2, 0xa4, 0x93, 0xe2, 0x87, 0xb5, 0xcc, 0x91, 0xe2, 0xa5, 0x90, 0xe2, 0xa5, 0x9e, 0xe2, 0x86, 0xbd, 0xe2, 0xa5, 0x96, 0xe2, 0xa5, 0x9f, 0xe2, 0x87, 0x81, 0xe2, 0xa5, 0x97, 0xe2, 0x8a, 0xa4, 0xe2, 0x86, 0xa7, 0xe2, 0x87, 0x93, 0xf0, 0x9d, 0x92, 0x9f, 0xc4, 0x90, 0xc5, 0x8a, 0xc3, 0x90, 0xc3, 0x89, 0xc4, 0x9a, 0xc3, 0x8a, 0xd0, 0xad, 0xc4, 0x96, 0xf0, 0x9d, 0x94, 0x88, 0xc3, 0x88, 0xe2, 0x88, 0x88, 0xc4, 0x92, 0xe2, 0x97, 0xbb, 0xe2, 0x96, 0xab, 0xc4, 0x98, 0xf0, 0x9d, 0x94, 0xbc, 0xce, 0x95, 0xe2, 0xa9, 0xb5, 0xe2, 0x89, 0x82, 0xe2, 0x87, 0x8c, 0xe2, 0x84, 0xb0, 0xe2, 0xa9, 0xb3, 0xce, 0x97, 0xc3, 0x8b, 0xe2, 0x88, 0x83, 0xe2, 0x85, 0x87, 0xd0, 0xa4, 0xf0, 0x9d, 0x94, 0x89, 0xe2, 0x97, 0xbc, 0xe2, 0x96, 0xaa, 0xf0, 0x9d, 0x94, 0xbd, 0xe2, 0x88, 0x80, 0xe2, 0x84, 0xb1, 0xe2, 0x84, 0xb1, 0xd0, 0x83, 0x3e, 0xce, 0x93, 0xcf, 0x9c, 0xc4, 0x9e, 0xc4, 0xa2, 0xc4, 0x9c, 0xd0, 0x93, 0xc4, 0xa0, 0xf0, 0x9d, 0x94, 0x8a, 0xe2, 0x8b, 0x99, 0xf0, 0x9d, 0x94, 0xbe, 0xe2, 0x89, 0xa5, 0xe2, 0x8b, 0x9b, 0xe2, 0x89, 0xa7, 0xe2, 0xaa, 0xa2, 0xe2, 0x89, 0xb7, 0xe2, 0xa9, 0xbe, 0xe2, 0x89, 0xb3, 0xf0, 0x9d, 0x92, 0xa2, 0xe2, 0x89, 0xab, 0xd0, 0xaa, 0xcb, 0x87, 0x5e, 0xc4, 0xa4, 0xe2, 0x84, 0x8c, 0xe2, 0x84, 0x8b, 0xe2, 0x84, 0x8d, 0xe2, 0x94, 0x80, 0xe2, 0x84, 0x8b, 0xc4, 0xa6, 0xe2, 0x89, 0x8e, 0xe2, 0x89, 0x8f, 0xd0, 0x95, 0xc4, 0xb2, 0xd0, 0x81, 0xc3, 0x8d, 0xc3, 0x8e, 0xd0, 0x98, 0xc4, 0xb0, 0xe2, 0x84, 0x91, 0xc3, 0x8c, 0xe2, 0x84, 0x91, 0xc4, 0xaa, 0xe2, 0x85, 0x88, 0xe2, 0x87, 0x92, 0xe2, 0x88, 0xac, 0xe2, 0x88, 0xab, 0xe2, 0x8b, 0x82, 0xe2, 0x81, 0xa3, 0xe2, 0x81, 0xa2, 0xc4, 0xae, 0xf0, 0x9d, 0x95, 0x80, 0xce, 0x99, 0xe2, 0x84, 0x90, 0xc4, 0xa8, 0xd0, 0x86, 0xc3, 0x8f, 0xc4, 0xb4, 0xd0, 0x99, 0xf0, 0x9d, 0x94, 0x8d, 0xf0, 0x9d, 0x95, 0x81, 0xf0, 0x9d, 0x92, 0xa5, 0xd0, 0x88, 0xd0, 0x84, 0xd0, 0xa5, 0xd0, 0x8c, 0xce, 0x9a, 0xc4, 0xb6, 0xd0, 0x9a, 0xf0, 0x9d, 0x94, 0x8e, 0xf0, 0x9d, 0x95, 0x82, 0xf0, 0x9d, 0x92, 0xa6, 0xd0, 0x89, 0x3c, 0xc4, 0xb9, 0xce, 0x9b, 0xe2, 0x9f, 0xaa, 0xe2, 0x84, 0x92, 0xe2, 0x86, 0x9e, 0xc4, 0xbd, 0xc4, 0xbb, 0xd0, 0x9b, 0xe2, 0x9f, 0xa8, 0xe2, 0x86, 0x90, 0xe2, 0x87, 0xa4, 0xe2, 0x87, 0x86, 0xe2, 0x8c, 0x88, 0xe2, 0x9f, 0xa6, 0xe2, 0xa5, 0xa1, 0xe2, 0x87, 0x83, 0xe2, 0xa5, 0x99, 0xe2, 0x8c, 0x8a, 0xe2, 0x86, 0x94, 0xe2, 0xa5, 0x8e, 0xe2, 0x8a, 0xa3, 0xe2, 0x86, 0xa4, 0xe2, 0xa5, 0x9a, 0xe2, 0x8a, 0xb2, 0xe2, 0xa7, 0x8f, 0xe2, 0x8a, 0xb4, 0xe2, 0xa5, 0x91, 0xe2, 0xa5, 0xa0, 0xe2, 0x86, 0xbf, 0xe2, 0xa5, 0x98, 0xe2, 0x86, 0xbc, 0xe2, 0xa5, 0x92, 0xe2, 0x87, 0x90, 0xe2, 0x87, 0x94, 0xe2, 0x8b, 0x9a, 0xe2, 0x89, 0xa6, 0xe2, 0x89, 0xb6, 0xe2, 0xaa, 0xa1, 0xe2, 0xa9, 0xbd, 0xe2, 0x89, 0xb2, 0xf0, 0x9d, 0x94, 0x8f, 0xe2, 0x8b, 0x98, 0xe2, 0x87, 0x9a, 0xc4, 0xbf, 0xe2, 0x9f, 0xb5, 0xe2, 0x9f, 0xb7, 0xe2, 0x9f, 0xb6, 0xe2, 0x9f, 0xb8, 0xe2, 0x9f, 0xba, 0xe2, 0x9f, 0xb9, 0xf0, 0x9d, 0x95, 0x83, 0xe2, 0x86, 0x99, 0xe2, 0x86, 0x98, 0xe2, 0x84, 0x92, 0xe2, 0x86, 0xb0, 0xc5, 0x81, 0xe2, 0x89, 0xaa, 0xe2, 0xa4, 0x85, 0xd0, 0x9c, 0xe2, 0x81, 0x9f, 0xe2, 0x84, 0xb3, 0xf0, 0x9d, 0x94, 0x90, 0xe2, 0x88, 0x93, 0xf0, 0x9d, 0x95, 0x84, 0xe2, 0x84, 0xb3, 0xce, 0x9c, 0xd0, 0x8a, 0xc5, 0x83, 0xc5, 0x87, 0xc5, 0x85, 0xd0, 0x9d, 0xe2, 0x80, 0x8b, 0xe2, 0x80, 0x8b, 0xe2, 0x80, 0x8b, 0xe2, 0x80, 0x8b, 0xe2, 0x89, 0xab, 0xe2, 0x89, 0xaa, 0xa, 0xf0, 0x9d, 0x94, 0x91, 0xe2, 0x81, 0xa0, 0xc2, 0xa0, 0xe2, 0x84, 0x95, 0xe2, 0xab, 0xac, 0xe2, 0x89, 0xa2, 0xe2, 0x89, 0xad, 0xe2, 0x88, 0xa6, 0xe2, 0x88, 0x89, 0xe2, 0x89, 0xa0, 0xe2, 0x89, 0x82, 0xcc, 0xb8, 0xe2, 0x88, 0x84, 0xe2, 0x89, 0xaf, 0xe2, 0x89, 0xb1, 0xe2, 0x89, 0xa7, 0xcc, 0xb8, 0xe2, 0x89, 0xab, 0xcc, 0xb8, 0xe2, 0x89, 0xb9, 0xe2, 0xa9, 0xbe, 0xcc, 0xb8, 0xe2, 0x89, 0xb5, 0xe2, 0x89, 0x8e, 0xcc, 0xb8, 0xe2, 0x89, 0x8f, 0xcc, 0xb8, 0xe2, 0x8b, 0xaa, 0xe2, 0xa7, 0x8f, 0xcc, 0xb8, 0xe2, 0x8b, 0xac, 0xe2, 0x89, 0xae, 0xe2, 0x89, 0xb0, 0xe2, 0x89, 0xb8, 0xe2, 0x89, 0xaa, 0xcc, 0xb8, 0xe2, 0xa9, 0xbd, 0xcc, 0xb8, 0xe2, 0x89, 0xb4, 0xe2, 0xaa, 0xa2, 0xcc, 0xb8, 0xe2, 0xaa, 0xa1, 0xcc, 0xb8, 0xe2, 0x8a, 0x80, 0xe2, 0xaa, 0xaf, 0xcc, 0xb8, 0xe2, 0x8b, 0xa0, 0xe2, 0x88, 0x8c, 0xe2, 0x8b, 0xab, 0xe2, 0xa7, 0x90, 0xcc, 0xb8, 0xe2, 0x8b, 0xad, 0xe2, 0x8a, 0x8f, 0xcc, 0xb8, 0xe2, 0x8b, 0xa2, 0xe2, 0x8a, 0x90, 0xcc, 0xb8, 0xe2, 0x8b, 0xa3, 0xe2, 0x8a, 0x82, 0xe2, 0x83, 0x92, 0xe2, 0x8a, 0x88, 0xe2, 0x8a, 0x81, 0xe2, 0xaa, 0xb0, 0xcc, 0xb8, 0xe2, 0x8b, 0xa1, 0xe2, 0x89, 0xbf, 0xcc, 0xb8, 0xe2, 0x8a, 0x83, 0xe2, 0x83, 0x92, 0xe2, 0x8a, 0x89, 0xe2, 0x89, 0x81, 0xe2, 0x89, 0x84, 0xe2, 0x89, 0x87, 0xe2, 0x89, 0x89, 0xe2, 0x88, 0xa4, 0xf0, 0x9d, 0x92, 0xa9, 0xc3, 0x91, 0xce, 0x9d, 0xc5, 0x92, 0xc3, 0x93, 0xc3, 0x94, 0xd0, 0x9e, 0xc5, 0x90, 0xf0, 0x9d, 0x94, 0x92, 0xc3, 0x92, 0xc5, 0x8c, 0xce, 0xa9, 0xce, 0x9f, 0xf0, 0x9d, 0x95, 0x86, 0xe2, 0x80, 0x9c, 0xe2, 0x80, 0x98, 0xe2, 0xa9, 0x94, 0xf0, 0x9d, 0x92, 0xaa, 0xc3, 0x98, 0xc3, 0x95, 0xe2, 0xa8, 0xb7, 0xc3, 0x96, 0xe2, 0x80, 0xbe, 0xe2, 0x8f, 0x9e, 0xe2, 0x8e, 0xb4, 0xe2, 0x8f, 0x9c, 0xe2, 0x88, 0x82, 0xd0, 0x9f, 0xf0, 0x9d, 0x94, 0x93, 0xce, 0xa6, 0xce, 0xa0, 0xc2, 0xb1, 0xe2, 0x84, 0x8c, 0xe2, 0x84, 0x99, 0xe2, 0xaa, 0xbb, 0xe2, 0x89, 0xba, 0xe2, 0xaa, 0xaf, 0xe2, 0x89, 0xbc, 0xe2, 0x89, 0xbe, 0xe2, 0x80, 0xb3, 0xe2, 0x88, 0x8f, 0xe2, 0x88, 0xb7, 0xe2, 0x88, 0x9d, 0xf0, 0x9d, 0x92, 0xab, 0xce, 0xa8, 0x22, 0xf0, 0x9d, 0x94, 0x94, 0xe2, 0x84, 0x9a, 0xf0, 0x9d, 0x92, 0xac, 0xe2, 0xa4, 0x90, 0xc2, 0xae, 0xc5, 0x94, 0xe2, 0x9f, 0xab, 0xe2, 0x86, 0xa0, 0xe2, 0xa4, 0x96, 0xc5, 0x98, 0xc5, 0x96, 0xd0, 0xa0, 0xe2, 0x84, 0x9c, 0xe2, 0x88, 0x8b, 0xe2, 0x87, 0x8b, 0xe2, 0xa5, 0xaf, 0xe2, 0x84, 0x9c, 0xce, 0xa1, 0xe2, 0x9f, 0xa9, 0xe2, 0x86, 0x92, 0xe2, 0x87, 0xa5, 0xe2, 0x87, 0x84, 0xe2, 0x8c, 0x89, 0xe2, 0x9f, 0xa7, 0xe2, 0xa5, 0x9d, 0xe2, 0x87, 0x82, 0xe2, 0xa5, 0x95, 0xe2, 0x8c, 0x8b, 0xe2, 0x8a, 0xa2, 0xe2, 0x86, 0xa6, 0xe2, 0xa5, 0x9b, 0xe2, 0x8a, 0xb3, 0xe2, 0xa7, 0x90, 0xe2, 0x8a, 0xb5, 0xe2, 0xa5, 0x8f, 0xe2, 0xa5, 0x9c, 0xe2, 0x86, 0xbe, 0xe2, 0xa5, 0x94, 0xe2, 0x87, 0x80, 0xe2, 0xa5, 0x93, 0xe2, 0x87, 0x92, 0xe2, 0x84, 0x9d, 0xe2, 0xa5, 0xb0, 0xe2, 0x87, 0x9b, 0xe2, 0x84, 0x9b, 0xe2, 0x86, 0xb1, 0xe2, 0xa7, 0xb4, 0xd0, 0xa9, 0xd0, 0xa8, 0xd0, 0xac, 0xc5, 0x9a, 0xe2, 0xaa, 0xbc, 0xc5, 0xa0, 0xc5, 0x9e, 0xc5, 0x9c, 0xd0, 0xa1, 0xf0, 0x9d, 0x94, 0x96, 0xe2, 0x86, 0x93, 0xe2, 0x86, 0x90, 0xe2, 0x86, 0x92, 0xe2, 0x86, 0x91, 0xce, 0xa3, 0xe2, 0x88, 0x98, 0xf0, 0x9d, 0x95, 0x8a, 0xe2, 0x88, 0x9a, 0xe2, 0x96, 0xa1, 0xe2, 0x8a, 0x93, 0xe2, 0x8a, 0x8f, 0xe2, 0x8a, 0x91, 0xe2, 0x8a, 0x90, 0xe2, 0x8a, 0x92, 0xe2, 0x8a, 0x94, 0xf0, 0x9d, 0x92, 0xae, 0xe2, 0x8b, 0x86, 0xe2, 0x8b, 0x90, 0xe2, 0x8b, 0x90, 0xe2, 0x8a, 0x86, 0xe2, 0x89, 0xbb, 0xe2, 0xaa, 0xb0, 0xe2, 0x89, 0xbd, 0xe2, 0x89, 0xbf, 0xe2, 0x88, 0x8b, 0xe2, 0x88, 0x91, 0xe2, 0x8b, 0x91, 0xe2, 0x8a, 0x83, 0xe2, 0x8a, 0x87, 0xe2, 0x8b, 0x91, 0xc3, 0x9e, 0xe2, 0x84, 0xa2, 0xd0, 0x8b, 0xd0, 0xa6, 0x9, 0xce, 0xa4, 0xc5, 0xa4, 0xc5, 0xa2, 0xd0, 0xa2, 0xf0, 0x9d, 0x94, 0x97, 0xe2, 0x88, 0xb4, 0xce, 0x98, 0xe2, 0x81, 0x9f, 0xe2, 0x80, 0x8a, 0xe2, 0x80, 0x89, 0xe2, 0x88, 0xbc, 0xe2, 0x89, 0x83, 0xe2, 0x89, 0x85, 0xe2, 0x89, 0x88, 0xf0, 0x9d, 0x95, 0x8b, 0xe2, 0x83, 0x9b, 0xf0, 0x9d, 0x92, 0xaf, 0xc5, 0xa6, 0xc3, 0x9a, 0xe2, 0x86, 0x9f, 0xe2, 0xa5, 0x89, 0xd0, 0x8e, 0xc5, 0xac, 0xc3, 0x9b, 0xd0, 0xa3, 0xc5, 0xb0, 0xf0, 0x9d, 0x94, 0x98, 0xc3, 0x99, 0xc5, 0xaa, 0x5f, 0xe2, 0x8f, 0x9f, 0xe2, 0x8e, 0xb5, 0xe2, 0x8f, 0x9d, 0xe2, 0x8b, 0x83, 0xe2, 0x8a, 0x8e, 0xc5, 0xb2, 0xf0, 0x9d, 0x95, 0x8c, 0xe2, 0x86, 0x91, 0xe2, 0xa4, 0x92, 0xe2, 0x87, 0x85, 0xe2, 0x86, 0x95, 0xe2, 0xa5, 0xae, 0xe2, 0x8a, 0xa5, 0xe2, 0x86, 0xa5, 0xe2, 0x87, 0x91, 0xe2, 0x87, 0x95, 0xe2, 0x86, 0x96, 0xe2, 0x86, 0x97, 0xcf, 0x92, 0xce, 0xa5, 0xc5, 0xae, 0xf0, 0x9d, 0x92, 0xb0, 0xc5, 0xa8, 0xc3, 0x9c, 0xe2, 0x8a, 0xab, 0xe2, 0xab, 0xab, 0xd0, 0x92, 0xe2, 0x8a, 0xa9, 0xe2, 0xab, 0xa6, 0xe2, 0x8b, 0x81, 0xe2, 0x80, 0x96, 0xe2, 0x80, 0x96, 0xe2, 0x88, 0xa3, 0x7c, 0xe2, 0x9d, 0x98, 0xe2, 0x89, 0x80, 0xe2, 0x80, 0x8a, 0xf0, 0x9d, 0x94, 0x99, 0xf0, 0x9d, 0x95, 0x8d, 0xf0, 0x9d, 0x92, 0xb1, 0xe2, 0x8a, 0xaa, 0xc5, 0xb4, 0xe2, 0x8b, 0x80, 0xf0, 0x9d, 0x94, 0x9a, 0xf0, 0x9d, 0x95, 0x8e, 0xf0, 0x9d, 0x92, 0xb2, 0xf0, 0x9d, 0x94, 0x9b, 0xce, 0x9e, 0xf0, 0x9d, 0x95, 0x8f, 0xf0, 0x9d, 0x92, 0xb3, 0xd0, 0xaf, 0xd0, 0x87, 0xd0, 0xae, 0xc3, 0x9d, 0xc5, 0xb6, 0xd0, 0xab, 0xf0, 0x9d, 0x94, 0x9c, 0xf0, 0x9d, 0x95, 0x90, 0xf0, 0x9d, 0x92, 0xb4, 0xc5, 0xb8, 0xd0, 0x96, 0xc5, 0xb9, 0xc5, 0xbd, 0xd0, 0x97, 0xc5, 0xbb, 0xe2, 0x80, 0x8b, 0xce, 0x96, 0xe2, 0x84, 0xa8, 0xe2, 0x84, 0xa4, 0xf0, 0x9d, 0x92, 0xb5, 0xc3, 0xa1, 0xc4, 0x83, 0xe2, 0x88, 0xbe, 0xe2, 0x88, 0xbe, 0xcc, 0xb3, 0xe2, 0x88, 0xbf, 0xc3, 0xa2, 0xc2, 0xb4, 0xd0, 0xb0, 0xc3, 0xa6, 0xe2, 0x81, 0xa1, 0xf0, 0x9d, 0x94, 0x9e, 0xc3, 0xa0, 0xe2, 0x84, 0xb5, 0xe2, 0x84, 0xb5, 0xce, 0xb1, 0xc4, 0x81, 0xe2, 0xa8, 0xbf, 0x26, 0xe2, 0x88, 0xa7, 0xe2, 0xa9, 0x95, 0xe2, 0xa9, 0x9c, 0xe2, 0xa9, 0x98, 0xe2, 0xa9, 0x9a, 0xe2, 0x88, 0xa0, 0xe2, 0xa6, 0xa4, 0xe2, 0x88, 0xa0, 0xe2, 0x88, 0xa1, 0xe2, 0xa6, 0xa8, 0xe2, 0xa6, 0xa9, 0xe2, 0xa6, 0xaa, 0xe2, 0xa6, 0xab, 0xe2, 0xa6, 0xac, 0xe2, 0xa6, 0xad, 0xe2, 0xa6, 0xae, 0xe2, 0xa6, 0xaf, 0xe2, 0x88, 0x9f, 0xe2, 0x8a, 0xbe, 0xe2, 0xa6, 0x9d, 0xe2, 0x88, 0xa2, 0xc3, 0x85, 0xe2, 0x8d, 0xbc, 0xc4, 0x85, 0xf0, 0x9d, 0x95, 0x92, 0xe2, 0x89, 0x88, 0xe2, 0xa9, 0xb0, 0xe2, 0xa9, 0xaf, 0xe2, 0x89, 0x8a, 0xe2, 0x89, 0x8b, 0x27, 0xe2, 0x89, 0x88, 0xe2, 0x89, 0x8a, 0xc3, 0xa5, 0xf0, 0x9d, 0x92, 0xb6, 0x2a, 0xe2, 0x89, 0x88, 0xe2, 0x89, 0x8d, 0xc3, 0xa3, 0xc3, 0xa4, 0xe2, 0x88, 0xb3, 0xe2, 0xa8, 0x91, 0xe2, 0xab, 0xad, 0xe2, 0x89, 0x8c, 0xcf, 0xb6, 0xe2, 0x80, 0xb5, 0xe2, 0x88, 0xbd, 0xe2, 0x8b, 0x8d, 0xe2, 0x8a, 0xbd, 0xe2, 0x8c, 0x85, 0xe2, 0x8c, 0x85, 0xe2, 0x8e, 0xb5, 0xe2, 0x8e, 0xb6, 0xe2, 0x89, 0x8c, 0xd0, 0xb1, 0xe2, 0x80, 0x9e, 0xe2, 0x88, 0xb5, 0xe2, 0x88, 0xb5, 0xe2, 0xa6, 0xb0, 0xcf, 0xb6, 0xe2, 0x84, 0xac, 0xce, 0xb2, 0xe2, 0x84, 0xb6, 0xe2, 0x89, 0xac, 0xf0, 0x9d, 0x94, 0x9f, 0xe2, 0x8b, 0x82, 0xe2, 0x97, 0xaf, 0xe2, 0x8b, 0x83, 0xe2, 0xa8, 0x80, 0xe2, 0xa8, 0x81, 0xe2, 0xa8, 0x82, 0xe2, 0xa8, 0x86, 0xe2, 0x98, 0x85, 0xe2, 0x96, 0xbd, 0xe2, 0x96, 0xb3, 0xe2, 0xa8, 0x84, 0xe2, 0x8b, 0x81, 0xe2, 0x8b, 0x80, 0xe2, 0xa4, 0x8d, 0xe2, 0xa7, 0xab, 0xe2, 0x96, 0xaa, 0xe2, 0x96, 0xb4, 0xe2, 0x96, 0xbe, 0xe2, 0x97, 0x82, 0xe2, 0x96, 0xb8, 0xe2, 0x90, 0xa3, 0xe2, 0x96, 0x92, 0xe2, 0x96, 0x91, 0xe2, 0x96, 0x93, 0xe2, 0x96, 0x88, 0x3d, 0xe2, 0x83, 0xa5, 0xe2, 0x89, 0xa1, 0xe2, 0x83, 0xa5, 0xe2, 0x8c, 0x90, 0xf0, 0x9d, 0x95, 0x93, 0xe2, 0x8a, 0xa5, 0xe2, 0x8a, 0xa5, 0xe2, 0x8b, 0x88, 0xe2, 0x95, 0x97, 0xe2, 0x95, 0x94, 0xe2, 0x95, 0x96, 0xe2, 0x95, 0x93, 0xe2, 0x95, 0x90, 0xe2, 0x95, 0xa6, 0xe2, 0x95, 0xa9, 0xe2, 0x95, 0xa4, 0xe2, 0x95, 0xa7, 0xe2, 0x95, 0x9d, 0xe2, 0x95, 0x9a, 0xe2, 0x95, 0x9c, 0xe2, 0x95, 0x99, 0xe2, 0x95, 0x91, 0xe2, 0x95, 0xac, 0xe2, 0x95, 0xa3, 0xe2, 0x95, 0xa0, 0xe2, 0x95, 0xab, 0xe2, 0x95, 0xa2, 0xe2, 0x95, 0x9f, 0xe2, 0xa7, 0x89, 0xe2, 0x95, 0x95, 0xe2, 0x95, 0x92, 0xe2, 0x94, 0x90, 0xe2, 0x94, 0x8c, 0xe2, 0x94, 0x80, 0xe2, 0x95, 0xa5, 0xe2, 0x95, 0xa8, 0xe2, 0x94, 0xac, 0xe2, 0x94, 0xb4, 0xe2, 0x8a, 0x9f, 0xe2, 0x8a, 0x9e, 0xe2, 0x8a, 0xa0, 0xe2, 0x95, 0x9b, 0xe2, 0x95, 0x98, 0xe2, 0x94, 0x98, 0xe2, 0x94, 0x94, 0xe2, 0x94, 0x82, 0xe2, 0x95, 0xaa, 0xe2, 0x95, 0xa1, 0xe2, 0x95, 0x9e, 0xe2, 0x94, 0xbc, 0xe2, 0x94, 0xa4, 0xe2, 0x94, 0x9c, 0xe2, 0x80, 0xb5, 0xcb, 0x98, 0xc2, 0xa6, 0xf0, 0x9d, 0x92, 0xb7, 0xe2, 0x81, 0x8f, 0xe2, 0x88, 0xbd, 0xe2, 0x8b, 0x8d, 0x5c, 0xe2, 0xa7, 0x85, 0xe2, 0x9f, 0x88, 0xe2, 0x80, 0xa2, 0xe2, 0x80, 0xa2, 0xe2, 0x89, 0x8e, 0xe2, 0xaa, 0xae, 0xe2, 0x89, 0x8f, 0xe2, 0x89, 0x8f, 0xc4, 0x87, 0xe2, 0x88, 0xa9, 0xe2, 0xa9, 0x84, 0xe2, 0xa9, 0x89, 0xe2, 0xa9, 0x8b, 0xe2, 0xa9, 0x87, 0xe2, 0xa9, 0x80, 0xe2, 0x88, 0xa9, 0xef, 0xb8, 0x80, 0xe2, 0x81, 0x81, 0xcb, 0x87, 0xe2, 0xa9, 0x8d, 0xc4, 0x8d, 0xc3, 0xa7, 0xc4, 0x89, 0xe2, 0xa9, 0x8c, 0xe2, 0xa9, 0x90, 0xc4, 0x8b, 0xc2, 0xb8, 0xe2, 0xa6, 0xb2, 0xc2, 0xa2, 0xc2, 0xb7, 0xf0, 0x9d, 0x94, 0xa0, 0xd1, 0x87, 0xe2, 0x9c, 0x93, 0xe2, 0x9c, 0x93, 0xcf, 0x87, 0xe2, 0x97, 0x8b, 0xe2, 0xa7, 0x83, 0xcb, 0x86, 0xe2, 0x89, 0x97, 0xe2, 0x86, 0xba, 0xe2, 0x86, 0xbb, 0xc2, 0xae, 0xe2, 0x93, 0x88, 0xe2, 0x8a, 0x9b, 0xe2, 0x8a, 0x9a, 0xe2, 0x8a, 0x9d, 0xe2, 0x89, 0x97, 0xe2, 0xa8, 0x90, 0xe2, 0xab, 0xaf, 0xe2, 0xa7, 0x82, 0xe2, 0x99, 0xa3, 0xe2, 0x99, 0xa3, 0x3a, 0xe2, 0x89, 0x94, 0xe2, 0x89, 0x94, 0x2c, 0x40, 0xe2, 0x88, 0x81, 0xe2, 0x88, 0x98, 0xe2, 0x88, 0x81, 0xe2, 0x84, 0x82, 0xe2, 0x89, 0x85, 0xe2, 0xa9, 0xad, 0xe2, 0x88, 0xae, 0xf0, 0x9d, 0x95, 0x94, 0xe2, 0x88, 0x90, 0xc2, 0xa9, 0xe2, 0x84, 0x97, 0xe2, 0x86, 0xb5, 0xe2, 0x9c, 0x97, 0xf0, 0x9d, 0x92, 0xb8, 0xe2, 0xab, 0x8f, 0xe2, 0xab, 0x91, 0xe2, 0xab, 0x90, 0xe2, 0xab, 0x92, 0xe2, 0x8b, 0xaf, 0xe2, 0xa4, 0xb8, 0xe2, 0xa4, 0xb5, 0xe2, 0x8b, 0x9e, 0xe2, 0x8b, 0x9f, 0xe2, 0x86, 0xb6, 0xe2, 0xa4, 0xbd, 0xe2, 0x88, 0xaa, 0xe2, 0xa9, 0x88, 0xe2, 0xa9, 0x86, 0xe2, 0xa9, 0x8a, 0xe2, 0x8a, 0x8d, 0xe2, 0xa9, 0x85, 0xe2, 0x88, 0xaa, 0xef, 0xb8, 0x80, 0xe2, 0x86, 0xb7, 0xe2, 0xa4, 0xbc, 0xe2, 0x8b, 0x9e, 0xe2, 0x8b, 0x9f, 0xe2, 0x8b, 0x8e, 0xe2, 0x8b, 0x8f, 0xc2, 0xa4, 0xe2, 0x86, 0xb6, 0xe2, 0x86, 0xb7, 0xe2, 0x8b, 0x8e, 0xe2, 0x8b, 0x8f, 0xe2, 0x88, 0xb2, 0xe2, 0x88, 0xb1, 0xe2, 0x8c, 0xad, 0xe2, 0x87, 0x93, 0xe2, 0xa5, 0xa5, 0xe2, 0x80, 0xa0, 0xe2, 0x84, 0xb8, 0xe2, 0x86, 0x93, 0xe2, 0x80, 0x90, 0xe2, 0x8a, 0xa3, 0xe2, 0xa4, 0x8f, 0xcb, 0x9d, 0xc4, 0x8f, 0xd0, 0xb4, 0xe2, 0x85, 0x86, 0xe2, 0x80, 0xa1, 0xe2, 0x87, 0x8a, 0xe2, 0xa9, 0xb7, 0xc2, 0xb0, 0xce, 0xb4, 0xe2, 0xa6, 0xb1, 0xe2, 0xa5, 0xbf, 0xf0, 0x9d, 0x94, 0xa1, 0xe2, 0x87, 0x83, 0xe2, 0x87, 0x82, 0xe2, 0x8b, 0x84, 0xe2, 0x8b, 0x84, 0xe2, 0x99, 0xa6, 0xe2, 0x99, 0xa6, 0xc2, 0xa8, 0xcf, 0x9d, 0xe2, 0x8b, 0xb2, 0xc3, 0xb7, 0xc3, 0xb7, 0xe2, 0x8b, 0x87, 0xe2, 0x8b, 0x87, 0xd1, 0x92, 0xe2, 0x8c, 0x9e, 0xe2, 0x8c, 0x8d, 0x24, 0xf0, 0x9d, 0x95, 0x95, 0xcb, 0x99, 0xe2, 0x89, 0x90, 0xe2, 0x89, 0x91, 0xe2, 0x88, 0xb8, 0xe2, 0x88, 0x94, 0xe2, 0x8a, 0xa1, 0xe2, 0x8c, 0x86, 0xe2, 0x86, 0x93, 0xe2, 0x87, 0x8a, 0xe2, 0x87, 0x83, 0xe2, 0x87, 0x82, 0xe2, 0xa4, 0x90, 0xe2, 0x8c, 0x9f, 0xe2, 0x8c, 0x8c, 0xf0, 0x9d, 0x92, 0xb9, 0xd1, 0x95, 0xe2, 0xa7, 0xb6, 0xc4, 0x91, 0xe2, 0x8b, 0xb1, 0xe2, 0x96, 0xbf, 0xe2, 0x96, 0xbe, 0xe2, 0x87, 0xb5, 0xe2, 0xa5, 0xaf, 0xe2, 0xa6, 0xa6, 0xd1, 0x9f, 0xe2, 0x9f, 0xbf, 0xe2, 0xa9, 0xb7, 0xe2, 0x89, 0x91, 0xc3, 0xa9, 0xe2, 0xa9, 0xae, 0xc4, 0x9b, 0xe2, 0x89, 0x96, 0xc3, 0xaa, 0xe2, 0x89, 0x95, 0xd1, 0x8d, 0xc4, 0x97, 0xe2, 0x85, 0x87, 0xe2, 0x89, 0x92, 0xf0, 0x9d, 0x94, 0xa2, 0xe2, 0xaa, 0x9a, 0xc3, 0xa8, 0xe2, 0xaa, 0x96, 0xe2, 0xaa, 0x98, 0xe2, 0xaa, 0x99, 0xe2, 0x8f, 0xa7, 0xe2, 0x84, 0x93, 0xe2, 0xaa, 0x95, 0xe2, 0xaa, 0x97, 0xc4, 0x93, 0xe2, 0x88, 0x85, 0xe2, 0x88, 0x85, 0xe2, 0x88, 0x85, 0xe2, 0x80, 0x83, 0xe2, 0x80, 0x84, 0xe2, 0x80, 0x85, 0xc5, 0x8b, 0xe2, 0x80, 0x82, 0xc4, 0x99, 0xf0, 0x9d, 0x95, 0x96, 0xe2, 0x8b, 0x95, 0xe2, 0xa7, 0xa3, 0xe2, 0xa9, 0xb1, 0xce, 0xb5, 0xce, 0xb5, 0xcf, 0xb5, 0xe2, 0x89, 0x96, 0xe2, 0x89, 0x95, 0xe2, 0x89, 0x82, 0xe2, 0xaa, 0x96, 0xe2, 0xaa, 0x95, 0x3d, 0xe2, 0x89, 0x9f, 0xe2, 0x89, 0xa1, 0xe2, 0xa9, 0xb8, 0xe2, 0xa7, 0xa5, 0xe2, 0x89, 0x93, 0xe2, 0xa5, 0xb1, 0xe2, 0x84, 0xaf, 0xe2, 0x89, 0x90, 0xe2, 0x89, 0x82, 0xce, 0xb7, 0xc3, 0xb0, 0xc3, 0xab, 0xe2, 0x82, 0xac, 0x21, 0xe2, 0x88, 0x83, 0xe2, 0x84, 0xb0, 0xe2, 0x85, 0x87, 0xe2, 0x89, 0x92, 0xd1, 0x84, 0xe2, 0x99, 0x80, 0xef, 0xac, 0x83, 0xef, 0xac, 0x80, 0xef, 0xac, 0x84, 0xf0, 0x9d, 0x94, 0xa3, 0xef, 0xac, 0x81, 0x66, 0x6a, 0xe2, 0x99, 0xad, 0xef, 0xac, 0x82, 0xe2, 0x96, 0xb1, 0xc6, 0x92, 0xf0, 0x9d, 0x95, 0x97, 0xe2, 0x88, 0x80, 0xe2, 0x8b, 0x94, 0xe2, 0xab, 0x99, 0xe2, 0xa8, 0x8d, 0xc2, 0xbd, 0xe2, 0x85, 0x93, 0xc2, 0xbc, 0xe2, 0x85, 0x95, 0xe2, 0x85, 0x99, 0xe2, 0x85, 0x9b, 0xe2, 0x85, 0x94, 0xe2, 0x85, 0x96, 0xc2, 0xbe, 0xe2, 0x85, 0x97, 0xe2, 0x85, 0x9c, 0xe2, 0x85, 0x98, 0xe2, 0x85, 0x9a, 0xe2, 0x85, 0x9d, 0xe2, 0x85, 0x9e, 0xe2, 0x81, 0x84, 0xe2, 0x8c, 0xa2, 0xf0, 0x9d, 0x92, 0xbb, 0xe2, 0x89, 0xa7, 0xe2, 0xaa, 0x8c, 0xc7, 0xb5, 0xce, 0xb3, 0xcf, 0x9d, 0xe2, 0xaa, 0x86, 0xc4, 0x9f, 0xc4, 0x9d, 0xd0, 0xb3, 0xc4, 0xa1, 0xe2, 0x89, 0xa5, 0xe2, 0x8b, 0x9b, 0xe2, 0x89, 0xa5, 0xe2, 0x89, 0xa7, 0xe2, 0xa9, 0xbe, 0xe2, 0xa9, 0xbe, 0xe2, 0xaa, 0xa9, 0xe2, 0xaa, 0x80, 0xe2, 0xaa, 0x82, 0xe2, 0xaa, 0x84, 0xe2, 0x8b, 0x9b, 0xef, 0xb8, 0x80, 0xe2, 0xaa, 0x94, 0xf0, 0x9d, 0x94, 0xa4, 0xe2, 0x89, 0xab, 0xe2, 0x8b, 0x99, 0xe2, 0x84, 0xb7, 0xd1, 0x93, 0xe2, 0x89, 0xb7, 0xe2, 0xaa, 0x92, 0xe2, 0xaa, 0xa5, 0xe2, 0xaa, 0xa4, 0xe2, 0x89, 0xa9, 0xe2, 0xaa, 0x8a, 0xe2, 0xaa, 0x8a, 0xe2, 0xaa, 0x88, 0xe2, 0xaa, 0x88, 0xe2, 0x89, 0xa9, 0xe2, 0x8b, 0xa7, 0xf0, 0x9d, 0x95, 0x98, 0x60, 0xe2, 0x84, 0x8a, 0xe2, 0x89, 0xb3, 0xe2, 0xaa, 0x8e, 0xe2, 0xaa, 0x90, 0x3e, 0xe2, 0xaa, 0xa7, 0xe2, 0xa9, 0xba, 0xe2, 0x8b, 0x97, 0xe2, 0xa6, 0x95, 0xe2, 0xa9, 0xbc, 0xe2, 0xaa, 0x86, 0xe2, 0xa5, 0xb8, 0xe2, 0x8b, 0x97, 0xe2, 0x8b, 0x9b, 0xe2, 0xaa, 0x8c, 0xe2, 0x89, 0xb7, 0xe2, 0x89, 0xb3, 0xe2, 0x89, 0xa9, 0xef, 0xb8, 0x80, 0xe2, 0x89, 0xa9, 0xef, 0xb8, 0x80, 0xe2, 0x87, 0x94, 0xe2, 0x80, 0x8a, 0xc2, 0xbd, 0xe2, 0x84, 0x8b, 0xd1, 0x8a, 0xe2, 0x86, 0x94, 0xe2, 0xa5, 0x88, 0xe2, 0x86, 0xad, 0xe2, 0x84, 0x8f, 0xc4, 0xa5, 0xe2, 0x99, 0xa5, 0xe2, 0x99, 0xa5, 0xe2, 0x80, 0xa6, 0xe2, 0x8a, 0xb9, 0xf0, 0x9d, 0x94, 0xa5, 0xe2, 0xa4, 0xa5, 0xe2, 0xa4, 0xa6, 0xe2, 0x87, 0xbf, 0xe2, 0x88, 0xbb, 0xe2, 0x86, 0xa9, 0xe2, 0x86, 0xaa, 0xf0, 0x9d, 0x95, 0x99, 0xe2, 0x80, 0x95, 0xf0, 0x9d, 0x92, 0xbd, 0xe2, 0x84, 0x8f, 0xc4, 0xa7, 0xe2, 0x81, 0x83, 0xe2, 0x80, 0x90, 0xc3, 0xad, 0xe2, 0x81, 0xa3, 0xc3, 0xae, 0xd0, 0xb8, 0xd0, 0xb5, 0xc2, 0xa1, 0xe2, 0x87, 0x94, 0xf0, 0x9d, 0x94, 0xa6, 0xc3, 0xac, 0xe2, 0x85, 0x88, 0xe2, 0xa8, 0x8c, 0xe2, 0x88, 0xad, 0xe2, 0xa7, 0x9c, 0xe2, 0x84, 0xa9, 0xc4, 0xb3, 0xc4, 0xab, 0xe2, 0x84, 0x91, 0xe2, 0x84, 0x90, 0xe2, 0x84, 0x91, 0xc4, 0xb1, 0xe2, 0x8a, 0xb7, 0xc6, 0xb5, 0xe2, 0x88, 0x88, 0xe2, 0x84, 0x85, 0xe2, 0x88, 0x9e, 0xe2, 0xa7, 0x9d, 0xc4, 0xb1, 0xe2, 0x88, 0xab, 0xe2, 0x8a, 0xba, 0xe2, 0x84, 0xa4, 0xe2, 0x8a, 0xba, 0xe2, 0xa8, 0x97, 0xe2, 0xa8, 0xbc, 0xd1, 0x91, 0xc4, 0xaf, 0xf0, 0x9d, 0x95, 0x9a, 0xce, 0xb9, 0xe2, 0xa8, 0xbc, 0xc2, 0xbf, 0xf0, 0x9d, 0x92, 0xbe, 0xe2, 0x88, 0x88, 0xe2, 0x8b, 0xb9, 0xe2, 0x8b, 0xb5, 0xe2, 0x8b, 0xb4, 0xe2, 0x8b, 0xb3, 0xe2, 0x88, 0x88, 0xe2, 0x81, 0xa2, 0xc4, 0xa9, 0xd1, 0x96, 0xc3, 0xaf, 0xc4, 0xb5, 0xd0, 0xb9, 0xf0, 0x9d, 0x94, 0xa7, 0xc8, 0xb7, 0xf0, 0x9d, 0x95, 0x9b, 0xf0, 0x9d, 0x92, 0xbf, 0xd1, 0x98, 0xd1, 0x94, 0xce, 0xba, 0xcf, 0xb0, 0xc4, 0xb7, 0xd0, 0xba, 0xf0, 0x9d, 0x94, 0xa8, 0xc4, 0xb8, 0xd1, 0x85, 0xd1, 0x9c, 0xf0, 0x9d, 0x95, 0x9c, 0xf0, 0x9d, 0x93, 0x80, 0xe2, 0x87, 0x9a, 0xe2, 0x87, 0x90, 0xe2, 0xa4, 0x9b, 0xe2, 0xa4, 0x8e, 0xe2, 0x89, 0xa6, 0xe2, 0xaa, 0x8b, 0xe2, 0xa5, 0xa2, 0xc4, 0xba, 0xe2, 0xa6, 0xb4, 0xe2, 0x84, 0x92, 0xce, 0xbb, 0xe2, 0x9f, 0xa8, 0xe2, 0xa6, 0x91, 0xe2, 0x9f, 0xa8, 0xe2, 0xaa, 0x85, 0xc2, 0xab, 0xe2, 0x86, 0x90, 0xe2, 0x87, 0xa4, 0xe2, 0xa4, 0x9f, 0xe2, 0xa4, 0x9d, 0xe2, 0x86, 0xa9, 0xe2, 0x86, 0xab, 0xe2, 0xa4, 0xb9, 0xe2, 0xa5, 0xb3, 0xe2, 0x86, 0xa2, 0xe2, 0xaa, 0xab, 0xe2, 0xa4, 0x99, 0xe2, 0xaa, 0xad, 0xe2, 0xaa, 0xad, 0xef, 0xb8, 0x80, 0xe2, 0xa4, 0x8c, 0xe2, 0x9d, 0xb2, 0x7b, 0x5b, 0xe2, 0xa6, 0x8b, 0xe2, 0xa6, 0x8f, 0xe2, 0xa6, 0x8d, 0xc4, 0xbe, 0xc4, 0xbc, 0xe2, 0x8c, 0x88, 0x7b, 0xd0, 0xbb, 0xe2, 0xa4, 0xb6, 0xe2, 0x80, 0x9c, 0xe2, 0x80, 0x9e, 0xe2, 0xa5, 0xa7, 0xe2, 0xa5, 0x8b, 0xe2, 0x86, 0xb2, 0xe2, 0x89, 0xa4, 0xe2, 0x86, 0x90, 0xe2, 0x86, 0xa2, 0xe2, 0x86, 0xbd, 0xe2, 0x86, 0xbc, 0xe2, 0x87, 0x87, 0xe2, 0x86, 0x94, 0xe2, 0x87, 0x86, 0xe2, 0x87, 0x8b, 0xe2, 0x86, 0xad, 0xe2, 0x8b, 0x8b, 0xe2, 0x8b, 0x9a, 0xe2, 0x89, 0xa4, 0xe2, 0x89, 0xa6, 0xe2, 0xa9, 0xbd, 0xe2, 0xa9, 0xbd, 0xe2, 0xaa, 0xa8, 0xe2, 0xa9, 0xbf, 0xe2, 0xaa, 0x81, 0xe2, 0xaa, 0x83, 0xe2, 0x8b, 0x9a, 0xef, 0xb8, 0x80, 0xe2, 0xaa, 0x93, 0xe2, 0xaa, 0x85, 0xe2, 0x8b, 0x96, 0xe2, 0x8b, 0x9a, 0xe2, 0xaa, 0x8b, 0xe2, 0x89, 0xb6, 0xe2, 0x89, 0xb2, 0xe2, 0xa5, 0xbc, 0xe2, 0x8c, 0x8a, 0xf0, 0x9d, 0x94, 0xa9, 0xe2, 0x89, 0xb6, 0xe2, 0xaa, 0x91, 0xe2, 0x86, 0xbd, 0xe2, 0x86, 0xbc, 0xe2, 0xa5, 0xaa, 0xe2, 0x96, 0x84, 0xd1, 0x99, 0xe2, 0x89, 0xaa, 0xe2, 0x87, 0x87, 0xe2, 0x8c, 0x9e, 0xe2, 0xa5, 0xab, 0xe2, 0x97, 0xba, 0xc5, 0x80, 0xe2, 0x8e, 0xb0, 0xe2, 0x8e, 0xb0, 0xe2, 0x89, 0xa8, 0xe2, 0xaa, 0x89, 0xe2, 0xaa, 0x89, 0xe2, 0xaa, 0x87, 0xe2, 0xaa, 0x87, 0xe2, 0x89, 0xa8, 0xe2, 0x8b, 0xa6, 0xe2, 0x9f, 0xac, 0xe2, 0x87, 0xbd, 0xe2, 0x9f, 0xa6, 0xe2, 0x9f, 0xb5, 0xe2, 0x9f, 0xb7, 0xe2, 0x9f, 0xbc, 0xe2, 0x9f, 0xb6, 0xe2, 0x86, 0xab, 0xe2, 0x86, 0xac, 0xe2, 0xa6, 0x85, 0xf0, 0x9d, 0x95, 0x9d, 0xe2, 0xa8, 0xad, 0xe2, 0xa8, 0xb4, 0xe2, 0x88, 0x97, 0x5f, 0xe2, 0x97, 0x8a, 0xe2, 0x97, 0x8a, 0xe2, 0xa7, 0xab, 0x28, 0xe2, 0xa6, 0x93, 0xe2, 0x87, 0x86, 0xe2, 0x8c, 0x9f, 0xe2, 0x87, 0x8b, 0xe2, 0xa5, 0xad, 0xe2, 0x80, 0x8e, 0xe2, 0x8a, 0xbf, 0xe2, 0x80, 0xb9, 0xf0, 0x9d, 0x93, 0x81, 0xe2, 0x86, 0xb0, 0xe2, 0x89, 0xb2, 0xe2, 0xaa, 0x8d, 0xe2, 0xaa, 0x8f, 0x5b, 0xe2, 0x80, 0x98, 0xe2, 0x80, 0x9a, 0xc5, 0x82, 0x3c, 0xe2, 0xaa, 0xa6, 0xe2, 0xa9, 0xb9, 0xe2, 0x8b, 0x96, 0xe2, 0x8b, 0x8b, 0xe2, 0x8b, 0x89, 0xe2, 0xa5, 0xb6, 0xe2, 0xa9, 0xbb, 0xe2, 0xa6, 0x96, 0xe2, 0x97, 0x83, 0xe2, 0x8a, 0xb4, 0xe2, 0x97, 0x82, 0xe2, 0xa5, 0x8a, 0xe2, 0xa5, 0xa6, 0xe2, 0x89, 0xa8, 0xef, 0xb8, 0x80, 0xe2, 0x89, 0xa8, 0xef, 0xb8, 0x80, 0xe2, 0x88, 0xba, 0xc2, 0xaf, 0xe2, 0x99, 0x82, 0xe2, 0x9c, 0xa0, 0xe2, 0x9c, 0xa0, 0xe2, 0x86, 0xa6, 0xe2, 0x86, 0xa6, 0xe2, 0x86, 0xa7, 0xe2, 0x86, 0xa4, 0xe2, 0x86, 0xa5, 0xe2, 0x96, 0xae, 0xe2, 0xa8, 0xa9, 0xd0, 0xbc, 0xe2, 0x80, 0x94, 0xe2, 0x88, 0xa1, 0xf0, 0x9d, 0x94, 0xaa, 0xe2, 0x84, 0xa7, 0xc2, 0xb5, 0xe2, 0x88, 0xa3, 0x2a, 0xe2, 0xab, 0xb0, 0xc2, 0xb7, 0xe2, 0x88, 0x92, 0xe2, 0x8a, 0x9f, 0xe2, 0x88, 0xb8, 0xe2, 0xa8, 0xaa, 0xe2, 0xab, 0x9b, 0xe2, 0x80, 0xa6, 0xe2, 0x88, 0x93, 0xe2, 0x8a, 0xa7, 0xf0, 0x9d, 0x95, 0x9e, 0xe2, 0x88, 0x93, 0xf0, 0x9d, 0x93, 0x82, 0xe2, 0x88, 0xbe, 0xce, 0xbc, 0xe2, 0x8a, 0xb8, 0xe2, 0x8a, 0xb8, 0xe2, 0x8b, 0x99, 0xcc, 0xb8, 0xe2, 0x89, 0xab, 0xe2, 0x83, 0x92, 0xe2, 0x89, 0xab, 0xcc, 0xb8, 0xe2, 0x87, 0x8d, 0xe2, 0x87, 0x8e, 0xe2, 0x8b, 0x98, 0xcc, 0xb8, 0xe2, 0x89, 0xaa, 0xe2, 0x83, 0x92, 0xe2, 0x89, 0xaa, 0xcc, 0xb8, 0xe2, 0x87, 0x8f, 0xe2, 0x8a, 0xaf, 0xe2, 0x8a, 0xae, 0xe2, 0x88, 0x87, 0xc5, 0x84, 0xe2, 0x88, 0xa0, 0xe2, 0x83, 0x92, 0xe2, 0x89, 0x89, 0xe2, 0xa9, 0xb0, 0xcc, 0xb8, 0xe2, 0x89, 0x8b, 0xcc, 0xb8, 0xc5, 0x89, 0xe2, 0x89, 0x89, 0xe2, 0x99, 0xae, 0xe2, 0x99, 0xae, 0xe2, 0x84, 0x95, 0xc2, 0xa0, 0xe2, 0x89, 0x8e, 0xcc, 0xb8, 0xe2, 0x89, 0x8f, 0xcc, 0xb8, 0xe2, 0xa9, 0x83, 0xc5, 0x88, 0xc5, 0x86, 0xe2, 0x89, 0x87, 0xe2, 0xa9, 0xad, 0xcc, 0xb8, 0xe2, 0xa9, 0x82, 0xd0, 0xbd, 0xe2, 0x80, 0x93, 0xe2, 0x89, 0xa0, 0xe2, 0x87, 0x97, 0xe2, 0xa4, 0xa4, 0xe2, 0x86, 0x97, 0xe2, 0x86, 0x97, 0xe2, 0x89, 0x90, 0xcc, 0xb8, 0xe2, 0x89, 0xa2, 0xe2, 0xa4, 0xa8, 0xe2, 0x89, 0x82, 0xcc, 0xb8, 0xe2, 0x88, 0x84, 0xe2, 0x88, 0x84, 0xf0, 0x9d, 0x94, 0xab, 0xe2, 0x89, 0xa7, 0xcc, 0xb8, 0xe2, 0x89, 0xb1, 0xe2, 0x89, 0xb1, 0xe2, 0x89, 0xa7, 0xcc, 0xb8, 0xe2, 0xa9, 0xbe, 0xcc, 0xb8, 0xe2, 0xa9, 0xbe, 0xcc, 0xb8, 0xe2, 0x89, 0xb5, 0xe2, 0x89, 0xaf, 0xe2, 0x89, 0xaf, 0xe2, 0x87, 0x8e, 0xe2, 0x86, 0xae, 0xe2, 0xab, 0xb2, 0xe2, 0x88, 0x8b, 0xe2, 0x8b, 0xbc, 0xe2, 0x8b, 0xba, 0xe2, 0x88, 0x8b, 0xd1, 0x9a, 0xe2, 0x87, 0x8d, 0xe2, 0x89, 0xa6, 0xcc, 0xb8, 0xe2, 0x86, 0x9a, 0xe2, 0x80, 0xa5, 0xe2, 0x89, 0xb0, 0xe2, 0x86, 0x9a, 0xe2, 0x86, 0xae, 0xe2, 0x89, 0xb0, 0xe2, 0x89, 0xa6, 0xcc, 0xb8, 0xe2, 0xa9, 0xbd, 0xcc, 0xb8, 0xe2, 0xa9, 0xbd, 0xcc, 0xb8, 0xe2, 0x89, 0xae, 0xe2, 0x89, 0xb4, 0xe2, 0x89, 0xae, 0xe2, 0x8b, 0xaa, 0xe2, 0x8b, 0xac, 0xe2, 0x88, 0xa4, 0xf0, 0x9d, 0x95, 0x9f, 0xc2, 0xac, 0xe2, 0x88, 0x89, 0xe2, 0x8b, 0xb9, 0xcc, 0xb8, 0xe2, 0x8b, 0xb5, 0xcc, 0xb8, 0xe2, 0x88, 0x89, 0xe2, 0x8b, 0xb7, 0xe2, 0x8b, 0xb6, 0xe2, 0x88, 0x8c, 0xe2, 0x88, 0x8c, 0xe2, 0x8b, 0xbe, 0xe2, 0x8b, 0xbd, 0xe2, 0x88, 0xa6, 0xe2, 0x88, 0xa6, 0xe2, 0xab, 0xbd, 0xe2, 0x83, 0xa5, 0xe2, 0x88, 0x82, 0xcc, 0xb8, 0xe2, 0xa8, 0x94, 0xe2, 0x8a, 0x80, 0xe2, 0x8b, 0xa0, 0xe2, 0xaa, 0xaf, 0xcc, 0xb8, 0xe2, 0x8a, 0x80, 0xe2, 0xaa, 0xaf, 0xcc, 0xb8, 0xe2, 0x87, 0x8f, 0xe2, 0x86, 0x9b, 0xe2, 0xa4, 0xb3, 0xcc, 0xb8, 0xe2, 0x86, 0x9d, 0xcc, 0xb8, 0xe2, 0x86, 0x9b, 0xe2, 0x8b, 0xab, 0xe2, 0x8b, 0xad, 0xe2, 0x8a, 0x81, 0xe2, 0x8b, 0xa1, 0xe2, 0xaa, 0xb0, 0xcc, 0xb8, 0xf0, 0x9d, 0x93, 0x83, 0xe2, 0x88, 0xa4, 0xe2, 0x88, 0xa6, 0xe2, 0x89, 0x81, 0xe2, 0x89, 0x84, 0xe2, 0x89, 0x84, 0xe2, 0x88, 0xa4, 0xe2, 0x88, 0xa6, 0xe2, 0x8b, 0xa2, 0xe2, 0x8b, 0xa3, 0xe2, 0x8a, 0x84, 0xe2, 0xab, 0x85, 0xcc, 0xb8, 0xe2, 0x8a, 0x88, 0xe2, 0x8a, 0x82, 0xe2, 0x83, 0x92, 0xe2, 0x8a, 0x88, 0xe2, 0xab, 0x85, 0xcc, 0xb8, 0xe2, 0x8a, 0x81, 0xe2, 0xaa, 0xb0, 0xcc, 0xb8, 0xe2, 0x8a, 0x85, 0xe2, 0xab, 0x86, 0xcc, 0xb8, 0xe2, 0x8a, 0x89, 0xe2, 0x8a, 0x83, 0xe2, 0x83, 0x92, 0xe2, 0x8a, 0x89, 0xe2, 0xab, 0x86, 0xcc, 0xb8, 0xe2, 0x89, 0xb9, 0xc3, 0xb1, 0xe2, 0x89, 0xb8, 0xe2, 0x8b, 0xaa, 0xe2, 0x8b, 0xac, 0xe2, 0x8b, 0xab, 0xe2, 0x8b, 0xad, 0xce, 0xbd, 0x23, 0xe2, 0x84, 0x96, 0xe2, 0x80, 0x87, 0xe2, 0x8a, 0xad, 0xe2, 0xa4, 0x84, 0xe2, 0x89, 0x8d, 0xe2, 0x83, 0x92, 0xe2, 0x8a, 0xac, 0xe2, 0x89, 0xa5, 0xe2, 0x83, 0x92, 0x3e, 0xe2, 0x83, 0x92, 0xe2, 0xa7, 0x9e, 0xe2, 0xa4, 0x82, 0xe2, 0x89, 0xa4, 0xe2, 0x83, 0x92, 0x3c, 0xe2, 0x83, 0x92, 0xe2, 0x8a, 0xb4, 0xe2, 0x83, 0x92, 0xe2, 0xa4, 0x83, 0xe2, 0x8a, 0xb5, 0xe2, 0x83, 0x92, 0xe2, 0x88, 0xbc, 0xe2, 0x83, 0x92, 0xe2, 0x87, 0x96, 0xe2, 0xa4, 0xa3, 0xe2, 0x86, 0x96, 0xe2, 0x86, 0x96, 0xe2, 0xa4, 0xa7, 0xe2, 0x93, 0x88, 0xc3, 0xb3, 0xe2, 0x8a, 0x9b, 0xe2, 0x8a, 0x9a, 0xc3, 0xb4, 0xd0, 0xbe, 0xe2, 0x8a, 0x9d, 0xc5, 0x91, 0xe2, 0xa8, 0xb8, 0xe2, 0x8a, 0x99, 0xe2, 0xa6, 0xbc, 0xc5, 0x93, 0xe2, 0xa6, 0xbf, 0xf0, 0x9d, 0x94, 0xac, 0xcb, 0x9b, 0xc3, 0xb2, 0xe2, 0xa7, 0x81, 0xe2, 0xa6, 0xb5, 0xce, 0xa9, 0xe2, 0x88, 0xae, 0xe2, 0x86, 0xba, 0xe2, 0xa6, 0xbe, 0xe2, 0xa6, 0xbb, 0xe2, 0x80, 0xbe, 0xe2, 0xa7, 0x80, 0xc5, 0x8d, 0xcf, 0x89, 0xce, 0xbf, 0xe2, 0xa6, 0xb6, 0xe2, 0x8a, 0x96, 0xf0, 0x9d, 0x95, 0xa0, 0xe2, 0xa6, 0xb7, 0xe2, 0xa6, 0xb9, 0xe2, 0x8a, 0x95, 0xe2, 0x88, 0xa8, 0xe2, 0x86, 0xbb, 0xe2, 0xa9, 0x9d, 0xe2, 0x84, 0xb4, 0xe2, 0x84, 0xb4, 0xc2, 0xaa, 0xc2, 0xba, 0xe2, 0x8a, 0xb6, 0xe2, 0xa9, 0x96, 0xe2, 0xa9, 0x97, 0xe2, 0xa9, 0x9b, 0xe2, 0x84, 0xb4, 0xc3, 0xb8, 0xe2, 0x8a, 0x98, 0xc3, 0xb5, 0xe2, 0x8a, 0x97, 0xe2, 0xa8, 0xb6, 0xc3, 0xb6, 0xe2, 0x8c, 0xbd, 0xe2, 0x88, 0xa5, 0xc2, 0xb6, 0xe2, 0x88, 0xa5, 0xe2, 0xab, 0xb3, 0xe2, 0xab, 0xbd, 0xe2, 0x88, 0x82, 0xd0, 0xbf, 0x25, 0x2e, 0xe2, 0x80, 0xb0, 0xe2, 0x8a, 0xa5, 0xe2, 0x80, 0xb1, 0xf0, 0x9d, 0x94, 0xad, 0xcf, 0x86, 0xcf, 0x95, 0xe2, 0x84, 0xb3, 0xe2, 0x98, 0x8e, 0xcf, 0x80, 0xe2, 0x8b, 0x94, 0xcf, 0x96, 0xe2, 0x84, 0x8f, 0xe2, 0x84, 0x8e, 0xe2, 0x84, 0x8f, 0x2b, 0xe2, 0xa8, 0xa3, 0xe2, 0x8a, 0x9e, 0xe2, 0xa8, 0xa2, 0xe2, 0x88, 0x94, 0xe2, 0xa8, 0xa5, 0xe2, 0xa9, 0xb2, 0xc2, 0xb1, 0xe2, 0xa8, 0xa6, 0xe2, 0xa8, 0xa7, 0xc2, 0xb1, 0xe2, 0xa8, 0x95, 0xf0, 0x9d, 0x95, 0xa1, 0xc2, 0xa3, 0xe2, 0x89, 0xba, 0xe2, 0xaa, 0xb3, 0xe2, 0xaa, 0xb7, 0xe2, 0x89, 0xbc, 0xe2, 0xaa, 0xaf, 0xe2, 0x89, 0xba, 0xe2, 0xaa, 0xb7, 0xe2, 0x89, 0xbc, 0xe2, 0xaa, 0xaf, 0xe2, 0xaa, 0xb9, 0xe2, 0xaa, 0xb5, 0xe2, 0x8b, 0xa8, 0xe2, 0x89, 0xbe, 0xe2, 0x80, 0xb2, 0xe2, 0x84, 0x99, 0xe2, 0xaa, 0xb5, 0xe2, 0xaa, 0xb9, 0xe2, 0x8b, 0xa8, 0xe2, 0x88, 0x8f, 0xe2, 0x8c, 0xae, 0xe2, 0x8c, 0x92, 0xe2, 0x8c, 0x93, 0xe2, 0x88, 0x9d, 0xe2, 0x88, 0x9d, 0xe2, 0x89, 0xbe, 0xe2, 0x8a, 0xb0, 0xf0, 0x9d, 0x93, 0x85, 0xcf, 0x88, 0xe2, 0x80, 0x88, 0xf0, 0x9d, 0x94, 0xae, 0xe2, 0xa8, 0x8c, 0xf0, 0x9d, 0x95, 0xa2, 0xe2, 0x81, 0x97, 0xf0, 0x9d, 0x93, 0x86, 0xe2, 0x84, 0x8d, 0xe2, 0xa8, 0x96, 0x3f, 0xe2, 0x89, 0x9f, 0x22, 0xe2, 0x87, 0x9b, 0xe2, 0x87, 0x92, 0xe2, 0xa4, 0x9c, 0xe2, 0xa4, 0x8f, 0xe2, 0xa5, 0xa4, 0xe2, 0x88, 0xbd, 0xcc, 0xb1, 0xc5, 0x95, 0xe2, 0x88, 0x9a, 0xe2, 0xa6, 0xb3, 0xe2, 0x9f, 0xa9, 0xe2, 0xa6, 0x92, 0xe2, 0xa6, 0xa5, 0xe2, 0x9f, 0xa9, 0xc2, 0xbb, 0xe2, 0x86, 0x92, 0xe2, 0xa5, 0xb5, 0xe2, 0x87, 0xa5, 0xe2, 0xa4, 0xa0, 0xe2, 0xa4, 0xb3, 0xe2, 0xa4, 0x9e, 0xe2, 0x86, 0xaa, 0xe2, 0x86, 0xac, 0xe2, 0xa5, 0x85, 0xe2, 0xa5, 0xb4, 0xe2, 0x86, 0xa3, 0xe2, 0x86, 0x9d, 0xe2, 0xa4, 0x9a, 0xe2, 0x88, 0xb6, 0xe2, 0x84, 0x9a, 0xe2, 0xa4, 0x8d, 0xe2, 0x9d, 0xb3, 0x7d, 0x5d, 0xe2, 0xa6, 0x8c, 0xe2, 0xa6, 0x8e, 0xe2, 0xa6, 0x90, 0xc5, 0x99, 0xc5, 0x97, 0xe2, 0x8c, 0x89, 0x7d, 0xd1, 0x80, 0xe2, 0xa4, 0xb7, 0xe2, 0xa5, 0xa9, 0xe2, 0x80, 0x9d, 0xe2, 0x80, 0x9d, 0xe2, 0x86, 0xb3, 0xe2, 0x84, 0x9c, 0xe2, 0x84, 0x9b, 0xe2, 0x84, 0x9c, 0xe2, 0x84, 0x9d, 0xe2, 0x96, 0xad, 0xc2, 0xae, 0xe2, 0xa5, 0xbd, 0xe2, 0x8c, 0x8b, 0xf0, 0x9d, 0x94, 0xaf, 0xe2, 0x87, 0x81, 0xe2, 0x87, 0x80, 0xe2, 0xa5, 0xac, 0xcf, 0x81, 0xcf, 0xb1, 0xe2, 0x86, 0x92, 0xe2, 0x86, 0xa3, 0xe2, 0x87, 0x81, 0xe2, 0x87, 0x80, 0xe2, 0x87, 0x84, 0xe2, 0x87, 0x8c, 0xe2, 0x87, 0x89, 0xe2, 0x86, 0x9d, 0xe2, 0x8b, 0x8c, 0xcb, 0x9a, 0xe2, 0x89, 0x93, 0xe2, 0x87, 0x84, 0xe2, 0x87, 0x8c, 0xe2, 0x80, 0x8f, 0xe2, 0x8e, 0xb1, 0xe2, 0x8e, 0xb1, 0xe2, 0xab, 0xae, 0xe2, 0x9f, 0xad, 0xe2, 0x87, 0xbe, 0xe2, 0x9f, 0xa7, 0xe2, 0xa6, 0x86, 0xf0, 0x9d, 0x95, 0xa3, 0xe2, 0xa8, 0xae, 0xe2, 0xa8, 0xb5, 0x29, 0xe2, 0xa6, 0x94, 0xe2, 0xa8, 0x92, 0xe2, 0x87, 0x89, 0xe2, 0x80, 0xba, 0xf0, 0x9d, 0x93, 0x87, 0xe2, 0x86, 0xb1, 0x5d, 0xe2, 0x80, 0x99, 0xe2, 0x80, 0x99, 0xe2, 0x8b, 0x8c, 0xe2, 0x8b, 0x8a, 0xe2, 0x96, 0xb9, 0xe2, 0x8a, 0xb5, 0xe2, 0x96, 0xb8, 0xe2, 0xa7, 0x8e, 0xe2, 0xa5, 0xa8, 0xe2, 0x84, 0x9e, 0xc5, 0x9b, 0xe2, 0x80, 0x9a, 0xe2, 0x89, 0xbb, 0xe2, 0xaa, 0xb4, 0xe2, 0xaa, 0xb8, 0xc5, 0xa1, 0xe2, 0x89, 0xbd, 0xe2, 0xaa, 0xb0, 0xc5, 0x9f, 0xc5, 0x9d, 0xe2, 0xaa, 0xb6, 0xe2, 0xaa, 0xba, 0xe2, 0x8b, 0xa9, 0xe2, 0xa8, 0x93, 0xe2, 0x89, 0xbf, 0xd1, 0x81, 0xe2, 0x8b, 0x85, 0xe2, 0x8a, 0xa1, 0xe2, 0xa9, 0xa6, 0xe2, 0x87, 0x98, 0xe2, 0xa4, 0xa5, 0xe2, 0x86, 0x98, 0xe2, 0x86, 0x98, 0xc2, 0xa7, 0x3b, 0xe2, 0xa4, 0xa9, 0xe2, 0x88, 0x96, 0xe2, 0x88, 0x96, 0xe2, 0x9c, 0xb6, 0xf0, 0x9d, 0x94, 0xb0, 0xe2, 0x8c, 0xa2, 0xe2, 0x99, 0xaf, 0xd1, 0x89, 0xd1, 0x88, 0xe2, 0x88, 0xa3, 0xe2, 0x88, 0xa5, 0xc2, 0xad, 0xcf, 0x83, 0xcf, 0x82, 0xcf, 0x82, 0xe2, 0x88, 0xbc, 0xe2, 0xa9, 0xaa, 0xe2, 0x89, 0x83, 0xe2, 0x89, 0x83, 0xe2, 0xaa, 0x9e, 0xe2, 0xaa, 0xa0, 0xe2, 0xaa, 0x9d, 0xe2, 0xaa, 0x9f, 0xe2, 0x89, 0x86, 0xe2, 0xa8, 0xa4, 0xe2, 0xa5, 0xb2, 0xe2, 0x86, 0x90, 0xe2, 0x88, 0x96, 0xe2, 0xa8, 0xb3, 0xe2, 0xa7, 0xa4, 0xe2, 0x88, 0xa3, 0xe2, 0x8c, 0xa3, 0xe2, 0xaa, 0xaa, 0xe2, 0xaa, 0xac, 0xe2, 0xaa, 0xac, 0xef, 0xb8, 0x80, 0xd1, 0x8c, 0x2f, 0xe2, 0xa7, 0x84, 0xe2, 0x8c, 0xbf, 0xf0, 0x9d, 0x95, 0xa4, 0xe2, 0x99, 0xa0, 0xe2, 0x99, 0xa0, 0xe2, 0x88, 0xa5, 0xe2, 0x8a, 0x93, 0xe2, 0x8a, 0x93, 0xef, 0xb8, 0x80, 0xe2, 0x8a, 0x94, 0xe2, 0x8a, 0x94, 0xef, 0xb8, 0x80, 0xe2, 0x8a, 0x8f, 0xe2, 0x8a, 0x91, 0xe2, 0x8a, 0x8f, 0xe2, 0x8a, 0x91, 0xe2, 0x8a, 0x90, 0xe2, 0x8a, 0x92, 0xe2, 0x8a, 0x90, 0xe2, 0x8a, 0x92, 0xe2, 0x96, 0xa1, 0xe2, 0x96, 0xa1, 0xe2, 0x96, 0xaa, 0xe2, 0x96, 0xaa, 0xe2, 0x86, 0x92, 0xf0, 0x9d, 0x93, 0x88, 0xe2, 0x88, 0x96, 0xe2, 0x8c, 0xa3, 0xe2, 0x8b, 0x86, 0xe2, 0x98, 0x86, 0xe2, 0x98, 0x85, 0xcf, 0xb5, 0xcf, 0x95, 0xc2, 0xaf, 0xe2, 0x8a, 0x82, 0xe2, 0xab, 0x85, 0xe2, 0xaa, 0xbd, 0xe2, 0x8a, 0x86, 0xe2, 0xab, 0x83, 0xe2, 0xab, 0x81, 0xe2, 0xab, 0x8b, 0xe2, 0x8a, 0x8a, 0xe2, 0xaa, 0xbf, 0xe2, 0xa5, 0xb9, 0xe2, 0x8a, 0x82, 0xe2, 0x8a, 0x86, 0xe2, 0xab, 0x85, 0xe2, 0x8a, 0x8a, 0xe2, 0xab, 0x8b, 0xe2, 0xab, 0x87, 0xe2, 0xab, 0x95, 0xe2, 0xab, 0x93, 0xe2, 0x89, 0xbb, 0xe2, 0xaa, 0xb8, 0xe2, 0x89, 0xbd, 0xe2, 0xaa, 0xb0, 0xe2, 0xaa, 0xba, 0xe2, 0xaa, 0xb6, 0xe2, 0x8b, 0xa9, 0xe2, 0x89, 0xbf, 0xe2, 0x88, 0x91, 0xe2, 0x99, 0xaa, 0xe2, 0x8a, 0x83, 0xc2, 0xb9, 0xc2, 0xb2, 0xc2, 0xb3, 0xe2, 0xab, 0x86, 0xe2, 0xaa, 0xbe, 0xe2, 0xab, 0x98, 0xe2, 0x8a, 0x87, 0xe2, 0xab, 0x84, 0xe2, 0x9f, 0x89, 0xe2, 0xab, 0x97, 0xe2, 0xa5, 0xbb, 0xe2, 0xab, 0x82, 0xe2, 0xab, 0x8c, 0xe2, 0x8a, 0x8b, 0xe2, 0xab, 0x80, 0xe2, 0x8a, 0x83, 0xe2, 0x8a, 0x87, 0xe2, 0xab, 0x86, 0xe2, 0x8a, 0x8b, 0xe2, 0xab, 0x8c, 0xe2, 0xab, 0x88, 0xe2, 0xab, 0x94, 0xe2, 0xab, 0x96, 0xe2, 0x87, 0x99, 0xe2, 0xa4, 0xa6, 0xe2, 0x86, 0x99, 0xe2, 0x86, 0x99, 0xe2, 0xa4, 0xaa, 0xc3, 0x9f, 0xe2, 0x8c, 0x96, 0xcf, 0x84, 0xe2, 0x8e, 0xb4, 0xc5, 0xa5, 0xc5, 0xa3, 0xd1, 0x82, 0xe2, 0x83, 0x9b, 0xe2, 0x8c, 0x95, 0xf0, 0x9d, 0x94, 0xb1, 0xe2, 0x88, 0xb4, 0xe2, 0x88, 0xb4, 0xce, 0xb8, 0xcf, 0x91, 0xcf, 0x91, 0xe2, 0x89, 0x88, 0xe2, 0x88, 0xbc, 0xe2, 0x80, 0x89, 0xe2, 0x89, 0x88, 0xe2, 0x88, 0xbc, 0xc3, 0xbe, 0xcb, 0x9c, 0xc3, 0x97, 0xe2, 0x8a, 0xa0, 0xe2, 0xa8, 0xb1, 0xe2, 0xa8, 0xb0, 0xe2, 0x88, 0xad, 0xe2, 0xa4, 0xa8, 0xe2, 0x8a, 0xa4, 0xe2, 0x8c, 0xb6, 0xe2, 0xab, 0xb1, 0xf0, 0x9d, 0x95, 0xa5, 0xe2, 0xab, 0x9a, 0xe2, 0xa4, 0xa9, 0xe2, 0x80, 0xb4, 0xe2, 0x84, 0xa2, 0xe2, 0x96, 0xb5, 0xe2, 0x96, 0xbf, 0xe2, 0x97, 0x83, 0xe2, 0x8a, 0xb4, 0xe2, 0x89, 0x9c, 0xe2, 0x96, 0xb9, 0xe2, 0x8a, 0xb5, 0xe2, 0x97, 0xac, 0xe2, 0x89, 0x9c, 0xe2, 0xa8, 0xba, 0xe2, 0xa8, 0xb9, 0xe2, 0xa7, 0x8d, 0xe2, 0xa8, 0xbb, 0xe2, 0x8f, 0xa2, 0xf0, 0x9d, 0x93, 0x89, 0xd1, 0x86, 0xd1, 0x9b, 0xc5, 0xa7, 0xe2, 0x89, 0xac, 0xe2, 0x86, 0x9e, 0xe2, 0x86, 0xa0, 0xe2, 0x87, 0x91, 0xe2, 0xa5, 0xa3, 0xc3, 0xba, 0xe2, 0x86, 0x91, 0xd1, 0x9e, 0xc5, 0xad, 0xc3, 0xbb, 0xd1, 0x83, 0xe2, 0x87, 0x85, 0xc5, 0xb1, 0xe2, 0xa5, 0xae, 0xe2, 0xa5, 0xbe, 0xf0, 0x9d, 0x94, 0xb2, 0xc3, 0xb9, 0xe2, 0x86, 0xbf, 0xe2, 0x86, 0xbe, 0xe2, 0x96, 0x80, 0xe2, 0x8c, 0x9c, 0xe2, 0x8c, 0x9c, 0xe2, 0x8c, 0x8f, 0xe2, 0x97, 0xb8, 0xc5, 0xab, 0xc2, 0xa8, 0xc5, 0xb3, 0xf0, 0x9d, 0x95, 0xa6, 0xe2, 0x86, 0x91, 0xe2, 0x86, 0x95, 0xe2, 0x86, 0xbf, 0xe2, 0x86, 0xbe, 0xe2, 0x8a, 0x8e, 0xcf, 0x85, 0xcf, 0x92, 0xcf, 0x85, 0xe2, 0x87, 0x88, 0xe2, 0x8c, 0x9d, 0xe2, 0x8c, 0x9d, 0xe2, 0x8c, 0x8e, 0xc5, 0xaf, 0xe2, 0x97, 0xb9, 0xf0, 0x9d, 0x93, 0x8a, 0xe2, 0x8b, 0xb0, 0xc5, 0xa9, 0xe2, 0x96, 0xb5, 0xe2, 0x96, 0xb4, 0xe2, 0x87, 0x88, 0xc3, 0xbc, 0xe2, 0xa6, 0xa7, 0xe2, 0x87, 0x95, 0xe2, 0xab, 0xa8, 0xe2, 0xab, 0xa9, 0xe2, 0x8a, 0xa8, 0xe2, 0xa6, 0x9c, 0xcf, 0xb5, 0xcf, 0xb0, 0xe2, 0x88, 0x85, 0xcf, 0x95, 0xcf, 0x96, 0xe2, 0x88, 0x9d, 0xe2, 0x86, 0x95, 0xcf, 0xb1, 0xcf, 0x82, 0xe2, 0x8a, 0x8a, 0xef, 0xb8, 0x80, 0xe2, 0xab, 0x8b, 0xef, 0xb8, 0x80, 0xe2, 0x8a, 0x8b, 0xef, 0xb8, 0x80, 0xe2, 0xab, 0x8c, 0xef, 0xb8, 0x80, 0xcf, 0x91, 0xe2, 0x8a, 0xb2, 0xe2, 0x8a, 0xb3, 0xd0, 0xb2, 0xe2, 0x8a, 0xa2, 0xe2, 0x88, 0xa8, 0xe2, 0x8a, 0xbb, 0xe2, 0x89, 0x9a, 0xe2, 0x8b, 0xae, 0x7c, 0x7c, 0xf0, 0x9d, 0x94, 0xb3, 0xe2, 0x8a, 0xb2, 0xe2, 0x8a, 0x82, 0xe2, 0x83, 0x92, 0xe2, 0x8a, 0x83, 0xe2, 0x83, 0x92, 0xf0, 0x9d, 0x95, 0xa7, 0xe2, 0x88, 0x9d, 0xe2, 0x8a, 0xb3, 0xf0, 0x9d, 0x93, 0x8b, 0xe2, 0xab, 0x8b, 0xef, 0xb8, 0x80, 0xe2, 0x8a, 0x8a, 0xef, 0xb8, 0x80, 0xe2, 0xab, 0x8c, 0xef, 0xb8, 0x80, 0xe2, 0x8a, 0x8b, 0xef, 0xb8, 0x80, 0xe2, 0xa6, 0x9a, 0xc5, 0xb5, 0xe2, 0xa9, 0x9f, 0xe2, 0x88, 0xa7, 0xe2, 0x89, 0x99, 0xe2, 0x84, 0x98, 0xf0, 0x9d, 0x94, 0xb4, 0xf0, 0x9d, 0x95, 0xa8, 0xe2, 0x84, 0x98, 0xe2, 0x89, 0x80, 0xe2, 0x89, 0x80, 0xf0, 0x9d, 0x93, 0x8c, 0xe2, 0x8b, 0x82, 0xe2, 0x97, 0xaf, 0xe2, 0x8b, 0x83, 0xe2, 0x96, 0xbd, 0xf0, 0x9d, 0x94, 0xb5, 0xe2, 0x9f, 0xba, 0xe2, 0x9f, 0xb7, 0xce, 0xbe, 0xe2, 0x9f, 0xb8, 0xe2, 0x9f, 0xb5, 0xe2, 0x9f, 0xbc, 0xe2, 0x8b, 0xbb, 0xe2, 0xa8, 0x80, 0xf0, 0x9d, 0x95, 0xa9, 0xe2, 0xa8, 0x81, 0xe2, 0xa8, 0x82, 0xe2, 0x9f, 0xb9, 0xe2, 0x9f, 0xb6, 0xf0, 0x9d, 0x93, 0x8d, 0xe2, 0xa8, 0x86, 0xe2, 0xa8, 0x84, 0xe2, 0x96, 0xb3, 0xe2, 0x8b, 0x81, 0xe2, 0x8b, 0x80, 0xc3, 0xbd, 0xd1, 0x8f, 0xc5, 0xb7, 0xd1, 0x8b, 0xc2, 0xa5, 0xf0, 0x9d, 0x94, 0xb6, 0xd1, 0x97, 0xf0, 0x9d, 0x95, 0xaa, 0xf0, 0x9d, 0x93, 0x8e, 0xd1, 0x8e, 0xc3, 0xbf, 0xc5, 0xba, 0xc5, 0xbe, 0xd0, 0xb7, 0xc5, 0xbc, 0xe2, 0x84, 0xa8, 0xce, 0xb6, 0xf0, 0x9d, 0x94, 0xb7, 0xd0, 0xb6, 0xe2, 0x87, 0x9d, 0xf0, 0x9d, 0x95, 0xab, 0xf0, 0x9d, 0x93, 0x8f, 0xe2, 0x80, 0x8d, 0xe2, 0x80, 0x8c}
var _html5entitiesCharactersIndex = "\x02\x01\x02\x02\x02\x04\x02\x02\x02\x03\x02\x04\x03\x02\x04\x03\x02\x02\x03\x03\x03\x02\x03\x03\x02\x04\x04\x02\x03\x03\x02\x02\x02\x03\x03\x03\x02\x02\x02\x03\x02\x02\x02\x03\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x04\x03\x03\x03\x03\x02\x02\x02\x03\x03\x03\x02\x02\x03\x02\x04\x02\x02\x02\x01\x02\x03\x03\x04\x02\x03\x03\x03\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x04\x02\x02\x02\x02\x02\x02\x02\x02\x04\x02\x03\x02\x03\x03\x02\x04\x02\x03\x03\x03\x03\x03\x02\x02\x03\x03\x02\x04\x03\x03\x04\x03\x03\x03\x02\x01\x02\x02\x02\x02\x02\x02\x02\x04\x03\x04\x03\x03\x03\x03\x03\x03\x03\x04\x03\x02\x02\x01\x02\x03\x03\x03\x03\x03\x02\x03\x03\x02\x02\x02\x02\x02\x02\x02\x03\x02\x03\x02\x03\x03\x03\x03\x03\x03\x03\x02\x04\x02\x03\x02\x02\x02\x02\x02\x04\x04\x04\x02\x02\x02\x02\x02\x02\x02\x04\x04\x04\x02\x01\x02\x02\x03\x03\x03\x02\x02\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x04\x03\x03\x02\x03\x03\x03\x03\x03\x03\x04\x03\x03\x03\x03\x02\x03\x03\x02\x03\x03\x04\x03\x04\x03\x02\x02\x02\x02\x02\x02\x03\x03\x03\x03\x03\x03\x01\x04\x03\x02\x03\x03\x03\x03\x03\x03\x03\x05\x03\x03\x03\x05\x05\x03\x05\x03\x05\x05\x03\x05\x03\x03\x03\x03\x05\x05\x03\x05\x05\x03\x05\x03\x03\x03\x05\x03\x05\x03\x05\x03\x06\x03\x03\x05\x03\x05\x06\x03\x03\x03\x03\x03\x03\x04\x02\x02\x02\x02\x02\x02\x02\x04\x02\x02\x02\x02\x04\x03\x03\x03\x04\x02\x02\x03\x02\x03\x03\x03\x03\x03\x02\x04\x02\x02\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x04\x02\x01\x04\x03\x04\x03\x02\x02\x03\x03\x03\x02\x02\x02\x03\x03\x03\x03\x03\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x02\x02\x02\x02\x03\x02\x02\x02\x02\x04\x03\x03\x03\x03\x02\x03\x04\x03\x03\x03\x03\x03\x03\x03\x03\x04\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x02\x03\x02\x02\x01\x02\x02\x02\x02\x04\x03\x02\x06\x03\x03\x03\x03\x03\x04\x03\x04\x02\x02\x03\x03\x02\x02\x02\x02\x02\x04\x02\x02\x01\x03\x03\x03\x03\x03\x02\x04\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x02\x02\x02\x04\x02\x02\x03\x03\x02\x03\x03\x03\x03\x03\x03\x01\x03\x03\x03\x04\x04\x04\x03\x02\x03\x04\x04\x04\x04\x02\x04\x04\x02\x02\x02\x02\x02\x02\x04\x04\x04\x02\x02\x02\x02\x02\x02\x03\x02\x03\x03\x04\x02\x02\x03\x05\x03\x02\x02\x02\x02\x03\x04\x02\x03\x03\x02\x02\x03\x01\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x02\x03\x02\x04\x03\x03\x03\x03\x03\x01\x03\x03\x02\x04\x01\x03\x03\x02\x02\x03\x03\x03\x03\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x02\x03\x03\x03\x03\x02\x03\x02\x03\x03\x04\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x04\x06\x03\x04\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x02\x02\x04\x03\x03\x03\x01\x03\x03\x03\x03\x03\x03\x03\x03\x02\x03\x03\x03\x03\x03\x03\x06\x03\x02\x03\x02\x02\x02\x03\x03\x02\x02\x03\x02\x02\x04\x02\x03\x03\x02\x03\x03\x02\x03\x03\x03\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x01\x03\x03\x01\x01\x03\x03\x03\x03\x03\x03\x03\x04\x03\x02\x03\x03\x03\x04\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x06\x03\x03\x03\x03\x03\x03\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x02\x02\x02\x03\x03\x03\x03\x02\x02\x03\x03\x04\x03\x03\x03\x03\x03\x03\x02\x02\x03\x02\x02\x03\x03\x02\x03\x03\x01\x04\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x04\x02\x03\x02\x03\x03\x03\x03\x03\x03\x02\x03\x03\x03\x02\x03\x02\x03\x02\x03\x02\x02\x03\x03\x04\x03\x02\x03\x03\x03\x03\x03\x03\x03\x02\x03\x03\x03\x03\x03\x03\x02\x03\x02\x04\x03\x03\x03\x02\x02\x02\x03\x03\x03\x03\x03\x01\x03\x03\x03\x03\x03\x03\x03\x03\x03\x02\x02\x02\x03\x01\x03\x03\x03\x03\x02\x03\x03\x03\x03\x04\x03\x02\x03\x03\x03\x02\x04\x03\x03\x03\x03\x02\x03\x02\x03\x03\x03\x03\x03\x02\x03\x03\x03\x03\x03\x03\x03\x03\x04\x03\x03\x02\x02\x02\x03\x02\x02\x02\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x06\x03\x04\x03\x03\x03\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x04\x01\x03\x03\x03\x03\x01\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x06\x06\x03\x03\x02\x03\x02\x03\x03\x03\x03\x02\x03\x03\x03\x03\x04\x03\x03\x03\x03\x03\x03\x04\x03\x04\x03\x02\x03\x03\x02\x03\x02\x02\x02\x02\x03\x04\x02\x03\x03\x03\x03\x03\x02\x02\x03\x03\x03\x02\x03\x02\x03\x03\x03\x03\x02\x03\x03\x03\x03\x03\x03\x02\x02\x04\x02\x03\x02\x04\x03\x03\x03\x03\x03\x03\x03\x02\x02\x02\x02\x02\x04\x02\x04\x04\x02\x02\x02\x02\x02\x02\x04\x02\x02\x02\x04\x04\x03\x03\x03\x03\x03\x03\x03\x02\x03\x03\x02\x03\x03\x03\x03\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x06\x03\x03\x01\x01\x03\x03\x03\x02\x02\x03\x01\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x06\x03\x03\x03\x03\x03\x03\x03\x03\x03\x04\x03\x03\x03\x03\x03\x03\x02\x03\x03\x03\x03\x03\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x04\x03\x03\x03\x01\x03\x03\x03\x01\x03\x03\x03\x03\x03\x03\x03\x03\x04\x03\x03\x03\x03\x01\x03\x03\x02\x01\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x06\x06\x03\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x02\x03\x03\x04\x03\x02\x03\x01\x03\x02\x03\x03\x03\x03\x03\x03\x03\x03\x04\x03\x04\x03\x02\x03\x03\x05\x06\x05\x03\x03\x05\x06\x05\x03\x03\x03\x03\x02\x06\x03\x05\x05\x02\x03\x03\x03\x03\x02\x05\x05\x03\x02\x02\x03\x05\x03\x02\x03\x03\x03\x03\x03\x03\x05\x03\x03\x05\x03\x03\x04\x05\x03\x03\x05\x05\x05\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x02\x03\x05\x03\x03\x03\x03\x03\x03\x05\x05\x05\x03\x03\x03\x03\x03\x03\x04\x02\x03\x05\x05\x03\x03\x03\x03\x03\x03\x03\x03\x03\x06\x05\x03\x03\x03\x05\x03\x05\x03\x03\x05\x05\x03\x03\x03\x03\x03\x05\x04\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x05\x03\x06\x03\x05\x03\x05\x03\x05\x03\x06\x03\x05\x03\x02\x03\x03\x03\x03\x03\x02\x01\x03\x03\x03\x03\x06\x03\x06\x04\x03\x03\x06\x04\x06\x03\x06\x06\x03\x03\x03\x03\x03\x03\x02\x03\x03\x02\x02\x03\x02\x03\x03\x03\x02\x03\x04\x02\x02\x03\x03\x02\x03\x03\x03\x03\x03\x03\x02\x02\x02\x03\x03\x04\x03\x03\x03\x03\x03\x03\x03\x03\x02\x02\x03\x03\x03\x03\x03\x02\x03\x02\x03\x03\x02\x03\x03\x02\x03\x03\x03\x03\x02\x01\x01\x03\x03\x03\x04\x02\x02\x03\x03\x02\x03\x02\x03\x03\x03\x01\x03\x03\x03\x03\x03\x03\x02\x03\x03\x02\x03\x04\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x04\x02\x03\x04\x03\x04\x03\x04\x03\x03\x01\x03\x01\x03\x03\x03\x03\x03\x05\x02\x03\x03\x03\x03\x03\x03\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x01\x01\x03\x03\x03\x02\x02\x03\x01\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x02\x03\x03\x04\x03\x03\x03\x02\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x04\x03\x03\x01\x03\x03\x03\x03\x04\x03\x01\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x02\x03\x03\x03\x03\x02\x03\x03\x02\x02\x03\x03\x03\x03\x03\x02\x03\x03\x03\x03\x03\x03\x03\x02\x01\x03\x03\x03\x03\x04\x03\x03\x02\x02\x03\x03\x02\x02\x02\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x06\x02\x01\x03\x03\x04\x03\x03\x03\x03\x06\x03\x06\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x04\x03\x03\x03\x03\x03\x02\x02\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x02\x02\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x02\x03\x02\x03\x02\x02\x02\x03\x03\x04\x03\x03\x02\x02\x02\x03\x03\x03\x03\x03\x02\x02\x02\x03\x03\x03\x03\x03\x03\x03\x03\x04\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x04\x02\x02\x02\x03\x03\x03\x03\x03\x02\x03\x02\x02\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x03\x03\x03\x03\x03\x02\x02\x02\x04\x03\x03\x03\x03\x03\x02\x02\x02\x03\x03\x03\x03\x02\x03\x04\x03\x02\x03\x03\x03\x02\x03\x03\x03\x03\x03\x03\x02\x02\x03\x02\x02\x03\x03\x02\x02\x06\x06\x06\x06\x02\x03\x03\x02\x03\x03\x03\x03\x03\x01\x01\x04\x03\x06\x06\x04\x03\x03\x04\x06\x06\x06\x06\x03\x02\x03\x03\x03\x03\x04\x04\x03\x03\x03\x04\x03\x03\x03\x03\x04\x03\x03\x02\x03\x03\x03\x03\x03\x04\x03\x03\x03\x03\x04\x03\x03\x03\x03\x03\x02\x02\x02\x02\x02\x04\x02\x04\x04\x02\x02\x02\x02\x02\x02\x03\x02\x04\x02\x03\x04\x04\x03\x03"
yuin-goldmark-9a7f4c9/util/html5entities.go 0000664 0000000 0000000 00000002311 15037761217 0021034 0 ustar 00root root 0000000 0000000 package util
import (
"sync"
)
//go:generate go run ../_tools emb-structs -i ../_tools/html5entities.json -o ./html5entities.gen.go
var _html5entitiesOnce sync.Once
var _html5entitiesMap map[string]*HTML5Entity
func buildHTML5Entities() {
_html5entitiesOnce.Do(func() {
entities := make([]HTML5Entity, _html5entitiesLength)
_html5entitiesMap = make(map[string]*HTML5Entity, _html5entitiesLength)
cName := 0
cCharacters := 0
for i := 0; i < _html5entitiesLength; i++ {
tName := cName + int(_html5entitiesNameIndex[i])
tCharacters := cCharacters + int(_html5entitiesCharactersIndex[i])
name := _html5entitiesName[cName:tName]
e := &entities[i]
e.Name = name
e.Characters = _html5entitiesCharacters[cCharacters:tCharacters]
_html5entitiesMap[name] = e
cName = tName
cCharacters = tCharacters
}
})
}
// HTML5Entity struct represents HTML5 entitites.
type HTML5Entity struct {
Name string
Characters []byte
}
// LookUpHTML5EntityByName returns (an HTML5Entity, true) if an entity named
// given name is found, otherwise (nil, false).
func LookUpHTML5EntityByName(name string) (*HTML5Entity, bool) {
buildHTML5Entities()
v, ok := _html5entitiesMap[name]
return v, ok
}
yuin-goldmark-9a7f4c9/util/unicode_case_folding.gen.go 0000664 0000000 0000000 00000067210 15037761217 0023142 0 ustar 00root root 0000000 0000000 // Code generated by _tools; DO NOT EDIT.
package util
const _unicodeCaseFoldingLength = 1530
var _unicodeCaseFoldingFrom = [...]rune{0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0xb5, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0x100, 0x102, 0x104, 0x106, 0x108, 0x10a, 0x10c, 0x10e, 0x110, 0x112, 0x114, 0x116, 0x118, 0x11a, 0x11c, 0x11e, 0x120, 0x122, 0x124, 0x126, 0x128, 0x12a, 0x12c, 0x12e, 0x130, 0x132, 0x134, 0x136, 0x139, 0x13b, 0x13d, 0x13f, 0x141, 0x143, 0x145, 0x147, 0x149, 0x14a, 0x14c, 0x14e, 0x150, 0x152, 0x154, 0x156, 0x158, 0x15a, 0x15c, 0x15e, 0x160, 0x162, 0x164, 0x166, 0x168, 0x16a, 0x16c, 0x16e, 0x170, 0x172, 0x174, 0x176, 0x178, 0x179, 0x17b, 0x17d, 0x17f, 0x181, 0x182, 0x184, 0x186, 0x187, 0x189, 0x18a, 0x18b, 0x18e, 0x18f, 0x190, 0x191, 0x193, 0x194, 0x196, 0x197, 0x198, 0x19c, 0x19d, 0x19f, 0x1a0, 0x1a2, 0x1a4, 0x1a6, 0x1a7, 0x1a9, 0x1ac, 0x1ae, 0x1af, 0x1b1, 0x1b2, 0x1b3, 0x1b5, 0x1b7, 0x1b8, 0x1bc, 0x1c4, 0x1c5, 0x1c7, 0x1c8, 0x1ca, 0x1cb, 0x1cd, 0x1cf, 0x1d1, 0x1d3, 0x1d5, 0x1d7, 0x1d9, 0x1db, 0x1de, 0x1e0, 0x1e2, 0x1e4, 0x1e6, 0x1e8, 0x1ea, 0x1ec, 0x1ee, 0x1f0, 0x1f1, 0x1f2, 0x1f4, 0x1f6, 0x1f7, 0x1f8, 0x1fa, 0x1fc, 0x1fe, 0x200, 0x202, 0x204, 0x206, 0x208, 0x20a, 0x20c, 0x20e, 0x210, 0x212, 0x214, 0x216, 0x218, 0x21a, 0x21c, 0x21e, 0x220, 0x222, 0x224, 0x226, 0x228, 0x22a, 0x22c, 0x22e, 0x230, 0x232, 0x23a, 0x23b, 0x23d, 0x23e, 0x241, 0x243, 0x244, 0x245, 0x246, 0x248, 0x24a, 0x24c, 0x24e, 0x345, 0x370, 0x372, 0x376, 0x37f, 0x386, 0x388, 0x389, 0x38a, 0x38c, 0x38e, 0x38f, 0x390, 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39a, 0x39b, 0x39c, 0x39d, 0x39e, 0x39f, 0x3a0, 0x3a1, 0x3a3, 0x3a4, 0x3a5, 0x3a6, 0x3a7, 0x3a8, 0x3a9, 0x3aa, 0x3ab, 0x3b0, 0x3c2, 0x3cf, 0x3d0, 0x3d1, 0x3d5, 0x3d6, 0x3d8, 0x3da, 0x3dc, 0x3de, 0x3e0, 0x3e2, 0x3e4, 0x3e6, 0x3e8, 0x3ea, 0x3ec, 0x3ee, 0x3f0, 0x3f1, 0x3f4, 0x3f5, 0x3f7, 0x3f9, 0x3fa, 0x3fd, 0x3fe, 0x3ff, 0x400, 0x401, 0x402, 0x403, 0x404, 0x405, 0x406, 0x407, 0x408, 0x409, 0x40a, 0x40b, 0x40c, 0x40d, 0x40e, 0x40f, 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, 0x418, 0x419, 0x41a, 0x41b, 0x41c, 0x41d, 0x41e, 0x41f, 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, 0x428, 0x429, 0x42a, 0x42b, 0x42c, 0x42d, 0x42e, 0x42f, 0x460, 0x462, 0x464, 0x466, 0x468, 0x46a, 0x46c, 0x46e, 0x470, 0x472, 0x474, 0x476, 0x478, 0x47a, 0x47c, 0x47e, 0x480, 0x48a, 0x48c, 0x48e, 0x490, 0x492, 0x494, 0x496, 0x498, 0x49a, 0x49c, 0x49e, 0x4a0, 0x4a2, 0x4a4, 0x4a6, 0x4a8, 0x4aa, 0x4ac, 0x4ae, 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8, 0x4ba, 0x4bc, 0x4be, 0x4c0, 0x4c1, 0x4c3, 0x4c5, 0x4c7, 0x4c9, 0x4cb, 0x4cd, 0x4d0, 0x4d2, 0x4d4, 0x4d6, 0x4d8, 0x4da, 0x4dc, 0x4de, 0x4e0, 0x4e2, 0x4e4, 0x4e6, 0x4e8, 0x4ea, 0x4ec, 0x4ee, 0x4f0, 0x4f2, 0x4f4, 0x4f6, 0x4f8, 0x4fa, 0x4fc, 0x4fe, 0x500, 0x502, 0x504, 0x506, 0x508, 0x50a, 0x50c, 0x50e, 0x510, 0x512, 0x514, 0x516, 0x518, 0x51a, 0x51c, 0x51e, 0x520, 0x522, 0x524, 0x526, 0x528, 0x52a, 0x52c, 0x52e, 0x531, 0x532, 0x533, 0x534, 0x535, 0x536, 0x537, 0x538, 0x539, 0x53a, 0x53b, 0x53c, 0x53d, 0x53e, 0x53f, 0x540, 0x541, 0x542, 0x543, 0x544, 0x545, 0x546, 0x547, 0x548, 0x549, 0x54a, 0x54b, 0x54c, 0x54d, 0x54e, 0x54f, 0x550, 0x551, 0x552, 0x553, 0x554, 0x555, 0x556, 0x587, 0x10a0, 0x10a1, 0x10a2, 0x10a3, 0x10a4, 0x10a5, 0x10a6, 0x10a7, 0x10a8, 0x10a9, 0x10aa, 0x10ab, 0x10ac, 0x10ad, 0x10ae, 0x10af, 0x10b0, 0x10b1, 0x10b2, 0x10b3, 0x10b4, 0x10b5, 0x10b6, 0x10b7, 0x10b8, 0x10b9, 0x10ba, 0x10bb, 0x10bc, 0x10bd, 0x10be, 0x10bf, 0x10c0, 0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5, 0x10c7, 0x10cd, 0x13f8, 0x13f9, 0x13fa, 0x13fb, 0x13fc, 0x13fd, 0x1c80, 0x1c81, 0x1c82, 0x1c83, 0x1c84, 0x1c85, 0x1c86, 0x1c87, 0x1c88, 0x1c90, 0x1c91, 0x1c92, 0x1c93, 0x1c94, 0x1c95, 0x1c96, 0x1c97, 0x1c98, 0x1c99, 0x1c9a, 0x1c9b, 0x1c9c, 0x1c9d, 0x1c9e, 0x1c9f, 0x1ca0, 0x1ca1, 0x1ca2, 0x1ca3, 0x1ca4, 0x1ca5, 0x1ca6, 0x1ca7, 0x1ca8, 0x1ca9, 0x1caa, 0x1cab, 0x1cac, 0x1cad, 0x1cae, 0x1caf, 0x1cb0, 0x1cb1, 0x1cb2, 0x1cb3, 0x1cb4, 0x1cb5, 0x1cb6, 0x1cb7, 0x1cb8, 0x1cb9, 0x1cba, 0x1cbd, 0x1cbe, 0x1cbf, 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08, 0x1e0a, 0x1e0c, 0x1e0e, 0x1e10, 0x1e12, 0x1e14, 0x1e16, 0x1e18, 0x1e1a, 0x1e1c, 0x1e1e, 0x1e20, 0x1e22, 0x1e24, 0x1e26, 0x1e28, 0x1e2a, 0x1e2c, 0x1e2e, 0x1e30, 0x1e32, 0x1e34, 0x1e36, 0x1e38, 0x1e3a, 0x1e3c, 0x1e3e, 0x1e40, 0x1e42, 0x1e44, 0x1e46, 0x1e48, 0x1e4a, 0x1e4c, 0x1e4e, 0x1e50, 0x1e52, 0x1e54, 0x1e56, 0x1e58, 0x1e5a, 0x1e5c, 0x1e5e, 0x1e60, 0x1e62, 0x1e64, 0x1e66, 0x1e68, 0x1e6a, 0x1e6c, 0x1e6e, 0x1e70, 0x1e72, 0x1e74, 0x1e76, 0x1e78, 0x1e7a, 0x1e7c, 0x1e7e, 0x1e80, 0x1e82, 0x1e84, 0x1e86, 0x1e88, 0x1e8a, 0x1e8c, 0x1e8e, 0x1e90, 0x1e92, 0x1e94, 0x1e96, 0x1e97, 0x1e98, 0x1e99, 0x1e9a, 0x1e9b, 0x1e9e, 0x1ea0, 0x1ea2, 0x1ea4, 0x1ea6, 0x1ea8, 0x1eaa, 0x1eac, 0x1eae, 0x1eb0, 0x1eb2, 0x1eb4, 0x1eb6, 0x1eb8, 0x1eba, 0x1ebc, 0x1ebe, 0x1ec0, 0x1ec2, 0x1ec4, 0x1ec6, 0x1ec8, 0x1eca, 0x1ecc, 0x1ece, 0x1ed0, 0x1ed2, 0x1ed4, 0x1ed6, 0x1ed8, 0x1eda, 0x1edc, 0x1ede, 0x1ee0, 0x1ee2, 0x1ee4, 0x1ee6, 0x1ee8, 0x1eea, 0x1eec, 0x1eee, 0x1ef0, 0x1ef2, 0x1ef4, 0x1ef6, 0x1ef8, 0x1efa, 0x1efc, 0x1efe, 0x1f08, 0x1f09, 0x1f0a, 0x1f0b, 0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, 0x1f18, 0x1f19, 0x1f1a, 0x1f1b, 0x1f1c, 0x1f1d, 0x1f28, 0x1f29, 0x1f2a, 0x1f2b, 0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, 0x1f38, 0x1f39, 0x1f3a, 0x1f3b, 0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, 0x1f48, 0x1f49, 0x1f4a, 0x1f4b, 0x1f4c, 0x1f4d, 0x1f50, 0x1f52, 0x1f54, 0x1f56, 0x1f59, 0x1f5b, 0x1f5d, 0x1f5f, 0x1f68, 0x1f69, 0x1f6a, 0x1f6b, 0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, 0x1f80, 0x1f81, 0x1f82, 0x1f83, 0x1f84, 0x1f85, 0x1f86, 0x1f87, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f90, 0x1f91, 0x1f92, 0x1f93, 0x1f94, 0x1f95, 0x1f96, 0x1f97, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1fa0, 0x1fa1, 0x1fa2, 0x1fa3, 0x1fa4, 0x1fa5, 0x1fa6, 0x1fa7, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fb2, 0x1fb3, 0x1fb4, 0x1fb6, 0x1fb7, 0x1fb8, 0x1fb9, 0x1fba, 0x1fbb, 0x1fbc, 0x1fbe, 0x1fc2, 0x1fc3, 0x1fc4, 0x1fc6, 0x1fc7, 0x1fc8, 0x1fc9, 0x1fca, 0x1fcb, 0x1fcc, 0x1fd2, 0x1fd3, 0x1fd6, 0x1fd7, 0x1fd8, 0x1fd9, 0x1fda, 0x1fdb, 0x1fe2, 0x1fe3, 0x1fe4, 0x1fe6, 0x1fe7, 0x1fe8, 0x1fe9, 0x1fea, 0x1feb, 0x1fec, 0x1ff2, 0x1ff3, 0x1ff4, 0x1ff6, 0x1ff7, 0x1ff8, 0x1ff9, 0x1ffa, 0x1ffb, 0x1ffc, 0x2126, 0x212a, 0x212b, 0x2132, 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216a, 0x216b, 0x216c, 0x216d, 0x216e, 0x216f, 0x2183, 0x24b6, 0x24b7, 0x24b8, 0x24b9, 0x24ba, 0x24bb, 0x24bc, 0x24bd, 0x24be, 0x24bf, 0x24c0, 0x24c1, 0x24c2, 0x24c3, 0x24c4, 0x24c5, 0x24c6, 0x24c7, 0x24c8, 0x24c9, 0x24ca, 0x24cb, 0x24cc, 0x24cd, 0x24ce, 0x24cf, 0x2c00, 0x2c01, 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, 0x2c08, 0x2c09, 0x2c0a, 0x2c0b, 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x2c10, 0x2c11, 0x2c12, 0x2c13, 0x2c14, 0x2c15, 0x2c16, 0x2c17, 0x2c18, 0x2c19, 0x2c1a, 0x2c1b, 0x2c1c, 0x2c1d, 0x2c1e, 0x2c1f, 0x2c20, 0x2c21, 0x2c22, 0x2c23, 0x2c24, 0x2c25, 0x2c26, 0x2c27, 0x2c28, 0x2c29, 0x2c2a, 0x2c2b, 0x2c2c, 0x2c2d, 0x2c2e, 0x2c2f, 0x2c60, 0x2c62, 0x2c63, 0x2c64, 0x2c67, 0x2c69, 0x2c6b, 0x2c6d, 0x2c6e, 0x2c6f, 0x2c70, 0x2c72, 0x2c75, 0x2c7e, 0x2c7f, 0x2c80, 0x2c82, 0x2c84, 0x2c86, 0x2c88, 0x2c8a, 0x2c8c, 0x2c8e, 0x2c90, 0x2c92, 0x2c94, 0x2c96, 0x2c98, 0x2c9a, 0x2c9c, 0x2c9e, 0x2ca0, 0x2ca2, 0x2ca4, 0x2ca6, 0x2ca8, 0x2caa, 0x2cac, 0x2cae, 0x2cb0, 0x2cb2, 0x2cb4, 0x2cb6, 0x2cb8, 0x2cba, 0x2cbc, 0x2cbe, 0x2cc0, 0x2cc2, 0x2cc4, 0x2cc6, 0x2cc8, 0x2cca, 0x2ccc, 0x2cce, 0x2cd0, 0x2cd2, 0x2cd4, 0x2cd6, 0x2cd8, 0x2cda, 0x2cdc, 0x2cde, 0x2ce0, 0x2ce2, 0x2ceb, 0x2ced, 0x2cf2, 0xa640, 0xa642, 0xa644, 0xa646, 0xa648, 0xa64a, 0xa64c, 0xa64e, 0xa650, 0xa652, 0xa654, 0xa656, 0xa658, 0xa65a, 0xa65c, 0xa65e, 0xa660, 0xa662, 0xa664, 0xa666, 0xa668, 0xa66a, 0xa66c, 0xa680, 0xa682, 0xa684, 0xa686, 0xa688, 0xa68a, 0xa68c, 0xa68e, 0xa690, 0xa692, 0xa694, 0xa696, 0xa698, 0xa69a, 0xa722, 0xa724, 0xa726, 0xa728, 0xa72a, 0xa72c, 0xa72e, 0xa732, 0xa734, 0xa736, 0xa738, 0xa73a, 0xa73c, 0xa73e, 0xa740, 0xa742, 0xa744, 0xa746, 0xa748, 0xa74a, 0xa74c, 0xa74e, 0xa750, 0xa752, 0xa754, 0xa756, 0xa758, 0xa75a, 0xa75c, 0xa75e, 0xa760, 0xa762, 0xa764, 0xa766, 0xa768, 0xa76a, 0xa76c, 0xa76e, 0xa779, 0xa77b, 0xa77d, 0xa77e, 0xa780, 0xa782, 0xa784, 0xa786, 0xa78b, 0xa78d, 0xa790, 0xa792, 0xa796, 0xa798, 0xa79a, 0xa79c, 0xa79e, 0xa7a0, 0xa7a2, 0xa7a4, 0xa7a6, 0xa7a8, 0xa7aa, 0xa7ab, 0xa7ac, 0xa7ad, 0xa7ae, 0xa7b0, 0xa7b1, 0xa7b2, 0xa7b3, 0xa7b4, 0xa7b6, 0xa7b8, 0xa7ba, 0xa7bc, 0xa7be, 0xa7c0, 0xa7c2, 0xa7c4, 0xa7c5, 0xa7c6, 0xa7c7, 0xa7c9, 0xa7d0, 0xa7d6, 0xa7d8, 0xa7f5, 0xab70, 0xab71, 0xab72, 0xab73, 0xab74, 0xab75, 0xab76, 0xab77, 0xab78, 0xab79, 0xab7a, 0xab7b, 0xab7c, 0xab7d, 0xab7e, 0xab7f, 0xab80, 0xab81, 0xab82, 0xab83, 0xab84, 0xab85, 0xab86, 0xab87, 0xab88, 0xab89, 0xab8a, 0xab8b, 0xab8c, 0xab8d, 0xab8e, 0xab8f, 0xab90, 0xab91, 0xab92, 0xab93, 0xab94, 0xab95, 0xab96, 0xab97, 0xab98, 0xab99, 0xab9a, 0xab9b, 0xab9c, 0xab9d, 0xab9e, 0xab9f, 0xaba0, 0xaba1, 0xaba2, 0xaba3, 0xaba4, 0xaba5, 0xaba6, 0xaba7, 0xaba8, 0xaba9, 0xabaa, 0xabab, 0xabac, 0xabad, 0xabae, 0xabaf, 0xabb0, 0xabb1, 0xabb2, 0xabb3, 0xabb4, 0xabb5, 0xabb6, 0xabb7, 0xabb8, 0xabb9, 0xabba, 0xabbb, 0xabbc, 0xabbd, 0xabbe, 0xabbf, 0xfb00, 0xfb01, 0xfb02, 0xfb03, 0xfb04, 0xfb05, 0xfb06, 0xfb13, 0xfb14, 0xfb15, 0xfb16, 0xfb17, 0xff21, 0xff22, 0xff23, 0xff24, 0xff25, 0xff26, 0xff27, 0xff28, 0xff29, 0xff2a, 0xff2b, 0xff2c, 0xff2d, 0xff2e, 0xff2f, 0xff30, 0xff31, 0xff32, 0xff33, 0xff34, 0xff35, 0xff36, 0xff37, 0xff38, 0xff39, 0xff3a, 0x10400, 0x10401, 0x10402, 0x10403, 0x10404, 0x10405, 0x10406, 0x10407, 0x10408, 0x10409, 0x1040a, 0x1040b, 0x1040c, 0x1040d, 0x1040e, 0x1040f, 0x10410, 0x10411, 0x10412, 0x10413, 0x10414, 0x10415, 0x10416, 0x10417, 0x10418, 0x10419, 0x1041a, 0x1041b, 0x1041c, 0x1041d, 0x1041e, 0x1041f, 0x10420, 0x10421, 0x10422, 0x10423, 0x10424, 0x10425, 0x10426, 0x10427, 0x104b0, 0x104b1, 0x104b2, 0x104b3, 0x104b4, 0x104b5, 0x104b6, 0x104b7, 0x104b8, 0x104b9, 0x104ba, 0x104bb, 0x104bc, 0x104bd, 0x104be, 0x104bf, 0x104c0, 0x104c1, 0x104c2, 0x104c3, 0x104c4, 0x104c5, 0x104c6, 0x104c7, 0x104c8, 0x104c9, 0x104ca, 0x104cb, 0x104cc, 0x104cd, 0x104ce, 0x104cf, 0x104d0, 0x104d1, 0x104d2, 0x104d3, 0x10570, 0x10571, 0x10572, 0x10573, 0x10574, 0x10575, 0x10576, 0x10577, 0x10578, 0x10579, 0x1057a, 0x1057c, 0x1057d, 0x1057e, 0x1057f, 0x10580, 0x10581, 0x10582, 0x10583, 0x10584, 0x10585, 0x10586, 0x10587, 0x10588, 0x10589, 0x1058a, 0x1058c, 0x1058d, 0x1058e, 0x1058f, 0x10590, 0x10591, 0x10592, 0x10594, 0x10595, 0x10c80, 0x10c81, 0x10c82, 0x10c83, 0x10c84, 0x10c85, 0x10c86, 0x10c87, 0x10c88, 0x10c89, 0x10c8a, 0x10c8b, 0x10c8c, 0x10c8d, 0x10c8e, 0x10c8f, 0x10c90, 0x10c91, 0x10c92, 0x10c93, 0x10c94, 0x10c95, 0x10c96, 0x10c97, 0x10c98, 0x10c99, 0x10c9a, 0x10c9b, 0x10c9c, 0x10c9d, 0x10c9e, 0x10c9f, 0x10ca0, 0x10ca1, 0x10ca2, 0x10ca3, 0x10ca4, 0x10ca5, 0x10ca6, 0x10ca7, 0x10ca8, 0x10ca9, 0x10caa, 0x10cab, 0x10cac, 0x10cad, 0x10cae, 0x10caf, 0x10cb0, 0x10cb1, 0x10cb2, 0x118a0, 0x118a1, 0x118a2, 0x118a3, 0x118a4, 0x118a5, 0x118a6, 0x118a7, 0x118a8, 0x118a9, 0x118aa, 0x118ab, 0x118ac, 0x118ad, 0x118ae, 0x118af, 0x118b0, 0x118b1, 0x118b2, 0x118b3, 0x118b4, 0x118b5, 0x118b6, 0x118b7, 0x118b8, 0x118b9, 0x118ba, 0x118bb, 0x118bc, 0x118bd, 0x118be, 0x118bf, 0x16e40, 0x16e41, 0x16e42, 0x16e43, 0x16e44, 0x16e45, 0x16e46, 0x16e47, 0x16e48, 0x16e49, 0x16e4a, 0x16e4b, 0x16e4c, 0x16e4d, 0x16e4e, 0x16e4f, 0x16e50, 0x16e51, 0x16e52, 0x16e53, 0x16e54, 0x16e55, 0x16e56, 0x16e57, 0x16e58, 0x16e59, 0x16e5a, 0x16e5b, 0x16e5c, 0x16e5d, 0x16e5e, 0x16e5f, 0x1e900, 0x1e901, 0x1e902, 0x1e903, 0x1e904, 0x1e905, 0x1e906, 0x1e907, 0x1e908, 0x1e909, 0x1e90a, 0x1e90b, 0x1e90c, 0x1e90d, 0x1e90e, 0x1e90f, 0x1e910, 0x1e911, 0x1e912, 0x1e913, 0x1e914, 0x1e915, 0x1e916, 0x1e917, 0x1e918, 0x1e919, 0x1e91a, 0x1e91b, 0x1e91c, 0x1e91d, 0x1e91e, 0x1e91f, 0x1e920, 0x1e921}
var _unicodeCaseFoldingTo = [...]rune{97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 956, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 248, 249, 250, 251, 252, 253, 254, 115, 115, 257, 259, 261, 263, 265, 267, 269, 271, 273, 275, 277, 279, 281, 283, 285, 287, 289, 291, 293, 295, 297, 299, 301, 303, 105, 775, 307, 309, 311, 314, 316, 318, 320, 322, 324, 326, 328, 700, 110, 331, 333, 335, 337, 339, 341, 343, 345, 347, 349, 351, 353, 355, 357, 359, 361, 363, 365, 367, 369, 371, 373, 375, 255, 378, 380, 382, 115, 595, 387, 389, 596, 392, 598, 599, 396, 477, 601, 603, 402, 608, 611, 617, 616, 409, 623, 626, 629, 417, 419, 421, 640, 424, 643, 429, 648, 432, 650, 651, 436, 438, 658, 441, 445, 454, 454, 457, 457, 460, 460, 462, 464, 466, 468, 470, 472, 474, 476, 479, 481, 483, 485, 487, 489, 491, 493, 495, 106, 780, 499, 499, 501, 405, 447, 505, 507, 509, 511, 513, 515, 517, 519, 521, 523, 525, 527, 529, 531, 533, 535, 537, 539, 541, 543, 414, 547, 549, 551, 553, 555, 557, 559, 561, 563, 11365, 572, 410, 11366, 578, 384, 649, 652, 583, 585, 587, 589, 591, 953, 881, 883, 887, 1011, 940, 941, 942, 943, 972, 973, 974, 953, 776, 769, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 963, 964, 965, 966, 967, 968, 969, 970, 971, 965, 776, 769, 963, 983, 946, 952, 966, 960, 985, 987, 989, 991, 993, 995, 997, 999, 1001, 1003, 1005, 1007, 954, 961, 952, 949, 1016, 1010, 1019, 891, 892, 893, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 1101, 1102, 1103, 1121, 1123, 1125, 1127, 1129, 1131, 1133, 1135, 1137, 1139, 1141, 1143, 1145, 1147, 1149, 1151, 1153, 1163, 1165, 1167, 1169, 1171, 1173, 1175, 1177, 1179, 1181, 1183, 1185, 1187, 1189, 1191, 1193, 1195, 1197, 1199, 1201, 1203, 1205, 1207, 1209, 1211, 1213, 1215, 1231, 1218, 1220, 1222, 1224, 1226, 1228, 1230, 1233, 1235, 1237, 1239, 1241, 1243, 1245, 1247, 1249, 1251, 1253, 1255, 1257, 1259, 1261, 1263, 1265, 1267, 1269, 1271, 1273, 1275, 1277, 1279, 1281, 1283, 1285, 1287, 1289, 1291, 1293, 1295, 1297, 1299, 1301, 1303, 1305, 1307, 1309, 1311, 1313, 1315, 1317, 1319, 1321, 1323, 1325, 1327, 1377, 1378, 1379, 1380, 1381, 1382, 1383, 1384, 1385, 1386, 1387, 1388, 1389, 1390, 1391, 1392, 1393, 1394, 1395, 1396, 1397, 1398, 1399, 1400, 1401, 1402, 1403, 1404, 1405, 1406, 1407, 1408, 1409, 1410, 1411, 1412, 1413, 1414, 1381, 1410, 11520, 11521, 11522, 11523, 11524, 11525, 11526, 11527, 11528, 11529, 11530, 11531, 11532, 11533, 11534, 11535, 11536, 11537, 11538, 11539, 11540, 11541, 11542, 11543, 11544, 11545, 11546, 11547, 11548, 11549, 11550, 11551, 11552, 11553, 11554, 11555, 11556, 11557, 11559, 11565, 5104, 5105, 5106, 5107, 5108, 5109, 1074, 1076, 1086, 1089, 1090, 1090, 1098, 1123, 42571, 4304, 4305, 4306, 4307, 4308, 4309, 4310, 4311, 4312, 4313, 4314, 4315, 4316, 4317, 4318, 4319, 4320, 4321, 4322, 4323, 4324, 4325, 4326, 4327, 4328, 4329, 4330, 4331, 4332, 4333, 4334, 4335, 4336, 4337, 4338, 4339, 4340, 4341, 4342, 4343, 4344, 4345, 4346, 4349, 4350, 4351, 7681, 7683, 7685, 7687, 7689, 7691, 7693, 7695, 7697, 7699, 7701, 7703, 7705, 7707, 7709, 7711, 7713, 7715, 7717, 7719, 7721, 7723, 7725, 7727, 7729, 7731, 7733, 7735, 7737, 7739, 7741, 7743, 7745, 7747, 7749, 7751, 7753, 7755, 7757, 7759, 7761, 7763, 7765, 7767, 7769, 7771, 7773, 7775, 7777, 7779, 7781, 7783, 7785, 7787, 7789, 7791, 7793, 7795, 7797, 7799, 7801, 7803, 7805, 7807, 7809, 7811, 7813, 7815, 7817, 7819, 7821, 7823, 7825, 7827, 7829, 104, 817, 116, 776, 119, 778, 121, 778, 97, 702, 7777, 115, 115, 7841, 7843, 7845, 7847, 7849, 7851, 7853, 7855, 7857, 7859, 7861, 7863, 7865, 7867, 7869, 7871, 7873, 7875, 7877, 7879, 7881, 7883, 7885, 7887, 7889, 7891, 7893, 7895, 7897, 7899, 7901, 7903, 7905, 7907, 7909, 7911, 7913, 7915, 7917, 7919, 7921, 7923, 7925, 7927, 7929, 7931, 7933, 7935, 7936, 7937, 7938, 7939, 7940, 7941, 7942, 7943, 7952, 7953, 7954, 7955, 7956, 7957, 7968, 7969, 7970, 7971, 7972, 7973, 7974, 7975, 7984, 7985, 7986, 7987, 7988, 7989, 7990, 7991, 8000, 8001, 8002, 8003, 8004, 8005, 965, 787, 965, 787, 768, 965, 787, 769, 965, 787, 834, 8017, 8019, 8021, 8023, 8032, 8033, 8034, 8035, 8036, 8037, 8038, 8039, 7936, 953, 7937, 953, 7938, 953, 7939, 953, 7940, 953, 7941, 953, 7942, 953, 7943, 953, 7936, 953, 7937, 953, 7938, 953, 7939, 953, 7940, 953, 7941, 953, 7942, 953, 7943, 953, 7968, 953, 7969, 953, 7970, 953, 7971, 953, 7972, 953, 7973, 953, 7974, 953, 7975, 953, 7968, 953, 7969, 953, 7970, 953, 7971, 953, 7972, 953, 7973, 953, 7974, 953, 7975, 953, 8032, 953, 8033, 953, 8034, 953, 8035, 953, 8036, 953, 8037, 953, 8038, 953, 8039, 953, 8032, 953, 8033, 953, 8034, 953, 8035, 953, 8036, 953, 8037, 953, 8038, 953, 8039, 953, 8048, 953, 945, 953, 940, 953, 945, 834, 945, 834, 953, 8112, 8113, 8048, 8049, 945, 953, 953, 8052, 953, 951, 953, 942, 953, 951, 834, 951, 834, 953, 8050, 8051, 8052, 8053, 951, 953, 953, 776, 768, 953, 776, 769, 953, 834, 953, 776, 834, 8144, 8145, 8054, 8055, 965, 776, 768, 965, 776, 769, 961, 787, 965, 834, 965, 776, 834, 8160, 8161, 8058, 8059, 8165, 8060, 953, 969, 953, 974, 953, 969, 834, 969, 834, 953, 8056, 8057, 8060, 8061, 969, 953, 969, 107, 229, 8526, 8560, 8561, 8562, 8563, 8564, 8565, 8566, 8567, 8568, 8569, 8570, 8571, 8572, 8573, 8574, 8575, 8580, 9424, 9425, 9426, 9427, 9428, 9429, 9430, 9431, 9432, 9433, 9434, 9435, 9436, 9437, 9438, 9439, 9440, 9441, 9442, 9443, 9444, 9445, 9446, 9447, 9448, 9449, 11312, 11313, 11314, 11315, 11316, 11317, 11318, 11319, 11320, 11321, 11322, 11323, 11324, 11325, 11326, 11327, 11328, 11329, 11330, 11331, 11332, 11333, 11334, 11335, 11336, 11337, 11338, 11339, 11340, 11341, 11342, 11343, 11344, 11345, 11346, 11347, 11348, 11349, 11350, 11351, 11352, 11353, 11354, 11355, 11356, 11357, 11358, 11359, 11361, 619, 7549, 637, 11368, 11370, 11372, 593, 625, 592, 594, 11379, 11382, 575, 576, 11393, 11395, 11397, 11399, 11401, 11403, 11405, 11407, 11409, 11411, 11413, 11415, 11417, 11419, 11421, 11423, 11425, 11427, 11429, 11431, 11433, 11435, 11437, 11439, 11441, 11443, 11445, 11447, 11449, 11451, 11453, 11455, 11457, 11459, 11461, 11463, 11465, 11467, 11469, 11471, 11473, 11475, 11477, 11479, 11481, 11483, 11485, 11487, 11489, 11491, 11500, 11502, 11507, 42561, 42563, 42565, 42567, 42569, 42571, 42573, 42575, 42577, 42579, 42581, 42583, 42585, 42587, 42589, 42591, 42593, 42595, 42597, 42599, 42601, 42603, 42605, 42625, 42627, 42629, 42631, 42633, 42635, 42637, 42639, 42641, 42643, 42645, 42647, 42649, 42651, 42787, 42789, 42791, 42793, 42795, 42797, 42799, 42803, 42805, 42807, 42809, 42811, 42813, 42815, 42817, 42819, 42821, 42823, 42825, 42827, 42829, 42831, 42833, 42835, 42837, 42839, 42841, 42843, 42845, 42847, 42849, 42851, 42853, 42855, 42857, 42859, 42861, 42863, 42874, 42876, 7545, 42879, 42881, 42883, 42885, 42887, 42892, 613, 42897, 42899, 42903, 42905, 42907, 42909, 42911, 42913, 42915, 42917, 42919, 42921, 614, 604, 609, 620, 618, 670, 647, 669, 43859, 42933, 42935, 42937, 42939, 42941, 42943, 42945, 42947, 42900, 642, 7566, 42952, 42954, 42961, 42967, 42969, 42998, 5024, 5025, 5026, 5027, 5028, 5029, 5030, 5031, 5032, 5033, 5034, 5035, 5036, 5037, 5038, 5039, 5040, 5041, 5042, 5043, 5044, 5045, 5046, 5047, 5048, 5049, 5050, 5051, 5052, 5053, 5054, 5055, 5056, 5057, 5058, 5059, 5060, 5061, 5062, 5063, 5064, 5065, 5066, 5067, 5068, 5069, 5070, 5071, 5072, 5073, 5074, 5075, 5076, 5077, 5078, 5079, 5080, 5081, 5082, 5083, 5084, 5085, 5086, 5087, 5088, 5089, 5090, 5091, 5092, 5093, 5094, 5095, 5096, 5097, 5098, 5099, 5100, 5101, 5102, 5103, 102, 102, 102, 105, 102, 108, 102, 102, 105, 102, 102, 108, 115, 116, 115, 116, 1396, 1398, 1396, 1381, 1396, 1387, 1406, 1398, 1396, 1389, 65345, 65346, 65347, 65348, 65349, 65350, 65351, 65352, 65353, 65354, 65355, 65356, 65357, 65358, 65359, 65360, 65361, 65362, 65363, 65364, 65365, 65366, 65367, 65368, 65369, 65370, 66600, 66601, 66602, 66603, 66604, 66605, 66606, 66607, 66608, 66609, 66610, 66611, 66612, 66613, 66614, 66615, 66616, 66617, 66618, 66619, 66620, 66621, 66622, 66623, 66624, 66625, 66626, 66627, 66628, 66629, 66630, 66631, 66632, 66633, 66634, 66635, 66636, 66637, 66638, 66639, 66776, 66777, 66778, 66779, 66780, 66781, 66782, 66783, 66784, 66785, 66786, 66787, 66788, 66789, 66790, 66791, 66792, 66793, 66794, 66795, 66796, 66797, 66798, 66799, 66800, 66801, 66802, 66803, 66804, 66805, 66806, 66807, 66808, 66809, 66810, 66811, 66967, 66968, 66969, 66970, 66971, 66972, 66973, 66974, 66975, 66976, 66977, 66979, 66980, 66981, 66982, 66983, 66984, 66985, 66986, 66987, 66988, 66989, 66990, 66991, 66992, 66993, 66995, 66996, 66997, 66998, 66999, 67000, 67001, 67003, 67004, 68800, 68801, 68802, 68803, 68804, 68805, 68806, 68807, 68808, 68809, 68810, 68811, 68812, 68813, 68814, 68815, 68816, 68817, 68818, 68819, 68820, 68821, 68822, 68823, 68824, 68825, 68826, 68827, 68828, 68829, 68830, 68831, 68832, 68833, 68834, 68835, 68836, 68837, 68838, 68839, 68840, 68841, 68842, 68843, 68844, 68845, 68846, 68847, 68848, 68849, 68850, 71872, 71873, 71874, 71875, 71876, 71877, 71878, 71879, 71880, 71881, 71882, 71883, 71884, 71885, 71886, 71887, 71888, 71889, 71890, 71891, 71892, 71893, 71894, 71895, 71896, 71897, 71898, 71899, 71900, 71901, 71902, 71903, 93792, 93793, 93794, 93795, 93796, 93797, 93798, 93799, 93800, 93801, 93802, 93803, 93804, 93805, 93806, 93807, 93808, 93809, 93810, 93811, 93812, 93813, 93814, 93815, 93816, 93817, 93818, 93819, 93820, 93821, 93822, 93823, 125218, 125219, 125220, 125221, 125222, 125223, 125224, 125225, 125226, 125227, 125228, 125229, 125230, 125231, 125232, 125233, 125234, 125235, 125236, 125237, 125238, 125239, 125240, 125241, 125242, 125243, 125244, 125245, 125246, 125247, 125248, 125249, 125250, 125251}
var _unicodeCaseFoldingToIndex = "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x03\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x03\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x02\x02\x02\x02\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x03\x03\x03\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x03\x01\x01\x01\x01\x02\x01\x02\x02\x02\x02\x03\x01\x01\x01\x01\x02\x03\x03\x02\x03\x01\x01\x01\x01\x03\x03\x02\x02\x03\x01\x01\x01\x01\x01\x02\x02\x02\x02\x03\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x02\x02\x03\x03\x02\x02\x02\x02\x02\x02\x02\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
yuin-goldmark-9a7f4c9/util/unicode_case_folding.go 0000664 0000000 0000000 00000001105 15037761217 0022361 0 ustar 00root root 0000000 0000000 package util
//go:generate go run ../_tools unicode-case-folding-map -o ../_tools/unicode-case-folding-map.json
//go:generate go run ../_tools emb-structs -i ../_tools/unicode-case-folding-map.json -o ./unicode_case_folding.gen.go
var unicodeCaseFoldings map[rune][]rune
func init() {
unicodeCaseFoldings = make(map[rune][]rune, _unicodeCaseFoldingLength)
cTo := 0
for i := 0; i < _unicodeCaseFoldingLength; i++ {
tTo := cTo + int(_unicodeCaseFoldingToIndex[i])
to := _unicodeCaseFoldingTo[cTo:tTo]
unicodeCaseFoldings[_unicodeCaseFoldingFrom[i]] = to
cTo = tTo
}
}
yuin-goldmark-9a7f4c9/util/util.go 0000664 0000000 0000000 00000072453 15037761217 0017231 0 ustar 00root root 0000000 0000000 // Package util provides utility functions for the goldmark.
package util
import (
"bytes"
"io"
"net/url"
"regexp"
"sort"
"strconv"
"unicode"
"unicode/utf8"
)
// A CopyOnWriteBuffer is a byte buffer that copies buffer when
// it need to be changed.
type CopyOnWriteBuffer struct {
buffer []byte
copied bool
}
// NewCopyOnWriteBuffer returns a new CopyOnWriteBuffer.
func NewCopyOnWriteBuffer(buffer []byte) CopyOnWriteBuffer {
return CopyOnWriteBuffer{
buffer: buffer,
copied: false,
}
}
// Write writes given bytes to the buffer.
// Write allocate new buffer and clears it at the first time.
func (b *CopyOnWriteBuffer) Write(value []byte) {
if !b.copied {
b.buffer = make([]byte, 0, len(b.buffer)+20)
b.copied = true
}
b.buffer = append(b.buffer, value...)
}
// WriteString writes given string to the buffer.
// WriteString allocate new buffer and clears it at the first time.
func (b *CopyOnWriteBuffer) WriteString(value string) {
b.Write(StringToReadOnlyBytes(value))
}
// Append appends given bytes to the buffer.
// Append copy buffer at the first time.
func (b *CopyOnWriteBuffer) Append(value []byte) {
if !b.copied {
tmp := make([]byte, len(b.buffer), len(b.buffer)+20)
copy(tmp, b.buffer)
b.buffer = tmp
b.copied = true
}
b.buffer = append(b.buffer, value...)
}
// AppendString appends given string to the buffer.
// AppendString copy buffer at the first time.
func (b *CopyOnWriteBuffer) AppendString(value string) {
b.Append(StringToReadOnlyBytes(value))
}
// WriteByte writes the given byte to the buffer.
// WriteByte allocate new buffer and clears it at the first time.
func (b *CopyOnWriteBuffer) WriteByte(c byte) error {
if !b.copied {
b.buffer = make([]byte, 0, len(b.buffer)+20)
b.copied = true
}
b.buffer = append(b.buffer, c)
return nil
}
// AppendByte appends given bytes to the buffer.
// AppendByte copy buffer at the first time.
func (b *CopyOnWriteBuffer) AppendByte(c byte) {
if !b.copied {
tmp := make([]byte, len(b.buffer), len(b.buffer)+20)
copy(tmp, b.buffer)
b.buffer = tmp
b.copied = true
}
b.buffer = append(b.buffer, c)
}
// Bytes returns bytes of this buffer.
func (b *CopyOnWriteBuffer) Bytes() []byte {
return b.buffer
}
// IsCopied returns true if buffer has been copied, otherwise false.
func (b *CopyOnWriteBuffer) IsCopied() bool {
return b.copied
}
// IsEscapedPunctuation returns true if character at a given index i
// is an escaped punctuation, otherwise false.
func IsEscapedPunctuation(source []byte, i int) bool {
return source[i] == '\\' && i < len(source)-1 && IsPunct(source[i+1])
}
// ReadWhile read the given source while pred is true.
func ReadWhile(source []byte, index [2]int, pred func(byte) bool) (int, bool) {
j := index[0]
ok := false
for ; j < index[1]; j++ {
c1 := source[j]
if pred(c1) {
ok = true
continue
}
break
}
return j, ok
}
// IsBlank returns true if the given string is all space characters.
func IsBlank(bs []byte) bool {
for _, b := range bs {
if !IsSpace(b) {
return false
}
}
return true
}
// VisualizeSpaces visualize invisible space characters.
func VisualizeSpaces(bs []byte) []byte {
bs = bytes.Replace(bs, []byte(" "), []byte("[SPACE]"), -1)
bs = bytes.Replace(bs, []byte("\t"), []byte("[TAB]"), -1)
bs = bytes.Replace(bs, []byte("\n"), []byte("[NEWLINE]\n"), -1)
bs = bytes.Replace(bs, []byte("\r"), []byte("[CR]"), -1)
bs = bytes.Replace(bs, []byte("\v"), []byte("[VTAB]"), -1)
bs = bytes.Replace(bs, []byte("\x00"), []byte("[NUL]"), -1)
bs = bytes.Replace(bs, []byte("\ufffd"), []byte("[U+FFFD]"), -1)
return bs
}
// TabWidth calculates actual width of a tab at the given position.
func TabWidth(currentPos int) int {
return 4 - currentPos%4
}
// IndentPosition searches an indent position with the given width for the given line.
// If the line contains tab characters, paddings may be not zero.
// currentPos==0 and width==2:
//
// position: 0 1
// [TAB]aaaa
// width: 1234 5678
//
// width=2 is in the tab character. In this case, IndentPosition returns
// (pos=1, padding=2).
func IndentPosition(bs []byte, currentPos, width int) (pos, padding int) {
return IndentPositionPadding(bs, currentPos, 0, width)
}
// IndentPositionPadding searches an indent position with the given width for the given line.
// This function is mostly same as IndentPosition except this function
// takes account into additional paddings.
func IndentPositionPadding(bs []byte, currentPos, paddingv, width int) (pos, padding int) {
if width == 0 {
return 0, paddingv
}
w := 0
i := 0
l := len(bs)
p := paddingv
for ; i < l; i++ {
if p > 0 {
p--
w++
continue
}
if bs[i] == '\t' && w < width {
w += TabWidth(currentPos + w)
} else if bs[i] == ' ' && w < width {
w++
} else {
break
}
}
if w >= width {
return i - paddingv, w - width
}
return -1, -1
}
// DedentPosition dedents lines by the given width.
//
// Deprecated: This function has bugs. Use util.IndentPositionPadding and util.FirstNonSpacePosition.
func DedentPosition(bs []byte, currentPos, width int) (pos, padding int) {
if width == 0 {
return 0, 0
}
w := 0
l := len(bs)
i := 0
for ; i < l; i++ {
if bs[i] == '\t' {
w += TabWidth(currentPos + w)
} else if bs[i] == ' ' {
w++
} else {
break
}
}
if w >= width {
return i, w - width
}
return i, 0
}
// DedentPositionPadding dedents lines by the given width.
// This function is mostly same as DedentPosition except this function
// takes account into additional paddings.
//
// Deprecated: This function has bugs. Use util.IndentPositionPadding and util.FirstNonSpacePosition.
func DedentPositionPadding(bs []byte, currentPos, paddingv, width int) (pos, padding int) {
if width == 0 {
return 0, paddingv
}
w := 0
i := 0
l := len(bs)
for ; i < l; i++ {
if bs[i] == '\t' {
w += TabWidth(currentPos + w)
} else if bs[i] == ' ' {
w++
} else {
break
}
}
if w >= width {
return i - paddingv, w - width
}
return i - paddingv, 0
}
// IndentWidth calculate an indent width for the given line.
func IndentWidth(bs []byte, currentPos int) (width, pos int) {
l := len(bs)
for i := 0; i < l; i++ {
b := bs[i]
if b == ' ' {
width++
pos++
} else if b == '\t' {
width += TabWidth(currentPos + width)
pos++
} else {
break
}
}
return
}
// FirstNonSpacePosition returns a position line that is a first nonspace
// character.
func FirstNonSpacePosition(bs []byte) int {
i := 0
for ; i < len(bs); i++ {
c := bs[i]
if c == ' ' || c == '\t' {
continue
}
if c == '\n' {
return -1
}
return i
}
return -1
}
// FindClosure returns a position that closes the given opener.
// If codeSpan is set true, it ignores characters in code spans.
// If allowNesting is set true, closures correspond to nested opener will be
// ignored.
//
// Deprecated: This function can not handle newlines. Many elements
// can be existed over multiple lines(e.g. link labels).
// Use text.Reader.FindClosure.
func FindClosure(bs []byte, opener, closure byte, codeSpan, allowNesting bool) int {
i := 0
opened := 1
codeSpanOpener := 0
for i < len(bs) {
c := bs[i]
if codeSpan && codeSpanOpener != 0 && c == '`' {
codeSpanCloser := 0
for ; i < len(bs); i++ {
if bs[i] == '`' {
codeSpanCloser++
} else {
i--
break
}
}
if codeSpanCloser == codeSpanOpener {
codeSpanOpener = 0
}
} else if codeSpanOpener == 0 && c == '\\' && i < len(bs)-1 && IsPunct(bs[i+1]) {
i += 2
continue
} else if codeSpan && codeSpanOpener == 0 && c == '`' {
for ; i < len(bs); i++ {
if bs[i] == '`' {
codeSpanOpener++
} else {
i--
break
}
}
} else if (codeSpan && codeSpanOpener == 0) || !codeSpan {
if c == closure {
opened--
if opened == 0 {
return i
}
} else if c == opener {
if !allowNesting {
return -1
}
opened++
}
}
i++
}
return -1
}
// TrimLeft trims characters in the given s from head of the source.
// bytes.TrimLeft offers same functionalities, but bytes.TrimLeft
// allocates new buffer for the result.
func TrimLeft(source, b []byte) []byte {
i := 0
for ; i < len(source); i++ {
c := source[i]
found := false
for j := 0; j < len(b); j++ {
if c == b[j] {
found = true
break
}
}
if !found {
break
}
}
return source[i:]
}
// TrimRight trims characters in the given s from tail of the source.
func TrimRight(source, b []byte) []byte {
i := len(source) - 1
for ; i >= 0; i-- {
c := source[i]
found := false
for j := 0; j < len(b); j++ {
if c == b[j] {
found = true
break
}
}
if !found {
break
}
}
return source[:i+1]
}
// TrimLeftLength returns a length of leading specified characters.
func TrimLeftLength(source, s []byte) int {
return len(source) - len(TrimLeft(source, s))
}
// TrimRightLength returns a length of trailing specified characters.
func TrimRightLength(source, s []byte) int {
return len(source) - len(TrimRight(source, s))
}
// TrimLeftSpaceLength returns a length of leading space characters.
func TrimLeftSpaceLength(source []byte) int {
i := 0
for ; i < len(source); i++ {
if !IsSpace(source[i]) {
break
}
}
return i
}
// TrimRightSpaceLength returns a length of trailing space characters.
func TrimRightSpaceLength(source []byte) int {
l := len(source)
i := l - 1
for ; i >= 0; i-- {
if !IsSpace(source[i]) {
break
}
}
if i < 0 {
return l
}
return l - 1 - i
}
// TrimLeftSpace returns a subslice of the given string by slicing off all leading
// space characters.
func TrimLeftSpace(source []byte) []byte {
return TrimLeft(source, spaces)
}
// TrimRightSpace returns a subslice of the given string by slicing off all trailing
// space characters.
func TrimRightSpace(source []byte) []byte {
return TrimRight(source, spaces)
}
// DoFullUnicodeCaseFolding performs full unicode case folding to given bytes.
func DoFullUnicodeCaseFolding(v []byte) []byte {
var rbuf []byte
cob := NewCopyOnWriteBuffer(v)
n := 0
for i := 0; i < len(v); i++ {
c := v[i]
if c < 0xb5 {
if c >= 0x41 && c <= 0x5a {
// A-Z to a-z
cob.Write(v[n:i])
_ = cob.WriteByte(c + 32)
n = i + 1
}
continue
}
if !utf8.RuneStart(c) {
continue
}
r, length := utf8.DecodeRune(v[i:])
if r == utf8.RuneError {
continue
}
folded, ok := unicodeCaseFoldings[r]
if !ok {
continue
}
cob.Write(v[n:i])
if rbuf == nil {
rbuf = make([]byte, 4)
}
for _, f := range folded {
l := utf8.EncodeRune(rbuf, f)
cob.Write(rbuf[:l])
}
i += length - 1
n = i + 1
}
if cob.IsCopied() {
cob.Write(v[n:])
}
return cob.Bytes()
}
// ReplaceSpaces replaces sequence of spaces with the given repl.
func ReplaceSpaces(source []byte, repl byte) []byte {
var ret []byte
start := -1
for i, c := range source {
iss := IsSpace(c)
if start < 0 && iss {
start = i
continue
} else if start >= 0 && iss {
continue
} else if start >= 0 {
if ret == nil {
ret = make([]byte, 0, len(source))
ret = append(ret, source[:start]...)
}
ret = append(ret, repl)
start = -1
}
if ret != nil {
ret = append(ret, c)
}
}
if start >= 0 && ret != nil {
ret = append(ret, repl)
}
if ret == nil {
return source
}
return ret
}
// ToRune decode given bytes start at pos and returns a rune.
func ToRune(source []byte, pos int) rune {
i := pos
for ; i >= 0; i-- {
if utf8.RuneStart(source[i]) {
break
}
}
r, _ := utf8.DecodeRune(source[i:])
return r
}
// ToValidRune returns 0xFFFD if the given rune is invalid, otherwise v.
func ToValidRune(v rune) rune {
if v == 0 || !utf8.ValidRune(v) {
return rune(0xFFFD)
}
return v
}
// ToLinkReference converts given bytes into a valid link reference string.
// ToLinkReference performs unicode case folding, trims leading and trailing spaces, converts into lower
// case and replace spaces with a single space character.
func ToLinkReference(v []byte) string {
v = TrimLeftSpace(v)
v = TrimRightSpace(v)
v = DoFullUnicodeCaseFolding(v)
return string(ReplaceSpaces(v, ' '))
}
var htmlQuote = []byte(""")
var htmlAmp = []byte("&")
var htmlLess = []byte("<")
var htmlGreater = []byte(">")
var htmlEscapeTable = [256]*[]byte{nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, &htmlQuote, nil, nil, nil, &htmlAmp, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, &htmlLess, nil, &htmlGreater, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil} //nolint:golint,lll
// EscapeHTMLByte returns HTML escaped bytes if the given byte should be escaped,
// otherwise nil.
func EscapeHTMLByte(b byte) []byte {
v := htmlEscapeTable[b]
if v != nil {
return *v
}
return nil
}
// EscapeHTML escapes characters that should be escaped in HTML text.
func EscapeHTML(v []byte) []byte {
cob := NewCopyOnWriteBuffer(v)
n := 0
for i := 0; i < len(v); i++ {
c := v[i]
escaped := htmlEscapeTable[c]
if escaped != nil {
cob.Write(v[n:i])
cob.Write(*escaped)
n = i + 1
}
}
if cob.IsCopied() {
cob.Write(v[n:])
}
return cob.Bytes()
}
// UnescapePunctuations unescapes blackslash escaped punctuations.
func UnescapePunctuations(source []byte) []byte {
cob := NewCopyOnWriteBuffer(source)
limit := len(source)
n := 0
for i := 0; i < limit; {
c := source[i]
if i < limit-1 && c == '\\' && IsPunct(source[i+1]) {
cob.Write(source[n:i])
_ = cob.WriteByte(source[i+1])
i += 2
n = i
continue
}
i++
}
if cob.IsCopied() {
cob.Write(source[n:])
}
return cob.Bytes()
}
// ResolveNumericReferences resolve numeric references like 'Ӓ" .
func ResolveNumericReferences(source []byte) []byte {
cob := NewCopyOnWriteBuffer(source)
buf := make([]byte, 6)
limit := len(source)
var ok bool
n := 0
for i := 0; i < limit; i++ {
if source[i] == '&' {
pos := i
next := i + 1
if next < limit && source[next] == '#' {
nnext := next + 1
if nnext < limit {
nc := source[nnext]
// code point like #x22;
if nnext < limit && nc == 'x' || nc == 'X' {
start := nnext + 1
i, ok = ReadWhile(source, [2]int{start, limit}, IsHexDecimal)
if ok && i < limit && source[i] == ';' {
v, _ := strconv.ParseUint(BytesToReadOnlyString(source[start:i]), 16, 32)
cob.Write(source[n:pos])
n = i + 1
runeSize := utf8.EncodeRune(buf, ToValidRune(rune(v)))
cob.Write(buf[:runeSize])
continue
}
// code point like #1234;
} else if nc >= '0' && nc <= '9' {
start := nnext
i, ok = ReadWhile(source, [2]int{start, limit}, IsNumeric)
if ok && i < limit && i-start < 8 && source[i] == ';' {
v, _ := strconv.ParseUint(BytesToReadOnlyString(source[start:i]), 0, 32)
cob.Write(source[n:pos])
n = i + 1
runeSize := utf8.EncodeRune(buf, ToValidRune(rune(v)))
cob.Write(buf[:runeSize])
continue
}
}
}
}
i = next - 1
}
}
if cob.IsCopied() {
cob.Write(source[n:])
}
return cob.Bytes()
}
// ResolveEntityNames resolve entity references like 'ö" .
func ResolveEntityNames(source []byte) []byte {
cob := NewCopyOnWriteBuffer(source)
limit := len(source)
var ok bool
n := 0
for i := 0; i < limit; i++ {
if source[i] == '&' {
pos := i
next := i + 1
if !(next < limit && source[next] == '#') {
start := next
i, ok = ReadWhile(source, [2]int{start, limit}, IsAlphaNumeric)
if ok && i < limit && source[i] == ';' {
name := BytesToReadOnlyString(source[start:i])
entity, ok := LookUpHTML5EntityByName(name)
if ok {
cob.Write(source[n:pos])
n = i + 1
cob.Write(entity.Characters)
continue
}
}
}
i = next - 1
}
}
if cob.IsCopied() {
cob.Write(source[n:])
}
return cob.Bytes()
}
var htmlSpace = []byte("%20")
// URLEscape escape the given URL.
// If resolveReference is set true:
// 1. unescape punctuations
// 2. resolve numeric references
// 3. resolve entity references
//
// URL encoded values (%xx) are kept as is.
func URLEscape(v []byte, resolveReference bool) []byte {
if resolveReference {
v = UnescapePunctuations(v)
v = ResolveNumericReferences(v)
v = ResolveEntityNames(v)
}
cob := NewCopyOnWriteBuffer(v)
limit := len(v)
n := 0
for i := 0; i < limit; {
c := v[i]
if urlEscapeTable[c] == 1 {
i++
continue
}
if c == '%' && i+2 < limit && IsHexDecimal(v[i+1]) && IsHexDecimal(v[i+1]) {
i += 3
continue
}
u8len := utf8lenTable[c]
if u8len == 99 { // invalid utf8 leading byte, skip it
i++
continue
}
if c == ' ' {
cob.Write(v[n:i])
cob.Write(htmlSpace)
i++
n = i
continue
}
if int(u8len) > len(v) {
u8len = int8(len(v) - 1)
}
if u8len == 0 {
i++
n = i
continue
}
cob.Write(v[n:i])
stop := i + int(u8len)
if stop > len(v) {
i++
n = i
continue
}
cob.Write(StringToReadOnlyBytes(url.QueryEscape(string(v[i:stop]))))
i += int(u8len)
n = i
}
if cob.IsCopied() && n < limit {
cob.Write(v[n:])
}
return cob.Bytes()
}
// FindURLIndex returns a stop index value if the given bytes seem an URL.
// This function is equivalent to [A-Za-z][A-Za-z0-9.+-]{1,31}:[^<>\x00-\x20]* .
func FindURLIndex(b []byte) int {
i := 0
if !(len(b) > 0 && urlTable[b[i]]&7 == 7) {
return -1
}
i++
for ; i < len(b); i++ {
c := b[i]
if urlTable[c]&4 != 4 {
break
}
}
if i == 1 || i > 33 || i >= len(b) {
return -1
}
if b[i] != ':' {
return -1
}
i++
for ; i < len(b); i++ {
c := b[i]
if urlTable[c]&1 != 1 {
break
}
}
return i
}
var emailDomainRegexp = regexp.MustCompile(`^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*`) //nolint:golint,lll
// FindEmailIndex returns a stop index value if the given bytes seem an email address.
func FindEmailIndex(b []byte) int {
// TODO: eliminate regexps
i := 0
for ; i < len(b); i++ {
c := b[i]
if emailTable[c]&1 != 1 {
break
}
}
if i == 0 {
return -1
}
if i >= len(b) || b[i] != '@' {
return -1
}
i++
if i >= len(b) {
return -1
}
match := emailDomainRegexp.FindSubmatchIndex(b[i:])
if match == nil {
return -1
}
return i + match[1]
}
var spaces = []byte(" \t\n\x0b\x0c\x0d")
var spaceTable = [256]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} //nolint:golint,lll
var punctTable = [256]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} //nolint:golint,lll
// a-zA-Z0-9, ;/?:@&=+$,-_.!~*'()#
var urlEscapeTable = [256]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} //nolint:golint,lll
var utf8lenTable = [256]int8{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 99, 99, 99, 99, 99, 99, 99, 99} //nolint:golint,lll
var urlTable = [256]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 5, 5, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 0, 1, 0, 1, 1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, 1, 1, 1, 1, 1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} //nolint:golint,lll
var emailTable = [256]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} //nolint:golint,lll
// UTF8Len returns a byte length of the utf-8 character.
func UTF8Len(b byte) int8 {
return utf8lenTable[b]
}
// IsPunct returns true if the given character is a punctuation, otherwise false.
func IsPunct(c byte) bool {
return punctTable[c] == 1
}
// IsPunctRune returns true if the given rune is a punctuation, otherwise false.
func IsPunctRune(r rune) bool {
return unicode.IsSymbol(r) || unicode.IsPunct(r)
}
// IsSpace returns true if the given character is a space, otherwise false.
func IsSpace(c byte) bool {
return spaceTable[c] == 1
}
// IsSpaceRune returns true if the given rune is a space, otherwise false.
func IsSpaceRune(r rune) bool {
return int32(r) <= 256 && IsSpace(byte(r)) || unicode.IsSpace(r)
}
// IsNumeric returns true if the given character is a numeric, otherwise false.
func IsNumeric(c byte) bool {
return c >= '0' && c <= '9'
}
// IsHexDecimal returns true if the given character is a hexdecimal, otherwise false.
func IsHexDecimal(c byte) bool {
return c >= '0' && c <= '9' || c >= 'a' && c <= 'f' || c >= 'A' && c <= 'F'
}
// IsAlphaNumeric returns true if the given character is a alphabet or a numeric, otherwise false.
func IsAlphaNumeric(c byte) bool {
return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9'
}
// A BufWriter is a subset of the bufio.Writer .
type BufWriter interface {
io.Writer
Available() int
Buffered() int
Flush() error
WriteByte(c byte) error
WriteRune(r rune) (size int, err error)
WriteString(s string) (int, error)
}
// A PrioritizedValue struct holds pair of an arbitrary value and a priority.
type PrioritizedValue struct {
// Value is an arbitrary value that you want to prioritize.
Value interface{}
// Priority is a priority of the value.
Priority int
}
// PrioritizedSlice is a slice of the PrioritizedValues.
type PrioritizedSlice []PrioritizedValue
// Sort sorts the PrioritizedSlice in ascending order.
func (s PrioritizedSlice) Sort() {
sort.Slice(s, func(i, j int) bool {
return s[i].Priority < s[j].Priority
})
}
// Remove removes the given value from this slice.
func (s PrioritizedSlice) Remove(v interface{}) PrioritizedSlice {
i := 0
found := false
for ; i < len(s); i++ {
if s[i].Value == v {
found = true
break
}
}
if !found {
return s
}
return append(s[:i], s[i+1:]...)
}
// Prioritized returns a new PrioritizedValue.
func Prioritized(v interface{}, priority int) PrioritizedValue {
return PrioritizedValue{v, priority}
}
func bytesHash(b []byte) uint64 {
var hash uint64 = 5381
for _, c := range b {
hash = ((hash << 5) + hash) + uint64(c)
}
return hash
}
// BytesFilter is a efficient data structure for checking whether bytes exist or not.
// BytesFilter is thread-safe.
type BytesFilter interface {
// Add adds given bytes to this set.
Add([]byte)
// Contains return true if this set contains given bytes, otherwise false.
Contains([]byte) bool
// Extend copies this filter and adds given bytes to new filter.
Extend(...[]byte) BytesFilter
// ExtendString copies this filter and adds given bytes to new filter.
// Given string must be separated by a comma.
ExtendString(string) BytesFilter
}
type bytesFilter struct {
chars [256]uint8
threshold int
slots [][][]byte
}
// NewBytesFilter returns a new BytesFilter.
func NewBytesFilter(elements ...[]byte) BytesFilter {
s := &bytesFilter{
threshold: 3,
slots: make([][][]byte, 64),
}
for _, element := range elements {
s.Add(element)
}
return s
}
// NewBytesFilterString returns a new BytesFilter.
// Given string must be separated by a comma.
func NewBytesFilterString(elements string) BytesFilter {
s := &bytesFilter{
threshold: 3,
slots: make([][][]byte, 64),
}
start := 0
for i := 0; i < len(elements); i++ {
if elements[i] == ',' {
s.Add(StringToReadOnlyBytes(elements[start:i]))
start = i + 1
}
}
if start < len(elements) {
s.Add(StringToReadOnlyBytes(elements[start:]))
}
return s
}
func (s *bytesFilter) Add(b []byte) {
l := len(b)
m := s.threshold
if l < s.threshold {
m = l
}
for i := 0; i < m; i++ {
s.chars[b[i]] |= 1 << uint8(i)
}
h := bytesHash(b) % uint64(len(s.slots))
slot := s.slots[h]
if slot == nil {
slot = [][]byte{}
}
s.slots[h] = append(slot, b)
}
func (s *bytesFilter) Extend(bs ...[]byte) BytesFilter {
newFilter := NewBytesFilter().(*bytesFilter)
newFilter.chars = s.chars
newFilter.threshold = s.threshold
for k, v := range s.slots {
newSlot := make([][]byte, len(v))
copy(newSlot, v)
newFilter.slots[k] = v
}
for _, b := range bs {
newFilter.Add(b)
}
return newFilter
}
func (s *bytesFilter) ExtendString(elements string) BytesFilter {
newFilter := NewBytesFilter().(*bytesFilter)
newFilter.chars = s.chars
newFilter.threshold = s.threshold
for k, v := range s.slots {
newSlot := make([][]byte, len(v))
copy(newSlot, v)
newFilter.slots[k] = v
}
start := 0
for i := 0; i < len(elements); i++ {
if elements[i] == ',' {
newFilter.Add(StringToReadOnlyBytes(elements[start:i]))
start = i + 1
}
}
if start < len(elements) {
newFilter.Add(StringToReadOnlyBytes(elements[start:]))
}
return newFilter
}
func (s *bytesFilter) Contains(b []byte) bool {
l := len(b)
m := s.threshold
if l < s.threshold {
m = l
}
for i := 0; i < m; i++ {
if (s.chars[b[i]] & (1 << uint8(i))) == 0 {
return false
}
}
h := bytesHash(b) % uint64(len(s.slots))
slot := s.slots[h]
if len(slot) == 0 {
return false
}
for _, element := range slot {
if bytes.Equal(element, b) {
return true
}
}
return false
}
yuin-goldmark-9a7f4c9/util/util_cjk.go 0000664 0000000 0000000 00000025675 15037761217 0020064 0 ustar 00root root 0000000 0000000 package util
import "unicode"
var cjkRadicalsSupplement = &unicode.RangeTable{
R16: []unicode.Range16{
{0x2E80, 0x2EFF, 1},
},
}
var kangxiRadicals = &unicode.RangeTable{
R16: []unicode.Range16{
{0x2F00, 0x2FDF, 1},
},
}
var ideographicDescriptionCharacters = &unicode.RangeTable{
R16: []unicode.Range16{
{0x2FF0, 0x2FFF, 1},
},
}
var cjkSymbolsAndPunctuation = &unicode.RangeTable{
R16: []unicode.Range16{
{0x3000, 0x303F, 1},
},
}
var hiragana = &unicode.RangeTable{
R16: []unicode.Range16{
{0x3040, 0x309F, 1},
},
}
var katakana = &unicode.RangeTable{
R16: []unicode.Range16{
{0x30A0, 0x30FF, 1},
},
}
var kanbun = &unicode.RangeTable{
R16: []unicode.Range16{
{0x3130, 0x318F, 1},
{0x3190, 0x319F, 1},
},
}
var cjkStrokes = &unicode.RangeTable{
R16: []unicode.Range16{
{0x31C0, 0x31EF, 1},
},
}
var katakanaPhoneticExtensions = &unicode.RangeTable{
R16: []unicode.Range16{
{0x31F0, 0x31FF, 1},
},
}
var cjkCompatibility = &unicode.RangeTable{
R16: []unicode.Range16{
{0x3300, 0x33FF, 1},
},
}
var cjkUnifiedIdeographsExtensionA = &unicode.RangeTable{
R16: []unicode.Range16{
{0x3400, 0x4DBF, 1},
},
}
var cjkUnifiedIdeographs = &unicode.RangeTable{
R16: []unicode.Range16{
{0x4E00, 0x9FFF, 1},
},
}
var yiSyllables = &unicode.RangeTable{
R16: []unicode.Range16{
{0xA000, 0xA48F, 1},
},
}
var yiRadicals = &unicode.RangeTable{
R16: []unicode.Range16{
{0xA490, 0xA4CF, 1},
},
}
var cjkCompatibilityIdeographs = &unicode.RangeTable{
R16: []unicode.Range16{
{0xF900, 0xFAFF, 1},
},
}
var verticalForms = &unicode.RangeTable{
R16: []unicode.Range16{
{0xFE10, 0xFE1F, 1},
},
}
var cjkCompatibilityForms = &unicode.RangeTable{
R16: []unicode.Range16{
{0xFE30, 0xFE4F, 1},
},
}
var smallFormVariants = &unicode.RangeTable{
R16: []unicode.Range16{
{0xFE50, 0xFE6F, 1},
},
}
var halfwidthAndFullwidthForms = &unicode.RangeTable{
R16: []unicode.Range16{
{0xFF00, 0xFFEF, 1},
},
}
var kanaSupplement = &unicode.RangeTable{
R32: []unicode.Range32{
{0x1B000, 0x1B0FF, 1},
},
}
var kanaExtendedA = &unicode.RangeTable{
R32: []unicode.Range32{
{0x1B100, 0x1B12F, 1},
},
}
var smallKanaExtension = &unicode.RangeTable{
R32: []unicode.Range32{
{0x1B130, 0x1B16F, 1},
},
}
var cjkUnifiedIdeographsExtensionB = &unicode.RangeTable{
R32: []unicode.Range32{
{0x20000, 0x2A6DF, 1},
},
}
var cjkUnifiedIdeographsExtensionC = &unicode.RangeTable{
R32: []unicode.Range32{
{0x2A700, 0x2B73F, 1},
},
}
var cjkUnifiedIdeographsExtensionD = &unicode.RangeTable{
R32: []unicode.Range32{
{0x2B740, 0x2B81F, 1},
},
}
var cjkUnifiedIdeographsExtensionE = &unicode.RangeTable{
R32: []unicode.Range32{
{0x2B820, 0x2CEAF, 1},
},
}
var cjkUnifiedIdeographsExtensionF = &unicode.RangeTable{
R32: []unicode.Range32{
{0x2CEB0, 0x2EBEF, 1},
},
}
var cjkCompatibilityIdeographsSupplement = &unicode.RangeTable{
R32: []unicode.Range32{
{0x2F800, 0x2FA1F, 1},
},
}
var cjkUnifiedIdeographsExtensionG = &unicode.RangeTable{
R32: []unicode.Range32{
{0x30000, 0x3134F, 1},
},
}
// IsEastAsianWideRune returns trhe if the given rune is an east asian wide character, otherwise false.
func IsEastAsianWideRune(r rune) bool {
return unicode.Is(unicode.Hiragana, r) ||
unicode.Is(unicode.Katakana, r) ||
unicode.Is(unicode.Han, r) ||
unicode.Is(unicode.Lm, r) ||
unicode.Is(unicode.Hangul, r) ||
unicode.Is(cjkSymbolsAndPunctuation, r)
}
// IsSpaceDiscardingUnicodeRune returns true if the given rune is space-discarding unicode character, otherwise false.
// See https://www.w3.org/TR/2020/WD-css-text-3-20200429/#space-discard-set
func IsSpaceDiscardingUnicodeRune(r rune) bool {
return unicode.Is(cjkRadicalsSupplement, r) ||
unicode.Is(kangxiRadicals, r) ||
unicode.Is(ideographicDescriptionCharacters, r) ||
unicode.Is(cjkSymbolsAndPunctuation, r) ||
unicode.Is(hiragana, r) ||
unicode.Is(katakana, r) ||
unicode.Is(kanbun, r) ||
unicode.Is(cjkStrokes, r) ||
unicode.Is(katakanaPhoneticExtensions, r) ||
unicode.Is(cjkCompatibility, r) ||
unicode.Is(cjkUnifiedIdeographsExtensionA, r) ||
unicode.Is(cjkUnifiedIdeographs, r) ||
unicode.Is(yiSyllables, r) ||
unicode.Is(yiRadicals, r) ||
unicode.Is(cjkCompatibilityIdeographs, r) ||
unicode.Is(verticalForms, r) ||
unicode.Is(cjkCompatibilityForms, r) ||
unicode.Is(smallFormVariants, r) ||
unicode.Is(halfwidthAndFullwidthForms, r) ||
unicode.Is(kanaSupplement, r) ||
unicode.Is(kanaExtendedA, r) ||
unicode.Is(smallKanaExtension, r) ||
unicode.Is(cjkUnifiedIdeographsExtensionB, r) ||
unicode.Is(cjkUnifiedIdeographsExtensionC, r) ||
unicode.Is(cjkUnifiedIdeographsExtensionD, r) ||
unicode.Is(cjkUnifiedIdeographsExtensionE, r) ||
unicode.Is(cjkUnifiedIdeographsExtensionF, r) ||
unicode.Is(cjkCompatibilityIdeographsSupplement, r) ||
unicode.Is(cjkUnifiedIdeographsExtensionG, r)
}
// EastAsianWidth returns the east asian width of the given rune.
// See https://www.unicode.org/reports/tr11/tr11-36.html
func EastAsianWidth(r rune) string {
switch {
case r == 0x3000,
(0xFF01 <= r && r <= 0xFF60),
(0xFFE0 <= r && r <= 0xFFE6):
return "F"
case r == 0x20A9,
(0xFF61 <= r && r <= 0xFFBE),
(0xFFC2 <= r && r <= 0xFFC7),
(0xFFCA <= r && r <= 0xFFCF),
(0xFFD2 <= r && r <= 0xFFD7),
(0xFFDA <= r && r <= 0xFFDC),
(0xFFE8 <= r && r <= 0xFFEE):
return "H"
case (0x1100 <= r && r <= 0x115F),
(0x11A3 <= r && r <= 0x11A7),
(0x11FA <= r && r <= 0x11FF),
(0x2329 <= r && r <= 0x232A),
(0x2E80 <= r && r <= 0x2E99),
(0x2E9B <= r && r <= 0x2EF3),
(0x2F00 <= r && r <= 0x2FD5),
(0x2FF0 <= r && r <= 0x2FFB),
(0x3001 <= r && r <= 0x303E),
(0x3041 <= r && r <= 0x3096),
(0x3099 <= r && r <= 0x30FF),
(0x3105 <= r && r <= 0x312D),
(0x3131 <= r && r <= 0x318E),
(0x3190 <= r && r <= 0x31BA),
(0x31C0 <= r && r <= 0x31E3),
(0x31F0 <= r && r <= 0x321E),
(0x3220 <= r && r <= 0x3247),
(0x3250 <= r && r <= 0x32FE),
(0x3300 <= r && r <= 0x4DBF),
(0x4E00 <= r && r <= 0xA48C),
(0xA490 <= r && r <= 0xA4C6),
(0xA960 <= r && r <= 0xA97C),
(0xAC00 <= r && r <= 0xD7A3),
(0xD7B0 <= r && r <= 0xD7C6),
(0xD7CB <= r && r <= 0xD7FB),
(0xF900 <= r && r <= 0xFAFF),
(0xFE10 <= r && r <= 0xFE19),
(0xFE30 <= r && r <= 0xFE52),
(0xFE54 <= r && r <= 0xFE66),
(0xFE68 <= r && r <= 0xFE6B),
(0x1B000 <= r && r <= 0x1B001),
(0x1F200 <= r && r <= 0x1F202),
(0x1F210 <= r && r <= 0x1F23A),
(0x1F240 <= r && r <= 0x1F248),
(0x1F250 <= r && r <= 0x1F251),
(0x20000 <= r && r <= 0x2F73F),
(0x2B740 <= r && r <= 0x2FFFD),
(0x30000 <= r && r <= 0x3FFFD):
return "W"
case (0x0020 <= r && r <= 0x007E),
(0x00A2 <= r && r <= 0x00A3),
(0x00A5 <= r && r <= 0x00A6),
r == 0x00AC,
r == 0x00AF,
(0x27E6 <= r && r <= 0x27ED),
(0x2985 <= r && r <= 0x2986):
return "Na"
case (0x00A1 == r),
(0x00A4 == r),
(0x00A7 <= r && r <= 0x00A8),
(0x00AA == r),
(0x00AD <= r && r <= 0x00AE),
(0x00B0 <= r && r <= 0x00B4),
(0x00B6 <= r && r <= 0x00BA),
(0x00BC <= r && r <= 0x00BF),
(0x00C6 == r),
(0x00D0 == r),
(0x00D7 <= r && r <= 0x00D8),
(0x00DE <= r && r <= 0x00E1),
(0x00E6 == r),
(0x00E8 <= r && r <= 0x00EA),
(0x00EC <= r && r <= 0x00ED),
(0x00F0 == r),
(0x00F2 <= r && r <= 0x00F3),
(0x00F7 <= r && r <= 0x00FA),
(0x00FC == r),
(0x00FE == r),
(0x0101 == r),
(0x0111 == r),
(0x0113 == r),
(0x011B == r),
(0x0126 <= r && r <= 0x0127),
(0x012B == r),
(0x0131 <= r && r <= 0x0133),
(0x0138 == r),
(0x013F <= r && r <= 0x0142),
(0x0144 == r),
(0x0148 <= r && r <= 0x014B),
(0x014D == r),
(0x0152 <= r && r <= 0x0153),
(0x0166 <= r && r <= 0x0167),
(0x016B == r),
(0x01CE == r),
(0x01D0 == r),
(0x01D2 == r),
(0x01D4 == r),
(0x01D6 == r),
(0x01D8 == r),
(0x01DA == r),
(0x01DC == r),
(0x0251 == r),
(0x0261 == r),
(0x02C4 == r),
(0x02C7 == r),
(0x02C9 <= r && r <= 0x02CB),
(0x02CD == r),
(0x02D0 == r),
(0x02D8 <= r && r <= 0x02DB),
(0x02DD == r),
(0x02DF == r),
(0x0300 <= r && r <= 0x036F),
(0x0391 <= r && r <= 0x03A1),
(0x03A3 <= r && r <= 0x03A9),
(0x03B1 <= r && r <= 0x03C1),
(0x03C3 <= r && r <= 0x03C9),
(0x0401 == r),
(0x0410 <= r && r <= 0x044F),
(0x0451 == r),
(0x2010 == r),
(0x2013 <= r && r <= 0x2016),
(0x2018 <= r && r <= 0x2019),
(0x201C <= r && r <= 0x201D),
(0x2020 <= r && r <= 0x2022),
(0x2024 <= r && r <= 0x2027),
(0x2030 == r),
(0x2032 <= r && r <= 0x2033),
(0x2035 == r),
(0x203B == r),
(0x203E == r),
(0x2074 == r),
(0x207F == r),
(0x2081 <= r && r <= 0x2084),
(0x20AC == r),
(0x2103 == r),
(0x2105 == r),
(0x2109 == r),
(0x2113 == r),
(0x2116 == r),
(0x2121 <= r && r <= 0x2122),
(0x2126 == r),
(0x212B == r),
(0x2153 <= r && r <= 0x2154),
(0x215B <= r && r <= 0x215E),
(0x2160 <= r && r <= 0x216B),
(0x2170 <= r && r <= 0x2179),
(0x2189 == r),
(0x2190 <= r && r <= 0x2199),
(0x21B8 <= r && r <= 0x21B9),
(0x21D2 == r),
(0x21D4 == r),
(0x21E7 == r),
(0x2200 == r),
(0x2202 <= r && r <= 0x2203),
(0x2207 <= r && r <= 0x2208),
(0x220B == r),
(0x220F == r),
(0x2211 == r),
(0x2215 == r),
(0x221A == r),
(0x221D <= r && r <= 0x2220),
(0x2223 == r),
(0x2225 == r),
(0x2227 <= r && r <= 0x222C),
(0x222E == r),
(0x2234 <= r && r <= 0x2237),
(0x223C <= r && r <= 0x223D),
(0x2248 == r),
(0x224C == r),
(0x2252 == r),
(0x2260 <= r && r <= 0x2261),
(0x2264 <= r && r <= 0x2267),
(0x226A <= r && r <= 0x226B),
(0x226E <= r && r <= 0x226F),
(0x2282 <= r && r <= 0x2283),
(0x2286 <= r && r <= 0x2287),
(0x2295 == r),
(0x2299 == r),
(0x22A5 == r),
(0x22BF == r),
(0x2312 == r),
(0x2460 <= r && r <= 0x24E9),
(0x24EB <= r && r <= 0x254B),
(0x2550 <= r && r <= 0x2573),
(0x2580 <= r && r <= 0x258F),
(0x2592 <= r && r <= 0x2595),
(0x25A0 <= r && r <= 0x25A1),
(0x25A3 <= r && r <= 0x25A9),
(0x25B2 <= r && r <= 0x25B3),
(0x25B6 <= r && r <= 0x25B7),
(0x25BC <= r && r <= 0x25BD),
(0x25C0 <= r && r <= 0x25C1),
(0x25C6 <= r && r <= 0x25C8),
(0x25CB == r),
(0x25CE <= r && r <= 0x25D1),
(0x25E2 <= r && r <= 0x25E5),
(0x25EF == r),
(0x2605 <= r && r <= 0x2606),
(0x2609 == r),
(0x260E <= r && r <= 0x260F),
(0x2614 <= r && r <= 0x2615),
(0x261C == r),
(0x261E == r),
(0x2640 == r),
(0x2642 == r),
(0x2660 <= r && r <= 0x2661),
(0x2663 <= r && r <= 0x2665),
(0x2667 <= r && r <= 0x266A),
(0x266C <= r && r <= 0x266D),
(0x266F == r),
(0x269E <= r && r <= 0x269F),
(0x26BE <= r && r <= 0x26BF),
(0x26C4 <= r && r <= 0x26CD),
(0x26CF <= r && r <= 0x26E1),
(0x26E3 == r),
(0x26E8 <= r && r <= 0x26FF),
(0x273D == r),
(0x2757 == r),
(0x2776 <= r && r <= 0x277F),
(0x2B55 <= r && r <= 0x2B59),
(0x3248 <= r && r <= 0x324F),
(0xE000 <= r && r <= 0xF8FF),
(0xFE00 <= r && r <= 0xFE0F),
(0xFFFD == r),
(0x1F100 <= r && r <= 0x1F10A),
(0x1F110 <= r && r <= 0x1F12D),
(0x1F130 <= r && r <= 0x1F169),
(0x1F170 <= r && r <= 0x1F19A),
(0xE0100 <= r && r <= 0xE01EF),
(0xF0000 <= r && r <= 0xFFFFD),
(0x100000 <= r && r <= 0x10FFFD):
return "A"
default:
return "N"
}
}
yuin-goldmark-9a7f4c9/util/util_safe.go 0000664 0000000 0000000 00000000520 15037761217 0020211 0 ustar 00root root 0000000 0000000 //go:build appengine || js
// +build appengine js
package util
// BytesToReadOnlyString returns a string converted from given bytes.
func BytesToReadOnlyString(b []byte) string {
return string(b)
}
// StringToReadOnlyBytes returns bytes converted from given string.
func StringToReadOnlyBytes(s string) []byte {
return []byte(s)
}
yuin-goldmark-9a7f4c9/util/util_unsafe_go120.go 0000664 0000000 0000000 00000001063 15037761217 0021467 0 ustar 00root root 0000000 0000000 //go:build !appengine && !js && !go1.21
// +build !appengine,!js,!go1.21
package util
import (
"reflect"
"unsafe"
)
// BytesToReadOnlyString returns a string converted from given bytes.
func BytesToReadOnlyString(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}
// StringToReadOnlyBytes returns bytes converted from given string.
func StringToReadOnlyBytes(s string) (bs []byte) {
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
bh := (*reflect.SliceHeader)(unsafe.Pointer(&bs))
bh.Data = sh.Data
bh.Cap = sh.Len
bh.Len = sh.Len
return
}
yuin-goldmark-9a7f4c9/util/util_unsafe_go121.go 0000664 0000000 0000000 00000000675 15037761217 0021500 0 ustar 00root root 0000000 0000000 //go:build !appengine && !js && go1.21
// +build !appengine,!js,go1.21
package util
import (
"unsafe"
)
// BytesToReadOnlyString returns a string converted from given bytes.
func BytesToReadOnlyString(b []byte) string {
return unsafe.String(unsafe.SliceData(b), len(b))
}
// StringToReadOnlyBytes returns bytes converted from given string.
func StringToReadOnlyBytes(s string) []byte {
return unsafe.Slice(unsafe.StringData(s), len(s))
}