replace zxq.co/ripple/hanayo
This commit is contained in:
2
vendor/github.com/frustra/bbcode/.gitignore
generated
vendored
Normal file
2
vendor/github.com/frustra/bbcode/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/bbcode
|
||||
/bbcode.test
|
3
vendor/github.com/frustra/bbcode/.travis.yml
generated
vendored
Normal file
3
vendor/github.com/frustra/bbcode/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
language: go
|
||||
go: 1.2
|
||||
script: go test -v
|
19
vendor/github.com/frustra/bbcode/LICENSE
generated
vendored
Normal file
19
vendor/github.com/frustra/bbcode/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (C) 2015 Frustra.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
123
vendor/github.com/frustra/bbcode/README.md
generated
vendored
Normal file
123
vendor/github.com/frustra/bbcode/README.md
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
# bbcode [](http://travis-ci.org/frustra/bbcode)
|
||||
|
||||
frustra/bbcode is a fast BBCode compiler for Go. It supports custom tags, safe html output (for user-specified input),
|
||||
and allows for graceful parsing of syntax errors similar to the output of a regex bbcode compiler.
|
||||
|
||||
Visit the godoc here: [http://godoc.org/github.com/frustra/bbcode](http://godoc.org/github.com/frustra/bbcode)
|
||||
|
||||
## Usage
|
||||
|
||||
To get started compiling some text, create a compiler instance:
|
||||
```go
|
||||
compiler := bbcode.NewCompiler(true, true) // autoCloseTags, ignoreUnmatchedClosingTags
|
||||
fmt.Println(compiler.Compile("[b]Hello World[/b]"))
|
||||
|
||||
// Output:
|
||||
// <b>Hello World</b>
|
||||
```
|
||||
|
||||
## Supported BBCode Syntax
|
||||
```
|
||||
[tag]basic tag[/tag]
|
||||
[tag1][tag2]nested tags[/tag2][/tag1]
|
||||
|
||||
[tag=value]tag with value[/tag]
|
||||
[tag arg=value]tag with named argument[/tag]
|
||||
[tag="quote value"]tag with quoted value[/tag]
|
||||
|
||||
[tag=value foo="hello world" bar=baz]multiple tag arguments[/tag]
|
||||
```
|
||||
|
||||
## Default Tags
|
||||
* `[b]text[/b]` --> `<b>text</b>` (b, i, u, and s all map the same)
|
||||
* `[url]link[/url]` --> `<a href="link">link</a>`
|
||||
* `[url=link]text[/url]` --> `<a href="link">text</a>`
|
||||
* `[img]link[/img]` --> `<img src="link">`
|
||||
* `[img=link]alt[/img]` --> `<img alt="alt" title="alt" src="link">`
|
||||
* `[center]text[/center]` --> `<div style="text-align: center;">text</div>`
|
||||
* `[color=red]text[/color]` --> `<span style="color: red;">text</span>`
|
||||
* `[size=2]text[/size]` --> `<span class="size2">text</span>`
|
||||
* `[quote]text[/quote]` --> `<blockquote><cite>Quote</cite>text</blockquote>`
|
||||
* `[quote=Somebody]text[/quote]` --> `<blockquote><cite>Somebody said:</cite>text</blockquote>`
|
||||
* `[quote name=Somebody]text[/quote]` --> `<blockquote><cite>Somebody said:</cite>text</blockquote>`
|
||||
* `[code][b]anything[/b][/code]` --> `<pre>[b]anything[/b]</pre>`
|
||||
|
||||
Lists are not currently implemented as a default tag, but can be added as a custom tag.
|
||||
A working implementation of list tags can be found [here](https://gist.github.com/xthexder/44f4b9cec3ed7876780d)
|
||||
|
||||
## Adding Custom Tags
|
||||
Custom tag handlers can be added to a compiler using the `compiler.SetTag(tag, handler)` function:
|
||||
```go
|
||||
compiler.SetTag("center", func(node *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
|
||||
// Create a new div element to output
|
||||
out := bbcode.NewHTMLTag("")
|
||||
out.Name = "div"
|
||||
|
||||
// Set the style attribute of our output div
|
||||
out.Attrs["style"] = "text-align: center;"
|
||||
|
||||
// Returning true here means continue to parse child nodes.
|
||||
// This should be false if children are parsed by this tag's handler, like in the [code] tag.
|
||||
return out, true
|
||||
})
|
||||
```
|
||||
|
||||
Tag values can be read from the opening tag like this:
|
||||
Main tag value `[tag={value}]`: `node.GetOpeningTag().Value`
|
||||
Tag arguments `[tag name={value}]`: `node.GetOpeningTag().Args["name"]`
|
||||
|
||||
`bbcode.NewHTMLTag(text)` creates a text node by default. By setting `tag.Name`, the node because an html tag prefixed by the text. The closing html tag is not rendered unless child elements exist. The closing tag can be forced by adding a blank text node:
|
||||
```go
|
||||
out := bbcode.NewHTMLTag("")
|
||||
out.Name = "div"
|
||||
out.AppendChild(nil) // equivalent to out.AppendChild(bbcode.NewHTMLTag(""))
|
||||
```
|
||||
|
||||
For more examples of tag definitions, look at the default tag implementations in [compiler.go](https://github.com/frustra/bbcode/blob/master/compiler.go)
|
||||
|
||||
## Overriding Default Tags
|
||||
The built-in tags can be overridden simply by redefining the tag with `compiler.SetTag(tag, handler)`
|
||||
|
||||
To remove a tag, set the tag handler to nil:
|
||||
```go
|
||||
compiler.SetTag("quote", nil)
|
||||
```
|
||||
|
||||
The default tags can also be modified without completely redefining the tag by calling the default handler:
|
||||
```go
|
||||
compiler.SetTag("url", func(node *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
|
||||
out, appendExpr := bbcode.DefaultTagCompilers["url"](node)
|
||||
out.Attrs["class"] = "bbcode-link"
|
||||
return out, appendExpr
|
||||
})
|
||||
```
|
||||
|
||||
## Auto-Close Tags
|
||||
Input:
|
||||
```
|
||||
[center][b]text[/center]
|
||||
```
|
||||
|
||||
Enabled Output:
|
||||
```html
|
||||
<div style="text-align: center;"><b>text</b></div>
|
||||
```
|
||||
Disabled Output:
|
||||
```html
|
||||
<div style="text-align: center;">[b]text</div>
|
||||
```
|
||||
|
||||
## Ignore Unmatched Closing Tags
|
||||
Input:
|
||||
```
|
||||
[center]text[/b][/center]
|
||||
```
|
||||
|
||||
Enabled Output:
|
||||
```html
|
||||
<div style="text-align: center;">text</div>
|
||||
```
|
||||
Disabled Output:
|
||||
```html
|
||||
<div style="text-align: center;">text[/b]</div>
|
||||
```
|
42
vendor/github.com/frustra/bbcode/bbcode.go
generated
vendored
Normal file
42
vendor/github.com/frustra/bbcode/bbcode.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2015 Frustra. All rights reserved.
|
||||
// Use of this source code is governed by the MIT
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package bbcode implements a parser and HTML generator for BBCode.
|
||||
package bbcode
|
||||
|
||||
import "sort"
|
||||
|
||||
type BBOpeningTag struct {
|
||||
Name string
|
||||
Value string
|
||||
Args map[string]string
|
||||
Raw string
|
||||
}
|
||||
|
||||
type BBClosingTag struct {
|
||||
Name string
|
||||
Raw string
|
||||
}
|
||||
|
||||
func (t *BBOpeningTag) String() string {
|
||||
str := t.Name
|
||||
if len(t.Value) > 0 {
|
||||
str += "=" + t.Value
|
||||
}
|
||||
keys := make([]string, len(t.Args))
|
||||
i := 0
|
||||
for key := range t.Args {
|
||||
keys[i] = key
|
||||
i++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, key := range keys {
|
||||
v := t.Args[key]
|
||||
str += " " + key
|
||||
if len(v) > 0 {
|
||||
str += "=" + v
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
16
vendor/github.com/frustra/bbcode/cmd/bbcode/main.go
generated
vendored
Normal file
16
vendor/github.com/frustra/bbcode/cmd/bbcode/main.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/frustra/bbcode"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
compiler := bbcode.NewCompiler(true, true)
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
for scanner.Scan() {
|
||||
fmt.Println(compiler.Compile(scanner.Text()))
|
||||
}
|
||||
}
|
256
vendor/github.com/frustra/bbcode/compiler.go
generated
vendored
Normal file
256
vendor/github.com/frustra/bbcode/compiler.go
generated
vendored
Normal file
@@ -0,0 +1,256 @@
|
||||
// Copyright 2015 Frustra. All rights reserved.
|
||||
// Use of this source code is governed by the MIT
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bbcode
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type TagCompilerFunc func(*BBCodeNode) (*HTMLTag, bool)
|
||||
|
||||
type Compiler struct {
|
||||
tagCompilers map[string]TagCompilerFunc
|
||||
defaultCompiler TagCompilerFunc
|
||||
AutoCloseTags bool
|
||||
IgnoreUnmatchedClosingTags bool
|
||||
}
|
||||
|
||||
func NewCompiler(autoCloseTags, ignoreUnmatchedClosingTags bool) Compiler {
|
||||
compiler := Compiler{make(map[string]TagCompilerFunc), DefaultTagCompiler, autoCloseTags, ignoreUnmatchedClosingTags}
|
||||
|
||||
for tag, compilerFunc := range DefaultTagCompilers {
|
||||
compiler.SetTag(tag, compilerFunc)
|
||||
}
|
||||
return compiler
|
||||
}
|
||||
|
||||
func (c Compiler) Compile(str string) string {
|
||||
tokens := Lex(str)
|
||||
tree := Parse(tokens)
|
||||
return c.CompileTree(tree).String()
|
||||
}
|
||||
|
||||
func (c Compiler) SetDefault(compiler TagCompilerFunc) {
|
||||
if compiler == nil {
|
||||
panic("Default tag compiler can't be nil")
|
||||
} else {
|
||||
c.defaultCompiler = compiler
|
||||
}
|
||||
}
|
||||
|
||||
func (c Compiler) SetTag(tag string, compiler TagCompilerFunc) {
|
||||
if compiler == nil {
|
||||
delete(c.tagCompilers, tag)
|
||||
} else {
|
||||
c.tagCompilers[tag] = compiler
|
||||
}
|
||||
}
|
||||
|
||||
// CompileTree transforms BBCodeNode into an HTML tag.
|
||||
func (c Compiler) CompileTree(node *BBCodeNode) *HTMLTag {
|
||||
var out = NewHTMLTag("")
|
||||
if node.ID == TEXT {
|
||||
out.Value = node.Value.(string)
|
||||
InsertNewlines(out)
|
||||
for _, child := range node.Children {
|
||||
out.AppendChild(c.CompileTree(child))
|
||||
}
|
||||
} else if node.ID == CLOSING_TAG {
|
||||
if !c.IgnoreUnmatchedClosingTags {
|
||||
out.Value = node.Value.(BBClosingTag).Raw
|
||||
InsertNewlines(out)
|
||||
}
|
||||
for _, child := range node.Children {
|
||||
out.AppendChild(c.CompileTree(child))
|
||||
}
|
||||
} else if node.ClosingTag == nil && !c.AutoCloseTags {
|
||||
out.Value = node.Value.(BBOpeningTag).Raw
|
||||
InsertNewlines(out)
|
||||
for _, child := range node.Children {
|
||||
out.AppendChild(c.CompileTree(child))
|
||||
}
|
||||
} else {
|
||||
in := node.GetOpeningTag()
|
||||
|
||||
compileFunc, ok := c.tagCompilers[in.Name]
|
||||
if !ok {
|
||||
compileFunc = c.defaultCompiler
|
||||
}
|
||||
var appendExpr bool
|
||||
node.Compiler = &c
|
||||
out, appendExpr = compileFunc(node)
|
||||
if appendExpr {
|
||||
if len(node.Children) == 0 {
|
||||
out.AppendChild(NewHTMLTag(""))
|
||||
} else {
|
||||
for _, child := range node.Children {
|
||||
out.AppendChild(c.CompileTree(child))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func CompileText(in *BBCodeNode) string {
|
||||
out := ""
|
||||
if in.ID == TEXT {
|
||||
out = in.Value.(string)
|
||||
}
|
||||
for _, child := range in.Children {
|
||||
out += CompileText(child)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func CompileRaw(in *BBCodeNode) *HTMLTag {
|
||||
out := NewHTMLTag("")
|
||||
if in.ID == TEXT {
|
||||
out.Value = in.Value.(string)
|
||||
} else if in.ID == CLOSING_TAG {
|
||||
out.Value = in.Value.(BBClosingTag).Raw
|
||||
} else {
|
||||
out.Value = in.Value.(BBOpeningTag).Raw
|
||||
}
|
||||
for _, child := range in.Children {
|
||||
out.AppendChild(CompileRaw(child))
|
||||
}
|
||||
if in.ID == OPENING_TAG && in.ClosingTag != nil {
|
||||
tag := NewHTMLTag(in.ClosingTag.Raw)
|
||||
out.AppendChild(tag)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
var DefaultTagCompilers map[string]TagCompilerFunc
|
||||
var DefaultTagCompiler TagCompilerFunc
|
||||
|
||||
func init() {
|
||||
DefaultTagCompiler = func(node *BBCodeNode) (*HTMLTag, bool) {
|
||||
out := NewHTMLTag(node.GetOpeningTag().Raw)
|
||||
InsertNewlines(out)
|
||||
if len(node.Children) == 0 {
|
||||
out.AppendChild(NewHTMLTag(""))
|
||||
} else {
|
||||
for _, child := range node.Children {
|
||||
out.AppendChild(node.Compiler.CompileTree(child))
|
||||
}
|
||||
}
|
||||
if node.ClosingTag != nil {
|
||||
tag := NewHTMLTag(node.ClosingTag.Raw)
|
||||
InsertNewlines(tag)
|
||||
out.AppendChild(tag)
|
||||
}
|
||||
return out, false
|
||||
}
|
||||
|
||||
DefaultTagCompilers = make(map[string]TagCompilerFunc)
|
||||
DefaultTagCompilers["url"] = func(node *BBCodeNode) (*HTMLTag, bool) {
|
||||
out := NewHTMLTag("")
|
||||
out.Name = "a"
|
||||
value := node.GetOpeningTag().Value
|
||||
if value == "" {
|
||||
text := CompileText(node)
|
||||
if len(text) > 0 {
|
||||
out.Attrs["href"] = ValidURL(text)
|
||||
}
|
||||
} else {
|
||||
out.Attrs["href"] = ValidURL(value)
|
||||
}
|
||||
return out, true
|
||||
}
|
||||
|
||||
DefaultTagCompilers["img"] = func(node *BBCodeNode) (*HTMLTag, bool) {
|
||||
out := NewHTMLTag("")
|
||||
out.Name = "img"
|
||||
value := node.GetOpeningTag().Value
|
||||
if value == "" {
|
||||
out.Attrs["src"] = ValidURL(CompileText(node))
|
||||
} else {
|
||||
out.Attrs["src"] = ValidURL(value)
|
||||
text := CompileText(node)
|
||||
if len(text) > 0 {
|
||||
out.Attrs["alt"] = text
|
||||
out.Attrs["title"] = out.Attrs["alt"]
|
||||
}
|
||||
}
|
||||
return out, false
|
||||
}
|
||||
|
||||
DefaultTagCompilers["center"] = func(node *BBCodeNode) (*HTMLTag, bool) {
|
||||
out := NewHTMLTag("")
|
||||
out.Name = "div"
|
||||
out.Attrs["style"] = "text-align: center;"
|
||||
return out, true
|
||||
}
|
||||
|
||||
DefaultTagCompilers["color"] = func(node *BBCodeNode) (*HTMLTag, bool) {
|
||||
out := NewHTMLTag("")
|
||||
out.Name = "span"
|
||||
sanitize := func(r rune) rune {
|
||||
if r == '#' || r == ',' || r == '.' || r == '(' || r == ')' || r == '%' {
|
||||
return r
|
||||
} else if r >= '0' && r <= '9' {
|
||||
return r
|
||||
} else if r >= 'a' && r <= 'z' {
|
||||
return r
|
||||
} else if r >= 'A' && r <= 'Z' {
|
||||
return r
|
||||
}
|
||||
return -1
|
||||
}
|
||||
color := strings.Map(sanitize, node.GetOpeningTag().Value)
|
||||
out.Attrs["style"] = "color: " + color + ";"
|
||||
return out, true
|
||||
}
|
||||
|
||||
DefaultTagCompilers["size"] = func(node *BBCodeNode) (*HTMLTag, bool) {
|
||||
out := NewHTMLTag("")
|
||||
out.Name = "span"
|
||||
if size, err := strconv.Atoi(node.GetOpeningTag().Value); err == nil {
|
||||
out.Attrs["class"] = fmt.Sprintf("size%d", size)
|
||||
}
|
||||
return out, true
|
||||
}
|
||||
|
||||
DefaultTagCompilers["quote"] = func(node *BBCodeNode) (*HTMLTag, bool) {
|
||||
out := NewHTMLTag("")
|
||||
out.Name = "blockquote"
|
||||
who := ""
|
||||
in := node.GetOpeningTag()
|
||||
if name, ok := in.Args["name"]; ok && name != "" {
|
||||
who = name
|
||||
} else {
|
||||
who = in.Value
|
||||
}
|
||||
cite := NewHTMLTag("")
|
||||
cite.Name = "cite"
|
||||
if who != "" {
|
||||
cite.AppendChild(NewHTMLTag(who + " said:"))
|
||||
} else {
|
||||
cite.AppendChild(NewHTMLTag("Quote"))
|
||||
}
|
||||
return out.AppendChild(cite), true
|
||||
}
|
||||
|
||||
DefaultTagCompilers["code"] = func(node *BBCodeNode) (*HTMLTag, bool) {
|
||||
out := NewHTMLTag("")
|
||||
out.Name = "pre"
|
||||
for _, child := range node.Children {
|
||||
out.AppendChild(CompileRaw(child))
|
||||
}
|
||||
return out, false
|
||||
}
|
||||
|
||||
for _, tag := range []string{"i", "b", "u", "s"} {
|
||||
DefaultTagCompilers[tag] = func(node *BBCodeNode) (*HTMLTag, bool) {
|
||||
out := NewHTMLTag("")
|
||||
out.Name = node.GetOpeningTag().Name
|
||||
return out, true
|
||||
}
|
||||
}
|
||||
}
|
221
vendor/github.com/frustra/bbcode/compiler_test.go
generated
vendored
Normal file
221
vendor/github.com/frustra/bbcode/compiler_test.go
generated
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
// Copyright 2015 Frustra. All rights reserved.
|
||||
// Use of this source code is governed by the MIT
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bbcode
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var fullTestInput = `the quick brown [b]fox[/b]:
|
||||
[url=http://example][img]http://example.png[/img][/url]`
|
||||
|
||||
var fullTestOutput = `the quick brown <b>fox</b>:<br><a href="http://example"><img src="http://example.png"></a>`
|
||||
|
||||
func TestFullBasic(t *testing.T) {
|
||||
c := NewCompiler(false, false)
|
||||
input := fullTestInput
|
||||
output := fullTestOutput
|
||||
for in, out := range basicTests {
|
||||
input += in
|
||||
output += out
|
||||
}
|
||||
result := c.Compile(input)
|
||||
if result != output {
|
||||
t.Errorf("Failed to compile %s.\nExpected: %s, got: %s\n", input, output, result)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFullBasic(b *testing.B) {
|
||||
c := NewCompiler(false, false)
|
||||
input := fullTestInput
|
||||
for in := range basicTests {
|
||||
input += in
|
||||
}
|
||||
b.SetBytes(int64(len(input)))
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
c.Compile(input)
|
||||
}
|
||||
}
|
||||
|
||||
var basicTests = map[string]string{
|
||||
``: ``,
|
||||
`[url]http://example.com[/url]`: `<a href="http://example.com">http://example.com</a>`,
|
||||
`[img]http://example.com[/img]`: `<img src="http://example.com">`,
|
||||
`[img][/img]`: `<img src="">`,
|
||||
|
||||
`[url=http://example.com]example[/url]`: `<a href="http://example.com">example</a>`,
|
||||
`[img=http://example.com][/img]`: `<img src="http://example.com">`,
|
||||
|
||||
`[B]bold[/b]`: `<b>bold</b>`,
|
||||
`[i]italic[/i]`: `<i>italic</i>`,
|
||||
`[u]underline[/U]`: `<u>underline</u>`,
|
||||
`[s]strikethrough[/s]`: `<s>strikethrough</s>`,
|
||||
|
||||
`[u][b]something[/b] then [b]something else[/b][/u]`: `<u><b>something</b> then <b>something else</b></u>`,
|
||||
`blank[b][/b]`: `blank<b></b>`,
|
||||
|
||||
"test\nnewline\nnewline": `test<br>newline<br>newline`,
|
||||
"test\n\nnewline": `test<br><br>newline`,
|
||||
"[b]test[/b]\n\nnewline": `<b>test</b><br><br>newline`,
|
||||
"[b]test\nnewline[/b]": `<b>test<br>newline</b>`,
|
||||
|
||||
"[code][b]some[/b]\n[i]stuff[/i]\n[/quote][/code][b]more[/b]": "<pre>[b]some[/b]\n[i]stuff[/i]\n[/quote]</pre><b>more</b>",
|
||||
"[quote name=Someguy]hello[/quote]": `<blockquote><cite>Someguy said:</cite>hello</blockquote>`,
|
||||
"[center]hello[/center]": `<div style="text-align: center;">hello</div>`,
|
||||
"[size=6]hello[/size]": `<span class="size6">hello</span>`,
|
||||
"[center][b][color=#00BFFF][size=6]hello[/size][/color][/b][/center]": `<div style="text-align: center;"><b><span style="color: #00BFFF;"><span class="size6">hello</span></span></b></div>`,
|
||||
|
||||
`[not a tag][/not ]`: `[not a tag][/not ]`,
|
||||
`[not a tag]`: `[not a tag]`,
|
||||
}
|
||||
var basicMultiArgTests = map[string][]string{
|
||||
`[img=http://example.com]alt text[/img]`: []string{`<img`, ` src="http://example.com"`, ` alt="alt text"`, ` title="alt text"`, `>`},
|
||||
`[img = foo]bar[/img]`: []string{`<img`, ` src="foo"`, ` alt="bar"`, ` title="bar"`, `>`},
|
||||
}
|
||||
|
||||
func TestCompile(t *testing.T) {
|
||||
c := NewCompiler(false, false)
|
||||
for in, out := range basicTests {
|
||||
result := c.Compile(in)
|
||||
if result != out {
|
||||
t.Errorf("Failed to compile %s.\nExpected: %s, got: %s\n", in, out, result)
|
||||
}
|
||||
}
|
||||
for in, out := range basicMultiArgTests {
|
||||
result := c.Compile(in)
|
||||
if !strings.HasPrefix(result, out[0]) || !strings.HasSuffix(result, out[len(out)-1]) {
|
||||
t.Errorf("Failed to compile %s.\nExpected: %s, got: %s\n", in, out, result)
|
||||
}
|
||||
for i := 1; i < len(out)-1; i++ {
|
||||
if !strings.Contains(result, out[i]) {
|
||||
t.Errorf("Failed to compile %s.\nExpected: %s, got: %s\n", in, out, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var sanitizationTests = map[string]string{
|
||||
`<script>`: `<script>`,
|
||||
`[url]<script>[/url]`: `<a href="%3Cscript%3E"><script></a>`,
|
||||
|
||||
`[url=<script>]<script>[/url]`: `<a href="%3Cscript%3E"><script></a>`,
|
||||
|
||||
`[url=http://a.b/z?\]link[/url]`: `<a href="http://a.b/z?\">link</a>`,
|
||||
}
|
||||
var sanitizationMultiArgTests = map[string][]string{
|
||||
`[img=<script>]<script>[/img]`: []string{`<img`, ` src="%3Cscript%3E"`, ` alt="<script>"`, ` title="<script>"`, `>`},
|
||||
`[img="http://\"a.b/z"]"link"\[/img]`: []string{`<img`, ` src="http://"a.b/z"`, ` alt=""link"\"`, ` title=""link"\"`, `>`},
|
||||
}
|
||||
|
||||
func TestSanitization(t *testing.T) {
|
||||
c := NewCompiler(false, false)
|
||||
for in, out := range sanitizationTests {
|
||||
result := c.Compile(in)
|
||||
if result != out {
|
||||
t.Errorf("Failed to compile %s.\nExpected: %s, got: %s\n", in, out, result)
|
||||
}
|
||||
}
|
||||
for in, out := range sanitizationMultiArgTests {
|
||||
result := c.Compile(in)
|
||||
if !strings.HasPrefix(result, out[0]) || !strings.HasSuffix(result, out[len(out)-1]) {
|
||||
t.Errorf("Failed to compile %s.\nExpected: %s, got: %s\n", in, out, result)
|
||||
}
|
||||
for i := 1; i < len(out)-1; i++ {
|
||||
if !strings.Contains(result, out[i]) {
|
||||
t.Errorf("Failed to compile %s.\nExpected: %s, got: %s\n", in, out, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFullSanitization(t *testing.T) {
|
||||
c := NewCompiler(false, false)
|
||||
input := fullTestInput
|
||||
output := fullTestOutput
|
||||
for in, out := range sanitizationTests {
|
||||
input += in
|
||||
output += out
|
||||
}
|
||||
result := c.Compile(input)
|
||||
if result != output {
|
||||
t.Errorf("Failed to compile %s.\nExpected: %s, got: %s\n", input, output, result)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFullSanitization(b *testing.B) {
|
||||
c := NewCompiler(false, false)
|
||||
input := fullTestInput
|
||||
for in := range sanitizationTests {
|
||||
input += in
|
||||
}
|
||||
b.SetBytes(int64(len(input)))
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
c.Compile(input)
|
||||
}
|
||||
}
|
||||
|
||||
var brokenTests = map[string]string{
|
||||
"[b]": `[b]`,
|
||||
"[b]\n": `[b]<br>`,
|
||||
"[b]hello": `[b]hello`,
|
||||
"[b]hello\n": `[b]hello<br>`,
|
||||
"the quick brown [b][i]fox[/b][/i]\n[i]\n[b]hi[/b]][b][url=http://example[img]http://example.png[/img][/url][b]": `the quick brown <b>[i]fox</b>[/i]<br>[i]<br><b>hi</b>][b][url=http://example<img src="http://example.png">[/url][b]`,
|
||||
"the quick brown[/b][b]hello[/b]": `the quick brown[/b]<b>hello</b>`,
|
||||
"the quick brown[/b][/code]": `the quick brown[/b][/code]`,
|
||||
"[ b][ i]the quick brown[/i][/b=hello]": `[ b]<i>the quick brown</i>[/b=hello]`,
|
||||
"[b [herp@#$%]]the quick brown[/b]": `[b [herp@#$%]]the quick brown[/b]`,
|
||||
"[b=hello a=hi q]the quick brown[/b]": `<b>the quick brown</b>`,
|
||||
"[b]hi[": `[b]hi[`,
|
||||
"[b hi=derp": `[b hi=derp`,
|
||||
}
|
||||
|
||||
func TestBroken(t *testing.T) {
|
||||
c := NewCompiler(false, false)
|
||||
for in, out := range brokenTests {
|
||||
result := c.Compile(in)
|
||||
if result != out {
|
||||
t.Errorf("Failed to compile %s.\nExpected: %s, got: %s\n", in, out, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFullBroken(b *testing.B) {
|
||||
c := NewCompiler(false, false)
|
||||
input := fullTestInput
|
||||
for in := range brokenTests {
|
||||
input += in
|
||||
}
|
||||
b.SetBytes(int64(len(input)))
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
c.Compile(input)
|
||||
}
|
||||
}
|
||||
|
||||
var customTests = map[string]string{
|
||||
`[img]//foo/bar.png[/img]`: `<img src="//custom.png">`,
|
||||
`[url]//foo/bar.png[/url]`: `[url]//foo/bar.png[/url]`,
|
||||
}
|
||||
|
||||
func compileImg(node *BBCodeNode) (*HTMLTag, bool) {
|
||||
out, appendExpr := DefaultTagCompilers["img"](node)
|
||||
out.Attrs["src"] = "//custom.png"
|
||||
return out, appendExpr
|
||||
}
|
||||
|
||||
func TestCompileCustom(t *testing.T) {
|
||||
c := NewCompiler(false, false)
|
||||
c.SetTag("url", nil)
|
||||
c.SetTag("img", compileImg)
|
||||
for in, out := range customTests {
|
||||
result := c.Compile(in)
|
||||
if result != out {
|
||||
t.Errorf("Failed to compile %s.\nExpected: %s, got: %s\n", in, out, result)
|
||||
}
|
||||
}
|
||||
}
|
94
vendor/github.com/frustra/bbcode/html.go
generated
vendored
Normal file
94
vendor/github.com/frustra/bbcode/html.go
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright 2015 Frustra. All rights reserved.
|
||||
// Use of this source code is governed by the MIT
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bbcode
|
||||
|
||||
import (
|
||||
"html"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// HTMLTag represents a DOM node.
|
||||
type HTMLTag struct {
|
||||
Name string
|
||||
Value string
|
||||
Attrs map[string]string
|
||||
Children []*HTMLTag
|
||||
}
|
||||
|
||||
// NewHTMLTag creates a new HTMLTag with string contents specified by value.
|
||||
func NewHTMLTag(value string) *HTMLTag {
|
||||
return &HTMLTag{
|
||||
Value: value,
|
||||
Attrs: make(map[string]string),
|
||||
Children: make([]*HTMLTag, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *HTMLTag) String() string {
|
||||
var value string
|
||||
if len(t.Value) > 0 {
|
||||
value = html.EscapeString(t.Value)
|
||||
}
|
||||
var attrString string
|
||||
for key, value := range t.Attrs {
|
||||
attrString += " " + key + `="` + strings.Replace(html.EscapeString(value), "\n", "", -1) + `"`
|
||||
}
|
||||
if len(t.Children) > 0 {
|
||||
var childrenString string
|
||||
for _, child := range t.Children {
|
||||
childrenString += child.String()
|
||||
}
|
||||
if len(t.Name) > 0 {
|
||||
return value + "<" + t.Name + attrString + ">" + childrenString + "</" + t.Name + ">"
|
||||
} else {
|
||||
return value + childrenString
|
||||
}
|
||||
} else if len(t.Name) > 0 {
|
||||
return value + "<" + t.Name + attrString + ">"
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
func (t *HTMLTag) AppendChild(child *HTMLTag) *HTMLTag {
|
||||
if child == nil {
|
||||
t.Children = append(t.Children, NewHTMLTag(""))
|
||||
} else {
|
||||
t.Children = append(t.Children, child)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func InsertNewlines(out *HTMLTag) {
|
||||
if strings.ContainsRune(out.Value, '\n') {
|
||||
parts := strings.Split(out.Value, "\n")
|
||||
for i, part := range parts {
|
||||
if i == 0 {
|
||||
out.Value = parts[i]
|
||||
} else {
|
||||
out.AppendChild(NewlineTag())
|
||||
if len(part) > 0 {
|
||||
out.AppendChild(NewHTMLTag(part))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a new HTMLTag representing a line break
|
||||
func NewlineTag() *HTMLTag {
|
||||
var out = NewHTMLTag("")
|
||||
out.Name = "br"
|
||||
return out
|
||||
}
|
||||
|
||||
func ValidURL(raw string) string {
|
||||
u, err := url.Parse(raw)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return u.String()
|
||||
}
|
24
vendor/github.com/frustra/bbcode/html_test.go
generated
vendored
Normal file
24
vendor/github.com/frustra/bbcode/html_test.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2015 Frustra. All rights reserved.
|
||||
// Use of this source code is governed by the MIT
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bbcode
|
||||
|
||||
import "testing"
|
||||
|
||||
var urlTests = map[string]string{
|
||||
"http://example.com/path?query=value#fragment": "http://example.com/path?query=value#fragment",
|
||||
"<script>http://example.com": "%3Cscript%3Ehttp://example.com",
|
||||
"http://example.com/path?query=value#fragment<script>": "http://example.com/path?query=value#fragment%3Cscript%3E",
|
||||
"http://example.com/path?query=<script>": "http://example.com/path?query=<script>",
|
||||
"javascript:alert(1);": "javascript:alert(1);",
|
||||
}
|
||||
|
||||
func TestValidURL(t *testing.T) {
|
||||
for in, out := range urlTests {
|
||||
result := ValidURL(in)
|
||||
if result != out {
|
||||
t.Errorf("Failed to sanitize %s.\nExpected: %s, got: %s", in, out, result)
|
||||
}
|
||||
}
|
||||
}
|
260
vendor/github.com/frustra/bbcode/lexer.go
generated
vendored
Normal file
260
vendor/github.com/frustra/bbcode/lexer.go
generated
vendored
Normal file
@@ -0,0 +1,260 @@
|
||||
// Copyright 2015 Frustra. All rights reserved.
|
||||
// Use of this source code is governed by the MIT
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bbcode
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Token struct {
|
||||
ID string
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
type lexer struct {
|
||||
input string
|
||||
tokens chan Token
|
||||
|
||||
start int
|
||||
end int
|
||||
pos int
|
||||
|
||||
tagName string
|
||||
tagValue string
|
||||
tagTmpName string
|
||||
tagTmpValue string
|
||||
tagArgs map[string]string
|
||||
}
|
||||
|
||||
const (
|
||||
TEXT = "text"
|
||||
OPENING_TAG = "opening"
|
||||
CLOSING_TAG = "closing"
|
||||
)
|
||||
|
||||
func newLexer(str string) *lexer {
|
||||
return &lexer{
|
||||
input: str,
|
||||
tokens: make(chan Token),
|
||||
}
|
||||
}
|
||||
|
||||
func Lex(str string) chan Token {
|
||||
lex := newLexer(str)
|
||||
go lex.runStateMachine()
|
||||
return lex.tokens
|
||||
}
|
||||
|
||||
func (l *lexer) runStateMachine() {
|
||||
for state := lexText; state != nil; {
|
||||
state = state(l)
|
||||
}
|
||||
close(l.tokens)
|
||||
}
|
||||
|
||||
func (l *lexer) emit(id string, value interface{}) {
|
||||
if l.pos > 0 {
|
||||
// fmt.Println(l.input)
|
||||
// fmt.Printf("Emit %s: %+v\n", id, value)
|
||||
l.tokens <- Token{id, value}
|
||||
l.input = l.input[l.pos:]
|
||||
l.pos = 0
|
||||
}
|
||||
}
|
||||
|
||||
type stateFn func(*lexer) stateFn
|
||||
|
||||
func lexText(l *lexer) stateFn {
|
||||
for l.pos < len(l.input) {
|
||||
if l.input[l.pos] == '[' {
|
||||
l.emit(TEXT, l.input[:l.pos])
|
||||
return lexOpenBracket
|
||||
}
|
||||
l.pos++
|
||||
}
|
||||
l.emit(TEXT, l.input)
|
||||
return nil
|
||||
}
|
||||
|
||||
func lexOpenBracket(l *lexer) stateFn {
|
||||
l.pos++
|
||||
closingTag := false
|
||||
for l.pos < len(l.input) {
|
||||
switch l.input[l.pos] {
|
||||
case '[', ']':
|
||||
return lexText
|
||||
default:
|
||||
if l.input[l.pos] == '/' && !closingTag {
|
||||
closingTag = true
|
||||
} else if l.input[l.pos] != ' ' && l.input[l.pos] != '\t' && l.input[l.pos] != '\n' {
|
||||
if closingTag {
|
||||
return lexClosingTag
|
||||
} else {
|
||||
l.tagName = ""
|
||||
l.tagValue = ""
|
||||
l.tagArgs = make(map[string]string)
|
||||
return lexTagName
|
||||
}
|
||||
}
|
||||
}
|
||||
l.pos++
|
||||
}
|
||||
l.emit(TEXT, l.input)
|
||||
return nil
|
||||
}
|
||||
|
||||
func lexClosingTag(l *lexer) stateFn {
|
||||
whiteSpace := false
|
||||
l.start = l.pos
|
||||
l.end = l.pos
|
||||
for l.pos < len(l.input) {
|
||||
switch l.input[l.pos] {
|
||||
case '[':
|
||||
return lexText
|
||||
case ']':
|
||||
l.pos++
|
||||
l.emit(CLOSING_TAG, BBClosingTag{strings.ToLower(l.input[l.start:l.end]), l.input[:l.pos]})
|
||||
return lexText
|
||||
case ' ', '\t', '\n':
|
||||
whiteSpace = true
|
||||
default:
|
||||
if whiteSpace {
|
||||
return lexText
|
||||
} else {
|
||||
l.end++
|
||||
}
|
||||
}
|
||||
l.pos++
|
||||
}
|
||||
l.emit(TEXT, l.input)
|
||||
return nil
|
||||
}
|
||||
|
||||
func lexTagName(l *lexer) stateFn {
|
||||
l.tagTmpValue = ""
|
||||
whiteSpace := false
|
||||
l.start = l.pos
|
||||
l.end = l.pos
|
||||
for l.pos < len(l.input) {
|
||||
switch l.input[l.pos] {
|
||||
case '[':
|
||||
return lexText
|
||||
case ']':
|
||||
l.tagTmpName = l.input[l.start:l.end]
|
||||
return lexTagArgs
|
||||
case '=':
|
||||
l.tagTmpName = l.input[l.start:l.end]
|
||||
return lexTagValue
|
||||
case ' ', '\t', '\n':
|
||||
whiteSpace = true
|
||||
default:
|
||||
if whiteSpace {
|
||||
l.tagTmpName = l.input[l.start:l.end]
|
||||
return lexTagArgs
|
||||
} else {
|
||||
l.end++
|
||||
}
|
||||
}
|
||||
l.pos++
|
||||
}
|
||||
l.emit(TEXT, l.input)
|
||||
return nil
|
||||
}
|
||||
|
||||
func lexTagValue(l *lexer) stateFn {
|
||||
l.pos++
|
||||
loop:
|
||||
for l.pos < len(l.input) {
|
||||
switch l.input[l.pos] {
|
||||
case ' ', '\t', '\n':
|
||||
l.pos++
|
||||
case '"', '\'':
|
||||
return lexQuotedValue
|
||||
default:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
l.start = l.pos
|
||||
l.end = l.pos
|
||||
for l.pos < len(l.input) {
|
||||
switch l.input[l.pos] {
|
||||
case '[':
|
||||
return lexText
|
||||
case ']':
|
||||
l.tagTmpValue = l.input[l.start:l.end]
|
||||
return lexTagArgs
|
||||
case ' ', '\t', '\n':
|
||||
l.tagTmpValue = l.input[l.start:l.end]
|
||||
return lexTagArgs
|
||||
default:
|
||||
l.end++
|
||||
}
|
||||
l.pos++
|
||||
}
|
||||
l.emit(TEXT, l.input)
|
||||
return nil
|
||||
}
|
||||
|
||||
func lexQuotedValue(l *lexer) stateFn {
|
||||
quoteChar := l.input[l.pos]
|
||||
l.pos++
|
||||
l.start = l.pos
|
||||
var buf bytes.Buffer
|
||||
escape := false
|
||||
for l.pos < len(l.input) {
|
||||
if escape {
|
||||
if l.input[l.pos] == 'n' {
|
||||
buf.WriteRune('\n')
|
||||
} else {
|
||||
buf.WriteRune(rune(l.input[l.pos]))
|
||||
}
|
||||
escape = false
|
||||
} else {
|
||||
switch l.input[l.pos] {
|
||||
case '\\':
|
||||
escape = true
|
||||
case '\n':
|
||||
l.pos = l.start
|
||||
return lexText
|
||||
case quoteChar:
|
||||
l.pos++
|
||||
l.tagTmpValue = buf.String()
|
||||
return lexTagArgs
|
||||
default:
|
||||
buf.WriteRune(rune(l.input[l.pos]))
|
||||
}
|
||||
}
|
||||
l.pos++
|
||||
}
|
||||
l.pos = l.start
|
||||
return lexText
|
||||
}
|
||||
|
||||
func lexTagArgs(l *lexer) stateFn {
|
||||
if len(l.tagName) > 0 {
|
||||
l.tagArgs[strings.ToLower(l.tagTmpName)] = l.tagTmpValue
|
||||
} else {
|
||||
l.tagName = l.tagTmpName
|
||||
l.tagValue = l.tagTmpValue
|
||||
}
|
||||
for l.pos < len(l.input) {
|
||||
switch l.input[l.pos] {
|
||||
case '[':
|
||||
return lexText
|
||||
case ']':
|
||||
l.pos++
|
||||
l.emit(OPENING_TAG, BBOpeningTag{strings.ToLower(l.tagName), l.tagValue, l.tagArgs, l.input[:l.pos]})
|
||||
return lexText
|
||||
case ' ', '\t', '\n':
|
||||
l.pos++
|
||||
default:
|
||||
l.tagTmpName = ""
|
||||
return lexTagName
|
||||
}
|
||||
}
|
||||
l.emit(TEXT, l.input)
|
||||
return nil
|
||||
}
|
121
vendor/github.com/frustra/bbcode/lexer_test.go
generated
vendored
Normal file
121
vendor/github.com/frustra/bbcode/lexer_test.go
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
// Copyright 2015 Frustra. All rights reserved.
|
||||
// Use of this source code is governed by the MIT
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bbcode
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var prelexTests = map[string][]string{
|
||||
``: []string{},
|
||||
`[url]a[/url]`: []string{`<url>`, `a`, `</url>`},
|
||||
`[img][/img]`: []string{`<img>`, `</img>`},
|
||||
`[img = foo]bar[/img]`: []string{`<img=foo>`, `bar`, `</img>`},
|
||||
`[quote name=Someguy]hello[/quote]`: []string{`<quote name=Someguy>`, `hello`, `</quote>`},
|
||||
`[center][b][color=#00BFFF][size=6]hello[/size][/color][/b][/center]`: []string{`<center>`, `<b>`, `<color=#00BFFF>`, `<size=6>`, `hello`, `</size>`, `</color>`, `</b>`, `</center>`},
|
||||
`[b]`: []string{`<b>`},
|
||||
`blank[b][/b]`: []string{`blank`, `<b>`, `</b>`},
|
||||
`[b][/b]blank`: []string{`<b>`, `</b>`, `blank`},
|
||||
`[not a tag][/not]`: []string{`<not a tag>`, `</not>`},
|
||||
|
||||
`[u][b]something[/b] then [b]something else[/b][/u]`: []string{`<u>`, `<b>`, `something`, `</b>`, ` then `, `<b>`, `something else`, `</b>`, `</u>`},
|
||||
|
||||
"the quick brown [b][i]fox[/b][/i]\n[i]\n[b]hi[/b]][b][url=a[img]v[/img][/url][b]": []string{"the quick brown ", "<b>", "<i>", "fox", "</b>", "</i>", "\n", "<i>", "\n", "<b>", "hi", "</b>", "]", "<b>", "[url=a", "<img>", "v", "</img>", "</url>", "<b>"},
|
||||
"the quick brown[/b][b]hello[/b]": []string{"the quick brown", "</b>", "<b>", "hello", "</b>"},
|
||||
"the quick brown[/b][/code]": []string{"the quick brown", "</b>", "</code>"},
|
||||
"[quote\n name=xthexder\n time=555555\n ]hello[/quote]": []string{`<quote name=xthexder time=555555>`, `hello`, `</quote>`},
|
||||
"[q\nuot\ne\nna\nme\n=\nxthex\nder\n]hello[/quote]": []string{`<q der e me=xthex na uot>`, `hello`, `</quote>`},
|
||||
|
||||
`[ b][ i]the quick brown[/i][/b=hello]`: []string{`<b>`, `<i>`, `the quick brown`, `</i>`, `</b=hello>`},
|
||||
`[b [herp@#$%]]the quick brown[/b]`: []string{`[b `, `<herp@#$%>`, `]the quick brown`, `</b>`},
|
||||
`[b=hello a=hi q]the quick brown[/b]`: []string{`<b=hello a=hi q>`, `the quick brown`, `</b>`},
|
||||
`[b]hi[`: []string{`<b>`, `hi`, `[`},
|
||||
`[size=6 =hello]hi[/size]`: []string{`<size=6 =hello>`, `hi`, `</size>`},
|
||||
`[size=6 =hello =hi]hi[/size]`: []string{`<size=6 =hi>`, `hi`, `</size>`},
|
||||
|
||||
`[img = 'fo"o']bar[/img]`: []string{`<img=fo"o>`, `bar`, `</img>`},
|
||||
`[img = "foo'"]bar[/img]`: []string{`<img=foo'>`, `bar`, `</img>`},
|
||||
`[img = "\"'foo"]bar[/img]`: []string{`<img="'foo>`, `bar`, `</img>`},
|
||||
`[img = "f\oo\]\'fo\\o"]bar[/img]`: []string{`<img=foo]'fo\o>`, `bar`, `</img>`},
|
||||
`[img = "foo\]'fo\n\"o"]bar[/img]`: []string{"<img=foo]'fo\n\"o>", `bar`, `</img>`},
|
||||
`[quote name='Someguy']hello[/quote]`: []string{`<quote name=Someguy>`, `hello`, `</quote>`},
|
||||
`[center][b][color="#00BFFF"][size='6]hello[/size][/color][/b][/center]`: []string{`<center>`, `<b>`, `<color=#00BFFF>`, `[size='6]hello`, `</size>`, `</color>`, `</b>`, `</center>`},
|
||||
"[center][b][color=\"#00BFFF\"][size='6]hello[/size]\n[/color][/b][/center]": []string{`<center>`, `<b>`, `<color=#00BFFF>`, `[size='6]hello`, `</size>`, "\n", `</color>`, `</b>`, `</center>`},
|
||||
}
|
||||
|
||||
func TestLexer(t *testing.T) {
|
||||
for in, expected := range prelexTests {
|
||||
lexer := newLexer(in)
|
||||
go lexer.runStateMachine()
|
||||
ok, out := CheckResult(lexer, expected)
|
||||
if !ok {
|
||||
t.Errorf("Failed to prelex %s.\nExpected: %s, got: %s\n", in, PrintExpected(expected), PrintOutput(out))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func PrintExpected(expected []string) string {
|
||||
result := ""
|
||||
for i, v := range expected {
|
||||
if i > 0 {
|
||||
result += "_"
|
||||
}
|
||||
result += v
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func PrintOutput(out []Token) string {
|
||||
result := ""
|
||||
for i, v := range out {
|
||||
if i > 0 {
|
||||
result += "_"
|
||||
}
|
||||
switch t := v.Value.(type) {
|
||||
case string:
|
||||
result += t
|
||||
case BBOpeningTag:
|
||||
result += "<" + t.String() + ">"
|
||||
case BBClosingTag:
|
||||
result += "</" + t.Name + ">"
|
||||
default:
|
||||
result += fmt.Sprintf("{%v}", t)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func CheckResult(l *lexer, b []string) (bool, []Token) {
|
||||
i := 0
|
||||
out := make([]Token, 0)
|
||||
good := true
|
||||
for v := range l.tokens {
|
||||
out = append(out, v)
|
||||
if i < len(b) && good {
|
||||
switch t := v.Value.(type) {
|
||||
case string:
|
||||
if t != b[i] {
|
||||
good = false
|
||||
}
|
||||
case BBOpeningTag:
|
||||
if "<"+t.String()+">" != b[i] {
|
||||
good = false
|
||||
}
|
||||
case BBClosingTag:
|
||||
if "</"+t.Name+">" != b[i] {
|
||||
good = false
|
||||
}
|
||||
default:
|
||||
good = false
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i != len(b) {
|
||||
return false, out
|
||||
}
|
||||
return good, out
|
||||
}
|
60
vendor/github.com/frustra/bbcode/parser.go
generated
vendored
Normal file
60
vendor/github.com/frustra/bbcode/parser.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright 2015 Frustra. All rights reserved.
|
||||
// Use of this source code is governed by the MIT
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bbcode
|
||||
|
||||
type BBCodeNode struct {
|
||||
Token
|
||||
Parent *BBCodeNode
|
||||
Children []*BBCodeNode
|
||||
ClosingTag *BBClosingTag
|
||||
|
||||
Compiler *Compiler
|
||||
Info interface{}
|
||||
}
|
||||
|
||||
func (n *BBCodeNode) GetOpeningTag() *BBOpeningTag {
|
||||
if tag, ok := n.Value.(BBOpeningTag); ok {
|
||||
return &tag
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (n *BBCodeNode) appendChild(t Token) *BBCodeNode {
|
||||
if t.ID == CLOSING_TAG {
|
||||
curr := n
|
||||
closing := t.Value.(BBClosingTag)
|
||||
for curr.Parent != nil {
|
||||
if curr.ID == OPENING_TAG && curr.Value.(BBOpeningTag).Name == closing.Name {
|
||||
curr.ClosingTag = &closing
|
||||
return curr.Parent
|
||||
}
|
||||
curr = curr.Parent
|
||||
}
|
||||
}
|
||||
|
||||
// Join consecutive TEXT tokens
|
||||
if len(n.Children) == 0 && t.ID == TEXT && n.ID == TEXT {
|
||||
n.Value = n.Value.(string) + t.Value.(string)
|
||||
return n
|
||||
}
|
||||
|
||||
node := &BBCodeNode{t, n, make([]*BBCodeNode, 0, 5), nil, nil, nil}
|
||||
n.Children = append(n.Children, node)
|
||||
if t.ID == OPENING_TAG {
|
||||
return node
|
||||
} else {
|
||||
return n
|
||||
}
|
||||
}
|
||||
|
||||
func Parse(tokens chan Token) *BBCodeNode {
|
||||
root := &BBCodeNode{Token{TEXT, ""}, nil, make([]*BBCodeNode, 0, 5), nil, nil, nil}
|
||||
curr := root
|
||||
for tok := range tokens {
|
||||
curr = curr.appendChild(tok)
|
||||
}
|
||||
return root
|
||||
}
|
Reference in New Issue
Block a user