├── .gitignore ├── .tests-run.sh ├── .travis.yml ├── LICENSE ├── README.md ├── client ├── client.go └── client_test.go ├── gui ├── events.go └── gui.go ├── main.go ├── progressbar ├── fake.go ├── fake_test.go ├── gui.go ├── gui_test.go └── interface.go ├── providers ├── fake.go ├── fake_test.go ├── hackernews │ ├── client.go │ └── client_test.go ├── interface.go └── reddit │ ├── client.go │ └── client_test.go ├── story └── model.go ├── utils ├── consts.go ├── html.go ├── html_test.go ├── http.go ├── http_test.go ├── os.go └── os_test.go └── vendor ├── github.com ├── PuerkitoBio │ └── goquery │ │ ├── LICENSE │ │ ├── README.md │ │ ├── array.go │ │ ├── doc.go │ │ ├── expand.go │ │ ├── filter.go │ │ ├── iteration.go │ │ ├── manipulation.go │ │ ├── property.go │ │ ├── query.go │ │ ├── traversal.go │ │ ├── type.go │ │ └── utilities.go ├── andybalholm │ └── cascadia │ │ ├── LICENSE │ │ ├── README.md │ │ ├── parser.go │ │ └── selector.go ├── jroimartin │ └── gocui │ │ ├── AUTHORS │ │ ├── LICENSE │ │ ├── README.md │ │ ├── attribute.go │ │ ├── doc.go │ │ ├── edit.go │ │ ├── escape.go │ │ ├── gui.go │ │ ├── keybinding.go │ │ └── view.go ├── mattn │ └── go-runewidth │ │ ├── LICENSE │ │ ├── README.mkd │ │ ├── runewidth.go │ │ ├── runewidth_js.go │ │ ├── runewidth_posix.go │ │ └── runewidth_windows.go ├── mauidude │ └── go-readability │ │ ├── README.md │ │ └── readability.go └── nsf │ └── termbox-go │ ├── AUTHORS │ ├── LICENSE │ ├── README.md │ ├── api.go │ ├── api_common.go │ ├── api_windows.go │ ├── collect_terminfo.py │ ├── syscalls.go │ ├── syscalls_darwin.go │ ├── syscalls_darwin_amd64.go │ ├── syscalls_dragonfly.go │ ├── syscalls_freebsd.go │ ├── syscalls_linux.go │ ├── syscalls_netbsd.go │ ├── syscalls_openbsd.go │ ├── syscalls_windows.go │ ├── termbox.go │ ├── termbox_common.go │ ├── termbox_windows.go │ ├── terminfo.go │ └── terminfo_builtin.go ├── golang.org └── x │ └── net │ ├── LICENSE │ ├── PATENTS │ └── html │ ├── atom │ ├── atom.go │ ├── gen.go │ └── table.go │ ├── const.go │ ├── doc.go │ ├── doctype.go │ ├── entity.go │ ├── escape.go │ ├── foreign.go │ ├── node.go │ ├── parse.go │ ├── render.go │ └── token.go ├── gopkg.in └── cheggaaa │ └── pb.v1 │ ├── LICENSE │ ├── README.md │ ├── format.go │ ├── pb.go │ ├── pb_appengine.go │ ├── pb_nix.go │ ├── pb_solaris.go │ ├── pb_win.go │ ├── pb_x.go │ ├── pool.go │ ├── pool_win.go │ ├── pool_x.go │ ├── reader.go │ ├── runecount.go │ ├── termios_bsd.go │ └── termios_nix.go └── vendor.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | # Output of the go coverage tool, specifically when used with LiteIDE 27 | *.out 28 | -------------------------------------------------------------------------------- /.tests-run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | echo "" > coverage.txt 5 | 6 | for d in $(go list ./... | grep -v vendor); do 7 | go test -race -coverprofile=profile.out -covermode=atomic $d 8 | if [ -f profile.out ]; then 9 | cat profile.out >> coverage.txt 10 | rm profile.out 11 | fi 12 | done 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.7 5 | - 1.8 6 | - tip 7 | 8 | script: ./.tests-run.sh 9 | 10 | after_success: bash <(curl -s https://codecov.io/bash) 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Diego Garcia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gonews 2 | [![Build Status](https://travis-ci.org/drgarcia1986/gonews.svg)](https://travis-ci.org/drgarcia1986/gonews) 3 | [![Go Report Card](https://goreportcard.com/badge/drgarcia1986/gonews)](https://goreportcard.com/report/drgarcia1986/gonews) 4 | [![codecov](https://codecov.io/gh/drgarcia1986/gonews/branch/master/graph/badge.svg)](https://codecov.io/gh/drgarcia1986/gonews) 5 | 6 | A Golang CLI to browser news from [Hacker News](https://news.ycombinator.com/) and [Reddit](https://www.reddit.com) 7 | Also is a Golang implementation of the awesome [pynews-cli](https://github.com/mazulo/pynews_cli). 8 | 9 | ## Installing 10 | Get this project and install via `go get -u`. 11 | ``` 12 | $ go get -u github.com/drgarcia1986/gonews 13 | ``` 14 | Or you can download a binary on releases. 15 | 16 | ## Usage example 17 | To get the last 10 `new` stories just call `gonews` on command line and press `Enter` on the news wanted. 18 | 19 | You can determine the number max of news to return with parameter `--limit` and you can also choose between `new` or `top` stories 20 | with parameter `--type`. 21 | ``` 22 | $ gonews --limit 20 --type top 23 | ``` 24 | By default gonews use `hackernews` as provider, but you can change the provider with parameter `--provider`, e.g: 25 | ``` 26 | $ gonews --provider reddit-golang --type new --limit 10 27 | ``` 28 | For more information about usage, use command `gonews --help` 29 | ``` 30 | $ gonews --help 31 | -limit int 32 | Number of Stories to get (default 10) 33 | -provider string 34 | Stories Provider (hackernews, reddit, reddit-) (default "hackernews") 35 | -type string 36 | Stories Type ('new' or 'top') (default "top") 37 | ``` 38 | 39 | ## Observation 40 | This is a toy project to help me to pratice Golang, if you can help me with this, getting in touch :smile:. 41 | -------------------------------------------------------------------------------- /client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/drgarcia1986/gonews/progressbar" 5 | "github.com/drgarcia1986/gonews/providers" 6 | "github.com/drgarcia1986/gonews/story" 7 | ) 8 | 9 | type Client struct { 10 | provider providers.Provider 11 | pb progressbar.ProgressBar 12 | } 13 | 14 | func (c *Client) GetStories(storyType, limit int) ([]*story.Story, error) { 15 | generator, err := c.provider.GetStories(storyType, limit) 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | var stories []*story.Story 21 | c.pb.Start(limit) 22 | for future := range generator { 23 | request := <-future 24 | if request.Err != nil { 25 | return nil, err 26 | } 27 | stories = append(stories, request.Story) 28 | c.pb.Increment() 29 | } 30 | c.pb.Finish() 31 | return stories, nil 32 | } 33 | 34 | func New(provider providers.Provider, progressbar progressbar.ProgressBar) *Client { 35 | return &Client{provider: provider, pb: progressbar} 36 | } 37 | -------------------------------------------------------------------------------- /client/client_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/drgarcia1986/gonews/progressbar" 7 | "github.com/drgarcia1986/gonews/providers" 8 | "github.com/drgarcia1986/gonews/story" 9 | ) 10 | 11 | func TestGetStories(t *testing.T) { 12 | expectedStories := []*story.Story{ 13 | {"Foo", "http://golang.org"}, 14 | {"Bar", "http://google.com"}, 15 | } 16 | 17 | fake := &providers.Fake{expectedStories} 18 | c := New(fake, progressbar.New()) 19 | 20 | stories, err := c.GetStories(1, 1) 21 | if err != nil { 22 | t.Errorf("error on get stories: %v", err) 23 | } 24 | 25 | for i, s := range stories { 26 | if s.Title != expectedStories[i].Title { 27 | t.Errorf("Expected %s, got %s", expectedStories[i].Title, s.Title) 28 | } 29 | if s.URL != expectedStories[i].URL { 30 | t.Errorf("Expected %s, got %s", expectedStories[i].URL, s.URL) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /gui/events.go: -------------------------------------------------------------------------------- 1 | package gui 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/jroimartin/gocui" 8 | ) 9 | 10 | func quit(g *gocui.Gui, v *gocui.View) error { 11 | if name := v.Name(); name != "main" { 12 | g.Cursor = false 13 | return deleteView(name, g) 14 | } 15 | return gocui.ErrQuit 16 | } 17 | 18 | func cursorDown(g *gocui.Gui, v *gocui.View) error { 19 | if v == nil { 20 | return nil 21 | } 22 | 23 | cx, cy := v.Cursor() 24 | if line, err := v.Line(cy + 1); err != nil || line == "" { 25 | return nil 26 | } 27 | 28 | if err := v.SetCursor(cx, cy+1); err != nil { 29 | ox, oy := v.Origin() 30 | if err := v.SetOrigin(ox, oy+1); err != nil { 31 | return err 32 | } 33 | } 34 | 35 | return nil 36 | } 37 | 38 | func cursorUp(g *gocui.Gui, v *gocui.View) error { 39 | if v == nil { 40 | return nil 41 | } 42 | 43 | ox, oy := v.Origin() 44 | cx, cy := v.Cursor() 45 | if err := v.SetCursor(cx, cy-1); err != nil && oy > 0 { 46 | if err := v.SetOrigin(ox, oy-1); err != nil { 47 | return err 48 | } 49 | } 50 | 51 | return nil 52 | } 53 | 54 | func helpMsg(g *gocui.Gui, _ *gocui.View) error { 55 | maxX, maxY := g.Size() 56 | if v, err := g.SetView("help", maxX/2-30, maxY/2, maxX/2+15, maxY/2+6); err != nil { 57 | if err != gocui.ErrUnknownView { 58 | return err 59 | } 60 | v.Title = "Keyboard Shortcuts" 61 | fmt.Fprintln(v, ` 62 | - Enter : Open story in default browser 63 | - p : Preview (beta) 64 | - k / Arrow Up : Move up 65 | - j / Arrow Down : Move down 66 | - q / Ctrl+c : Close window 67 | - ? : Show this message 68 | `) 69 | 70 | if _, err := g.SetCurrentView("help"); err != nil { 71 | return err 72 | } 73 | } 74 | return nil 75 | } 76 | 77 | func deleteView(viewName string, g *gocui.Gui) error { 78 | if err := g.DeleteView(viewName); err != nil { 79 | return err 80 | } 81 | if _, err := g.SetCurrentView("main"); err != nil { 82 | return err 83 | } 84 | return nil 85 | } 86 | 87 | func showPreview(g *gocui.Gui, title, content string) error { 88 | maxX, maxY := g.Size() 89 | if v, err := g.SetView("preview", 0, 0, maxX-1, maxY-1); err != nil { 90 | if err != gocui.ErrUnknownView { 91 | return err 92 | } 93 | v.Title = fmt.Sprint("Preview - ", title) 94 | g.Cursor = true 95 | 96 | var length int 97 | for _, s := range strings.Split(content, "\n") { 98 | for { 99 | length = maxX - 2 100 | if length > len(s) { 101 | length = len(s) 102 | } 103 | 104 | text := strings.TrimSpace(s[:length]) 105 | if text != "" { 106 | fmt.Fprintln(v, text) 107 | } 108 | 109 | if length == len(s) { 110 | break 111 | } 112 | s = s[length:] 113 | } 114 | } 115 | 116 | if _, err := g.SetCurrentView("preview"); err != nil { 117 | return err 118 | } 119 | } 120 | return nil 121 | } 122 | -------------------------------------------------------------------------------- /gui/gui.go: -------------------------------------------------------------------------------- 1 | package gui 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/drgarcia1986/gonews/story" 7 | "github.com/drgarcia1986/gonews/utils" 8 | "github.com/jroimartin/gocui" 9 | ) 10 | 11 | var ( 12 | downKeys = []interface{}{'j', gocui.KeyArrowDown} 13 | upKeys = []interface{}{'k', gocui.KeyArrowUp} 14 | quitKeys = []interface{}{'q', gocui.KeyCtrlC} 15 | 16 | keybindingMap = []struct { 17 | keys []interface{} 18 | viewName string 19 | event func(*gocui.Gui, *gocui.View) error 20 | }{ 21 | {quitKeys, "", quit}, 22 | {downKeys, "", cursorDown}, 23 | {upKeys, "", cursorUp}, 24 | {[]interface{}{'?'}, "main", helpMsg}, 25 | } 26 | ) 27 | 28 | type Gui struct { 29 | items []*story.Story 30 | providerName string 31 | } 32 | 33 | func (gui *Gui) openStory(g *gocui.Gui, v *gocui.View) error { 34 | s, err := gui.getStoryOfCurrentLine(v) 35 | if err == nil && s != nil { 36 | return utils.OpenURL(s.URL) 37 | } 38 | return err 39 | } 40 | 41 | func (gui *Gui) preview(g *gocui.Gui, v *gocui.View) error { 42 | s, err := gui.getStoryOfCurrentLine(v) 43 | if err != nil { 44 | return err 45 | } 46 | 47 | if s == nil { 48 | return nil 49 | } 50 | 51 | content, err := utils.GetPreview(s.URL) 52 | if err != nil { 53 | return err 54 | } 55 | 56 | if content == "" { 57 | content = "No preview available" 58 | } 59 | return showPreview(g, s.Title, content) 60 | } 61 | 62 | func (gui *Gui) getStoryOfCurrentLine(v *gocui.View) (*story.Story, error) { 63 | _, cy := v.Cursor() 64 | line, err := v.Line(cy) 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | for _, s := range gui.items { 70 | if s.Title == line { 71 | return s, nil 72 | } 73 | } 74 | return nil, nil 75 | } 76 | 77 | func (gui *Gui) layout(g *gocui.Gui) error { 78 | maxX, maxY := g.Size() 79 | if v, err := g.SetView("main", 0, 0, maxX-1, maxY-1); err != nil { 80 | if err != gocui.ErrUnknownView { 81 | return err 82 | } 83 | v.Title = fmt.Sprintf("GoNews - %s ('?' for help)", gui.providerName) 84 | v.Highlight = true 85 | v.SelBgColor = gocui.ColorGreen 86 | v.SelFgColor = gocui.ColorBlack 87 | 88 | for _, story := range gui.items { 89 | fmt.Fprintln(v, story.Title) 90 | } 91 | 92 | if _, err := g.SetCurrentView("main"); err != nil { 93 | return err 94 | } 95 | } 96 | return nil 97 | } 98 | 99 | func (gui *Gui) keybindings(g *gocui.Gui) error { 100 | for _, bm := range keybindingMap { 101 | for _, key := range bm.keys { 102 | if err := g.SetKeybinding(bm.viewName, key, gocui.ModNone, bm.event); err != nil { 103 | return err 104 | } 105 | } 106 | } 107 | 108 | if err := g.SetKeybinding("main", gocui.KeyEnter, gocui.ModNone, gui.openStory); err != nil { 109 | return err 110 | } 111 | 112 | if err := g.SetKeybinding("main", 'p', gocui.ModNone, gui.preview); err != nil { 113 | return err 114 | } 115 | 116 | return nil 117 | } 118 | 119 | func (gui *Gui) Run() error { 120 | g, err := gocui.NewGui(gocui.OutputNormal) 121 | if err != nil { 122 | return err 123 | } 124 | defer g.Close() 125 | 126 | g.SetManagerFunc(gui.layout) 127 | if err := gui.keybindings(g); err != nil { 128 | return err 129 | } 130 | 131 | if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { 132 | return err 133 | } 134 | return nil 135 | } 136 | 137 | func New(items []*story.Story, providerName string) *Gui { 138 | guiItems := make([]*story.Story, len(items)) 139 | copy(guiItems, items) 140 | return &Gui{items: guiItems, providerName: providerName} 141 | } 142 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "strings" 8 | 9 | "github.com/drgarcia1986/gonews/client" 10 | "github.com/drgarcia1986/gonews/gui" 11 | "github.com/drgarcia1986/gonews/progressbar" 12 | "github.com/drgarcia1986/gonews/providers" 13 | "github.com/drgarcia1986/gonews/providers/hackernews" 14 | "github.com/drgarcia1986/gonews/providers/reddit" 15 | "github.com/drgarcia1986/gonews/story" 16 | "github.com/drgarcia1986/gonews/utils" 17 | ) 18 | 19 | var ( 20 | limit int 21 | storyType int 22 | flagStoryType string 23 | flagProviderType string 24 | ) 25 | 26 | func init() { 27 | flag.IntVar(&limit, "limit", 10, "Number of Stories to get") 28 | flag.StringVar(&flagStoryType, "type", "top", "Stories Type ('new' or 'top')") 29 | flag.StringVar( 30 | &flagProviderType, "provider", "hackernews", 31 | "Stories Provider (hackernews, reddit, reddit-)") 32 | 33 | flag.Parse() 34 | 35 | if len(flag.Args()) > 0 && flag.Args()[0] == "version" { 36 | fmt.Printf("GoNews %s\n", utils.Version) 37 | os.Exit(0) 38 | } 39 | 40 | if flagStoryType == "new" { 41 | storyType = story.NewStories 42 | } else { 43 | storyType = story.TopStories 44 | } 45 | } 46 | 47 | func getProvider(providerType string) providers.Provider { 48 | switch providerType { 49 | case "hackernews": 50 | return hackernews.New() 51 | case "reddit": 52 | return reddit.New() 53 | default: 54 | providerArgs := strings.Split(providerType, "-") 55 | if len(providerArgs) != 2 || providerArgs[0] != "reddit" { 56 | return nil 57 | } 58 | return reddit.NewSubReddit(providerArgs[1]) 59 | } 60 | } 61 | 62 | func main() { 63 | p := getProvider(flagProviderType) 64 | if p == nil { 65 | fmt.Println("Invalid Provider ", flagProviderType) 66 | os.Exit(0) 67 | } 68 | 69 | fmt.Printf("Getting %s stories\n", p.Name()) 70 | c := client.New(p, progressbar.New()) 71 | stories, err := c.GetStories(storyType, limit) 72 | if err != nil { 73 | panic(err) 74 | } 75 | 76 | g := gui.New(stories, p.Name()) 77 | if err = g.Run(); err != nil { 78 | panic(err) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /progressbar/fake.go: -------------------------------------------------------------------------------- 1 | package progressbar 2 | 3 | type FakeProgressBar struct { 4 | started bool 5 | incCount int 6 | finished bool 7 | } 8 | 9 | func (f *FakeProgressBar) Start(count int) { 10 | f.started = true 11 | } 12 | 13 | func (f *FakeProgressBar) Increment() { 14 | f.incCount++ 15 | } 16 | 17 | func (f *FakeProgressBar) Finish() { 18 | f.finished = true 19 | } 20 | 21 | func NewFake() ProgressBar { 22 | return new(FakeProgressBar) 23 | } 24 | -------------------------------------------------------------------------------- /progressbar/fake_test.go: -------------------------------------------------------------------------------- 1 | package progressbar 2 | 3 | import "testing" 4 | 5 | func TestFakeStart(t *testing.T) { 6 | p := new(FakeProgressBar) 7 | p.Start(1) 8 | 9 | if !p.started { 10 | t.Error("expected progressbar started") 11 | } 12 | } 13 | 14 | func TestFakeIncrement(t *testing.T) { 15 | p := new(FakeProgressBar) 16 | p.Increment() 17 | 18 | if p.incCount != 1 { 19 | t.Errorf("expected 1, got %d", p.incCount) 20 | } 21 | } 22 | 23 | func TestFakeFinish(t *testing.T) { 24 | p := NewFake() 25 | p.Finish() 26 | 27 | f, ok := p.(*FakeProgressBar) 28 | if !ok { 29 | t.Fatal("error in type assertation of progress interface") 30 | } 31 | 32 | if !f.finished { 33 | t.Error("expected progressbar finished") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /progressbar/gui.go: -------------------------------------------------------------------------------- 1 | package progressbar 2 | 3 | import pb "gopkg.in/cheggaaa/pb.v1" 4 | 5 | type GUIProgressBar struct { 6 | p *pb.ProgressBar 7 | } 8 | 9 | func (g *GUIProgressBar) Start(count int) { 10 | g.p = pb.StartNew(count) 11 | } 12 | 13 | func (g *GUIProgressBar) Increment() { 14 | g.p.Increment() 15 | } 16 | 17 | func (g *GUIProgressBar) Finish() { 18 | g.p.Finish() 19 | } 20 | 21 | func New() ProgressBar { 22 | return new(GUIProgressBar) 23 | } 24 | -------------------------------------------------------------------------------- /progressbar/gui_test.go: -------------------------------------------------------------------------------- 1 | package progressbar 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func TestGUIStartNew(t *testing.T) { 9 | p := new(GUIProgressBar) 10 | 11 | expected := 10 12 | p.Start(expected) 13 | 14 | if p.p == nil { 15 | t.Fatal("error on start new progressbar") 16 | } 17 | 18 | if p.p.Total != int64(expected) { 19 | t.Errorf("expected %d, got %d", expected, p.p.Total) 20 | } 21 | } 22 | 23 | func TestGUIIncrement(t *testing.T) { 24 | p := new(GUIProgressBar) 25 | 26 | p.Start(10) 27 | p.Increment() 28 | 29 | if p.p.Get() != int64(1) { 30 | t.Errorf("expected 1, got %d", p.p.Get()) 31 | } 32 | } 33 | 34 | func TestGUIFinish(t *testing.T) { 35 | p := New() 36 | 37 | p.Start(1) 38 | p.Increment() 39 | p.Finish() 40 | 41 | g, ok := p.(*GUIProgressBar) 42 | if !ok { 43 | t.Fatal("error in type assertation of progress interface") 44 | } 45 | 46 | expected := "100.00%" 47 | if !strings.Contains(g.p.String(), "100.00%") { 48 | t.Errorf("expected %s, got %s", expected, g.p.String()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /progressbar/interface.go: -------------------------------------------------------------------------------- 1 | package progressbar 2 | 3 | type ProgressBar interface { 4 | Start(int) 5 | Increment() 6 | Finish() 7 | } 8 | -------------------------------------------------------------------------------- /providers/fake.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import "github.com/drgarcia1986/gonews/story" 4 | 5 | type Fake struct { 6 | Stories []*story.Story 7 | } 8 | 9 | func (f *Fake) GetStories(storyType, limit int) (<-chan chan *StoryRequest, error) { 10 | generator := make(chan chan *StoryRequest, len(f.Stories)) 11 | go func() { 12 | defer close(generator) 13 | for _, s := range f.Stories { 14 | future := make(chan *StoryRequest, 1) 15 | future <- &StoryRequest{s, nil} 16 | close(future) 17 | 18 | generator <- future 19 | } 20 | }() 21 | return generator, nil 22 | } 23 | 24 | func (f *Fake) Name() string { 25 | return "fake" 26 | } 27 | 28 | func NewFake() Provider { 29 | stories := make([]*story.Story, 0) 30 | return &Fake{stories} 31 | } 32 | -------------------------------------------------------------------------------- /providers/fake_test.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/drgarcia1986/gonews/story" 7 | ) 8 | 9 | func TestFakeName(t *testing.T) { 10 | fake := NewFake() 11 | expected := "fake" 12 | if name := fake.Name(); name != expected { 13 | t.Errorf("Expected %s, got %s", expected, name) 14 | } 15 | } 16 | 17 | func TestFakeGetStories(t *testing.T) { 18 | stories := []*story.Story{ 19 | {"Foo", "http://golang.org"}, 20 | {"Bar", "http://google.com"}, 21 | } 22 | 23 | fake := &Fake{stories} 24 | generator, err := fake.GetStories(0, 0) 25 | if err != nil { 26 | t.Errorf("Error on get fake stories: %v", err) 27 | } 28 | 29 | i := 0 30 | for future := range generator { 31 | r := <-future 32 | 33 | if r.Err != nil { 34 | t.Errorf("Error on get future stories: %v", r.Err) 35 | } 36 | 37 | expectedTitle := fake.Stories[i].Title 38 | expectedURL := fake.Stories[i].URL 39 | 40 | if r.Story.Title != expectedTitle { 41 | t.Errorf("Expected %s, got %s", expectedTitle, r.Story.Title) 42 | } 43 | 44 | if r.Story.URL != expectedURL { 45 | t.Errorf("Expected %s, got %s", expectedURL, r.Story.URL) 46 | } 47 | i++ 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /providers/hackernews/client.go: -------------------------------------------------------------------------------- 1 | package hackernews 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | 9 | "github.com/drgarcia1986/gonews/providers" 10 | "github.com/drgarcia1986/gonews/story" 11 | ) 12 | 13 | var ( 14 | urlBase = "https://hacker-news.firebaseio.com/v0" 15 | urlTopStories = fmt.Sprintf("%s/topstories.json", urlBase) 16 | urlNewStories = fmt.Sprintf("%s/newstories.json", urlBase) 17 | urlStoryBase = fmt.Sprintf("%s/item", urlBase) 18 | ) 19 | 20 | type HackerNews struct{} 21 | 22 | func getStoryURL(id int) string { 23 | return fmt.Sprintf("%s/%d.json", urlStoryBase, id) 24 | } 25 | 26 | func getStoryIds(url string) ([]int, error) { 27 | resp, err := http.Get(url) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | defer resp.Body.Close() 33 | body, err := ioutil.ReadAll(resp.Body) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | ids := []int{} 39 | if err = json.Unmarshal(body, &ids); err != nil { 40 | return nil, err 41 | } 42 | return ids, nil 43 | } 44 | 45 | func getStory(id int) (*story.Story, error) { 46 | url := getStoryURL(id) 47 | 48 | resp, err := http.Get(url) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | defer resp.Body.Close() 54 | body, err := ioutil.ReadAll(resp.Body) 55 | if err != nil { 56 | return nil, err 57 | } 58 | 59 | var story story.Story 60 | 61 | if err = json.Unmarshal(body, &story); err != nil { 62 | return nil, err 63 | } 64 | return &story, nil 65 | } 66 | 67 | func getURL(storyType int) string { 68 | switch storyType { 69 | case story.TopStories: 70 | return urlTopStories 71 | default: 72 | return urlNewStories 73 | } 74 | } 75 | 76 | func storiesGenerator(targetIds []int) <-chan chan *providers.StoryRequest { 77 | generator := make(chan chan *providers.StoryRequest, len(targetIds)) 78 | 79 | go func() { 80 | defer close(generator) 81 | for _, id := range targetIds { 82 | generator <- func(id int) chan *providers.StoryRequest { 83 | future := make(chan *providers.StoryRequest, 1) 84 | go func() { 85 | defer close(future) 86 | story, err := getStory(id) 87 | future <- &providers.StoryRequest{story, err} 88 | }() 89 | return future 90 | }(id) 91 | } 92 | }() 93 | 94 | return generator 95 | } 96 | 97 | func (h *HackerNews) GetStories(storyType, limit int) (<-chan chan *providers.StoryRequest, error) { 98 | url := getURL(storyType) 99 | ids, err := getStoryIds(url) 100 | if err != nil { 101 | return nil, err 102 | } 103 | targetIds := ids[:limit] 104 | 105 | return storiesGenerator(targetIds), nil 106 | } 107 | 108 | func (h *HackerNews) Name() string { 109 | return "HackerNews" 110 | } 111 | 112 | func New() providers.Provider { 113 | return new(HackerNews) 114 | } 115 | -------------------------------------------------------------------------------- /providers/hackernews/client_test.go: -------------------------------------------------------------------------------- 1 | package hackernews 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httptest" 7 | "testing" 8 | 9 | "github.com/drgarcia1986/gonews/story" 10 | ) 11 | 12 | func TestHackernewsName(t *testing.T) { 13 | client := New() 14 | expected := "HackerNews" 15 | if name := client.Name(); name != expected { 16 | t.Errorf("Expected %s, got %s", expected, name) 17 | } 18 | } 19 | 20 | func TestGetUrl(t *testing.T) { 21 | if url := getURL(story.NewStories); url != urlNewStories { 22 | t.Errorf("Expected %s, got %s", urlNewStories, url) 23 | } 24 | 25 | if url := getURL(story.TopStories); url != urlTopStories { 26 | t.Errorf("Expected %s, got %s", urlTopStories, url) 27 | } 28 | } 29 | 30 | func TestHackernewsGetStoryUrl(t *testing.T) { 31 | expectedURL := fmt.Sprintf("%s/1.json", urlStoryBase) 32 | 33 | if url := getStoryURL(1); url != expectedURL { 34 | t.Errorf("Expected %s, got %s", expectedURL, url) 35 | } 36 | } 37 | 38 | func TestGetStoryIds(t *testing.T) { 39 | expectedIds := []interface{}{1111, 2222, 3333} 40 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 41 | fmt.Fprintf(w, "[%d, %d, %d]", expectedIds...) 42 | })) 43 | defer ts.Close() 44 | 45 | storyIds, err := getStoryIds(ts.URL) 46 | if err != nil { 47 | t.Errorf("Error on get story ids %v", err) 48 | } 49 | for i, value := range storyIds { 50 | if value != expectedIds[i] { 51 | t.Errorf("Expected %v, got %v", expectedIds, storyIds) 52 | } 53 | } 54 | } 55 | 56 | func TestGetStory(t *testing.T) { 57 | expectedTitle := "test" 58 | expectedURL := "http://test.com" 59 | 60 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 61 | fmt.Fprintf(w, ` 62 | { 63 | "by": "gopher", "descendants": 0, "id": 1, 64 | "score": 1, "time": 1482804640, "title": "%s", 65 | "type": "story", "url": "%s" 66 | }`, expectedTitle, expectedURL) 67 | })) 68 | defer ts.Close() 69 | 70 | urlStoryBase = ts.URL 71 | story, err := getStory(1) 72 | if err != nil { 73 | t.Errorf("Error on get story ids %v", err) 74 | } 75 | 76 | if story.Title != expectedTitle { 77 | t.Errorf("Expected %s, got %s", expectedTitle, story.Title) 78 | } 79 | if story.URL != expectedURL { 80 | t.Errorf("Expected %s, got %s", expectedURL, story.URL) 81 | } 82 | } 83 | 84 | func TestGetStories(t *testing.T) { 85 | urlTS := "/top.json" 86 | mux := http.NewServeMux() 87 | mux.HandleFunc(urlTS, func(w http.ResponseWriter, r *http.Request) { 88 | fmt.Fprintln(w, "[1, 2, 3]") 89 | }) 90 | 91 | expectedTitles := []string{"test", "test 2"} 92 | mux.HandleFunc("/1.json", func(w http.ResponseWriter, r *http.Request) { 93 | fmt.Fprintf(w, ` 94 | { 95 | "by": "gopher", "descendants": 0, "id": 1, 96 | "score": 1, "time": 1482804640, "title": "%s", 97 | "type": "story", "url": "http://test.com" 98 | } 99 | `, expectedTitles[0]) 100 | }) 101 | mux.HandleFunc("/2.json", func(w http.ResponseWriter, r *http.Request) { 102 | fmt.Fprintf(w, ` 103 | { 104 | "by": "gopher", "descendants": 0, "id": 2, 105 | "score": 1, "time": 1482804640, "title": "%s", 106 | "type": "story", "url": "http://test.com" 107 | } 108 | `, expectedTitles[1]) 109 | }) 110 | ts := httptest.NewServer(mux) 111 | defer ts.Close() 112 | 113 | urlStoryBase = ts.URL 114 | urlTopStories = fmt.Sprintf("%s/%s", ts.URL, urlTS) 115 | 116 | client := New() 117 | generator, err := client.GetStories(story.TopStories, 2) 118 | if err != nil { 119 | t.Errorf("Error on get stories ids %v", err) 120 | } 121 | 122 | i := 0 123 | for future := range generator { 124 | r := <-future 125 | 126 | if r.Err != nil { 127 | t.Errorf("Error on get future stories: %v", r.Err) 128 | } 129 | 130 | if r.Story.Title != expectedTitles[i] { 131 | t.Errorf("Expected %s, got %s", expectedTitles[i], r.Story.Title) 132 | } 133 | i++ 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /providers/interface.go: -------------------------------------------------------------------------------- 1 | package providers 2 | 3 | import "github.com/drgarcia1986/gonews/story" 4 | 5 | type StoryRequest struct { 6 | Story *story.Story 7 | Err error 8 | } 9 | 10 | type Provider interface { 11 | Name() string 12 | GetStories(int, int) (<-chan chan *StoryRequest, error) 13 | } 14 | -------------------------------------------------------------------------------- /providers/reddit/client.go: -------------------------------------------------------------------------------- 1 | package reddit 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | 8 | "github.com/drgarcia1986/gonews/providers" 9 | "github.com/drgarcia1986/gonews/story" 10 | "github.com/drgarcia1986/gonews/utils" 11 | ) 12 | 13 | var ( 14 | urlBase = "https://www.reddit.com" 15 | sufixUrlTopStories = "top.json" 16 | sufixUrlNewStories = "new.json" 17 | ) 18 | 19 | type redditResponse struct { 20 | Data struct { 21 | Children []struct { 22 | Data *story.Story `json:"data"` 23 | } `json:"children"` 24 | } `json:"data"` 25 | } 26 | 27 | type Reddit struct { 28 | subReddit string 29 | } 30 | 31 | func getURL(storyType, limit int, subReddit string) string { 32 | url := urlBase 33 | if subReddit != "" { 34 | url = fmt.Sprintf("%s/r/%s", url, subReddit) 35 | } 36 | 37 | switch storyType { 38 | case story.TopStories: 39 | url = fmt.Sprintf("%s/%s", url, sufixUrlTopStories) 40 | default: 41 | url = fmt.Sprintf("%s/%s", url, sufixUrlNewStories) 42 | } 43 | 44 | return fmt.Sprintf("%s?limit=%d", url, limit) 45 | } 46 | 47 | func getStories(url string) ([]*story.Story, error) { 48 | resp, err := utils.MakeRequest("GET", url, nil) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | defer resp.Body.Close() 54 | body, err := ioutil.ReadAll(resp.Body) 55 | 56 | if err != nil { 57 | return nil, err 58 | } 59 | 60 | var rr redditResponse 61 | if err = json.Unmarshal(body, &rr); err != nil { 62 | return nil, err 63 | } 64 | 65 | result := make([]*story.Story, 0) 66 | for _, s := range rr.Data.Children { 67 | result = append(result, s.Data) 68 | } 69 | 70 | return result, nil 71 | } 72 | 73 | func (r *Reddit) GetStories(storyType, limit int) (<-chan chan *providers.StoryRequest, error) { 74 | url := getURL(storyType, limit, r.subReddit) 75 | stories, err := getStories(url) 76 | if err != nil { 77 | return nil, err 78 | } 79 | 80 | generator := make(chan chan *providers.StoryRequest, len(stories)) 81 | go func() { 82 | defer close(generator) 83 | for _, s := range stories { 84 | f := make(chan *providers.StoryRequest, 1) 85 | f <- &providers.StoryRequest{s, nil} 86 | close(f) 87 | 88 | generator <- f 89 | } 90 | }() 91 | 92 | return generator, nil 93 | } 94 | 95 | func (r *Reddit) Name() string { 96 | if r.subReddit == "" { 97 | return "reddit" 98 | } 99 | return fmt.Sprintf("reddit-%s", r.subReddit) 100 | } 101 | 102 | func NewSubReddit(subReddit string) providers.Provider { 103 | return &Reddit{subReddit} 104 | } 105 | 106 | func New() providers.Provider { 107 | return new(Reddit) 108 | } 109 | -------------------------------------------------------------------------------- /providers/reddit/client_test.go: -------------------------------------------------------------------------------- 1 | package reddit 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httptest" 7 | "testing" 8 | 9 | "github.com/drgarcia1986/gonews/story" 10 | ) 11 | 12 | func TestName(t *testing.T) { 13 | client := New() 14 | expected := "reddit" 15 | if name := client.Name(); name != expected { 16 | t.Errorf("Expected %s, got %s", expected, name) 17 | } 18 | } 19 | 20 | func TestSubRedditName(t *testing.T) { 21 | client := NewSubReddit("golang") 22 | expected := "reddit-golang" 23 | if name := client.Name(); name != expected { 24 | t.Errorf("Expected %s, got %s", expected, name) 25 | } 26 | } 27 | 28 | func TestGetURL(t *testing.T) { 29 | var getUrlTests = []struct { 30 | storyType int 31 | limit int 32 | subReddit string 33 | expected string 34 | }{ 35 | {story.TopStories, 5, "", fmt.Sprintf("%s/top.json?limit=5", urlBase)}, 36 | {story.NewStories, 3, "", fmt.Sprintf("%s/new.json?limit=3", urlBase)}, 37 | {story.TopStories, 10, "golang", fmt.Sprintf("%s/r/golang/top.json?limit=10", urlBase)}, 38 | } 39 | 40 | for _, tt := range getUrlTests { 41 | actual := getURL(tt.storyType, tt.limit, tt.subReddit) 42 | if actual != tt.expected { 43 | t.Errorf( 44 | "getUrl(%d, %d, %s): expected %s, actual %s", 45 | tt.storyType, tt.limit, tt.subReddit, 46 | tt.expected, actual, 47 | ) 48 | } 49 | } 50 | } 51 | 52 | func TestGetStories(t *testing.T) { 53 | expectedTitle := "test" 54 | expectedURL := "http://test.com" 55 | 56 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 57 | fmt.Fprintf(w, ` 58 | {"data": {"children": [ 59 | {"data": { 60 | "title": "%s", 61 | "url": "%s" 62 | }} 63 | ]}}`, expectedTitle, expectedURL) 64 | })) 65 | defer ts.Close() 66 | 67 | stories, err := getStories(ts.URL) 68 | if err != nil { 69 | t.Errorf("Error on get stories %v", err) 70 | } 71 | 72 | if len(stories) != 1 { 73 | t.Errorf("Expected 1, got %d", len(stories)) 74 | } 75 | 76 | story := stories[0] 77 | if story.Title != expectedTitle { 78 | t.Errorf("Expected %s, got %s", expectedTitle, story.Title) 79 | } 80 | if story.URL != expectedURL { 81 | t.Errorf("Expected %s, got %s", expectedURL, story.URL) 82 | } 83 | } 84 | 85 | func TestGetStoriesGenerator(t *testing.T) { 86 | expectedTitles := []interface{}{"test", "test 2"} 87 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 88 | fmt.Fprintf(w, ` 89 | {"data": {"children": [ 90 | {"data": { 91 | "title": "%s", 92 | "url": "http://test.com" 93 | }}, 94 | {"data": { 95 | "title": "%s", 96 | "url": "http://test2.com" 97 | }} 98 | ]}}`, expectedTitles...) 99 | })) 100 | defer ts.Close() 101 | 102 | urlBase = ts.URL 103 | client := New() 104 | 105 | generator, err := client.GetStories(story.TopStories, 2) 106 | if err != nil { 107 | t.Errorf("Error on get stories %v", err) 108 | } 109 | 110 | i := 0 111 | for future := range generator { 112 | r := <-future 113 | 114 | if r.Err != nil { 115 | t.Errorf("Error on get future stories: %v", r.Err) 116 | } 117 | 118 | if r.Story.Title != expectedTitles[i] { 119 | t.Errorf("Expected %s, got %s", expectedTitles[i], r.Story.Title) 120 | } 121 | i++ 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /story/model.go: -------------------------------------------------------------------------------- 1 | package story 2 | 3 | const ( 4 | TopStories = iota 5 | NewStories 6 | ) 7 | 8 | type Story struct { 9 | Title string `json:"title"` 10 | URL string `json:"url"` 11 | } 12 | -------------------------------------------------------------------------------- /utils/consts.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | const Version = "0.1.1" 4 | -------------------------------------------------------------------------------- /utils/html.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "io/ioutil" 5 | "regexp" 6 | "strings" 7 | 8 | readability "github.com/mauidude/go-readability" 9 | ) 10 | 11 | var ( 12 | htmlCleanRe = regexp.MustCompile(`<[^>]*>`) 13 | htmlReplaces = []struct { 14 | from string 15 | to string 16 | }{ 17 | {"'", "'"}, 18 | } 19 | ) 20 | 21 | func GetPreview(url string) (string, error) { 22 | resp, err := MakeRequest("GET", url, nil) 23 | if err != nil { 24 | return "", err 25 | } 26 | 27 | defer resp.Body.Close() 28 | body, err := ioutil.ReadAll(resp.Body) 29 | if err != nil { 30 | return "", err 31 | } 32 | 33 | return cleanHTML(string(body)) 34 | } 35 | 36 | func cleanHTML(html string) (string, error) { 37 | doc, err := readability.NewDocument(html) 38 | if err != nil { 39 | return "", err 40 | } 41 | 42 | content := doc.Content() 43 | for _, r := range htmlReplaces { 44 | content = strings.Replace(content, r.from, r.to, -1) 45 | } 46 | 47 | return htmlCleanRe.ReplaceAllString(content, ""), nil 48 | } 49 | -------------------------------------------------------------------------------- /utils/html_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | func TestCleanHTML(t *testing.T) { 11 | var htmls = []struct { 12 | input string 13 | expected string 14 | }{ 15 | {"

I'm a gopher

", "I'm a gopher"}, 16 | {"foo", "foo"}, 17 | } 18 | 19 | for _, tt := range htmls { 20 | actual, err := cleanHTML(tt.input) 21 | if err != nil { 22 | t.Fatal("error on clean html: ", err) 23 | } 24 | if actual != tt.expected { 25 | t.Errorf("expected %s, got %s", tt.expected, actual) 26 | } 27 | } 28 | } 29 | 30 | func TestGetPreview(t *testing.T) { 31 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 32 | w.Write([]byte(` 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |

Hi from gonews tests

49 | 50 | 51 | 52 | 53 | 54 | 55 | `)) 56 | })) 57 | defer ts.Close() 58 | 59 | preview, err := GetPreview(ts.URL) 60 | if err != nil { 61 | t.Fatal("error on get preview: ", err) 62 | } 63 | 64 | actual := strings.TrimSpace(preview) 65 | expected := "Hi from gonews tests" 66 | if actual != expected { 67 | t.Errorf("expected %s, got %s", expected, actual) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /utils/http.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | ) 8 | 9 | func MakeRequest(method, url string, body io.Reader) (*http.Response, error) { 10 | client := new(http.Client) 11 | req, err := http.NewRequest(method, url, body) 12 | if err != nil { 13 | return nil, err 14 | } 15 | 16 | req.Header.Set("User-Agent", fmt.Sprintf("gonews:v%s (by /u/drgarcia1986)", Version)) 17 | return client.Do(req) 18 | } 19 | -------------------------------------------------------------------------------- /utils/http_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httptest" 7 | "testing" 8 | ) 9 | 10 | func TestMakeRequestWithUserAgentHeader(t *testing.T) { 11 | var userAgent string 12 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 13 | userAgent = r.Header.Get("User-Agent") 14 | })) 15 | defer ts.Close() 16 | 17 | expectedUserAgent := fmt.Sprintf("gonews:v%s (by /u/drgarcia1986)", Version) 18 | MakeRequest("GET", ts.URL, nil) 19 | if userAgent != expectedUserAgent { 20 | t.Errorf("Expected %s, got %s", expectedUserAgent, userAgent) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /utils/os.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "os/exec" 5 | "runtime" 6 | ) 7 | 8 | var ( 9 | execCommand = exec.Command 10 | runtimeOS = runtime.GOOS 11 | ) 12 | 13 | func OpenURL(url string) error { 14 | var cmd string 15 | switch runtimeOS { 16 | case "darwin": 17 | cmd = "open" 18 | default: 19 | cmd = "xdg-open" 20 | } 21 | return execCommand(cmd, url).Start() 22 | } 23 | -------------------------------------------------------------------------------- /utils/os_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "os/exec" 5 | "runtime" 6 | "testing" 7 | ) 8 | 9 | type FakeExecCommand struct { 10 | cmd string 11 | args []string 12 | } 13 | 14 | func (f *FakeExecCommand) execCommand(cmd string, args ...string) *exec.Cmd { 15 | f.cmd = cmd 16 | f.args = args 17 | 18 | return exec.Command("echo", "", ">", "/dev/null") 19 | } 20 | 21 | func TestOpen(t *testing.T) { 22 | tt := []struct { 23 | runtime string 24 | expected string 25 | }{ 26 | {"darwin", "open"}, 27 | {"linux", "xdg-open"}, 28 | } 29 | 30 | f := new(FakeExecCommand) 31 | 32 | execCommand = f.execCommand 33 | defer func() { execCommand = exec.Command }() 34 | defer func() { runtimeOS = runtime.GOOS }() 35 | 36 | url := "http://google.com" 37 | for _, tc := range tt { 38 | runtimeOS = tc.runtime 39 | if err := OpenURL(url); err != nil { 40 | t.Fatal("error on exec fake command: ", err) 41 | } 42 | 43 | if f.cmd != tc.expected { 44 | t.Errorf("expected %s, got %s", tc.expected, f.cmd) 45 | } 46 | 47 | if f.args[0] != url { 48 | t.Errorf("expected %s, got %s", url, f.args[0]) 49 | } 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2016, Martin Angers & Contributors 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/array.go: -------------------------------------------------------------------------------- 1 | package goquery 2 | 3 | import ( 4 | "golang.org/x/net/html" 5 | ) 6 | 7 | // First reduces the set of matched elements to the first in the set. 8 | // It returns a new Selection object, and an empty Selection object if the 9 | // the selection is empty. 10 | func (s *Selection) First() *Selection { 11 | return s.Eq(0) 12 | } 13 | 14 | // Last reduces the set of matched elements to the last in the set. 15 | // It returns a new Selection object, and an empty Selection object if 16 | // the selection is empty. 17 | func (s *Selection) Last() *Selection { 18 | return s.Eq(-1) 19 | } 20 | 21 | // Eq reduces the set of matched elements to the one at the specified index. 22 | // If a negative index is given, it counts backwards starting at the end of the 23 | // set. It returns a new Selection object, and an empty Selection object if the 24 | // index is invalid. 25 | func (s *Selection) Eq(index int) *Selection { 26 | if index < 0 { 27 | index += len(s.Nodes) 28 | } 29 | 30 | if index >= len(s.Nodes) || index < 0 { 31 | return newEmptySelection(s.document) 32 | } 33 | 34 | return s.Slice(index, index+1) 35 | } 36 | 37 | // Slice reduces the set of matched elements to a subset specified by a range 38 | // of indices. 39 | func (s *Selection) Slice(start, end int) *Selection { 40 | if start < 0 { 41 | start += len(s.Nodes) 42 | } 43 | if end < 0 { 44 | end += len(s.Nodes) 45 | } 46 | return pushStack(s, s.Nodes[start:end]) 47 | } 48 | 49 | // Get retrieves the underlying node at the specified index. 50 | // Get without parameter is not implemented, since the node array is available 51 | // on the Selection object. 52 | func (s *Selection) Get(index int) *html.Node { 53 | if index < 0 { 54 | index += len(s.Nodes) // Negative index gets from the end 55 | } 56 | return s.Nodes[index] 57 | } 58 | 59 | // Index returns the position of the first element within the Selection object 60 | // relative to its sibling elements. 61 | func (s *Selection) Index() int { 62 | if len(s.Nodes) > 0 { 63 | return newSingleSelection(s.Nodes[0], s.document).PrevAll().Length() 64 | } 65 | return -1 66 | } 67 | 68 | // IndexSelector returns the position of the first element within the 69 | // Selection object relative to the elements matched by the selector, or -1 if 70 | // not found. 71 | func (s *Selection) IndexSelector(selector string) int { 72 | if len(s.Nodes) > 0 { 73 | sel := s.document.Find(selector) 74 | return indexInSlice(sel.Nodes, s.Nodes[0]) 75 | } 76 | return -1 77 | } 78 | 79 | // IndexMatcher returns the position of the first element within the 80 | // Selection object relative to the elements matched by the matcher, or -1 if 81 | // not found. 82 | func (s *Selection) IndexMatcher(m Matcher) int { 83 | if len(s.Nodes) > 0 { 84 | sel := s.document.FindMatcher(m) 85 | return indexInSlice(sel.Nodes, s.Nodes[0]) 86 | } 87 | return -1 88 | } 89 | 90 | // IndexOfNode returns the position of the specified node within the Selection 91 | // object, or -1 if not found. 92 | func (s *Selection) IndexOfNode(node *html.Node) int { 93 | return indexInSlice(s.Nodes, node) 94 | } 95 | 96 | // IndexOfSelection returns the position of the first node in the specified 97 | // Selection object within this Selection object, or -1 if not found. 98 | func (s *Selection) IndexOfSelection(sel *Selection) int { 99 | if sel != nil && len(sel.Nodes) > 0 { 100 | return indexInSlice(s.Nodes, sel.Nodes[0]) 101 | } 102 | return -1 103 | } 104 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2016, Martin Angers & Contributors 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without modification, 5 | // are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright notice, 10 | // this list of conditions and the following disclaimer in the documentation and/or 11 | // other materials provided with the distribution. 12 | // * Neither the name of the author nor the names of its contributors may be used to 13 | // endorse or promote products derived from this software without specific prior written permission. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS 16 | // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 17 | // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 18 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 22 | // WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | /* 25 | Package goquery implements features similar to jQuery, including the chainable 26 | syntax, to manipulate and query an HTML document. 27 | 28 | It brings a syntax and a set of features similar to jQuery to the Go language. 29 | It is based on Go's net/html package and the CSS Selector library cascadia. 30 | Since the net/html parser returns nodes, and not a full-featured DOM 31 | tree, jQuery's stateful manipulation functions (like height(), css(), detach()) 32 | have been left off. 33 | 34 | Also, because the net/html parser requires UTF-8 encoding, so does goquery: it is 35 | the caller's responsibility to ensure that the source document provides UTF-8 encoded HTML. 36 | See the repository's wiki for various options on how to do this. 37 | 38 | Syntax-wise, it is as close as possible to jQuery, with the same method names when 39 | possible, and that warm and fuzzy chainable interface. jQuery being the 40 | ultra-popular library that it is, writing a similar HTML-manipulating 41 | library was better to follow its API than to start anew (in the same spirit as 42 | Go's fmt package), even though some of its methods are less than intuitive (looking 43 | at you, index()...). 44 | 45 | It is hosted on GitHub, along with additional documentation in the README.md 46 | file: https://github.com/puerkitobio/goquery 47 | 48 | Please note that because of the net/html dependency, goquery requires Go1.1+. 49 | 50 | The various methods are split into files based on the category of behavior. 51 | The three dots (...) indicate that various "overloads" are available. 52 | 53 | * array.go : array-like positional manipulation of the selection. 54 | - Eq() 55 | - First() 56 | - Get() 57 | - Index...() 58 | - Last() 59 | - Slice() 60 | 61 | * expand.go : methods that expand or augment the selection's set. 62 | - Add...() 63 | - AndSelf() 64 | - Union(), which is an alias for AddSelection() 65 | 66 | * filter.go : filtering methods, that reduce the selection's set. 67 | - End() 68 | - Filter...() 69 | - Has...() 70 | - Intersection(), which is an alias of FilterSelection() 71 | - Not...() 72 | 73 | * iteration.go : methods to loop over the selection's nodes. 74 | - Each() 75 | - EachWithBreak() 76 | - Map() 77 | 78 | * manipulation.go : methods for modifying the document 79 | - After...() 80 | - Append...() 81 | - Before...() 82 | - Clone() 83 | - Empty() 84 | - Prepend...() 85 | - Remove...() 86 | - ReplaceWith...() 87 | - Unwrap() 88 | - Wrap...() 89 | - WrapAll...() 90 | - WrapInner...() 91 | 92 | * property.go : methods that inspect and get the node's properties values. 93 | - Attr*(), RemoveAttr(), SetAttr() 94 | - AddClass(), HasClass(), RemoveClass(), ToggleClass() 95 | - Html() 96 | - Length() 97 | - Size(), which is an alias for Length() 98 | - Text() 99 | 100 | * query.go : methods that query, or reflect, a node's identity. 101 | - Contains() 102 | - Is...() 103 | 104 | * traversal.go : methods to traverse the HTML document tree. 105 | - Children...() 106 | - Contents() 107 | - Find...() 108 | - Next...() 109 | - Parent[s]...() 110 | - Prev...() 111 | - Siblings...() 112 | 113 | * type.go : definition of the types exposed by goquery. 114 | - Document 115 | - Selection 116 | - Matcher 117 | 118 | * utilities.go : definition of helper functions (and not methods on a *Selection) 119 | that are not part of jQuery, but are useful to goquery. 120 | - NodeName 121 | - OuterHtml 122 | */ 123 | package goquery 124 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/expand.go: -------------------------------------------------------------------------------- 1 | package goquery 2 | 3 | import "golang.org/x/net/html" 4 | 5 | // Add adds the selector string's matching nodes to those in the current 6 | // selection and returns a new Selection object. 7 | // The selector string is run in the context of the document of the current 8 | // Selection object. 9 | func (s *Selection) Add(selector string) *Selection { 10 | return s.AddNodes(findWithMatcher([]*html.Node{s.document.rootNode}, compileMatcher(selector))...) 11 | } 12 | 13 | // AddMatcher adds the matcher's matching nodes to those in the current 14 | // selection and returns a new Selection object. 15 | // The matcher is run in the context of the document of the current 16 | // Selection object. 17 | func (s *Selection) AddMatcher(m Matcher) *Selection { 18 | return s.AddNodes(findWithMatcher([]*html.Node{s.document.rootNode}, m)...) 19 | } 20 | 21 | // AddSelection adds the specified Selection object's nodes to those in the 22 | // current selection and returns a new Selection object. 23 | func (s *Selection) AddSelection(sel *Selection) *Selection { 24 | if sel == nil { 25 | return s.AddNodes() 26 | } 27 | return s.AddNodes(sel.Nodes...) 28 | } 29 | 30 | // Union is an alias for AddSelection. 31 | func (s *Selection) Union(sel *Selection) *Selection { 32 | return s.AddSelection(sel) 33 | } 34 | 35 | // AddNodes adds the specified nodes to those in the 36 | // current selection and returns a new Selection object. 37 | func (s *Selection) AddNodes(nodes ...*html.Node) *Selection { 38 | return pushStack(s, appendWithoutDuplicates(s.Nodes, nodes, nil)) 39 | } 40 | 41 | // AndSelf adds the previous set of elements on the stack to the current set. 42 | // It returns a new Selection object containing the current Selection combined 43 | // with the previous one. 44 | func (s *Selection) AndSelf() *Selection { 45 | return s.AddSelection(s.prevSel) 46 | } 47 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/filter.go: -------------------------------------------------------------------------------- 1 | package goquery 2 | 3 | import "golang.org/x/net/html" 4 | 5 | // Filter reduces the set of matched elements to those that match the selector string. 6 | // It returns a new Selection object for this subset of matching elements. 7 | func (s *Selection) Filter(selector string) *Selection { 8 | return s.FilterMatcher(compileMatcher(selector)) 9 | } 10 | 11 | // FilterMatcher reduces the set of matched elements to those that match 12 | // the given matcher. It returns a new Selection object for this subset 13 | // of matching elements. 14 | func (s *Selection) FilterMatcher(m Matcher) *Selection { 15 | return pushStack(s, winnow(s, m, true)) 16 | } 17 | 18 | // Not removes elements from the Selection that match the selector string. 19 | // It returns a new Selection object with the matching elements removed. 20 | func (s *Selection) Not(selector string) *Selection { 21 | return s.NotMatcher(compileMatcher(selector)) 22 | } 23 | 24 | // NotMatcher removes elements from the Selection that match the given matcher. 25 | // It returns a new Selection object with the matching elements removed. 26 | func (s *Selection) NotMatcher(m Matcher) *Selection { 27 | return pushStack(s, winnow(s, m, false)) 28 | } 29 | 30 | // FilterFunction reduces the set of matched elements to those that pass the function's test. 31 | // It returns a new Selection object for this subset of elements. 32 | func (s *Selection) FilterFunction(f func(int, *Selection) bool) *Selection { 33 | return pushStack(s, winnowFunction(s, f, true)) 34 | } 35 | 36 | // NotFunction removes elements from the Selection that pass the function's test. 37 | // It returns a new Selection object with the matching elements removed. 38 | func (s *Selection) NotFunction(f func(int, *Selection) bool) *Selection { 39 | return pushStack(s, winnowFunction(s, f, false)) 40 | } 41 | 42 | // FilterNodes reduces the set of matched elements to those that match the specified nodes. 43 | // It returns a new Selection object for this subset of elements. 44 | func (s *Selection) FilterNodes(nodes ...*html.Node) *Selection { 45 | return pushStack(s, winnowNodes(s, nodes, true)) 46 | } 47 | 48 | // NotNodes removes elements from the Selection that match the specified nodes. 49 | // It returns a new Selection object with the matching elements removed. 50 | func (s *Selection) NotNodes(nodes ...*html.Node) *Selection { 51 | return pushStack(s, winnowNodes(s, nodes, false)) 52 | } 53 | 54 | // FilterSelection reduces the set of matched elements to those that match a 55 | // node in the specified Selection object. 56 | // It returns a new Selection object for this subset of elements. 57 | func (s *Selection) FilterSelection(sel *Selection) *Selection { 58 | if sel == nil { 59 | return pushStack(s, winnowNodes(s, nil, true)) 60 | } 61 | return pushStack(s, winnowNodes(s, sel.Nodes, true)) 62 | } 63 | 64 | // NotSelection removes elements from the Selection that match a node in the specified 65 | // Selection object. It returns a new Selection object with the matching elements removed. 66 | func (s *Selection) NotSelection(sel *Selection) *Selection { 67 | if sel == nil { 68 | return pushStack(s, winnowNodes(s, nil, false)) 69 | } 70 | return pushStack(s, winnowNodes(s, sel.Nodes, false)) 71 | } 72 | 73 | // Intersection is an alias for FilterSelection. 74 | func (s *Selection) Intersection(sel *Selection) *Selection { 75 | return s.FilterSelection(sel) 76 | } 77 | 78 | // Has reduces the set of matched elements to those that have a descendant 79 | // that matches the selector. 80 | // It returns a new Selection object with the matching elements. 81 | func (s *Selection) Has(selector string) *Selection { 82 | return s.HasSelection(s.document.Find(selector)) 83 | } 84 | 85 | // HasMatcher reduces the set of matched elements to those that have a descendant 86 | // that matches the matcher. 87 | // It returns a new Selection object with the matching elements. 88 | func (s *Selection) HasMatcher(m Matcher) *Selection { 89 | return s.HasSelection(s.document.FindMatcher(m)) 90 | } 91 | 92 | // HasNodes reduces the set of matched elements to those that have a 93 | // descendant that matches one of the nodes. 94 | // It returns a new Selection object with the matching elements. 95 | func (s *Selection) HasNodes(nodes ...*html.Node) *Selection { 96 | return s.FilterFunction(func(_ int, sel *Selection) bool { 97 | // Add all nodes that contain one of the specified nodes 98 | for _, n := range nodes { 99 | if sel.Contains(n) { 100 | return true 101 | } 102 | } 103 | return false 104 | }) 105 | } 106 | 107 | // HasSelection reduces the set of matched elements to those that have a 108 | // descendant that matches one of the nodes of the specified Selection object. 109 | // It returns a new Selection object with the matching elements. 110 | func (s *Selection) HasSelection(sel *Selection) *Selection { 111 | if sel == nil { 112 | return s.HasNodes() 113 | } 114 | return s.HasNodes(sel.Nodes...) 115 | } 116 | 117 | // End ends the most recent filtering operation in the current chain and 118 | // returns the set of matched elements to its previous state. 119 | func (s *Selection) End() *Selection { 120 | if s.prevSel != nil { 121 | return s.prevSel 122 | } 123 | return newEmptySelection(s.document) 124 | } 125 | 126 | // Filter based on the matcher, and the indicator to keep (Filter) or 127 | // to get rid of (Not) the matching elements. 128 | func winnow(sel *Selection, m Matcher, keep bool) []*html.Node { 129 | // Optimize if keep is requested 130 | if keep { 131 | return m.Filter(sel.Nodes) 132 | } 133 | // Use grep 134 | return grep(sel, func(i int, s *Selection) bool { 135 | return !m.Match(s.Get(0)) 136 | }) 137 | } 138 | 139 | // Filter based on an array of nodes, and the indicator to keep (Filter) or 140 | // to get rid of (Not) the matching elements. 141 | func winnowNodes(sel *Selection, nodes []*html.Node, keep bool) []*html.Node { 142 | if len(nodes)+len(sel.Nodes) < minNodesForSet { 143 | return grep(sel, func(i int, s *Selection) bool { 144 | return isInSlice(nodes, s.Get(0)) == keep 145 | }) 146 | } 147 | 148 | set := make(map[*html.Node]bool) 149 | for _, n := range nodes { 150 | set[n] = true 151 | } 152 | return grep(sel, func(i int, s *Selection) bool { 153 | return set[s.Get(0)] == keep 154 | }) 155 | } 156 | 157 | // Filter based on a function test, and the indicator to keep (Filter) or 158 | // to get rid of (Not) the matching elements. 159 | func winnowFunction(sel *Selection, f func(int, *Selection) bool, keep bool) []*html.Node { 160 | return grep(sel, func(i int, s *Selection) bool { 161 | return f(i, s) == keep 162 | }) 163 | } 164 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/iteration.go: -------------------------------------------------------------------------------- 1 | package goquery 2 | 3 | // Each iterates over a Selection object, executing a function for each 4 | // matched element. It returns the current Selection object. The function 5 | // f is called for each element in the selection with the index of the 6 | // element in that selection starting at 0, and a *Selection that contains 7 | // only that element. 8 | func (s *Selection) Each(f func(int, *Selection)) *Selection { 9 | for i, n := range s.Nodes { 10 | f(i, newSingleSelection(n, s.document)) 11 | } 12 | return s 13 | } 14 | 15 | // EachWithBreak iterates over a Selection object, executing a function for each 16 | // matched element. It is identical to Each except that it is possible to break 17 | // out of the loop by returning false in the callback function. It returns the 18 | // current Selection object. 19 | func (s *Selection) EachWithBreak(f func(int, *Selection) bool) *Selection { 20 | for i, n := range s.Nodes { 21 | if !f(i, newSingleSelection(n, s.document)) { 22 | return s 23 | } 24 | } 25 | return s 26 | } 27 | 28 | // Map passes each element in the current matched set through a function, 29 | // producing a slice of string holding the returned values. The function 30 | // f is called for each element in the selection with the index of the 31 | // element in that selection starting at 0, and a *Selection that contains 32 | // only that element. 33 | func (s *Selection) Map(f func(int, *Selection) string) (result []string) { 34 | for i, n := range s.Nodes { 35 | result = append(result, f(i, newSingleSelection(n, s.document))) 36 | } 37 | 38 | return result 39 | } 40 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/property.go: -------------------------------------------------------------------------------- 1 | package goquery 2 | 3 | import ( 4 | "bytes" 5 | "regexp" 6 | "strings" 7 | 8 | "golang.org/x/net/html" 9 | ) 10 | 11 | var rxClassTrim = regexp.MustCompile("[\t\r\n]") 12 | 13 | // Attr gets the specified attribute's value for the first element in the 14 | // Selection. To get the value for each element individually, use a looping 15 | // construct such as Each or Map method. 16 | func (s *Selection) Attr(attrName string) (val string, exists bool) { 17 | if len(s.Nodes) == 0 { 18 | return 19 | } 20 | return getAttributeValue(attrName, s.Nodes[0]) 21 | } 22 | 23 | // AttrOr works like Attr but returns default value if attribute is not present. 24 | func (s *Selection) AttrOr(attrName, defaultValue string) string { 25 | if len(s.Nodes) == 0 { 26 | return defaultValue 27 | } 28 | 29 | val, exists := getAttributeValue(attrName, s.Nodes[0]) 30 | if !exists { 31 | return defaultValue 32 | } 33 | 34 | return val 35 | } 36 | 37 | // RemoveAttr removes the named attribute from each element in the set of matched elements. 38 | func (s *Selection) RemoveAttr(attrName string) *Selection { 39 | for _, n := range s.Nodes { 40 | removeAttr(n, attrName) 41 | } 42 | 43 | return s 44 | } 45 | 46 | // SetAttr sets the given attribute on each element in the set of matched elements. 47 | func (s *Selection) SetAttr(attrName, val string) *Selection { 48 | for _, n := range s.Nodes { 49 | attr := getAttributePtr(attrName, n) 50 | if attr == nil { 51 | n.Attr = append(n.Attr, html.Attribute{Key: attrName, Val: val}) 52 | } else { 53 | attr.Val = val 54 | } 55 | } 56 | 57 | return s 58 | } 59 | 60 | // Text gets the combined text contents of each element in the set of matched 61 | // elements, including their descendants. 62 | func (s *Selection) Text() string { 63 | var buf bytes.Buffer 64 | 65 | // Slightly optimized vs calling Each: no single selection object created 66 | var f func(*html.Node) 67 | f = func(n *html.Node) { 68 | if n.Type == html.TextNode { 69 | // Keep newlines and spaces, like jQuery 70 | buf.WriteString(n.Data) 71 | } 72 | if n.FirstChild != nil { 73 | for c := n.FirstChild; c != nil; c = c.NextSibling { 74 | f(c) 75 | } 76 | } 77 | } 78 | for _, n := range s.Nodes { 79 | f(n) 80 | } 81 | 82 | return buf.String() 83 | } 84 | 85 | // Size is an alias for Length. 86 | func (s *Selection) Size() int { 87 | return s.Length() 88 | } 89 | 90 | // Length returns the number of elements in the Selection object. 91 | func (s *Selection) Length() int { 92 | return len(s.Nodes) 93 | } 94 | 95 | // Html gets the HTML contents of the first element in the set of matched 96 | // elements. It includes text and comment nodes. 97 | func (s *Selection) Html() (ret string, e error) { 98 | // Since there is no .innerHtml, the HTML content must be re-created from 99 | // the nodes using html.Render. 100 | var buf bytes.Buffer 101 | 102 | if len(s.Nodes) > 0 { 103 | for c := s.Nodes[0].FirstChild; c != nil; c = c.NextSibling { 104 | e = html.Render(&buf, c) 105 | if e != nil { 106 | return 107 | } 108 | } 109 | ret = buf.String() 110 | } 111 | 112 | return 113 | } 114 | 115 | // AddClass adds the given class(es) to each element in the set of matched elements. 116 | // Multiple class names can be specified, separated by a space or via multiple arguments. 117 | func (s *Selection) AddClass(class ...string) *Selection { 118 | classStr := strings.TrimSpace(strings.Join(class, " ")) 119 | 120 | if classStr == "" { 121 | return s 122 | } 123 | 124 | tcls := getClassesSlice(classStr) 125 | for _, n := range s.Nodes { 126 | curClasses, attr := getClassesAndAttr(n, true) 127 | for _, newClass := range tcls { 128 | if !strings.Contains(curClasses, " "+newClass+" ") { 129 | curClasses += newClass + " " 130 | } 131 | } 132 | 133 | setClasses(n, attr, curClasses) 134 | } 135 | 136 | return s 137 | } 138 | 139 | // HasClass determines whether any of the matched elements are assigned the 140 | // given class. 141 | func (s *Selection) HasClass(class string) bool { 142 | class = " " + class + " " 143 | for _, n := range s.Nodes { 144 | classes, _ := getClassesAndAttr(n, false) 145 | if strings.Contains(classes, class) { 146 | return true 147 | } 148 | } 149 | return false 150 | } 151 | 152 | // RemoveClass removes the given class(es) from each element in the set of matched elements. 153 | // Multiple class names can be specified, separated by a space or via multiple arguments. 154 | // If no class name is provided, all classes are removed. 155 | func (s *Selection) RemoveClass(class ...string) *Selection { 156 | var rclasses []string 157 | 158 | classStr := strings.TrimSpace(strings.Join(class, " ")) 159 | remove := classStr == "" 160 | 161 | if !remove { 162 | rclasses = getClassesSlice(classStr) 163 | } 164 | 165 | for _, n := range s.Nodes { 166 | if remove { 167 | removeAttr(n, "class") 168 | } else { 169 | classes, attr := getClassesAndAttr(n, true) 170 | for _, rcl := range rclasses { 171 | classes = strings.Replace(classes, " "+rcl+" ", " ", -1) 172 | } 173 | 174 | setClasses(n, attr, classes) 175 | } 176 | } 177 | 178 | return s 179 | } 180 | 181 | // ToggleClass adds or removes the given class(es) for each element in the set of matched elements. 182 | // Multiple class names can be specified, separated by a space or via multiple arguments. 183 | func (s *Selection) ToggleClass(class ...string) *Selection { 184 | classStr := strings.TrimSpace(strings.Join(class, " ")) 185 | 186 | if classStr == "" { 187 | return s 188 | } 189 | 190 | tcls := getClassesSlice(classStr) 191 | 192 | for _, n := range s.Nodes { 193 | classes, attr := getClassesAndAttr(n, true) 194 | for _, tcl := range tcls { 195 | if strings.Contains(classes, " "+tcl+" ") { 196 | classes = strings.Replace(classes, " "+tcl+" ", " ", -1) 197 | } else { 198 | classes += tcl + " " 199 | } 200 | } 201 | 202 | setClasses(n, attr, classes) 203 | } 204 | 205 | return s 206 | } 207 | 208 | func getAttributePtr(attrName string, n *html.Node) *html.Attribute { 209 | if n == nil { 210 | return nil 211 | } 212 | 213 | for i, a := range n.Attr { 214 | if a.Key == attrName { 215 | return &n.Attr[i] 216 | } 217 | } 218 | return nil 219 | } 220 | 221 | // Private function to get the specified attribute's value from a node. 222 | func getAttributeValue(attrName string, n *html.Node) (val string, exists bool) { 223 | if a := getAttributePtr(attrName, n); a != nil { 224 | val = a.Val 225 | exists = true 226 | } 227 | return 228 | } 229 | 230 | // Get and normalize the "class" attribute from the node. 231 | func getClassesAndAttr(n *html.Node, create bool) (classes string, attr *html.Attribute) { 232 | // Applies only to element nodes 233 | if n.Type == html.ElementNode { 234 | attr = getAttributePtr("class", n) 235 | if attr == nil && create { 236 | n.Attr = append(n.Attr, html.Attribute{ 237 | Key: "class", 238 | Val: "", 239 | }) 240 | attr = &n.Attr[len(n.Attr)-1] 241 | } 242 | } 243 | 244 | if attr == nil { 245 | classes = " " 246 | } else { 247 | classes = rxClassTrim.ReplaceAllString(" "+attr.Val+" ", " ") 248 | } 249 | 250 | return 251 | } 252 | 253 | func getClassesSlice(classes string) []string { 254 | return strings.Split(rxClassTrim.ReplaceAllString(" "+classes+" ", " "), " ") 255 | } 256 | 257 | func removeAttr(n *html.Node, attrName string) { 258 | for i, a := range n.Attr { 259 | if a.Key == attrName { 260 | n.Attr[i], n.Attr[len(n.Attr)-1], n.Attr = 261 | n.Attr[len(n.Attr)-1], html.Attribute{}, n.Attr[:len(n.Attr)-1] 262 | return 263 | } 264 | } 265 | } 266 | 267 | func setClasses(n *html.Node, attr *html.Attribute, classes string) { 268 | classes = strings.TrimSpace(classes) 269 | if classes == "" { 270 | removeAttr(n, "class") 271 | return 272 | } 273 | 274 | attr.Val = classes 275 | } 276 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/query.go: -------------------------------------------------------------------------------- 1 | package goquery 2 | 3 | import "golang.org/x/net/html" 4 | 5 | // Is checks the current matched set of elements against a selector and 6 | // returns true if at least one of these elements matches. 7 | func (s *Selection) Is(selector string) bool { 8 | if len(s.Nodes) > 0 { 9 | return s.IsMatcher(compileMatcher(selector)) 10 | } 11 | 12 | return false 13 | } 14 | 15 | // IsMatcher checks the current matched set of elements against a matcher and 16 | // returns true if at least one of these elements matches. 17 | func (s *Selection) IsMatcher(m Matcher) bool { 18 | if len(s.Nodes) > 0 { 19 | if len(s.Nodes) == 1 { 20 | return m.Match(s.Nodes[0]) 21 | } 22 | return len(m.Filter(s.Nodes)) > 0 23 | } 24 | 25 | return false 26 | } 27 | 28 | // IsFunction checks the current matched set of elements against a predicate and 29 | // returns true if at least one of these elements matches. 30 | func (s *Selection) IsFunction(f func(int, *Selection) bool) bool { 31 | return s.FilterFunction(f).Length() > 0 32 | } 33 | 34 | // IsSelection checks the current matched set of elements against a Selection object 35 | // and returns true if at least one of these elements matches. 36 | func (s *Selection) IsSelection(sel *Selection) bool { 37 | return s.FilterSelection(sel).Length() > 0 38 | } 39 | 40 | // IsNodes checks the current matched set of elements against the specified nodes 41 | // and returns true if at least one of these elements matches. 42 | func (s *Selection) IsNodes(nodes ...*html.Node) bool { 43 | return s.FilterNodes(nodes...).Length() > 0 44 | } 45 | 46 | // Contains returns true if the specified Node is within, 47 | // at any depth, one of the nodes in the Selection object. 48 | // It is NOT inclusive, to behave like jQuery's implementation, and 49 | // unlike Javascript's .contains, so if the contained 50 | // node is itself in the selection, it returns false. 51 | func (s *Selection) Contains(n *html.Node) bool { 52 | return sliceContains(s.Nodes, n) 53 | } 54 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/type.go: -------------------------------------------------------------------------------- 1 | package goquery 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "net/http" 7 | "net/url" 8 | 9 | "github.com/andybalholm/cascadia" 10 | 11 | "golang.org/x/net/html" 12 | ) 13 | 14 | // Document represents an HTML document to be manipulated. Unlike jQuery, which 15 | // is loaded as part of a DOM document, and thus acts upon its containing 16 | // document, GoQuery doesn't know which HTML document to act upon. So it needs 17 | // to be told, and that's what the Document class is for. It holds the root 18 | // document node to manipulate, and can make selections on this document. 19 | type Document struct { 20 | *Selection 21 | Url *url.URL 22 | rootNode *html.Node 23 | } 24 | 25 | // NewDocumentFromNode is a Document constructor that takes a root html Node 26 | // as argument. 27 | func NewDocumentFromNode(root *html.Node) *Document { 28 | return newDocument(root, nil) 29 | } 30 | 31 | // NewDocument is a Document constructor that takes a string URL as argument. 32 | // It loads the specified document, parses it, and stores the root Document 33 | // node, ready to be manipulated. 34 | func NewDocument(url string) (*Document, error) { 35 | // Load the URL 36 | res, e := http.Get(url) 37 | if e != nil { 38 | return nil, e 39 | } 40 | return NewDocumentFromResponse(res) 41 | } 42 | 43 | // NewDocumentFromReader returns a Document from a generic reader. 44 | // It returns an error as second value if the reader's data cannot be parsed 45 | // as html. It does *not* check if the reader is also an io.Closer, so the 46 | // provided reader is never closed by this call, it is the responsibility 47 | // of the caller to close it if required. 48 | func NewDocumentFromReader(r io.Reader) (*Document, error) { 49 | root, e := html.Parse(r) 50 | if e != nil { 51 | return nil, e 52 | } 53 | return newDocument(root, nil), nil 54 | } 55 | 56 | // NewDocumentFromResponse is another Document constructor that takes an http response as argument. 57 | // It loads the specified response's document, parses it, and stores the root Document 58 | // node, ready to be manipulated. The response's body is closed on return. 59 | func NewDocumentFromResponse(res *http.Response) (*Document, error) { 60 | if res == nil { 61 | return nil, errors.New("Response is nil") 62 | } 63 | defer res.Body.Close() 64 | if res.Request == nil { 65 | return nil, errors.New("Response.Request is nil") 66 | } 67 | 68 | // Parse the HTML into nodes 69 | root, e := html.Parse(res.Body) 70 | if e != nil { 71 | return nil, e 72 | } 73 | 74 | // Create and fill the document 75 | return newDocument(root, res.Request.URL), nil 76 | } 77 | 78 | // CloneDocument creates a deep-clone of a document. 79 | func CloneDocument(doc *Document) *Document { 80 | return newDocument(cloneNode(doc.rootNode), doc.Url) 81 | } 82 | 83 | // Private constructor, make sure all fields are correctly filled. 84 | func newDocument(root *html.Node, url *url.URL) *Document { 85 | // Create and fill the document 86 | d := &Document{nil, url, root} 87 | d.Selection = newSingleSelection(root, d) 88 | return d 89 | } 90 | 91 | // Selection represents a collection of nodes matching some criteria. The 92 | // initial Selection can be created by using Document.Find, and then 93 | // manipulated using the jQuery-like chainable syntax and methods. 94 | type Selection struct { 95 | Nodes []*html.Node 96 | document *Document 97 | prevSel *Selection 98 | } 99 | 100 | // Helper constructor to create an empty selection 101 | func newEmptySelection(doc *Document) *Selection { 102 | return &Selection{nil, doc, nil} 103 | } 104 | 105 | // Helper constructor to create a selection of only one node 106 | func newSingleSelection(node *html.Node, doc *Document) *Selection { 107 | return &Selection{[]*html.Node{node}, doc, nil} 108 | } 109 | 110 | // Matcher is an interface that defines the methods to match 111 | // HTML nodes against a compiled selector string. Cascadia's 112 | // Selector implements this interface. 113 | type Matcher interface { 114 | Match(*html.Node) bool 115 | MatchAll(*html.Node) []*html.Node 116 | Filter([]*html.Node) []*html.Node 117 | } 118 | 119 | // compileMatcher compiles the selector string s and returns 120 | // the corresponding Matcher. If s is an invalid selector string, 121 | // it returns a Matcher that fails all matches. 122 | func compileMatcher(s string) Matcher { 123 | cs, err := cascadia.Compile(s) 124 | if err != nil { 125 | return invalidMatcher{} 126 | } 127 | return cs 128 | } 129 | 130 | // invalidMatcher is a Matcher that always fails to match. 131 | type invalidMatcher struct{} 132 | 133 | func (invalidMatcher) Match(n *html.Node) bool { return false } 134 | func (invalidMatcher) MatchAll(n *html.Node) []*html.Node { return nil } 135 | func (invalidMatcher) Filter(ns []*html.Node) []*html.Node { return nil } 136 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/utilities.go: -------------------------------------------------------------------------------- 1 | package goquery 2 | 3 | import ( 4 | "bytes" 5 | 6 | "golang.org/x/net/html" 7 | ) 8 | 9 | // used to determine if a set (map[*html.Node]bool) should be used 10 | // instead of iterating over a slice. The set uses more memory and 11 | // is slower than slice iteration for small N. 12 | const minNodesForSet = 1000 13 | 14 | var nodeNames = []string{ 15 | html.ErrorNode: "#error", 16 | html.TextNode: "#text", 17 | html.DocumentNode: "#document", 18 | html.CommentNode: "#comment", 19 | } 20 | 21 | // NodeName returns the node name of the first element in the selection. 22 | // It tries to behave in a similar way as the DOM's nodeName property 23 | // (https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeName). 24 | // 25 | // Go's net/html package defines the following node types, listed with 26 | // the corresponding returned value from this function: 27 | // 28 | // ErrorNode : #error 29 | // TextNode : #text 30 | // DocumentNode : #document 31 | // ElementNode : the element's tag name 32 | // CommentNode : #comment 33 | // DoctypeNode : the name of the document type 34 | // 35 | func NodeName(s *Selection) string { 36 | if s.Length() == 0 { 37 | return "" 38 | } 39 | switch n := s.Get(0); n.Type { 40 | case html.ElementNode, html.DoctypeNode: 41 | return n.Data 42 | default: 43 | if n.Type >= 0 && int(n.Type) < len(nodeNames) { 44 | return nodeNames[n.Type] 45 | } 46 | return "" 47 | } 48 | } 49 | 50 | // OuterHtml returns the outer HTML rendering of the first item in 51 | // the selection - that is, the HTML including the first element's 52 | // tag and attributes. 53 | // 54 | // Unlike InnerHtml, this is a function and not a method on the Selection, 55 | // because this is not a jQuery method (in javascript-land, this is 56 | // a property provided by the DOM). 57 | func OuterHtml(s *Selection) (string, error) { 58 | var buf bytes.Buffer 59 | 60 | if s.Length() == 0 { 61 | return "", nil 62 | } 63 | n := s.Get(0) 64 | if err := html.Render(&buf, n); err != nil { 65 | return "", err 66 | } 67 | return buf.String(), nil 68 | } 69 | 70 | // Loop through all container nodes to search for the target node. 71 | func sliceContains(container []*html.Node, contained *html.Node) bool { 72 | for _, n := range container { 73 | if nodeContains(n, contained) { 74 | return true 75 | } 76 | } 77 | 78 | return false 79 | } 80 | 81 | // Checks if the contained node is within the container node. 82 | func nodeContains(container *html.Node, contained *html.Node) bool { 83 | // Check if the parent of the contained node is the container node, traversing 84 | // upward until the top is reached, or the container is found. 85 | for contained = contained.Parent; contained != nil; contained = contained.Parent { 86 | if container == contained { 87 | return true 88 | } 89 | } 90 | return false 91 | } 92 | 93 | // Checks if the target node is in the slice of nodes. 94 | func isInSlice(slice []*html.Node, node *html.Node) bool { 95 | return indexInSlice(slice, node) > -1 96 | } 97 | 98 | // Returns the index of the target node in the slice, or -1. 99 | func indexInSlice(slice []*html.Node, node *html.Node) int { 100 | if node != nil { 101 | for i, n := range slice { 102 | if n == node { 103 | return i 104 | } 105 | } 106 | } 107 | return -1 108 | } 109 | 110 | // Appends the new nodes to the target slice, making sure no duplicate is added. 111 | // There is no check to the original state of the target slice, so it may still 112 | // contain duplicates. The target slice is returned because append() may create 113 | // a new underlying array. If targetSet is nil, a local set is created with the 114 | // target if len(target) + len(nodes) is greater than minNodesForSet. 115 | func appendWithoutDuplicates(target []*html.Node, nodes []*html.Node, targetSet map[*html.Node]bool) []*html.Node { 116 | // if there are not that many nodes, don't use the map, faster to just use nested loops 117 | // (unless a non-nil targetSet is passed, in which case the caller knows better). 118 | if targetSet == nil && len(target)+len(nodes) < minNodesForSet { 119 | for _, n := range nodes { 120 | if !isInSlice(target, n) { 121 | target = append(target, n) 122 | } 123 | } 124 | return target 125 | } 126 | 127 | // if a targetSet is passed, then assume it is reliable, otherwise create one 128 | // and initialize it with the current target contents. 129 | if targetSet == nil { 130 | targetSet = make(map[*html.Node]bool, len(target)) 131 | for _, n := range target { 132 | targetSet[n] = true 133 | } 134 | } 135 | for _, n := range nodes { 136 | if !targetSet[n] { 137 | target = append(target, n) 138 | targetSet[n] = true 139 | } 140 | } 141 | 142 | return target 143 | } 144 | 145 | // Loop through a selection, returning only those nodes that pass the predicate 146 | // function. 147 | func grep(sel *Selection, predicate func(i int, s *Selection) bool) (result []*html.Node) { 148 | for i, n := range sel.Nodes { 149 | if predicate(i, newSingleSelection(n, sel.document)) { 150 | result = append(result, n) 151 | } 152 | } 153 | return result 154 | } 155 | 156 | // Creates a new Selection object based on the specified nodes, and keeps the 157 | // source Selection object on the stack (linked list). 158 | func pushStack(fromSel *Selection, nodes []*html.Node) *Selection { 159 | result := &Selection{nodes, fromSel.document, fromSel} 160 | return result 161 | } 162 | -------------------------------------------------------------------------------- /vendor/github.com/andybalholm/cascadia/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Andy Balholm. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 17 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 18 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 20 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /vendor/github.com/andybalholm/cascadia/README.md: -------------------------------------------------------------------------------- 1 | # cascadia 2 | 3 | [![](https://travis-ci.org/andybalholm/cascadia.svg)](https://travis-ci.org/andybalholm/cascadia) 4 | 5 | The Cascadia package implements CSS selectors for use with the parse trees produced by the html package. 6 | 7 | To test CSS selectors without writing Go code, check out [cascadia](https://github.com/suntong/cascadia) the command line tool, a thin wrapper around this package. 8 | -------------------------------------------------------------------------------- /vendor/github.com/jroimartin/gocui/AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of gocui authors for copyright purposes. 2 | 3 | # Names should be added to this file as 4 | # Name or Organization contribution 5 | # Contribution 6 | # The email address is not required for organizations. 7 | 8 | Roi Martin 9 | Main developer 10 | 11 | Ryan Sullivan 12 | Toggleable view frames 13 | 14 | Matthieu Rakotojaona 15 | Wrapped views 16 | 17 | Harry Lawrence 18 | Basic mouse support 19 | 20 | Danny Tylman 21 | Masked views 22 | 23 | Frederik Deweerdt 24 | Colored fonts 25 | 26 | Henri Koski 27 | Custom current view color 28 | 29 | Dustin Willis Webber 30 | 256-colors output mode support 31 | -------------------------------------------------------------------------------- /vendor/github.com/jroimartin/gocui/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 The gocui Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | * Redistributions of source code must retain the above copyright 6 | notice, this list of conditions and the following disclaimer. 7 | * Redistributions in binary form must reproduce the above copyright 8 | notice, this list of conditions and the following disclaimer in the 9 | documentation and/or other materials provided with the distribution. 10 | * Neither the name of the gocui Authors nor the names of its contributors 11 | may be used to endorse or promote products derived from this software 12 | without specific prior written permission. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /vendor/github.com/jroimartin/gocui/README.md: -------------------------------------------------------------------------------- 1 | # GOCUI - Go Console User Interface 2 | 3 | [![GoDoc](https://godoc.org/github.com/jroimartin/gocui?status.svg)](https://godoc.org/github.com/jroimartin/gocui) 4 | 5 | Minimalist Go package aimed at creating Console User Interfaces. 6 | 7 | ## Features 8 | 9 | * Minimalist API. 10 | * Views (the "windows" in the GUI) implement the interface io.ReadWriter. 11 | * Support for overlapping views. 12 | * The GUI can be modified at runtime (concurrent-safe). 13 | * Global and view-level keybindings. 14 | * Mouse support. 15 | * Colored text. 16 | * Customizable edition mode. 17 | * Easy to build reusable widgets, complex layouts... 18 | 19 | ## Installation 20 | 21 | Execute: 22 | 23 | ``` 24 | $ go get github.com/jroimartin/gocui 25 | ``` 26 | 27 | ## Documentation 28 | 29 | Execute: 30 | 31 | ``` 32 | $ go doc github.com/jroimartin/gocui 33 | ``` 34 | 35 | Or visit [godoc.org](https://godoc.org/github.com/jroimartin/gocui) to read it 36 | online. 37 | 38 | ## Example 39 | 40 | ```go 41 | package main 42 | 43 | import ( 44 | "fmt" 45 | "log" 46 | 47 | "github.com/jroimartin/gocui" 48 | ) 49 | 50 | func main() { 51 | g, err := gocui.NewGui(gocui.OutputNormal) 52 | if err != nil { 53 | log.Panicln(err) 54 | } 55 | defer g.Close() 56 | 57 | g.SetManagerFunc(layout) 58 | 59 | if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { 60 | log.Panicln(err) 61 | } 62 | 63 | if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { 64 | log.Panicln(err) 65 | } 66 | } 67 | 68 | func layout(g *gocui.Gui) error { 69 | maxX, maxY := g.Size() 70 | if v, err := g.SetView("hello", maxX/2-7, maxY/2, maxX/2+7, maxY/2+2); err != nil { 71 | if err != gocui.ErrUnknownView { 72 | return err 73 | } 74 | fmt.Fprintln(v, "Hello world!") 75 | } 76 | return nil 77 | } 78 | 79 | func quit(g *gocui.Gui, v *gocui.View) error { 80 | return gocui.ErrQuit 81 | } 82 | ``` 83 | 84 | ## Screenshots 85 | 86 | ![r2cui](https://cloud.githubusercontent.com/assets/1223476/19418932/63645052-93ce-11e6-867c-da5e97e37237.png) 87 | 88 | ![_examples/demo.go](https://cloud.githubusercontent.com/assets/1223476/5992750/720b84f0-aa36-11e4-88ec-296fa3247b52.png) 89 | 90 | ![_examples/dynamic.go](https://cloud.githubusercontent.com/assets/1223476/5992751/76ad5cc2-aa36-11e4-8204-6a90269db827.png) 91 | 92 | ## Projects using gocui 93 | 94 | * [Komanda CLI](https://github.com/mephux/komanda-cli): IRC Client For Developers. 95 | * [Vuls](https://github.com/future-architect/vuls): Agentless vulnerability scanner for Linux/FreeBSD. 96 | * [SumoLogic sumoshell](https://github.com/SumoLogic/sumoshell): Terminal-only version of Sumo. 97 | 98 | Note: if your project is not listed here, let us know! :) 99 | -------------------------------------------------------------------------------- /vendor/github.com/jroimartin/gocui/attribute.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The gocui Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gocui 6 | 7 | import "github.com/nsf/termbox-go" 8 | 9 | // Attribute represents a terminal attribute, like color, font style, etc. They 10 | // can be combined using bitwise OR (|). Note that it is not possible to 11 | // combine multiple color attributes. 12 | type Attribute termbox.Attribute 13 | 14 | // Color attributes. 15 | const ( 16 | ColorDefault Attribute = Attribute(termbox.ColorDefault) 17 | ColorBlack = Attribute(termbox.ColorBlack) 18 | ColorRed = Attribute(termbox.ColorRed) 19 | ColorGreen = Attribute(termbox.ColorGreen) 20 | ColorYellow = Attribute(termbox.ColorYellow) 21 | ColorBlue = Attribute(termbox.ColorBlue) 22 | ColorMagenta = Attribute(termbox.ColorMagenta) 23 | ColorCyan = Attribute(termbox.ColorCyan) 24 | ColorWhite = Attribute(termbox.ColorWhite) 25 | ) 26 | 27 | // Text style attributes. 28 | const ( 29 | AttrBold Attribute = Attribute(termbox.AttrBold) 30 | AttrUnderline = Attribute(termbox.AttrUnderline) 31 | AttrReverse = Attribute(termbox.AttrReverse) 32 | ) 33 | -------------------------------------------------------------------------------- /vendor/github.com/jroimartin/gocui/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The gocui Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package gocui allows to create console user interfaces. 7 | 8 | Create a new GUI: 9 | 10 | g, err := gocui.NewGui(gocui.OutputNormal) 11 | if err != nil { 12 | // handle error 13 | } 14 | defer g.Close() 15 | 16 | // Set GUI managers and key bindings 17 | // ... 18 | 19 | if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { 20 | // handle error 21 | } 22 | 23 | Set GUI managers: 24 | 25 | g.SetManager(mgr1, mgr2) 26 | 27 | Managers are in charge of GUI's layout and can be used to build widgets. On 28 | each iteration of the GUI's main loop, the Layout function of each configured 29 | manager is executed. Managers are used to set-up and update the application's 30 | main views, being possible to freely change them during execution. Also, it is 31 | important to mention that a main loop iteration is executed on each reported 32 | event (key-press, mouse event, window resize, etc). 33 | 34 | GUIs are composed by Views, you can think of it as buffers. Views implement the 35 | io.ReadWriter interface, so you can just write to them if you want to modify 36 | their content. The same is valid for reading. 37 | 38 | Create and initialize a view with absolute coordinates: 39 | 40 | if v, err := g.SetView("viewname", 2, 2, 22, 7); err != nil { 41 | if err != gocui.ErrUnknownView { 42 | // handle error 43 | } 44 | fmt.Fprintln(v, "This is a new view") 45 | // ... 46 | } 47 | 48 | Views can also be created using relative coordinates: 49 | 50 | maxX, maxY := g.Size() 51 | if v, err := g.SetView("viewname", maxX/2-30, maxY/2, maxX/2+30, maxY/2+2); err != nil { 52 | // ... 53 | } 54 | 55 | Configure keybindings: 56 | 57 | if err := g.SetKeybinding("viewname", gocui.KeyEnter, gocui.ModNone, fcn); err != nil { 58 | // handle error 59 | } 60 | 61 | gocui implements full mouse support that can be enabled with: 62 | 63 | g.Mouse = true 64 | 65 | Mouse events are handled like any other keybinding: 66 | 67 | if err := g.SetKeybinding("viewname", gocui.MouseLeft, gocui.ModNone, fcn); err != nil { 68 | // handle error 69 | } 70 | 71 | IMPORTANT: Views can only be created, destroyed or updated in three ways: from 72 | the Layout function within managers, from keybinding callbacks or via 73 | *Gui.Execute(). The reason for this is that it allows gocui to be 74 | concurrent-safe. So, if you want to update your GUI from a goroutine, you must 75 | use *Gui.Execute(). For example: 76 | 77 | g.Execute(func(g *gocui.Gui) error { 78 | v, err := g.View("viewname") 79 | if err != nil { 80 | // handle error 81 | } 82 | v.Clear() 83 | fmt.Fprintln(v, "Writing from different goroutines") 84 | return nil 85 | }) 86 | 87 | By default, gocui provides a basic edition mode. This mode can be extended 88 | and customized creating a new Editor and assigning it to *View.Editor: 89 | 90 | type Editor interface { 91 | Edit(v *View, key Key, ch rune, mod Modifier) 92 | } 93 | 94 | DefaultEditor can be taken as example to create your own custom Editor: 95 | 96 | var DefaultEditor Editor = EditorFunc(simpleEditor) 97 | 98 | func simpleEditor(v *View, key Key, ch rune, mod Modifier) { 99 | switch { 100 | case ch != 0 && mod == 0: 101 | v.EditWrite(ch) 102 | case key == KeySpace: 103 | v.EditWrite(' ') 104 | case key == KeyBackspace || key == KeyBackspace2: 105 | v.EditDelete(true) 106 | // ... 107 | } 108 | } 109 | 110 | Colored text: 111 | 112 | Views allow to add colored text using ANSI colors. For example: 113 | 114 | fmt.Fprintln(v, "\x1b[0;31mHello world") 115 | 116 | For more information, see the examples in folder "_examples/". 117 | */ 118 | package gocui 119 | -------------------------------------------------------------------------------- /vendor/github.com/jroimartin/gocui/escape.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The gocui Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gocui 6 | 7 | import ( 8 | "errors" 9 | "strconv" 10 | ) 11 | 12 | type escapeInterpreter struct { 13 | state escapeState 14 | curch rune 15 | csiParam []string 16 | curFgColor, curBgColor Attribute 17 | mode OutputMode 18 | } 19 | 20 | type escapeState int 21 | 22 | const ( 23 | stateNone escapeState = iota 24 | stateEscape 25 | stateCSI 26 | stateParams 27 | ) 28 | 29 | var ( 30 | errNotCSI = errors.New("Not a CSI escape sequence") 31 | errCSINotANumber = errors.New("CSI escape sequence was expecting a number or a ;") 32 | errCSIParseError = errors.New("CSI escape sequence parsing error") 33 | errCSITooLong = errors.New("CSI escape sequence is too long") 34 | ) 35 | 36 | // runes in case of error will output the non-parsed runes as a string. 37 | func (ei *escapeInterpreter) runes() []rune { 38 | switch ei.state { 39 | case stateNone: 40 | return []rune{0x1b} 41 | case stateEscape: 42 | return []rune{0x1b, ei.curch} 43 | case stateCSI: 44 | return []rune{0x1b, '[', ei.curch} 45 | case stateParams: 46 | ret := []rune{0x1b, '['} 47 | for _, s := range ei.csiParam { 48 | ret = append(ret, []rune(s)...) 49 | ret = append(ret, ';') 50 | } 51 | return append(ret, ei.curch) 52 | } 53 | return nil 54 | } 55 | 56 | // newEscapeInterpreter returns an escapeInterpreter that will be able to parse 57 | // terminal escape sequences. 58 | func newEscapeInterpreter(mode OutputMode) *escapeInterpreter { 59 | ei := &escapeInterpreter{ 60 | state: stateNone, 61 | curFgColor: ColorDefault, 62 | curBgColor: ColorDefault, 63 | mode: mode, 64 | } 65 | return ei 66 | } 67 | 68 | // reset sets the escapeInterpreter in initial state. 69 | func (ei *escapeInterpreter) reset() { 70 | ei.state = stateNone 71 | ei.curFgColor = ColorDefault 72 | ei.curBgColor = ColorDefault 73 | ei.csiParam = nil 74 | } 75 | 76 | // parseOne parses a rune. If isEscape is true, it means that the rune is part 77 | // of an escape sequence, and as such should not be printed verbatim. Otherwise, 78 | // it's not an escape sequence. 79 | func (ei *escapeInterpreter) parseOne(ch rune) (isEscape bool, err error) { 80 | // Sanity checks to make sure we're not parsing something totally bogus. 81 | if len(ei.csiParam) > 20 { 82 | return false, errCSITooLong 83 | } 84 | if len(ei.csiParam) > 0 && len(ei.csiParam[len(ei.csiParam)-1]) > 255 { 85 | return false, errCSITooLong 86 | } 87 | ei.curch = ch 88 | switch ei.state { 89 | case stateNone: 90 | if ch == 0x1b { 91 | ei.state = stateEscape 92 | return true, nil 93 | } 94 | return false, nil 95 | case stateEscape: 96 | if ch == '[' { 97 | ei.state = stateCSI 98 | return true, nil 99 | } 100 | return false, errNotCSI 101 | case stateCSI: 102 | if ch >= '0' && ch <= '9' { 103 | ei.state = stateParams 104 | ei.csiParam = append(ei.csiParam, string(ch)) 105 | return true, nil 106 | } 107 | return false, errCSINotANumber 108 | case stateParams: 109 | switch { 110 | case ch >= '0' && ch <= '9': 111 | ei.csiParam[len(ei.csiParam)-1] += string(ch) 112 | return true, nil 113 | case ch == ';': 114 | ei.csiParam = append(ei.csiParam, "") 115 | return true, nil 116 | case ch == 'm': 117 | if len(ei.csiParam) < 1 { 118 | return false, errCSIParseError 119 | } 120 | 121 | var err error 122 | 123 | switch ei.mode { 124 | case OutputMode(OutputNormal): 125 | err = ei.outputNormal() 126 | case OutputMode(Output256): 127 | err = ei.output256() 128 | } 129 | if err != nil { 130 | return false, errCSIParseError 131 | } 132 | 133 | ei.state = stateNone 134 | ei.csiParam = nil 135 | return true, nil 136 | } 137 | } 138 | return false, nil 139 | } 140 | 141 | // outputNormal provides 8 different colors: 142 | // black, red, green, yellow, blue, magenta, cyan, white 143 | func (ei *escapeInterpreter) outputNormal() error { 144 | for _, param := range ei.csiParam { 145 | p, err := strconv.Atoi(param) 146 | if err != nil { 147 | return errCSIParseError 148 | } 149 | 150 | switch { 151 | case p >= 30 && p <= 37: 152 | ei.curFgColor = Attribute(p - 30 + 1) 153 | case p == 39: 154 | ei.curFgColor = ColorDefault 155 | case p >= 40 && p <= 47: 156 | ei.curBgColor = Attribute(p - 40 + 1) 157 | case p == 49: 158 | ei.curBgColor = ColorDefault 159 | case p == 1: 160 | ei.curFgColor |= AttrBold 161 | case p == 4: 162 | ei.curFgColor |= AttrUnderline 163 | case p == 7: 164 | ei.curFgColor |= AttrReverse 165 | case p == 0: 166 | ei.curFgColor = ColorDefault 167 | ei.curBgColor = ColorDefault 168 | } 169 | } 170 | 171 | return nil 172 | } 173 | 174 | // output256 allows you to leverage the 256-colors terminal mode: 175 | // 0x01 - 0x08: the 8 colors as in OutputNormal 176 | // 0x09 - 0x10: Color* | AttrBold 177 | // 0x11 - 0xe8: 216 different colors 178 | // 0xe9 - 0x1ff: 24 different shades of grey 179 | func (ei *escapeInterpreter) output256() error { 180 | if len(ei.csiParam) < 3 { 181 | return ei.outputNormal() 182 | } 183 | 184 | mode, err := strconv.Atoi(ei.csiParam[1]) 185 | if err != nil { 186 | return errCSIParseError 187 | } 188 | if mode != 5 { 189 | return ei.outputNormal() 190 | } 191 | 192 | fgbg, err := strconv.Atoi(ei.csiParam[0]) 193 | if err != nil { 194 | return errCSIParseError 195 | } 196 | color, err := strconv.Atoi(ei.csiParam[2]) 197 | if err != nil { 198 | return errCSIParseError 199 | } 200 | 201 | switch fgbg { 202 | case 38: 203 | ei.curFgColor = Attribute(color + 1) 204 | 205 | for _, param := range ei.csiParam[3:] { 206 | p, err := strconv.Atoi(param) 207 | if err != nil { 208 | return errCSIParseError 209 | } 210 | 211 | switch { 212 | case p == 1: 213 | ei.curFgColor |= AttrBold 214 | case p == 4: 215 | ei.curFgColor |= AttrUnderline 216 | case p == 7: 217 | ei.curFgColor |= AttrReverse 218 | } 219 | } 220 | case 48: 221 | ei.curBgColor = Attribute(color + 1) 222 | default: 223 | return errCSIParseError 224 | } 225 | 226 | return nil 227 | } 228 | -------------------------------------------------------------------------------- /vendor/github.com/jroimartin/gocui/keybinding.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The gocui Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gocui 6 | 7 | import "github.com/nsf/termbox-go" 8 | 9 | // Keybidings are used to link a given key-press event with a handler. 10 | type keybinding struct { 11 | viewName string 12 | key Key 13 | ch rune 14 | mod Modifier 15 | handler func(*Gui, *View) error 16 | } 17 | 18 | // newKeybinding returns a new Keybinding object. 19 | func newKeybinding(viewname string, key Key, ch rune, mod Modifier, handler func(*Gui, *View) error) (kb *keybinding) { 20 | kb = &keybinding{ 21 | viewName: viewname, 22 | key: key, 23 | ch: ch, 24 | mod: mod, 25 | handler: handler, 26 | } 27 | return kb 28 | } 29 | 30 | // matchKeypress returns if the keybinding matches the keypress. 31 | func (kb *keybinding) matchKeypress(key Key, ch rune, mod Modifier) bool { 32 | return kb.key == key && kb.ch == ch && kb.mod == mod 33 | } 34 | 35 | // matchView returns if the keybinding matches the current view. 36 | func (kb *keybinding) matchView(v *View) bool { 37 | if kb.viewName == "" { 38 | return true 39 | } 40 | return v != nil && kb.viewName == v.name 41 | } 42 | 43 | // Key represents special keys or keys combinations. 44 | type Key termbox.Key 45 | 46 | // Special keys. 47 | const ( 48 | KeyF1 Key = Key(termbox.KeyF1) 49 | KeyF2 = Key(termbox.KeyF2) 50 | KeyF3 = Key(termbox.KeyF3) 51 | KeyF4 = Key(termbox.KeyF4) 52 | KeyF5 = Key(termbox.KeyF5) 53 | KeyF6 = Key(termbox.KeyF6) 54 | KeyF7 = Key(termbox.KeyF7) 55 | KeyF8 = Key(termbox.KeyF8) 56 | KeyF9 = Key(termbox.KeyF9) 57 | KeyF10 = Key(termbox.KeyF10) 58 | KeyF11 = Key(termbox.KeyF11) 59 | KeyF12 = Key(termbox.KeyF12) 60 | KeyInsert = Key(termbox.KeyInsert) 61 | KeyDelete = Key(termbox.KeyDelete) 62 | KeyHome = Key(termbox.KeyHome) 63 | KeyEnd = Key(termbox.KeyEnd) 64 | KeyPgup = Key(termbox.KeyPgup) 65 | KeyPgdn = Key(termbox.KeyPgdn) 66 | KeyArrowUp = Key(termbox.KeyArrowUp) 67 | KeyArrowDown = Key(termbox.KeyArrowDown) 68 | KeyArrowLeft = Key(termbox.KeyArrowLeft) 69 | KeyArrowRight = Key(termbox.KeyArrowRight) 70 | 71 | MouseLeft = Key(termbox.MouseLeft) 72 | MouseMiddle = Key(termbox.MouseMiddle) 73 | MouseRight = Key(termbox.MouseRight) 74 | MouseRelease = Key(termbox.MouseRelease) 75 | MouseWheelUp = Key(termbox.MouseWheelUp) 76 | MouseWheelDown = Key(termbox.MouseWheelDown) 77 | ) 78 | 79 | // Keys combinations. 80 | const ( 81 | KeyCtrlTilde Key = Key(termbox.KeyCtrlTilde) 82 | KeyCtrl2 = Key(termbox.KeyCtrl2) 83 | KeyCtrlSpace = Key(termbox.KeyCtrlSpace) 84 | KeyCtrlA = Key(termbox.KeyCtrlA) 85 | KeyCtrlB = Key(termbox.KeyCtrlB) 86 | KeyCtrlC = Key(termbox.KeyCtrlC) 87 | KeyCtrlD = Key(termbox.KeyCtrlD) 88 | KeyCtrlE = Key(termbox.KeyCtrlE) 89 | KeyCtrlF = Key(termbox.KeyCtrlF) 90 | KeyCtrlG = Key(termbox.KeyCtrlG) 91 | KeyBackspace = Key(termbox.KeyBackspace) 92 | KeyCtrlH = Key(termbox.KeyCtrlH) 93 | KeyTab = Key(termbox.KeyTab) 94 | KeyCtrlI = Key(termbox.KeyCtrlI) 95 | KeyCtrlJ = Key(termbox.KeyCtrlJ) 96 | KeyCtrlK = Key(termbox.KeyCtrlK) 97 | KeyCtrlL = Key(termbox.KeyCtrlL) 98 | KeyEnter = Key(termbox.KeyEnter) 99 | KeyCtrlM = Key(termbox.KeyCtrlM) 100 | KeyCtrlN = Key(termbox.KeyCtrlN) 101 | KeyCtrlO = Key(termbox.KeyCtrlO) 102 | KeyCtrlP = Key(termbox.KeyCtrlP) 103 | KeyCtrlQ = Key(termbox.KeyCtrlQ) 104 | KeyCtrlR = Key(termbox.KeyCtrlR) 105 | KeyCtrlS = Key(termbox.KeyCtrlS) 106 | KeyCtrlT = Key(termbox.KeyCtrlT) 107 | KeyCtrlU = Key(termbox.KeyCtrlU) 108 | KeyCtrlV = Key(termbox.KeyCtrlV) 109 | KeyCtrlW = Key(termbox.KeyCtrlW) 110 | KeyCtrlX = Key(termbox.KeyCtrlX) 111 | KeyCtrlY = Key(termbox.KeyCtrlY) 112 | KeyCtrlZ = Key(termbox.KeyCtrlZ) 113 | KeyEsc = Key(termbox.KeyEsc) 114 | KeyCtrlLsqBracket = Key(termbox.KeyCtrlLsqBracket) 115 | KeyCtrl3 = Key(termbox.KeyCtrl3) 116 | KeyCtrl4 = Key(termbox.KeyCtrl4) 117 | KeyCtrlBackslash = Key(termbox.KeyCtrlBackslash) 118 | KeyCtrl5 = Key(termbox.KeyCtrl5) 119 | KeyCtrlRsqBracket = Key(termbox.KeyCtrlRsqBracket) 120 | KeyCtrl6 = Key(termbox.KeyCtrl6) 121 | KeyCtrl7 = Key(termbox.KeyCtrl7) 122 | KeyCtrlSlash = Key(termbox.KeyCtrlSlash) 123 | KeyCtrlUnderscore = Key(termbox.KeyCtrlUnderscore) 124 | KeySpace = Key(termbox.KeySpace) 125 | KeyBackspace2 = Key(termbox.KeyBackspace2) 126 | KeyCtrl8 = Key(termbox.KeyCtrl8) 127 | ) 128 | 129 | // Modifier allows to define special keys combinations. They can be used 130 | // in combination with Keys or Runes when a new keybinding is defined. 131 | type Modifier termbox.Modifier 132 | 133 | // Modifiers. 134 | const ( 135 | ModNone Modifier = Modifier(0) 136 | ModAlt = Modifier(termbox.ModAlt) 137 | ) 138 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-runewidth/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Yasuhiro Matsumoto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-runewidth/README.mkd: -------------------------------------------------------------------------------- 1 | go-runewidth 2 | ============ 3 | 4 | [![Build Status](https://travis-ci.org/mattn/go-runewidth.png?branch=master)](https://travis-ci.org/mattn/go-runewidth) 5 | [![Coverage Status](https://coveralls.io/repos/mattn/go-runewidth/badge.png?branch=HEAD)](https://coveralls.io/r/mattn/go-runewidth?branch=HEAD) 6 | [![GoDoc](https://godoc.org/github.com/mattn/go-runewidth?status.svg)](http://godoc.org/github.com/mattn/go-runewidth) 7 | [![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-runewidth)](https://goreportcard.com/report/github.com/mattn/go-runewidth) 8 | 9 | Provides functions to get fixed width of the character or string. 10 | 11 | Usage 12 | ----- 13 | 14 | ```go 15 | runewidth.StringWidth("つのだ☆HIRO") == 12 16 | ``` 17 | 18 | 19 | Author 20 | ------ 21 | 22 | Yasuhiro Matsumoto 23 | 24 | License 25 | ------- 26 | 27 | under the MIT License: http://mattn.mit-license.org/2013 28 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-runewidth/runewidth_js.go: -------------------------------------------------------------------------------- 1 | // +build js 2 | 3 | package runewidth 4 | 5 | func IsEastAsian() bool { 6 | // TODO: Implement this for the web. Detect east asian in a compatible way, and return true. 7 | return false 8 | } 9 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-runewidth/runewidth_posix.go: -------------------------------------------------------------------------------- 1 | // +build !windows,!js 2 | 3 | package runewidth 4 | 5 | import ( 6 | "os" 7 | "regexp" 8 | "strings" 9 | ) 10 | 11 | var reLoc = regexp.MustCompile(`^[a-z][a-z][a-z]?(?:_[A-Z][A-Z])?\.(.+)`) 12 | 13 | var mblenTable = map[string]int{ 14 | "utf-8": 6, 15 | "utf8": 6, 16 | "jis": 8, 17 | "eucjp": 3, 18 | "euckr": 2, 19 | "euccn": 2, 20 | "sjis": 2, 21 | "cp932": 2, 22 | "cp51932": 2, 23 | "cp936": 2, 24 | "cp949": 2, 25 | "cp950": 2, 26 | "big5": 2, 27 | "gbk": 2, 28 | "gb2312": 2, 29 | } 30 | 31 | func isEastAsian(locale string) bool { 32 | charset := strings.ToLower(locale) 33 | r := reLoc.FindStringSubmatch(locale) 34 | if len(r) == 2 { 35 | charset = strings.ToLower(r[1]) 36 | } 37 | 38 | if strings.HasSuffix(charset, "@cjk_narrow") { 39 | return false 40 | } 41 | 42 | for pos, b := range []byte(charset) { 43 | if b == '@' { 44 | charset = charset[:pos] 45 | break 46 | } 47 | } 48 | max := 1 49 | if m, ok := mblenTable[charset]; ok { 50 | max = m 51 | } 52 | if max > 1 && (charset[0] != 'u' || 53 | strings.HasPrefix(locale, "ja") || 54 | strings.HasPrefix(locale, "ko") || 55 | strings.HasPrefix(locale, "zh")) { 56 | return true 57 | } 58 | return false 59 | } 60 | 61 | // IsEastAsian return true if the current locale is CJK 62 | func IsEastAsian() bool { 63 | locale := os.Getenv("LC_CTYPE") 64 | if locale == "" { 65 | locale = os.Getenv("LANG") 66 | } 67 | 68 | // ignore C locale 69 | if locale == "POSIX" || locale == "C" { 70 | return false 71 | } 72 | if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') { 73 | return false 74 | } 75 | 76 | return isEastAsian(locale) 77 | } 78 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-runewidth/runewidth_windows.go: -------------------------------------------------------------------------------- 1 | package runewidth 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | var ( 8 | kernel32 = syscall.NewLazyDLL("kernel32") 9 | procGetConsoleOutputCP = kernel32.NewProc("GetConsoleOutputCP") 10 | ) 11 | 12 | // IsEastAsian return true if the current locale is CJK 13 | func IsEastAsian() bool { 14 | r1, _, _ := procGetConsoleOutputCP.Call() 15 | if r1 == 0 { 16 | return false 17 | } 18 | 19 | switch int(r1) { 20 | case 932, 51932, 936, 949, 950: 21 | return true 22 | } 23 | 24 | return false 25 | } 26 | -------------------------------------------------------------------------------- /vendor/github.com/mauidude/go-readability/README.md: -------------------------------------------------------------------------------- 1 | go-readability 2 | ============== 3 | 4 | go-readability is library for extracting the main content off of an HTML page. This library implements the readability algorithm created by arc90 labs and was heavily inspired by https://github.com/cantino/ruby-readability. 5 | 6 | Installation 7 | ------------ 8 | 9 | `go get github.com/mauidude/go-readability` 10 | 11 | Example 12 | ------- 13 | 14 | ``` 15 | import( 16 | "github.com/mauidude/go-readability" 17 | ) 18 | 19 | ... 20 | 21 | doc, err := readability.NewDocument(html) 22 | if err != nil { 23 | // do something ... 24 | } 25 | 26 | content := doc.Content() 27 | // do something with my content 28 | 29 | ``` 30 | 31 | 32 | Tests 33 | ----- 34 | 35 | To run tests 36 | `go test github.com/mauidude/go-readability` 37 | -------------------------------------------------------------------------------- /vendor/github.com/nsf/termbox-go/AUTHORS: -------------------------------------------------------------------------------- 1 | # Please keep this file sorted. 2 | 3 | Georg Reinke 4 | nsf 5 | -------------------------------------------------------------------------------- /vendor/github.com/nsf/termbox-go/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2012 termbox-go authors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /vendor/github.com/nsf/termbox-go/README.md: -------------------------------------------------------------------------------- 1 | ## Termbox 2 | Termbox is a library that provides a minimalistic API which allows the programmer to write text-based user interfaces. The library is crossplatform and has both terminal-based implementations on *nix operating systems and a winapi console based implementation for windows operating systems. The basic idea is an abstraction of the greatest common subset of features available on all major terminals and other terminal-like APIs in a minimalistic fashion. Small API means it is easy to implement, test, maintain and learn it, that's what makes the termbox a distinct library in its area. 3 | 4 | ### Installation 5 | Install and update this go package with `go get -u github.com/nsf/termbox-go` 6 | 7 | ### Examples 8 | For examples of what can be done take a look at demos in the _demos directory. You can try them with go run: `go run _demos/keyboard.go` 9 | 10 | There are also some interesting projects using termbox-go: 11 | - [godit](https://github.com/nsf/godit) is an emacsish lightweight text editor written using termbox. 12 | - [gomatrix](https://github.com/GeertJohan/gomatrix) connects to The Matrix and displays its data streams in your terminal. 13 | - [gotetris](https://github.com/jjinux/gotetris) is an implementation of Tetris. 14 | - [sokoban-go](https://github.com/rn2dy/sokoban-go) is an implementation of sokoban game. 15 | - [hecate](https://github.com/evanmiller/hecate) is a hex editor designed by Satan. 16 | - [httopd](https://github.com/verdverm/httopd) is top for httpd logs. 17 | - [mop](https://github.com/michaeldv/mop) is stock market tracker for hackers. 18 | - [termui](https://github.com/gizak/termui) is a terminal dashboard. 19 | - [termloop](https://github.com/JoelOtter/termloop) is a terminal game engine. 20 | - [xterm-color-chart](https://github.com/kutuluk/xterm-color-chart) is a XTerm 256 color chart. 21 | - [gocui](https://github.com/jroimartin/gocui) is a minimalist Go library aimed at creating console user interfaces. 22 | - [dry](https://github.com/moncho/dry) is an interactive cli to manage Docker containers. 23 | - [pxl](https://github.com/ichinaski/pxl) displays images in the terminal. 24 | - [snake-game](https://github.com/DyegoCosta/snake-game) is an implementation of the Snake game. 25 | - [gone](https://github.com/guillaumebreton/gone) is a CLI pomodoro® timer. 26 | - [Spoof.go](https://github.com/sabey/spoofgo) controllable movement spoofing from the cli 27 | - [lf](https://github.com/gokcehan/lf) is a terminal file manager 28 | 29 | ### API reference 30 | [godoc.org/github.com/nsf/termbox-go](http://godoc.org/github.com/nsf/termbox-go) 31 | -------------------------------------------------------------------------------- /vendor/github.com/nsf/termbox-go/api_common.go: -------------------------------------------------------------------------------- 1 | // termbox is a library for creating cross-platform text-based interfaces 2 | package termbox 3 | 4 | // public API, common OS agnostic part 5 | 6 | type ( 7 | InputMode int 8 | OutputMode int 9 | EventType uint8 10 | Modifier uint8 11 | Key uint16 12 | Attribute uint16 13 | ) 14 | 15 | // This type represents a termbox event. The 'Mod', 'Key' and 'Ch' fields are 16 | // valid if 'Type' is EventKey. The 'Width' and 'Height' fields are valid if 17 | // 'Type' is EventResize. The 'Err' field is valid if 'Type' is EventError. 18 | type Event struct { 19 | Type EventType // one of Event* constants 20 | Mod Modifier // one of Mod* constants or 0 21 | Key Key // one of Key* constants, invalid if 'Ch' is not 0 22 | Ch rune // a unicode character 23 | Width int // width of the screen 24 | Height int // height of the screen 25 | Err error // error in case if input failed 26 | MouseX int // x coord of mouse 27 | MouseY int // y coord of mouse 28 | N int // number of bytes written when getting a raw event 29 | } 30 | 31 | // A cell, single conceptual entity on the screen. The screen is basically a 2d 32 | // array of cells. 'Ch' is a unicode character, 'Fg' and 'Bg' are foreground 33 | // and background attributes respectively. 34 | type Cell struct { 35 | Ch rune 36 | Fg Attribute 37 | Bg Attribute 38 | } 39 | 40 | // To know if termbox has been initialized or not 41 | var ( 42 | IsInit bool = false 43 | ) 44 | 45 | // Key constants, see Event.Key field. 46 | const ( 47 | KeyF1 Key = 0xFFFF - iota 48 | KeyF2 49 | KeyF3 50 | KeyF4 51 | KeyF5 52 | KeyF6 53 | KeyF7 54 | KeyF8 55 | KeyF9 56 | KeyF10 57 | KeyF11 58 | KeyF12 59 | KeyInsert 60 | KeyDelete 61 | KeyHome 62 | KeyEnd 63 | KeyPgup 64 | KeyPgdn 65 | KeyArrowUp 66 | KeyArrowDown 67 | KeyArrowLeft 68 | KeyArrowRight 69 | key_min // see terminfo 70 | MouseLeft 71 | MouseMiddle 72 | MouseRight 73 | MouseRelease 74 | MouseWheelUp 75 | MouseWheelDown 76 | ) 77 | 78 | const ( 79 | KeyCtrlTilde Key = 0x00 80 | KeyCtrl2 Key = 0x00 81 | KeyCtrlSpace Key = 0x00 82 | KeyCtrlA Key = 0x01 83 | KeyCtrlB Key = 0x02 84 | KeyCtrlC Key = 0x03 85 | KeyCtrlD Key = 0x04 86 | KeyCtrlE Key = 0x05 87 | KeyCtrlF Key = 0x06 88 | KeyCtrlG Key = 0x07 89 | KeyBackspace Key = 0x08 90 | KeyCtrlH Key = 0x08 91 | KeyTab Key = 0x09 92 | KeyCtrlI Key = 0x09 93 | KeyCtrlJ Key = 0x0A 94 | KeyCtrlK Key = 0x0B 95 | KeyCtrlL Key = 0x0C 96 | KeyEnter Key = 0x0D 97 | KeyCtrlM Key = 0x0D 98 | KeyCtrlN Key = 0x0E 99 | KeyCtrlO Key = 0x0F 100 | KeyCtrlP Key = 0x10 101 | KeyCtrlQ Key = 0x11 102 | KeyCtrlR Key = 0x12 103 | KeyCtrlS Key = 0x13 104 | KeyCtrlT Key = 0x14 105 | KeyCtrlU Key = 0x15 106 | KeyCtrlV Key = 0x16 107 | KeyCtrlW Key = 0x17 108 | KeyCtrlX Key = 0x18 109 | KeyCtrlY Key = 0x19 110 | KeyCtrlZ Key = 0x1A 111 | KeyEsc Key = 0x1B 112 | KeyCtrlLsqBracket Key = 0x1B 113 | KeyCtrl3 Key = 0x1B 114 | KeyCtrl4 Key = 0x1C 115 | KeyCtrlBackslash Key = 0x1C 116 | KeyCtrl5 Key = 0x1D 117 | KeyCtrlRsqBracket Key = 0x1D 118 | KeyCtrl6 Key = 0x1E 119 | KeyCtrl7 Key = 0x1F 120 | KeyCtrlSlash Key = 0x1F 121 | KeyCtrlUnderscore Key = 0x1F 122 | KeySpace Key = 0x20 123 | KeyBackspace2 Key = 0x7F 124 | KeyCtrl8 Key = 0x7F 125 | ) 126 | 127 | // Alt modifier constant, see Event.Mod field and SetInputMode function. 128 | const ( 129 | ModAlt Modifier = 1 << iota 130 | ModMotion 131 | ) 132 | 133 | // Cell colors, you can combine a color with multiple attributes using bitwise 134 | // OR ('|'). 135 | const ( 136 | ColorDefault Attribute = iota 137 | ColorBlack 138 | ColorRed 139 | ColorGreen 140 | ColorYellow 141 | ColorBlue 142 | ColorMagenta 143 | ColorCyan 144 | ColorWhite 145 | ) 146 | 147 | // Cell attributes, it is possible to use multiple attributes by combining them 148 | // using bitwise OR ('|'). Although, colors cannot be combined. But you can 149 | // combine attributes and a single color. 150 | // 151 | // It's worth mentioning that some platforms don't support certain attibutes. 152 | // For example windows console doesn't support AttrUnderline. And on some 153 | // terminals applying AttrBold to background may result in blinking text. Use 154 | // them with caution and test your code on various terminals. 155 | const ( 156 | AttrBold Attribute = 1 << (iota + 9) 157 | AttrUnderline 158 | AttrReverse 159 | ) 160 | 161 | // Input mode. See SetInputMode function. 162 | const ( 163 | InputEsc InputMode = 1 << iota 164 | InputAlt 165 | InputMouse 166 | InputCurrent InputMode = 0 167 | ) 168 | 169 | // Output mode. See SetOutputMode function. 170 | const ( 171 | OutputCurrent OutputMode = iota 172 | OutputNormal 173 | Output256 174 | Output216 175 | OutputGrayscale 176 | ) 177 | 178 | // Event type. See Event.Type field. 179 | const ( 180 | EventKey EventType = iota 181 | EventResize 182 | EventMouse 183 | EventError 184 | EventInterrupt 185 | EventRaw 186 | EventNone 187 | ) 188 | -------------------------------------------------------------------------------- /vendor/github.com/nsf/termbox-go/api_windows.go: -------------------------------------------------------------------------------- 1 | package termbox 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | // public API 8 | 9 | // Initializes termbox library. This function should be called before any other functions. 10 | // After successful initialization, the library must be finalized using 'Close' function. 11 | // 12 | // Example usage: 13 | // err := termbox.Init() 14 | // if err != nil { 15 | // panic(err) 16 | // } 17 | // defer termbox.Close() 18 | func Init() error { 19 | var err error 20 | 21 | interrupt, err = create_event() 22 | if err != nil { 23 | return err 24 | } 25 | 26 | in, err = syscall.Open("CONIN$", syscall.O_RDWR, 0) 27 | if err != nil { 28 | return err 29 | } 30 | out, err = syscall.Open("CONOUT$", syscall.O_RDWR, 0) 31 | if err != nil { 32 | return err 33 | } 34 | 35 | err = get_console_mode(in, &orig_mode) 36 | if err != nil { 37 | return err 38 | } 39 | 40 | err = set_console_mode(in, enable_window_input) 41 | if err != nil { 42 | return err 43 | } 44 | 45 | orig_size = get_term_size(out) 46 | win_size := get_win_size(out) 47 | 48 | err = set_console_screen_buffer_size(out, win_size) 49 | if err != nil { 50 | return err 51 | } 52 | 53 | err = get_console_cursor_info(out, &orig_cursor_info) 54 | if err != nil { 55 | return err 56 | } 57 | 58 | show_cursor(false) 59 | term_size = get_term_size(out) 60 | back_buffer.init(int(term_size.x), int(term_size.y)) 61 | front_buffer.init(int(term_size.x), int(term_size.y)) 62 | back_buffer.clear() 63 | front_buffer.clear() 64 | clear() 65 | 66 | diffbuf = make([]diff_msg, 0, 32) 67 | 68 | go input_event_producer() 69 | IsInit = true 70 | return nil 71 | } 72 | 73 | // Finalizes termbox library, should be called after successful initialization 74 | // when termbox's functionality isn't required anymore. 75 | func Close() { 76 | // we ignore errors here, because we can't really do anything about them 77 | Clear(0, 0) 78 | Flush() 79 | 80 | // stop event producer 81 | cancel_comm <- true 82 | set_event(interrupt) 83 | select { 84 | case <-input_comm: 85 | default: 86 | } 87 | <-cancel_done_comm 88 | 89 | set_console_cursor_info(out, &orig_cursor_info) 90 | set_console_cursor_position(out, coord{}) 91 | set_console_screen_buffer_size(out, orig_size) 92 | set_console_mode(in, orig_mode) 93 | syscall.Close(in) 94 | syscall.Close(out) 95 | syscall.Close(interrupt) 96 | IsInit = false 97 | } 98 | 99 | // Interrupt an in-progress call to PollEvent by causing it to return 100 | // EventInterrupt. Note that this function will block until the PollEvent 101 | // function has successfully been interrupted. 102 | func Interrupt() { 103 | interrupt_comm <- struct{}{} 104 | } 105 | 106 | // Synchronizes the internal back buffer with the terminal. 107 | func Flush() error { 108 | update_size_maybe() 109 | prepare_diff_messages() 110 | for _, diff := range diffbuf { 111 | r := small_rect{ 112 | left: 0, 113 | top: diff.pos, 114 | right: term_size.x - 1, 115 | bottom: diff.pos + diff.lines - 1, 116 | } 117 | write_console_output(out, diff.chars, r) 118 | } 119 | if !is_cursor_hidden(cursor_x, cursor_y) { 120 | move_cursor(cursor_x, cursor_y) 121 | } 122 | return nil 123 | } 124 | 125 | // Sets the position of the cursor. See also HideCursor(). 126 | func SetCursor(x, y int) { 127 | if is_cursor_hidden(cursor_x, cursor_y) && !is_cursor_hidden(x, y) { 128 | show_cursor(true) 129 | } 130 | 131 | if !is_cursor_hidden(cursor_x, cursor_y) && is_cursor_hidden(x, y) { 132 | show_cursor(false) 133 | } 134 | 135 | cursor_x, cursor_y = x, y 136 | if !is_cursor_hidden(cursor_x, cursor_y) { 137 | move_cursor(cursor_x, cursor_y) 138 | } 139 | } 140 | 141 | // The shortcut for SetCursor(-1, -1). 142 | func HideCursor() { 143 | SetCursor(cursor_hidden, cursor_hidden) 144 | } 145 | 146 | // Changes cell's parameters in the internal back buffer at the specified 147 | // position. 148 | func SetCell(x, y int, ch rune, fg, bg Attribute) { 149 | if x < 0 || x >= back_buffer.width { 150 | return 151 | } 152 | if y < 0 || y >= back_buffer.height { 153 | return 154 | } 155 | 156 | back_buffer.cells[y*back_buffer.width+x] = Cell{ch, fg, bg} 157 | } 158 | 159 | // Returns a slice into the termbox's back buffer. You can get its dimensions 160 | // using 'Size' function. The slice remains valid as long as no 'Clear' or 161 | // 'Flush' function calls were made after call to this function. 162 | func CellBuffer() []Cell { 163 | return back_buffer.cells 164 | } 165 | 166 | // Wait for an event and return it. This is a blocking function call. 167 | func PollEvent() Event { 168 | select { 169 | case ev := <-input_comm: 170 | return ev 171 | case <-interrupt_comm: 172 | return Event{Type: EventInterrupt} 173 | } 174 | } 175 | 176 | // Returns the size of the internal back buffer (which is mostly the same as 177 | // console's window size in characters). But it doesn't always match the size 178 | // of the console window, after the console size has changed, the internal back 179 | // buffer will get in sync only after Clear or Flush function calls. 180 | func Size() (int, int) { 181 | return int(term_size.x), int(term_size.y) 182 | } 183 | 184 | // Clears the internal back buffer. 185 | func Clear(fg, bg Attribute) error { 186 | foreground, background = fg, bg 187 | update_size_maybe() 188 | back_buffer.clear() 189 | return nil 190 | } 191 | 192 | // Sets termbox input mode. Termbox has two input modes: 193 | // 194 | // 1. Esc input mode. When ESC sequence is in the buffer and it doesn't match 195 | // any known sequence. ESC means KeyEsc. This is the default input mode. 196 | // 197 | // 2. Alt input mode. When ESC sequence is in the buffer and it doesn't match 198 | // any known sequence. ESC enables ModAlt modifier for the next keyboard event. 199 | // 200 | // Both input modes can be OR'ed with Mouse mode. Setting Mouse mode bit up will 201 | // enable mouse button press/release and drag events. 202 | // 203 | // If 'mode' is InputCurrent, returns the current input mode. See also Input* 204 | // constants. 205 | func SetInputMode(mode InputMode) InputMode { 206 | if mode == InputCurrent { 207 | return input_mode 208 | } 209 | if mode&InputMouse != 0 { 210 | err := set_console_mode(in, enable_window_input|enable_mouse_input|enable_extended_flags) 211 | if err != nil { 212 | panic(err) 213 | } 214 | } else { 215 | err := set_console_mode(in, enable_window_input) 216 | if err != nil { 217 | panic(err) 218 | } 219 | } 220 | 221 | input_mode = mode 222 | return input_mode 223 | } 224 | 225 | // Sets the termbox output mode. 226 | // 227 | // Windows console does not support extra colour modes, 228 | // so this will always set and return OutputNormal. 229 | func SetOutputMode(mode OutputMode) OutputMode { 230 | return OutputNormal 231 | } 232 | 233 | // Sync comes handy when something causes desync between termbox's understanding 234 | // of a terminal buffer and the reality. Such as a third party process. Sync 235 | // forces a complete resync between the termbox and a terminal, it may not be 236 | // visually pretty though. At the moment on Windows it does nothing. 237 | func Sync() error { 238 | return nil 239 | } 240 | -------------------------------------------------------------------------------- /vendor/github.com/nsf/termbox-go/collect_terminfo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys, os, subprocess 4 | 5 | def escaped(s): 6 | return repr(s)[1:-1] 7 | 8 | def tput(term, name): 9 | try: 10 | return subprocess.check_output(['tput', '-T%s' % term, name]).decode() 11 | except subprocess.CalledProcessError as e: 12 | return e.output.decode() 13 | 14 | 15 | def w(s): 16 | if s == None: 17 | return 18 | sys.stdout.write(s) 19 | 20 | terminals = { 21 | 'xterm' : 'xterm', 22 | 'rxvt-256color' : 'rxvt_256color', 23 | 'rxvt-unicode' : 'rxvt_unicode', 24 | 'linux' : 'linux', 25 | 'Eterm' : 'eterm', 26 | 'screen' : 'screen' 27 | } 28 | 29 | keys = [ 30 | "F1", "kf1", 31 | "F2", "kf2", 32 | "F3", "kf3", 33 | "F4", "kf4", 34 | "F5", "kf5", 35 | "F6", "kf6", 36 | "F7", "kf7", 37 | "F8", "kf8", 38 | "F9", "kf9", 39 | "F10", "kf10", 40 | "F11", "kf11", 41 | "F12", "kf12", 42 | "INSERT", "kich1", 43 | "DELETE", "kdch1", 44 | "HOME", "khome", 45 | "END", "kend", 46 | "PGUP", "kpp", 47 | "PGDN", "knp", 48 | "KEY_UP", "kcuu1", 49 | "KEY_DOWN", "kcud1", 50 | "KEY_LEFT", "kcub1", 51 | "KEY_RIGHT", "kcuf1" 52 | ] 53 | 54 | funcs = [ 55 | "T_ENTER_CA", "smcup", 56 | "T_EXIT_CA", "rmcup", 57 | "T_SHOW_CURSOR", "cnorm", 58 | "T_HIDE_CURSOR", "civis", 59 | "T_CLEAR_SCREEN", "clear", 60 | "T_SGR0", "sgr0", 61 | "T_UNDERLINE", "smul", 62 | "T_BOLD", "bold", 63 | "T_BLINK", "blink", 64 | "T_REVERSE", "rev", 65 | "T_ENTER_KEYPAD", "smkx", 66 | "T_EXIT_KEYPAD", "rmkx" 67 | ] 68 | 69 | def iter_pairs(iterable): 70 | iterable = iter(iterable) 71 | while True: 72 | yield (next(iterable), next(iterable)) 73 | 74 | def do_term(term, nick): 75 | w("// %s\n" % term) 76 | w("var %s_keys = []string{\n\t" % nick) 77 | for k, v in iter_pairs(keys): 78 | w('"') 79 | w(escaped(tput(term, v))) 80 | w('",') 81 | w("\n}\n") 82 | w("var %s_funcs = []string{\n\t" % nick) 83 | for k,v in iter_pairs(funcs): 84 | w('"') 85 | if v == "sgr": 86 | w("\\033[3%d;4%dm") 87 | elif v == "cup": 88 | w("\\033[%d;%dH") 89 | else: 90 | w(escaped(tput(term, v))) 91 | w('", ') 92 | w("\n}\n\n") 93 | 94 | def do_terms(d): 95 | w("var terms = []struct {\n") 96 | w("\tname string\n") 97 | w("\tkeys []string\n") 98 | w("\tfuncs []string\n") 99 | w("}{\n") 100 | for k, v in d.items(): 101 | w('\t{"%s", %s_keys, %s_funcs},\n' % (k, v, v)) 102 | w("}\n\n") 103 | 104 | w("// +build !windows\n\npackage termbox\n\n") 105 | 106 | for k,v in terminals.items(): 107 | do_term(k, v) 108 | 109 | do_terms(terminals) 110 | 111 | -------------------------------------------------------------------------------- /vendor/github.com/nsf/termbox-go/syscalls.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package termbox 4 | 5 | /* 6 | #include 7 | #include 8 | */ 9 | import "C" 10 | 11 | type syscall_Termios C.struct_termios 12 | 13 | const ( 14 | syscall_IGNBRK = C.IGNBRK 15 | syscall_BRKINT = C.BRKINT 16 | syscall_PARMRK = C.PARMRK 17 | syscall_ISTRIP = C.ISTRIP 18 | syscall_INLCR = C.INLCR 19 | syscall_IGNCR = C.IGNCR 20 | syscall_ICRNL = C.ICRNL 21 | syscall_IXON = C.IXON 22 | syscall_OPOST = C.OPOST 23 | syscall_ECHO = C.ECHO 24 | syscall_ECHONL = C.ECHONL 25 | syscall_ICANON = C.ICANON 26 | syscall_ISIG = C.ISIG 27 | syscall_IEXTEN = C.IEXTEN 28 | syscall_CSIZE = C.CSIZE 29 | syscall_PARENB = C.PARENB 30 | syscall_CS8 = C.CS8 31 | syscall_VMIN = C.VMIN 32 | syscall_VTIME = C.VTIME 33 | 34 | // on darwin change these to (on *bsd too?): 35 | // C.TIOCGETA 36 | // C.TIOCSETA 37 | syscall_TCGETS = C.TCGETS 38 | syscall_TCSETS = C.TCSETS 39 | ) 40 | -------------------------------------------------------------------------------- /vendor/github.com/nsf/termbox-go/syscalls_darwin.go: -------------------------------------------------------------------------------- 1 | // Created by cgo -godefs - DO NOT EDIT 2 | // cgo -godefs syscalls.go 3 | 4 | // +build !amd64 5 | 6 | package termbox 7 | 8 | type syscall_Termios struct { 9 | Iflag uint32 10 | Oflag uint32 11 | Cflag uint32 12 | Lflag uint32 13 | Cc [20]uint8 14 | Ispeed uint32 15 | Ospeed uint32 16 | } 17 | 18 | const ( 19 | syscall_IGNBRK = 0x1 20 | syscall_BRKINT = 0x2 21 | syscall_PARMRK = 0x8 22 | syscall_ISTRIP = 0x20 23 | syscall_INLCR = 0x40 24 | syscall_IGNCR = 0x80 25 | syscall_ICRNL = 0x100 26 | syscall_IXON = 0x200 27 | syscall_OPOST = 0x1 28 | syscall_ECHO = 0x8 29 | syscall_ECHONL = 0x10 30 | syscall_ICANON = 0x100 31 | syscall_ISIG = 0x80 32 | syscall_IEXTEN = 0x400 33 | syscall_CSIZE = 0x300 34 | syscall_PARENB = 0x1000 35 | syscall_CS8 = 0x300 36 | syscall_VMIN = 0x10 37 | syscall_VTIME = 0x11 38 | 39 | syscall_TCGETS = 0x402c7413 40 | syscall_TCSETS = 0x802c7414 41 | ) 42 | -------------------------------------------------------------------------------- /vendor/github.com/nsf/termbox-go/syscalls_darwin_amd64.go: -------------------------------------------------------------------------------- 1 | // Created by cgo -godefs - DO NOT EDIT 2 | // cgo -godefs syscalls.go 3 | 4 | package termbox 5 | 6 | type syscall_Termios struct { 7 | Iflag uint64 8 | Oflag uint64 9 | Cflag uint64 10 | Lflag uint64 11 | Cc [20]uint8 12 | Pad_cgo_0 [4]byte 13 | Ispeed uint64 14 | Ospeed uint64 15 | } 16 | 17 | const ( 18 | syscall_IGNBRK = 0x1 19 | syscall_BRKINT = 0x2 20 | syscall_PARMRK = 0x8 21 | syscall_ISTRIP = 0x20 22 | syscall_INLCR = 0x40 23 | syscall_IGNCR = 0x80 24 | syscall_ICRNL = 0x100 25 | syscall_IXON = 0x200 26 | syscall_OPOST = 0x1 27 | syscall_ECHO = 0x8 28 | syscall_ECHONL = 0x10 29 | syscall_ICANON = 0x100 30 | syscall_ISIG = 0x80 31 | syscall_IEXTEN = 0x400 32 | syscall_CSIZE = 0x300 33 | syscall_PARENB = 0x1000 34 | syscall_CS8 = 0x300 35 | syscall_VMIN = 0x10 36 | syscall_VTIME = 0x11 37 | 38 | syscall_TCGETS = 0x40487413 39 | syscall_TCSETS = 0x80487414 40 | ) 41 | -------------------------------------------------------------------------------- /vendor/github.com/nsf/termbox-go/syscalls_dragonfly.go: -------------------------------------------------------------------------------- 1 | // Created by cgo -godefs - DO NOT EDIT 2 | // cgo -godefs syscalls.go 3 | 4 | package termbox 5 | 6 | type syscall_Termios struct { 7 | Iflag uint32 8 | Oflag uint32 9 | Cflag uint32 10 | Lflag uint32 11 | Cc [20]uint8 12 | Ispeed uint32 13 | Ospeed uint32 14 | } 15 | 16 | const ( 17 | syscall_IGNBRK = 0x1 18 | syscall_BRKINT = 0x2 19 | syscall_PARMRK = 0x8 20 | syscall_ISTRIP = 0x20 21 | syscall_INLCR = 0x40 22 | syscall_IGNCR = 0x80 23 | syscall_ICRNL = 0x100 24 | syscall_IXON = 0x200 25 | syscall_OPOST = 0x1 26 | syscall_ECHO = 0x8 27 | syscall_ECHONL = 0x10 28 | syscall_ICANON = 0x100 29 | syscall_ISIG = 0x80 30 | syscall_IEXTEN = 0x400 31 | syscall_CSIZE = 0x300 32 | syscall_PARENB = 0x1000 33 | syscall_CS8 = 0x300 34 | syscall_VMIN = 0x10 35 | syscall_VTIME = 0x11 36 | 37 | syscall_TCGETS = 0x402c7413 38 | syscall_TCSETS = 0x802c7414 39 | ) 40 | -------------------------------------------------------------------------------- /vendor/github.com/nsf/termbox-go/syscalls_freebsd.go: -------------------------------------------------------------------------------- 1 | // Created by cgo -godefs - DO NOT EDIT 2 | // cgo -godefs syscalls.go 3 | 4 | package termbox 5 | 6 | type syscall_Termios struct { 7 | Iflag uint32 8 | Oflag uint32 9 | Cflag uint32 10 | Lflag uint32 11 | Cc [20]uint8 12 | Ispeed uint32 13 | Ospeed uint32 14 | } 15 | 16 | const ( 17 | syscall_IGNBRK = 0x1 18 | syscall_BRKINT = 0x2 19 | syscall_PARMRK = 0x8 20 | syscall_ISTRIP = 0x20 21 | syscall_INLCR = 0x40 22 | syscall_IGNCR = 0x80 23 | syscall_ICRNL = 0x100 24 | syscall_IXON = 0x200 25 | syscall_OPOST = 0x1 26 | syscall_ECHO = 0x8 27 | syscall_ECHONL = 0x10 28 | syscall_ICANON = 0x100 29 | syscall_ISIG = 0x80 30 | syscall_IEXTEN = 0x400 31 | syscall_CSIZE = 0x300 32 | syscall_PARENB = 0x1000 33 | syscall_CS8 = 0x300 34 | syscall_VMIN = 0x10 35 | syscall_VTIME = 0x11 36 | 37 | syscall_TCGETS = 0x402c7413 38 | syscall_TCSETS = 0x802c7414 39 | ) 40 | -------------------------------------------------------------------------------- /vendor/github.com/nsf/termbox-go/syscalls_linux.go: -------------------------------------------------------------------------------- 1 | // Created by cgo -godefs - DO NOT EDIT 2 | // cgo -godefs syscalls.go 3 | 4 | package termbox 5 | 6 | import "syscall" 7 | 8 | type syscall_Termios syscall.Termios 9 | 10 | const ( 11 | syscall_IGNBRK = syscall.IGNBRK 12 | syscall_BRKINT = syscall.BRKINT 13 | syscall_PARMRK = syscall.PARMRK 14 | syscall_ISTRIP = syscall.ISTRIP 15 | syscall_INLCR = syscall.INLCR 16 | syscall_IGNCR = syscall.IGNCR 17 | syscall_ICRNL = syscall.ICRNL 18 | syscall_IXON = syscall.IXON 19 | syscall_OPOST = syscall.OPOST 20 | syscall_ECHO = syscall.ECHO 21 | syscall_ECHONL = syscall.ECHONL 22 | syscall_ICANON = syscall.ICANON 23 | syscall_ISIG = syscall.ISIG 24 | syscall_IEXTEN = syscall.IEXTEN 25 | syscall_CSIZE = syscall.CSIZE 26 | syscall_PARENB = syscall.PARENB 27 | syscall_CS8 = syscall.CS8 28 | syscall_VMIN = syscall.VMIN 29 | syscall_VTIME = syscall.VTIME 30 | 31 | syscall_TCGETS = syscall.TCGETS 32 | syscall_TCSETS = syscall.TCSETS 33 | ) 34 | -------------------------------------------------------------------------------- /vendor/github.com/nsf/termbox-go/syscalls_netbsd.go: -------------------------------------------------------------------------------- 1 | // Created by cgo -godefs - DO NOT EDIT 2 | // cgo -godefs syscalls.go 3 | 4 | package termbox 5 | 6 | type syscall_Termios struct { 7 | Iflag uint32 8 | Oflag uint32 9 | Cflag uint32 10 | Lflag uint32 11 | Cc [20]uint8 12 | Ispeed int32 13 | Ospeed int32 14 | } 15 | 16 | const ( 17 | syscall_IGNBRK = 0x1 18 | syscall_BRKINT = 0x2 19 | syscall_PARMRK = 0x8 20 | syscall_ISTRIP = 0x20 21 | syscall_INLCR = 0x40 22 | syscall_IGNCR = 0x80 23 | syscall_ICRNL = 0x100 24 | syscall_IXON = 0x200 25 | syscall_OPOST = 0x1 26 | syscall_ECHO = 0x8 27 | syscall_ECHONL = 0x10 28 | syscall_ICANON = 0x100 29 | syscall_ISIG = 0x80 30 | syscall_IEXTEN = 0x400 31 | syscall_CSIZE = 0x300 32 | syscall_PARENB = 0x1000 33 | syscall_CS8 = 0x300 34 | syscall_VMIN = 0x10 35 | syscall_VTIME = 0x11 36 | 37 | syscall_TCGETS = 0x402c7413 38 | syscall_TCSETS = 0x802c7414 39 | ) 40 | -------------------------------------------------------------------------------- /vendor/github.com/nsf/termbox-go/syscalls_openbsd.go: -------------------------------------------------------------------------------- 1 | // Created by cgo -godefs - DO NOT EDIT 2 | // cgo -godefs syscalls.go 3 | 4 | package termbox 5 | 6 | type syscall_Termios struct { 7 | Iflag uint32 8 | Oflag uint32 9 | Cflag uint32 10 | Lflag uint32 11 | Cc [20]uint8 12 | Ispeed int32 13 | Ospeed int32 14 | } 15 | 16 | const ( 17 | syscall_IGNBRK = 0x1 18 | syscall_BRKINT = 0x2 19 | syscall_PARMRK = 0x8 20 | syscall_ISTRIP = 0x20 21 | syscall_INLCR = 0x40 22 | syscall_IGNCR = 0x80 23 | syscall_ICRNL = 0x100 24 | syscall_IXON = 0x200 25 | syscall_OPOST = 0x1 26 | syscall_ECHO = 0x8 27 | syscall_ECHONL = 0x10 28 | syscall_ICANON = 0x100 29 | syscall_ISIG = 0x80 30 | syscall_IEXTEN = 0x400 31 | syscall_CSIZE = 0x300 32 | syscall_PARENB = 0x1000 33 | syscall_CS8 = 0x300 34 | syscall_VMIN = 0x10 35 | syscall_VTIME = 0x11 36 | 37 | syscall_TCGETS = 0x402c7413 38 | syscall_TCSETS = 0x802c7414 39 | ) 40 | -------------------------------------------------------------------------------- /vendor/github.com/nsf/termbox-go/syscalls_windows.go: -------------------------------------------------------------------------------- 1 | // Created by cgo -godefs - DO NOT EDIT 2 | // cgo -godefs -- -DUNICODE syscalls.go 3 | 4 | package termbox 5 | 6 | const ( 7 | foreground_blue = 0x1 8 | foreground_green = 0x2 9 | foreground_red = 0x4 10 | foreground_intensity = 0x8 11 | background_blue = 0x10 12 | background_green = 0x20 13 | background_red = 0x40 14 | background_intensity = 0x80 15 | std_input_handle = -0xa 16 | std_output_handle = -0xb 17 | key_event = 0x1 18 | mouse_event = 0x2 19 | window_buffer_size_event = 0x4 20 | enable_window_input = 0x8 21 | enable_mouse_input = 0x10 22 | enable_extended_flags = 0x80 23 | 24 | vk_f1 = 0x70 25 | vk_f2 = 0x71 26 | vk_f3 = 0x72 27 | vk_f4 = 0x73 28 | vk_f5 = 0x74 29 | vk_f6 = 0x75 30 | vk_f7 = 0x76 31 | vk_f8 = 0x77 32 | vk_f9 = 0x78 33 | vk_f10 = 0x79 34 | vk_f11 = 0x7a 35 | vk_f12 = 0x7b 36 | vk_insert = 0x2d 37 | vk_delete = 0x2e 38 | vk_home = 0x24 39 | vk_end = 0x23 40 | vk_pgup = 0x21 41 | vk_pgdn = 0x22 42 | vk_arrow_up = 0x26 43 | vk_arrow_down = 0x28 44 | vk_arrow_left = 0x25 45 | vk_arrow_right = 0x27 46 | vk_backspace = 0x8 47 | vk_tab = 0x9 48 | vk_enter = 0xd 49 | vk_esc = 0x1b 50 | vk_space = 0x20 51 | 52 | left_alt_pressed = 0x2 53 | left_ctrl_pressed = 0x8 54 | right_alt_pressed = 0x1 55 | right_ctrl_pressed = 0x4 56 | shift_pressed = 0x10 57 | 58 | generic_read = 0x80000000 59 | generic_write = 0x40000000 60 | console_textmode_buffer = 0x1 61 | ) 62 | -------------------------------------------------------------------------------- /vendor/github.com/nsf/termbox-go/termbox_common.go: -------------------------------------------------------------------------------- 1 | package termbox 2 | 3 | // private API, common OS agnostic part 4 | 5 | type cellbuf struct { 6 | width int 7 | height int 8 | cells []Cell 9 | } 10 | 11 | func (this *cellbuf) init(width, height int) { 12 | this.width = width 13 | this.height = height 14 | this.cells = make([]Cell, width*height) 15 | } 16 | 17 | func (this *cellbuf) resize(width, height int) { 18 | if this.width == width && this.height == height { 19 | return 20 | } 21 | 22 | oldw := this.width 23 | oldh := this.height 24 | oldcells := this.cells 25 | 26 | this.init(width, height) 27 | this.clear() 28 | 29 | minw, minh := oldw, oldh 30 | 31 | if width < minw { 32 | minw = width 33 | } 34 | if height < minh { 35 | minh = height 36 | } 37 | 38 | for i := 0; i < minh; i++ { 39 | srco, dsto := i*oldw, i*width 40 | src := oldcells[srco : srco+minw] 41 | dst := this.cells[dsto : dsto+minw] 42 | copy(dst, src) 43 | } 44 | } 45 | 46 | func (this *cellbuf) clear() { 47 | for i := range this.cells { 48 | c := &this.cells[i] 49 | c.Ch = ' ' 50 | c.Fg = foreground 51 | c.Bg = background 52 | } 53 | } 54 | 55 | const cursor_hidden = -1 56 | 57 | func is_cursor_hidden(x, y int) bool { 58 | return x == cursor_hidden || y == cursor_hidden 59 | } 60 | -------------------------------------------------------------------------------- /vendor/github.com/nsf/termbox-go/terminfo.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | // This file contains a simple and incomplete implementation of the terminfo 3 | // database. Information was taken from the ncurses manpages term(5) and 4 | // terminfo(5). Currently, only the string capabilities for special keys and for 5 | // functions without parameters are actually used. Colors are still done with 6 | // ANSI escape sequences. Other special features that are not (yet?) supported 7 | // are reading from ~/.terminfo, the TERMINFO_DIRS variable, Berkeley database 8 | // format and extended capabilities. 9 | 10 | package termbox 11 | 12 | import ( 13 | "bytes" 14 | "encoding/binary" 15 | "encoding/hex" 16 | "errors" 17 | "fmt" 18 | "io/ioutil" 19 | "os" 20 | "strings" 21 | ) 22 | 23 | const ( 24 | ti_magic = 0432 25 | ti_header_length = 12 26 | ti_mouse_enter = "\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h" 27 | ti_mouse_leave = "\x1b[?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l" 28 | ) 29 | 30 | func load_terminfo() ([]byte, error) { 31 | var data []byte 32 | var err error 33 | 34 | term := os.Getenv("TERM") 35 | if term == "" { 36 | return nil, fmt.Errorf("termbox: TERM not set") 37 | } 38 | 39 | // The following behaviour follows the one described in terminfo(5) as 40 | // distributed by ncurses. 41 | 42 | terminfo := os.Getenv("TERMINFO") 43 | if terminfo != "" { 44 | // if TERMINFO is set, no other directory should be searched 45 | return ti_try_path(terminfo) 46 | } 47 | 48 | // next, consider ~/.terminfo 49 | home := os.Getenv("HOME") 50 | if home != "" { 51 | data, err = ti_try_path(home + "/.terminfo") 52 | if err == nil { 53 | return data, nil 54 | } 55 | } 56 | 57 | // next, TERMINFO_DIRS 58 | dirs := os.Getenv("TERMINFO_DIRS") 59 | if dirs != "" { 60 | for _, dir := range strings.Split(dirs, ":") { 61 | if dir == "" { 62 | // "" -> "/usr/share/terminfo" 63 | dir = "/usr/share/terminfo" 64 | } 65 | data, err = ti_try_path(dir) 66 | if err == nil { 67 | return data, nil 68 | } 69 | } 70 | } 71 | 72 | // fall back to /usr/share/terminfo 73 | return ti_try_path("/usr/share/terminfo") 74 | } 75 | 76 | func ti_try_path(path string) (data []byte, err error) { 77 | // load_terminfo already made sure it is set 78 | term := os.Getenv("TERM") 79 | 80 | // first try, the typical *nix path 81 | terminfo := path + "/" + term[0:1] + "/" + term 82 | data, err = ioutil.ReadFile(terminfo) 83 | if err == nil { 84 | return 85 | } 86 | 87 | // fallback to darwin specific dirs structure 88 | terminfo = path + "/" + hex.EncodeToString([]byte(term[:1])) + "/" + term 89 | data, err = ioutil.ReadFile(terminfo) 90 | return 91 | } 92 | 93 | func setup_term_builtin() error { 94 | name := os.Getenv("TERM") 95 | if name == "" { 96 | return errors.New("termbox: TERM environment variable not set") 97 | } 98 | 99 | for _, t := range terms { 100 | if t.name == name { 101 | keys = t.keys 102 | funcs = t.funcs 103 | return nil 104 | } 105 | } 106 | 107 | compat_table := []struct { 108 | partial string 109 | keys []string 110 | funcs []string 111 | }{ 112 | {"xterm", xterm_keys, xterm_funcs}, 113 | {"rxvt", rxvt_unicode_keys, rxvt_unicode_funcs}, 114 | {"linux", linux_keys, linux_funcs}, 115 | {"Eterm", eterm_keys, eterm_funcs}, 116 | {"screen", screen_keys, screen_funcs}, 117 | // let's assume that 'cygwin' is xterm compatible 118 | {"cygwin", xterm_keys, xterm_funcs}, 119 | {"st", xterm_keys, xterm_funcs}, 120 | } 121 | 122 | // try compatibility variants 123 | for _, it := range compat_table { 124 | if strings.Contains(name, it.partial) { 125 | keys = it.keys 126 | funcs = it.funcs 127 | return nil 128 | } 129 | } 130 | 131 | return errors.New("termbox: unsupported terminal") 132 | } 133 | 134 | func setup_term() (err error) { 135 | var data []byte 136 | var header [6]int16 137 | var str_offset, table_offset int16 138 | 139 | data, err = load_terminfo() 140 | if err != nil { 141 | return setup_term_builtin() 142 | } 143 | 144 | rd := bytes.NewReader(data) 145 | // 0: magic number, 1: size of names section, 2: size of boolean section, 3: 146 | // size of numbers section (in integers), 4: size of the strings section (in 147 | // integers), 5: size of the string table 148 | 149 | err = binary.Read(rd, binary.LittleEndian, header[:]) 150 | if err != nil { 151 | return 152 | } 153 | 154 | if (header[1]+header[2])%2 != 0 { 155 | // old quirk to align everything on word boundaries 156 | header[2] += 1 157 | } 158 | str_offset = ti_header_length + header[1] + header[2] + 2*header[3] 159 | table_offset = str_offset + 2*header[4] 160 | 161 | keys = make([]string, 0xFFFF-key_min) 162 | for i, _ := range keys { 163 | keys[i], err = ti_read_string(rd, str_offset+2*ti_keys[i], table_offset) 164 | if err != nil { 165 | return 166 | } 167 | } 168 | funcs = make([]string, t_max_funcs) 169 | // the last two entries are reserved for mouse. because the table offset is 170 | // not there, the two entries have to fill in manually 171 | for i, _ := range funcs[:len(funcs)-2] { 172 | funcs[i], err = ti_read_string(rd, str_offset+2*ti_funcs[i], table_offset) 173 | if err != nil { 174 | return 175 | } 176 | } 177 | funcs[t_max_funcs-2] = ti_mouse_enter 178 | funcs[t_max_funcs-1] = ti_mouse_leave 179 | return nil 180 | } 181 | 182 | func ti_read_string(rd *bytes.Reader, str_off, table int16) (string, error) { 183 | var off int16 184 | 185 | _, err := rd.Seek(int64(str_off), 0) 186 | if err != nil { 187 | return "", err 188 | } 189 | err = binary.Read(rd, binary.LittleEndian, &off) 190 | if err != nil { 191 | return "", err 192 | } 193 | _, err = rd.Seek(int64(table+off), 0) 194 | if err != nil { 195 | return "", err 196 | } 197 | var bs []byte 198 | for { 199 | b, err := rd.ReadByte() 200 | if err != nil { 201 | return "", err 202 | } 203 | if b == byte(0x00) { 204 | break 205 | } 206 | bs = append(bs, b) 207 | } 208 | return string(bs), nil 209 | } 210 | 211 | // "Maps" the function constants from termbox.go to the number of the respective 212 | // string capability in the terminfo file. Taken from (ncurses) term.h. 213 | var ti_funcs = []int16{ 214 | 28, 40, 16, 13, 5, 39, 36, 27, 26, 34, 89, 88, 215 | } 216 | 217 | // Same as above for the special keys. 218 | var ti_keys = []int16{ 219 | 66, 68 /* apparently not a typo; 67 is F10 for whatever reason */, 69, 70, 220 | 71, 72, 73, 74, 75, 67, 216, 217, 77, 59, 76, 164, 82, 81, 87, 61, 79, 83, 221 | } 222 | -------------------------------------------------------------------------------- /vendor/github.com/nsf/termbox-go/terminfo_builtin.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package termbox 4 | 5 | // Eterm 6 | var eterm_keys = []string{ 7 | "\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~", "\x1b[8~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C", 8 | } 9 | var eterm_funcs = []string{ 10 | "\x1b7\x1b[?47h", "\x1b[2J\x1b[?47l\x1b8", "\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[2J", "\x1b[m\x0f", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "", "", "", "", 11 | } 12 | 13 | // screen 14 | var screen_keys = []string{ 15 | "\x1bOP", "\x1bOQ", "\x1bOR", "\x1bOS", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[1~", "\x1b[4~", "\x1b[5~", "\x1b[6~", "\x1bOA", "\x1bOB", "\x1bOD", "\x1bOC", 16 | } 17 | var screen_funcs = []string{ 18 | "\x1b[?1049h", "\x1b[?1049l", "\x1b[34h\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[J", "\x1b[m\x0f", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "\x1b[?1h\x1b=", "\x1b[?1l\x1b>", ti_mouse_enter, ti_mouse_leave, 19 | } 20 | 21 | // xterm 22 | var xterm_keys = []string{ 23 | "\x1bOP", "\x1bOQ", "\x1bOR", "\x1bOS", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1bOH", "\x1bOF", "\x1b[5~", "\x1b[6~", "\x1bOA", "\x1bOB", "\x1bOD", "\x1bOC", 24 | } 25 | var xterm_funcs = []string{ 26 | "\x1b[?1049h", "\x1b[?1049l", "\x1b[?12l\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[2J", "\x1b(B\x1b[m", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "\x1b[?1h\x1b=", "\x1b[?1l\x1b>", ti_mouse_enter, ti_mouse_leave, 27 | } 28 | 29 | // rxvt-unicode 30 | var rxvt_unicode_keys = []string{ 31 | "\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~", "\x1b[8~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C", 32 | } 33 | var rxvt_unicode_funcs = []string{ 34 | "\x1b[?1049h", "\x1b[r\x1b[?1049l", "\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[2J", "\x1b[m\x1b(B", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "\x1b=", "\x1b>", ti_mouse_enter, ti_mouse_leave, 35 | } 36 | 37 | // linux 38 | var linux_keys = []string{ 39 | "\x1b[[A", "\x1b[[B", "\x1b[[C", "\x1b[[D", "\x1b[[E", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[1~", "\x1b[4~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C", 40 | } 41 | var linux_funcs = []string{ 42 | "", "", "\x1b[?25h\x1b[?0c", "\x1b[?25l\x1b[?1c", "\x1b[H\x1b[J", "\x1b[0;10m", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "", "", "", "", 43 | } 44 | 45 | // rxvt-256color 46 | var rxvt_256color_keys = []string{ 47 | "\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~", "\x1b[8~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C", 48 | } 49 | var rxvt_256color_funcs = []string{ 50 | "\x1b7\x1b[?47h", "\x1b[2J\x1b[?47l\x1b8", "\x1b[?25h", "\x1b[?25l", "\x1b[H\x1b[2J", "\x1b[m\x0f", "\x1b[4m", "\x1b[1m", "\x1b[5m", "\x1b[7m", "\x1b=", "\x1b>", ti_mouse_enter, ti_mouse_leave, 51 | } 52 | 53 | var terms = []struct { 54 | name string 55 | keys []string 56 | funcs []string 57 | }{ 58 | {"Eterm", eterm_keys, eterm_funcs}, 59 | {"screen", screen_keys, screen_funcs}, 60 | {"xterm", xterm_keys, xterm_funcs}, 61 | {"rxvt-unicode", rxvt_unicode_keys, rxvt_unicode_funcs}, 62 | {"linux", linux_keys, linux_funcs}, 63 | {"rxvt-256color", rxvt_256color_keys, rxvt_256color_funcs}, 64 | } 65 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/html/atom/atom.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package atom provides integer codes (also known as atoms) for a fixed set of 6 | // frequently occurring HTML strings: tag names and attribute keys such as "p" 7 | // and "id". 8 | // 9 | // Sharing an atom's name between all elements with the same tag can result in 10 | // fewer string allocations when tokenizing and parsing HTML. Integer 11 | // comparisons are also generally faster than string comparisons. 12 | // 13 | // The value of an atom's particular code is not guaranteed to stay the same 14 | // between versions of this package. Neither is any ordering guaranteed: 15 | // whether atom.H1 < atom.H2 may also change. The codes are not guaranteed to 16 | // be dense. The only guarantees are that e.g. looking up "div" will yield 17 | // atom.Div, calling atom.Div.String will return "div", and atom.Div != 0. 18 | package atom // import "golang.org/x/net/html/atom" 19 | 20 | // Atom is an integer code for a string. The zero value maps to "". 21 | type Atom uint32 22 | 23 | // String returns the atom's name. 24 | func (a Atom) String() string { 25 | start := uint32(a >> 8) 26 | n := uint32(a & 0xff) 27 | if start+n > uint32(len(atomText)) { 28 | return "" 29 | } 30 | return atomText[start : start+n] 31 | } 32 | 33 | func (a Atom) string() string { 34 | return atomText[a>>8 : a>>8+a&0xff] 35 | } 36 | 37 | // fnv computes the FNV hash with an arbitrary starting value h. 38 | func fnv(h uint32, s []byte) uint32 { 39 | for i := range s { 40 | h ^= uint32(s[i]) 41 | h *= 16777619 42 | } 43 | return h 44 | } 45 | 46 | func match(s string, t []byte) bool { 47 | for i, c := range t { 48 | if s[i] != c { 49 | return false 50 | } 51 | } 52 | return true 53 | } 54 | 55 | // Lookup returns the atom whose name is s. It returns zero if there is no 56 | // such atom. The lookup is case sensitive. 57 | func Lookup(s []byte) Atom { 58 | if len(s) == 0 || len(s) > maxAtomLen { 59 | return 0 60 | } 61 | h := fnv(hash0, s) 62 | if a := table[h&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) { 63 | return a 64 | } 65 | if a := table[(h>>16)&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) { 66 | return a 67 | } 68 | return 0 69 | } 70 | 71 | // String returns a string whose contents are equal to s. In that sense, it is 72 | // equivalent to string(s) but may be more efficient. 73 | func String(s []byte) string { 74 | if a := Lookup(s); a != 0 { 75 | return a.String() 76 | } 77 | return string(s) 78 | } 79 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/html/const.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package html 6 | 7 | // Section 12.2.3.2 of the HTML5 specification says "The following elements 8 | // have varying levels of special parsing rules". 9 | // https://html.spec.whatwg.org/multipage/syntax.html#the-stack-of-open-elements 10 | var isSpecialElementMap = map[string]bool{ 11 | "address": true, 12 | "applet": true, 13 | "area": true, 14 | "article": true, 15 | "aside": true, 16 | "base": true, 17 | "basefont": true, 18 | "bgsound": true, 19 | "blockquote": true, 20 | "body": true, 21 | "br": true, 22 | "button": true, 23 | "caption": true, 24 | "center": true, 25 | "col": true, 26 | "colgroup": true, 27 | "dd": true, 28 | "details": true, 29 | "dir": true, 30 | "div": true, 31 | "dl": true, 32 | "dt": true, 33 | "embed": true, 34 | "fieldset": true, 35 | "figcaption": true, 36 | "figure": true, 37 | "footer": true, 38 | "form": true, 39 | "frame": true, 40 | "frameset": true, 41 | "h1": true, 42 | "h2": true, 43 | "h3": true, 44 | "h4": true, 45 | "h5": true, 46 | "h6": true, 47 | "head": true, 48 | "header": true, 49 | "hgroup": true, 50 | "hr": true, 51 | "html": true, 52 | "iframe": true, 53 | "img": true, 54 | "input": true, 55 | "isindex": true, 56 | "li": true, 57 | "link": true, 58 | "listing": true, 59 | "marquee": true, 60 | "menu": true, 61 | "meta": true, 62 | "nav": true, 63 | "noembed": true, 64 | "noframes": true, 65 | "noscript": true, 66 | "object": true, 67 | "ol": true, 68 | "p": true, 69 | "param": true, 70 | "plaintext": true, 71 | "pre": true, 72 | "script": true, 73 | "section": true, 74 | "select": true, 75 | "source": true, 76 | "style": true, 77 | "summary": true, 78 | "table": true, 79 | "tbody": true, 80 | "td": true, 81 | "template": true, 82 | "textarea": true, 83 | "tfoot": true, 84 | "th": true, 85 | "thead": true, 86 | "title": true, 87 | "tr": true, 88 | "track": true, 89 | "ul": true, 90 | "wbr": true, 91 | "xmp": true, 92 | } 93 | 94 | func isSpecialElement(element *Node) bool { 95 | switch element.Namespace { 96 | case "", "html": 97 | return isSpecialElementMap[element.Data] 98 | case "svg": 99 | return element.Data == "foreignObject" 100 | } 101 | return false 102 | } 103 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/html/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package html implements an HTML5-compliant tokenizer and parser. 7 | 8 | Tokenization is done by creating a Tokenizer for an io.Reader r. It is the 9 | caller's responsibility to ensure that r provides UTF-8 encoded HTML. 10 | 11 | z := html.NewTokenizer(r) 12 | 13 | Given a Tokenizer z, the HTML is tokenized by repeatedly calling z.Next(), 14 | which parses the next token and returns its type, or an error: 15 | 16 | for { 17 | tt := z.Next() 18 | if tt == html.ErrorToken { 19 | // ... 20 | return ... 21 | } 22 | // Process the current token. 23 | } 24 | 25 | There are two APIs for retrieving the current token. The high-level API is to 26 | call Token; the low-level API is to call Text or TagName / TagAttr. Both APIs 27 | allow optionally calling Raw after Next but before Token, Text, TagName, or 28 | TagAttr. In EBNF notation, the valid call sequence per token is: 29 | 30 | Next {Raw} [ Token | Text | TagName {TagAttr} ] 31 | 32 | Token returns an independent data structure that completely describes a token. 33 | Entities (such as "<") are unescaped, tag names and attribute keys are 34 | lower-cased, and attributes are collected into a []Attribute. For example: 35 | 36 | for { 37 | if z.Next() == html.ErrorToken { 38 | // Returning io.EOF indicates success. 39 | return z.Err() 40 | } 41 | emitToken(z.Token()) 42 | } 43 | 44 | The low-level API performs fewer allocations and copies, but the contents of 45 | the []byte values returned by Text, TagName and TagAttr may change on the next 46 | call to Next. For example, to extract an HTML page's anchor text: 47 | 48 | depth := 0 49 | for { 50 | tt := z.Next() 51 | switch tt { 52 | case ErrorToken: 53 | return z.Err() 54 | case TextToken: 55 | if depth > 0 { 56 | // emitBytes should copy the []byte it receives, 57 | // if it doesn't process it immediately. 58 | emitBytes(z.Text()) 59 | } 60 | case StartTagToken, EndTagToken: 61 | tn, _ := z.TagName() 62 | if len(tn) == 1 && tn[0] == 'a' { 63 | if tt == StartTagToken { 64 | depth++ 65 | } else { 66 | depth-- 67 | } 68 | } 69 | } 70 | } 71 | 72 | Parsing is done by calling Parse with an io.Reader, which returns the root of 73 | the parse tree (the document element) as a *Node. It is the caller's 74 | responsibility to ensure that the Reader provides UTF-8 encoded HTML. For 75 | example, to process each anchor node in depth-first order: 76 | 77 | doc, err := html.Parse(r) 78 | if err != nil { 79 | // ... 80 | } 81 | var f func(*html.Node) 82 | f = func(n *html.Node) { 83 | if n.Type == html.ElementNode && n.Data == "a" { 84 | // Do something with n... 85 | } 86 | for c := n.FirstChild; c != nil; c = c.NextSibling { 87 | f(c) 88 | } 89 | } 90 | f(doc) 91 | 92 | The relevant specifications include: 93 | https://html.spec.whatwg.org/multipage/syntax.html and 94 | https://html.spec.whatwg.org/multipage/syntax.html#tokenization 95 | */ 96 | package html // import "golang.org/x/net/html" 97 | 98 | // The tokenization algorithm implemented by this package is not a line-by-line 99 | // transliteration of the relatively verbose state-machine in the WHATWG 100 | // specification. A more direct approach is used instead, where the program 101 | // counter implies the state, such as whether it is tokenizing a tag or a text 102 | // node. Specification compliance is verified by checking expected and actual 103 | // outputs over a test suite rather than aiming for algorithmic fidelity. 104 | 105 | // TODO(nigeltao): Does a DOM API belong in this package or a separate one? 106 | // TODO(nigeltao): How does parsing interact with a JavaScript engine? 107 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/html/doctype.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package html 6 | 7 | import ( 8 | "strings" 9 | ) 10 | 11 | // parseDoctype parses the data from a DoctypeToken into a name, 12 | // public identifier, and system identifier. It returns a Node whose Type 13 | // is DoctypeNode, whose Data is the name, and which has attributes 14 | // named "system" and "public" for the two identifiers if they were present. 15 | // quirks is whether the document should be parsed in "quirks mode". 16 | func parseDoctype(s string) (n *Node, quirks bool) { 17 | n = &Node{Type: DoctypeNode} 18 | 19 | // Find the name. 20 | space := strings.IndexAny(s, whitespace) 21 | if space == -1 { 22 | space = len(s) 23 | } 24 | n.Data = s[:space] 25 | // The comparison to "html" is case-sensitive. 26 | if n.Data != "html" { 27 | quirks = true 28 | } 29 | n.Data = strings.ToLower(n.Data) 30 | s = strings.TrimLeft(s[space:], whitespace) 31 | 32 | if len(s) < 6 { 33 | // It can't start with "PUBLIC" or "SYSTEM". 34 | // Ignore the rest of the string. 35 | return n, quirks || s != "" 36 | } 37 | 38 | key := strings.ToLower(s[:6]) 39 | s = s[6:] 40 | for key == "public" || key == "system" { 41 | s = strings.TrimLeft(s, whitespace) 42 | if s == "" { 43 | break 44 | } 45 | quote := s[0] 46 | if quote != '"' && quote != '\'' { 47 | break 48 | } 49 | s = s[1:] 50 | q := strings.IndexRune(s, rune(quote)) 51 | var id string 52 | if q == -1 { 53 | id = s 54 | s = "" 55 | } else { 56 | id = s[:q] 57 | s = s[q+1:] 58 | } 59 | n.Attr = append(n.Attr, Attribute{Key: key, Val: id}) 60 | if key == "public" { 61 | key = "system" 62 | } else { 63 | key = "" 64 | } 65 | } 66 | 67 | if key != "" || s != "" { 68 | quirks = true 69 | } else if len(n.Attr) > 0 { 70 | if n.Attr[0].Key == "public" { 71 | public := strings.ToLower(n.Attr[0].Val) 72 | switch public { 73 | case "-//w3o//dtd w3 html strict 3.0//en//", "-/w3d/dtd html 4.0 transitional/en", "html": 74 | quirks = true 75 | default: 76 | for _, q := range quirkyIDs { 77 | if strings.HasPrefix(public, q) { 78 | quirks = true 79 | break 80 | } 81 | } 82 | } 83 | // The following two public IDs only cause quirks mode if there is no system ID. 84 | if len(n.Attr) == 1 && (strings.HasPrefix(public, "-//w3c//dtd html 4.01 frameset//") || 85 | strings.HasPrefix(public, "-//w3c//dtd html 4.01 transitional//")) { 86 | quirks = true 87 | } 88 | } 89 | if lastAttr := n.Attr[len(n.Attr)-1]; lastAttr.Key == "system" && 90 | strings.ToLower(lastAttr.Val) == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd" { 91 | quirks = true 92 | } 93 | } 94 | 95 | return n, quirks 96 | } 97 | 98 | // quirkyIDs is a list of public doctype identifiers that cause a document 99 | // to be interpreted in quirks mode. The identifiers should be in lower case. 100 | var quirkyIDs = []string{ 101 | "+//silmaril//dtd html pro v0r11 19970101//", 102 | "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", 103 | "-//as//dtd html 3.0 aswedit + extensions//", 104 | "-//ietf//dtd html 2.0 level 1//", 105 | "-//ietf//dtd html 2.0 level 2//", 106 | "-//ietf//dtd html 2.0 strict level 1//", 107 | "-//ietf//dtd html 2.0 strict level 2//", 108 | "-//ietf//dtd html 2.0 strict//", 109 | "-//ietf//dtd html 2.0//", 110 | "-//ietf//dtd html 2.1e//", 111 | "-//ietf//dtd html 3.0//", 112 | "-//ietf//dtd html 3.2 final//", 113 | "-//ietf//dtd html 3.2//", 114 | "-//ietf//dtd html 3//", 115 | "-//ietf//dtd html level 0//", 116 | "-//ietf//dtd html level 1//", 117 | "-//ietf//dtd html level 2//", 118 | "-//ietf//dtd html level 3//", 119 | "-//ietf//dtd html strict level 0//", 120 | "-//ietf//dtd html strict level 1//", 121 | "-//ietf//dtd html strict level 2//", 122 | "-//ietf//dtd html strict level 3//", 123 | "-//ietf//dtd html strict//", 124 | "-//ietf//dtd html//", 125 | "-//metrius//dtd metrius presentational//", 126 | "-//microsoft//dtd internet explorer 2.0 html strict//", 127 | "-//microsoft//dtd internet explorer 2.0 html//", 128 | "-//microsoft//dtd internet explorer 2.0 tables//", 129 | "-//microsoft//dtd internet explorer 3.0 html strict//", 130 | "-//microsoft//dtd internet explorer 3.0 html//", 131 | "-//microsoft//dtd internet explorer 3.0 tables//", 132 | "-//netscape comm. corp.//dtd html//", 133 | "-//netscape comm. corp.//dtd strict html//", 134 | "-//o'reilly and associates//dtd html 2.0//", 135 | "-//o'reilly and associates//dtd html extended 1.0//", 136 | "-//o'reilly and associates//dtd html extended relaxed 1.0//", 137 | "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//", 138 | "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//", 139 | "-//spyglass//dtd html 2.0 extended//", 140 | "-//sq//dtd html 2.0 hotmetal + extensions//", 141 | "-//sun microsystems corp.//dtd hotjava html//", 142 | "-//sun microsystems corp.//dtd hotjava strict html//", 143 | "-//w3c//dtd html 3 1995-03-24//", 144 | "-//w3c//dtd html 3.2 draft//", 145 | "-//w3c//dtd html 3.2 final//", 146 | "-//w3c//dtd html 3.2//", 147 | "-//w3c//dtd html 3.2s draft//", 148 | "-//w3c//dtd html 4.0 frameset//", 149 | "-//w3c//dtd html 4.0 transitional//", 150 | "-//w3c//dtd html experimental 19960712//", 151 | "-//w3c//dtd html experimental 970421//", 152 | "-//w3c//dtd w3 html//", 153 | "-//w3o//dtd w3 html 3.0//", 154 | "-//webtechs//dtd mozilla html 2.0//", 155 | "-//webtechs//dtd mozilla html//", 156 | } 157 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/html/escape.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package html 6 | 7 | import ( 8 | "bytes" 9 | "strings" 10 | "unicode/utf8" 11 | ) 12 | 13 | // These replacements permit compatibility with old numeric entities that 14 | // assumed Windows-1252 encoding. 15 | // https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference 16 | var replacementTable = [...]rune{ 17 | '\u20AC', // First entry is what 0x80 should be replaced with. 18 | '\u0081', 19 | '\u201A', 20 | '\u0192', 21 | '\u201E', 22 | '\u2026', 23 | '\u2020', 24 | '\u2021', 25 | '\u02C6', 26 | '\u2030', 27 | '\u0160', 28 | '\u2039', 29 | '\u0152', 30 | '\u008D', 31 | '\u017D', 32 | '\u008F', 33 | '\u0090', 34 | '\u2018', 35 | '\u2019', 36 | '\u201C', 37 | '\u201D', 38 | '\u2022', 39 | '\u2013', 40 | '\u2014', 41 | '\u02DC', 42 | '\u2122', 43 | '\u0161', 44 | '\u203A', 45 | '\u0153', 46 | '\u009D', 47 | '\u017E', 48 | '\u0178', // Last entry is 0x9F. 49 | // 0x00->'\uFFFD' is handled programmatically. 50 | // 0x0D->'\u000D' is a no-op. 51 | } 52 | 53 | // unescapeEntity reads an entity like "<" from b[src:] and writes the 54 | // corresponding "<" to b[dst:], returning the incremented dst and src cursors. 55 | // Precondition: b[src] == '&' && dst <= src. 56 | // attribute should be true if parsing an attribute value. 57 | func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { 58 | // https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference 59 | 60 | // i starts at 1 because we already know that s[0] == '&'. 61 | i, s := 1, b[src:] 62 | 63 | if len(s) <= 1 { 64 | b[dst] = b[src] 65 | return dst + 1, src + 1 66 | } 67 | 68 | if s[i] == '#' { 69 | if len(s) <= 3 { // We need to have at least "&#.". 70 | b[dst] = b[src] 71 | return dst + 1, src + 1 72 | } 73 | i++ 74 | c := s[i] 75 | hex := false 76 | if c == 'x' || c == 'X' { 77 | hex = true 78 | i++ 79 | } 80 | 81 | x := '\x00' 82 | for i < len(s) { 83 | c = s[i] 84 | i++ 85 | if hex { 86 | if '0' <= c && c <= '9' { 87 | x = 16*x + rune(c) - '0' 88 | continue 89 | } else if 'a' <= c && c <= 'f' { 90 | x = 16*x + rune(c) - 'a' + 10 91 | continue 92 | } else if 'A' <= c && c <= 'F' { 93 | x = 16*x + rune(c) - 'A' + 10 94 | continue 95 | } 96 | } else if '0' <= c && c <= '9' { 97 | x = 10*x + rune(c) - '0' 98 | continue 99 | } 100 | if c != ';' { 101 | i-- 102 | } 103 | break 104 | } 105 | 106 | if i <= 3 { // No characters matched. 107 | b[dst] = b[src] 108 | return dst + 1, src + 1 109 | } 110 | 111 | if 0x80 <= x && x <= 0x9F { 112 | // Replace characters from Windows-1252 with UTF-8 equivalents. 113 | x = replacementTable[x-0x80] 114 | } else if x == 0 || (0xD800 <= x && x <= 0xDFFF) || x > 0x10FFFF { 115 | // Replace invalid characters with the replacement character. 116 | x = '\uFFFD' 117 | } 118 | 119 | return dst + utf8.EncodeRune(b[dst:], x), src + i 120 | } 121 | 122 | // Consume the maximum number of characters possible, with the 123 | // consumed characters matching one of the named references. 124 | 125 | for i < len(s) { 126 | c := s[i] 127 | i++ 128 | // Lower-cased characters are more common in entities, so we check for them first. 129 | if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' { 130 | continue 131 | } 132 | if c != ';' { 133 | i-- 134 | } 135 | break 136 | } 137 | 138 | entityName := string(s[1:i]) 139 | if entityName == "" { 140 | // No-op. 141 | } else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' { 142 | // No-op. 143 | } else if x := entity[entityName]; x != 0 { 144 | return dst + utf8.EncodeRune(b[dst:], x), src + i 145 | } else if x := entity2[entityName]; x[0] != 0 { 146 | dst1 := dst + utf8.EncodeRune(b[dst:], x[0]) 147 | return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i 148 | } else if !attribute { 149 | maxLen := len(entityName) - 1 150 | if maxLen > longestEntityWithoutSemicolon { 151 | maxLen = longestEntityWithoutSemicolon 152 | } 153 | for j := maxLen; j > 1; j-- { 154 | if x := entity[entityName[:j]]; x != 0 { 155 | return dst + utf8.EncodeRune(b[dst:], x), src + j + 1 156 | } 157 | } 158 | } 159 | 160 | dst1, src1 = dst+i, src+i 161 | copy(b[dst:dst1], b[src:src1]) 162 | return dst1, src1 163 | } 164 | 165 | // unescape unescapes b's entities in-place, so that "a<b" becomes "a': 214 | esc = ">" 215 | case '"': 216 | // """ is shorter than """. 217 | esc = """ 218 | case '\r': 219 | esc = " " 220 | default: 221 | panic("unrecognized escape character") 222 | } 223 | s = s[i+1:] 224 | if _, err := w.WriteString(esc); err != nil { 225 | return err 226 | } 227 | i = strings.IndexAny(s, escapedChars) 228 | } 229 | _, err := w.WriteString(s) 230 | return err 231 | } 232 | 233 | // EscapeString escapes special characters like "<" to become "<". It 234 | // escapes only five such characters: <, >, &, ' and ". 235 | // UnescapeString(EscapeString(s)) == s always holds, but the converse isn't 236 | // always true. 237 | func EscapeString(s string) string { 238 | if strings.IndexAny(s, escapedChars) == -1 { 239 | return s 240 | } 241 | var buf bytes.Buffer 242 | escape(&buf, s) 243 | return buf.String() 244 | } 245 | 246 | // UnescapeString unescapes entities like "<" to become "<". It unescapes a 247 | // larger range of entities than EscapeString escapes. For example, "á" 248 | // unescapes to "á", as does "á" and "&xE1;". 249 | // UnescapeString(EscapeString(s)) == s always holds, but the converse isn't 250 | // always true. 251 | func UnescapeString(s string) string { 252 | for _, c := range s { 253 | if c == '&' { 254 | return string(unescape([]byte(s), false)) 255 | } 256 | } 257 | return s 258 | } 259 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/html/foreign.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package html 6 | 7 | import ( 8 | "strings" 9 | ) 10 | 11 | func adjustAttributeNames(aa []Attribute, nameMap map[string]string) { 12 | for i := range aa { 13 | if newName, ok := nameMap[aa[i].Key]; ok { 14 | aa[i].Key = newName 15 | } 16 | } 17 | } 18 | 19 | func adjustForeignAttributes(aa []Attribute) { 20 | for i, a := range aa { 21 | if a.Key == "" || a.Key[0] != 'x' { 22 | continue 23 | } 24 | switch a.Key { 25 | case "xlink:actuate", "xlink:arcrole", "xlink:href", "xlink:role", "xlink:show", 26 | "xlink:title", "xlink:type", "xml:base", "xml:lang", "xml:space", "xmlns:xlink": 27 | j := strings.Index(a.Key, ":") 28 | aa[i].Namespace = a.Key[:j] 29 | aa[i].Key = a.Key[j+1:] 30 | } 31 | } 32 | } 33 | 34 | func htmlIntegrationPoint(n *Node) bool { 35 | if n.Type != ElementNode { 36 | return false 37 | } 38 | switch n.Namespace { 39 | case "math": 40 | if n.Data == "annotation-xml" { 41 | for _, a := range n.Attr { 42 | if a.Key == "encoding" { 43 | val := strings.ToLower(a.Val) 44 | if val == "text/html" || val == "application/xhtml+xml" { 45 | return true 46 | } 47 | } 48 | } 49 | } 50 | case "svg": 51 | switch n.Data { 52 | case "desc", "foreignObject", "title": 53 | return true 54 | } 55 | } 56 | return false 57 | } 58 | 59 | func mathMLTextIntegrationPoint(n *Node) bool { 60 | if n.Namespace != "math" { 61 | return false 62 | } 63 | switch n.Data { 64 | case "mi", "mo", "mn", "ms", "mtext": 65 | return true 66 | } 67 | return false 68 | } 69 | 70 | // Section 12.2.5.5. 71 | var breakout = map[string]bool{ 72 | "b": true, 73 | "big": true, 74 | "blockquote": true, 75 | "body": true, 76 | "br": true, 77 | "center": true, 78 | "code": true, 79 | "dd": true, 80 | "div": true, 81 | "dl": true, 82 | "dt": true, 83 | "em": true, 84 | "embed": true, 85 | "h1": true, 86 | "h2": true, 87 | "h3": true, 88 | "h4": true, 89 | "h5": true, 90 | "h6": true, 91 | "head": true, 92 | "hr": true, 93 | "i": true, 94 | "img": true, 95 | "li": true, 96 | "listing": true, 97 | "menu": true, 98 | "meta": true, 99 | "nobr": true, 100 | "ol": true, 101 | "p": true, 102 | "pre": true, 103 | "ruby": true, 104 | "s": true, 105 | "small": true, 106 | "span": true, 107 | "strong": true, 108 | "strike": true, 109 | "sub": true, 110 | "sup": true, 111 | "table": true, 112 | "tt": true, 113 | "u": true, 114 | "ul": true, 115 | "var": true, 116 | } 117 | 118 | // Section 12.2.5.5. 119 | var svgTagNameAdjustments = map[string]string{ 120 | "altglyph": "altGlyph", 121 | "altglyphdef": "altGlyphDef", 122 | "altglyphitem": "altGlyphItem", 123 | "animatecolor": "animateColor", 124 | "animatemotion": "animateMotion", 125 | "animatetransform": "animateTransform", 126 | "clippath": "clipPath", 127 | "feblend": "feBlend", 128 | "fecolormatrix": "feColorMatrix", 129 | "fecomponenttransfer": "feComponentTransfer", 130 | "fecomposite": "feComposite", 131 | "feconvolvematrix": "feConvolveMatrix", 132 | "fediffuselighting": "feDiffuseLighting", 133 | "fedisplacementmap": "feDisplacementMap", 134 | "fedistantlight": "feDistantLight", 135 | "feflood": "feFlood", 136 | "fefunca": "feFuncA", 137 | "fefuncb": "feFuncB", 138 | "fefuncg": "feFuncG", 139 | "fefuncr": "feFuncR", 140 | "fegaussianblur": "feGaussianBlur", 141 | "feimage": "feImage", 142 | "femerge": "feMerge", 143 | "femergenode": "feMergeNode", 144 | "femorphology": "feMorphology", 145 | "feoffset": "feOffset", 146 | "fepointlight": "fePointLight", 147 | "fespecularlighting": "feSpecularLighting", 148 | "fespotlight": "feSpotLight", 149 | "fetile": "feTile", 150 | "feturbulence": "feTurbulence", 151 | "foreignobject": "foreignObject", 152 | "glyphref": "glyphRef", 153 | "lineargradient": "linearGradient", 154 | "radialgradient": "radialGradient", 155 | "textpath": "textPath", 156 | } 157 | 158 | // Section 12.2.5.1 159 | var mathMLAttributeAdjustments = map[string]string{ 160 | "definitionurl": "definitionURL", 161 | } 162 | 163 | var svgAttributeAdjustments = map[string]string{ 164 | "attributename": "attributeName", 165 | "attributetype": "attributeType", 166 | "basefrequency": "baseFrequency", 167 | "baseprofile": "baseProfile", 168 | "calcmode": "calcMode", 169 | "clippathunits": "clipPathUnits", 170 | "contentscripttype": "contentScriptType", 171 | "contentstyletype": "contentStyleType", 172 | "diffuseconstant": "diffuseConstant", 173 | "edgemode": "edgeMode", 174 | "externalresourcesrequired": "externalResourcesRequired", 175 | "filterres": "filterRes", 176 | "filterunits": "filterUnits", 177 | "glyphref": "glyphRef", 178 | "gradienttransform": "gradientTransform", 179 | "gradientunits": "gradientUnits", 180 | "kernelmatrix": "kernelMatrix", 181 | "kernelunitlength": "kernelUnitLength", 182 | "keypoints": "keyPoints", 183 | "keysplines": "keySplines", 184 | "keytimes": "keyTimes", 185 | "lengthadjust": "lengthAdjust", 186 | "limitingconeangle": "limitingConeAngle", 187 | "markerheight": "markerHeight", 188 | "markerunits": "markerUnits", 189 | "markerwidth": "markerWidth", 190 | "maskcontentunits": "maskContentUnits", 191 | "maskunits": "maskUnits", 192 | "numoctaves": "numOctaves", 193 | "pathlength": "pathLength", 194 | "patterncontentunits": "patternContentUnits", 195 | "patterntransform": "patternTransform", 196 | "patternunits": "patternUnits", 197 | "pointsatx": "pointsAtX", 198 | "pointsaty": "pointsAtY", 199 | "pointsatz": "pointsAtZ", 200 | "preservealpha": "preserveAlpha", 201 | "preserveaspectratio": "preserveAspectRatio", 202 | "primitiveunits": "primitiveUnits", 203 | "refx": "refX", 204 | "refy": "refY", 205 | "repeatcount": "repeatCount", 206 | "repeatdur": "repeatDur", 207 | "requiredextensions": "requiredExtensions", 208 | "requiredfeatures": "requiredFeatures", 209 | "specularconstant": "specularConstant", 210 | "specularexponent": "specularExponent", 211 | "spreadmethod": "spreadMethod", 212 | "startoffset": "startOffset", 213 | "stddeviation": "stdDeviation", 214 | "stitchtiles": "stitchTiles", 215 | "surfacescale": "surfaceScale", 216 | "systemlanguage": "systemLanguage", 217 | "tablevalues": "tableValues", 218 | "targetx": "targetX", 219 | "targety": "targetY", 220 | "textlength": "textLength", 221 | "viewbox": "viewBox", 222 | "viewtarget": "viewTarget", 223 | "xchannelselector": "xChannelSelector", 224 | "ychannelselector": "yChannelSelector", 225 | "zoomandpan": "zoomAndPan", 226 | } 227 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/html/node.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package html 6 | 7 | import ( 8 | "golang.org/x/net/html/atom" 9 | ) 10 | 11 | // A NodeType is the type of a Node. 12 | type NodeType uint32 13 | 14 | const ( 15 | ErrorNode NodeType = iota 16 | TextNode 17 | DocumentNode 18 | ElementNode 19 | CommentNode 20 | DoctypeNode 21 | scopeMarkerNode 22 | ) 23 | 24 | // Section 12.2.3.3 says "scope markers are inserted when entering applet 25 | // elements, buttons, object elements, marquees, table cells, and table 26 | // captions, and are used to prevent formatting from 'leaking'". 27 | var scopeMarker = Node{Type: scopeMarkerNode} 28 | 29 | // A Node consists of a NodeType and some Data (tag name for element nodes, 30 | // content for text) and are part of a tree of Nodes. Element nodes may also 31 | // have a Namespace and contain a slice of Attributes. Data is unescaped, so 32 | // that it looks like "a 0 { 160 | return (*s)[i-1] 161 | } 162 | return nil 163 | } 164 | 165 | // index returns the index of the top-most occurrence of n in the stack, or -1 166 | // if n is not present. 167 | func (s *nodeStack) index(n *Node) int { 168 | for i := len(*s) - 1; i >= 0; i-- { 169 | if (*s)[i] == n { 170 | return i 171 | } 172 | } 173 | return -1 174 | } 175 | 176 | // insert inserts a node at the given index. 177 | func (s *nodeStack) insert(i int, n *Node) { 178 | (*s) = append(*s, nil) 179 | copy((*s)[i+1:], (*s)[i:]) 180 | (*s)[i] = n 181 | } 182 | 183 | // remove removes a node from the stack. It is a no-op if n is not present. 184 | func (s *nodeStack) remove(n *Node) { 185 | i := s.index(n) 186 | if i == -1 { 187 | return 188 | } 189 | copy((*s)[i:], (*s)[i+1:]) 190 | j := len(*s) - 1 191 | (*s)[j] = nil 192 | *s = (*s)[:j] 193 | } 194 | -------------------------------------------------------------------------------- /vendor/golang.org/x/net/html/render.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package html 6 | 7 | import ( 8 | "bufio" 9 | "errors" 10 | "fmt" 11 | "io" 12 | "strings" 13 | ) 14 | 15 | type writer interface { 16 | io.Writer 17 | io.ByteWriter 18 | WriteString(string) (int, error) 19 | } 20 | 21 | // Render renders the parse tree n to the given writer. 22 | // 23 | // Rendering is done on a 'best effort' basis: calling Parse on the output of 24 | // Render will always result in something similar to the original tree, but it 25 | // is not necessarily an exact clone unless the original tree was 'well-formed'. 26 | // 'Well-formed' is not easily specified; the HTML5 specification is 27 | // complicated. 28 | // 29 | // Calling Parse on arbitrary input typically results in a 'well-formed' parse 30 | // tree. However, it is possible for Parse to yield a 'badly-formed' parse tree. 31 | // For example, in a 'well-formed' parse tree, no element is a child of 32 | // another element: parsing "" results in two sibling elements. 33 | // Similarly, in a 'well-formed' parse tree, no element is a child of a 34 | // element: parsing "

" results in a

with two sibling 35 | // children; the is reparented to the

's parent. However, calling 36 | // Parse on "
" does not return an error, but the result has an 37 | // element with an child, and is therefore not 'well-formed'. 38 | // 39 | // Programmatically constructed trees are typically also 'well-formed', but it 40 | // is possible to construct a tree that looks innocuous but, when rendered and 41 | // re-parsed, results in a different tree. A simple example is that a solitary 42 | // text node would become a tree containing , and elements. 43 | // Another example is that the programmatic equivalent of "abc" 44 | // becomes "abc". 45 | func Render(w io.Writer, n *Node) error { 46 | if x, ok := w.(writer); ok { 47 | return render(x, n) 48 | } 49 | buf := bufio.NewWriter(w) 50 | if err := render(buf, n); err != nil { 51 | return err 52 | } 53 | return buf.Flush() 54 | } 55 | 56 | // plaintextAbort is returned from render1 when a element 57 | // has been rendered. No more end tags should be rendered after that. 58 | var plaintextAbort = errors.New("html: internal error (plaintext abort)") 59 | 60 | func render(w writer, n *Node) error { 61 | err := render1(w, n) 62 | if err == plaintextAbort { 63 | err = nil 64 | } 65 | return err 66 | } 67 | 68 | func render1(w writer, n *Node) error { 69 | // Render non-element nodes; these are the easy cases. 70 | switch n.Type { 71 | case ErrorNode: 72 | return errors.New("html: cannot render an ErrorNode node") 73 | case TextNode: 74 | return escape(w, n.Data) 75 | case DocumentNode: 76 | for c := n.FirstChild; c != nil; c = c.NextSibling { 77 | if err := render1(w, c); err != nil { 78 | return err 79 | } 80 | } 81 | return nil 82 | case ElementNode: 83 | // No-op. 84 | case CommentNode: 85 | if _, err := w.WriteString("<!--"); err != nil { 86 | return err 87 | } 88 | if _, err := w.WriteString(n.Data); err != nil { 89 | return err 90 | } 91 | if _, err := w.WriteString("-->"); err != nil { 92 | return err 93 | } 94 | return nil 95 | case DoctypeNode: 96 | if _, err := w.WriteString("<!DOCTYPE "); err != nil { 97 | return err 98 | } 99 | if _, err := w.WriteString(n.Data); err != nil { 100 | return err 101 | } 102 | if n.Attr != nil { 103 | var p, s string 104 | for _, a := range n.Attr { 105 | switch a.Key { 106 | case "public": 107 | p = a.Val 108 | case "system": 109 | s = a.Val 110 | } 111 | } 112 | if p != "" { 113 | if _, err := w.WriteString(" PUBLIC "); err != nil { 114 | return err 115 | } 116 | if err := writeQuoted(w, p); err != nil { 117 | return err 118 | } 119 | if s != "" { 120 | if err := w.WriteByte(' '); err != nil { 121 | return err 122 | } 123 | if err := writeQuoted(w, s); err != nil { 124 | return err 125 | } 126 | } 127 | } else if s != "" { 128 | if _, err := w.WriteString(" SYSTEM "); err != nil { 129 | return err 130 | } 131 | if err := writeQuoted(w, s); err != nil { 132 | return err 133 | } 134 | } 135 | } 136 | return w.WriteByte('>') 137 | default: 138 | return errors.New("html: unknown node type") 139 | } 140 | 141 | // Render the <xxx> opening tag. 142 | if err := w.WriteByte('<'); err != nil { 143 | return err 144 | } 145 | if _, err := w.WriteString(n.Data); err != nil { 146 | return err 147 | } 148 | for _, a := range n.Attr { 149 | if err := w.WriteByte(' '); err != nil { 150 | return err 151 | } 152 | if a.Namespace != "" { 153 | if _, err := w.WriteString(a.Namespace); err != nil { 154 | return err 155 | } 156 | if err := w.WriteByte(':'); err != nil { 157 | return err 158 | } 159 | } 160 | if _, err := w.WriteString(a.Key); err != nil { 161 | return err 162 | } 163 | if _, err := w.WriteString(`="`); err != nil { 164 | return err 165 | } 166 | if err := escape(w, a.Val); err != nil { 167 | return err 168 | } 169 | if err := w.WriteByte('"'); err != nil { 170 | return err 171 | } 172 | } 173 | if voidElements[n.Data] { 174 | if n.FirstChild != nil { 175 | return fmt.Errorf("html: void element <%s> has child nodes", n.Data) 176 | } 177 | _, err := w.WriteString("/>") 178 | return err 179 | } 180 | if err := w.WriteByte('>'); err != nil { 181 | return err 182 | } 183 | 184 | // Add initial newline where there is danger of a newline beging ignored. 185 | if c := n.FirstChild; c != nil && c.Type == TextNode && strings.HasPrefix(c.Data, "\n") { 186 | switch n.Data { 187 | case "pre", "listing", "textarea": 188 | if err := w.WriteByte('\n'); err != nil { 189 | return err 190 | } 191 | } 192 | } 193 | 194 | // Render any child nodes. 195 | switch n.Data { 196 | case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "xmp": 197 | for c := n.FirstChild; c != nil; c = c.NextSibling { 198 | if c.Type == TextNode { 199 | if _, err := w.WriteString(c.Data); err != nil { 200 | return err 201 | } 202 | } else { 203 | if err := render1(w, c); err != nil { 204 | return err 205 | } 206 | } 207 | } 208 | if n.Data == "plaintext" { 209 | // Don't render anything else. <plaintext> must be the 210 | // last element in the file, with no closing tag. 211 | return plaintextAbort 212 | } 213 | default: 214 | for c := n.FirstChild; c != nil; c = c.NextSibling { 215 | if err := render1(w, c); err != nil { 216 | return err 217 | } 218 | } 219 | } 220 | 221 | // Render the </xxx> closing tag. 222 | if _, err := w.WriteString("</"); err != nil { 223 | return err 224 | } 225 | if _, err := w.WriteString(n.Data); err != nil { 226 | return err 227 | } 228 | return w.WriteByte('>') 229 | } 230 | 231 | // writeQuoted writes s to w surrounded by quotes. Normally it will use double 232 | // quotes, but if s contains a double quote, it will use single quotes. 233 | // It is used for writing the identifiers in a doctype declaration. 234 | // In valid HTML, they can't contain both types of quotes. 235 | func writeQuoted(w writer, s string) error { 236 | var q byte = '"' 237 | if strings.Contains(s, `"`) { 238 | q = '\'' 239 | } 240 | if err := w.WriteByte(q); err != nil { 241 | return err 242 | } 243 | if _, err := w.WriteString(s); err != nil { 244 | return err 245 | } 246 | if err := w.WriteByte(q); err != nil { 247 | return err 248 | } 249 | return nil 250 | } 251 | 252 | // Section 12.1.2, "Elements", gives this list of void elements. Void elements 253 | // are those that can't have any contents. 254 | var voidElements = map[string]bool{ 255 | "area": true, 256 | "base": true, 257 | "br": true, 258 | "col": true, 259 | "command": true, 260 | "embed": true, 261 | "hr": true, 262 | "img": true, 263 | "input": true, 264 | "keygen": true, 265 | "link": true, 266 | "meta": true, 267 | "param": true, 268 | "source": true, 269 | "track": true, 270 | "wbr": true, 271 | } 272 | -------------------------------------------------------------------------------- /vendor/gopkg.in/cheggaaa/pb.v1/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2015, Sergey Cherepanov 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /vendor/gopkg.in/cheggaaa/pb.v1/README.md: -------------------------------------------------------------------------------- 1 | # Terminal progress bar for Go 2 | 3 | Simple progress bar for console programs. 4 | 5 | 6 | ## Installation 7 | 8 | ``` 9 | go get gopkg.in/cheggaaa/pb.v1 10 | ``` 11 | 12 | ## Usage 13 | 14 | ```Go 15 | package main 16 | 17 | import ( 18 | "gopkg.in/cheggaaa/pb.v1" 19 | "time" 20 | ) 21 | 22 | func main() { 23 | count := 100000 24 | bar := pb.StartNew(count) 25 | for i := 0; i < count; i++ { 26 | bar.Increment() 27 | time.Sleep(time.Millisecond) 28 | } 29 | bar.FinishPrint("The End!") 30 | } 31 | 32 | ``` 33 | 34 | Result will be like this: 35 | 36 | ``` 37 | > go run test.go 38 | 37158 / 100000 [================>_______________________________] 37.16% 1m11s 39 | ``` 40 | 41 | ## Customization 42 | 43 | ```Go 44 | // create bar 45 | bar := pb.New(count) 46 | 47 | // refresh info every second (default 200ms) 48 | bar.SetRefreshRate(time.Second) 49 | 50 | // show percents (by default already true) 51 | bar.ShowPercent = true 52 | 53 | // show bar (by default already true) 54 | bar.ShowBar = true 55 | 56 | // no counters 57 | bar.ShowCounters = false 58 | 59 | // show "time left" 60 | bar.ShowTimeLeft = true 61 | 62 | // show average speed 63 | bar.ShowSpeed = true 64 | 65 | // sets the width of the progress bar 66 | bar.SetWidth(80) 67 | 68 | // sets the width of the progress bar, but if terminal size smaller will be ignored 69 | bar.SetMaxWidth(80) 70 | 71 | // convert output to readable format (like KB, MB) 72 | bar.SetUnits(pb.U_BYTES) 73 | 74 | // and start 75 | bar.Start() 76 | ``` 77 | 78 | ## Progress bar for IO Operations 79 | 80 | ```go 81 | // create and start bar 82 | bar := pb.New(myDataLen).SetUnits(pb.U_BYTES) 83 | bar.Start() 84 | 85 | // my io.Reader 86 | r := myReader 87 | 88 | // my io.Writer 89 | w := myWriter 90 | 91 | // create proxy reader 92 | reader := bar.NewProxyReader(r) 93 | 94 | // and copy from pb reader 95 | io.Copy(w, reader) 96 | 97 | ``` 98 | 99 | ```go 100 | // create and start bar 101 | bar := pb.New(myDataLen).SetUnits(pb.U_BYTES) 102 | bar.Start() 103 | 104 | // my io.Reader 105 | r := myReader 106 | 107 | // my io.Writer 108 | w := myWriter 109 | 110 | // create multi writer 111 | writer := io.MultiWriter(w, bar) 112 | 113 | // and copy 114 | io.Copy(writer, r) 115 | 116 | bar.Finish() 117 | ``` 118 | 119 | ## Custom Progress Bar Look-and-feel 120 | 121 | ```go 122 | bar.Format("<.- >") 123 | ``` 124 | 125 | ## Multiple Progress Bars (experimental and unstable) 126 | 127 | Do not print to terminal while pool is active. 128 | 129 | ```go 130 | package main 131 | 132 | import ( 133 | "math/rand" 134 | "sync" 135 | "time" 136 | 137 | "gopkg.in/cheggaaa/pb.v1" 138 | ) 139 | 140 | func main() { 141 | // create bars 142 | first := pb.New(200).Prefix("First ") 143 | second := pb.New(200).Prefix("Second ") 144 | third := pb.New(200).Prefix("Third ") 145 | // start pool 146 | pool, err := pb.StartPool(first, second, third) 147 | if err != nil { 148 | panic(err) 149 | } 150 | // update bars 151 | wg := new(sync.WaitGroup) 152 | for _, bar := range []*pb.ProgressBar{first, second, third} { 153 | wg.Add(1) 154 | go func(cb *pb.ProgressBar) { 155 | for n := 0; n < 200; n++ { 156 | cb.Increment() 157 | time.Sleep(time.Millisecond * time.Duration(rand.Intn(100))) 158 | } 159 | cb.Finish() 160 | wg.Done() 161 | }(bar) 162 | } 163 | wg.Wait() 164 | // close pool 165 | pool.Stop() 166 | } 167 | ``` 168 | 169 | The result will be as follows: 170 | 171 | ``` 172 | $ go run example/multiple.go 173 | First 141 / 1000 [===============>---------------------------------------] 14.10 % 44s 174 | Second 139 / 1000 [==============>---------------------------------------] 13.90 % 44s 175 | Third 152 / 1000 [================>--------------------------------------] 15.20 % 40s 176 | ``` 177 | -------------------------------------------------------------------------------- /vendor/gopkg.in/cheggaaa/pb.v1/format.go: -------------------------------------------------------------------------------- 1 | package pb 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "time" 7 | ) 8 | 9 | type Units int 10 | 11 | const ( 12 | // U_NO are default units, they represent a simple value and are not formatted at all. 13 | U_NO Units = iota 14 | // U_BYTES units are formatted in a human readable way (b, Bb, Mb, ...) 15 | U_BYTES 16 | // U_DURATION units are formatted in a human readable way (3h14m15s) 17 | U_DURATION 18 | ) 19 | 20 | func Format(i int64) *formatter { 21 | return &formatter{n: i} 22 | } 23 | 24 | type formatter struct { 25 | n int64 26 | unit Units 27 | width int 28 | perSec bool 29 | } 30 | 31 | func (f *formatter) Value(n int64) *formatter { 32 | f.n = n 33 | return f 34 | } 35 | 36 | func (f *formatter) To(unit Units) *formatter { 37 | f.unit = unit 38 | return f 39 | } 40 | 41 | func (f *formatter) Width(width int) *formatter { 42 | f.width = width 43 | return f 44 | } 45 | 46 | func (f *formatter) PerSec() *formatter { 47 | f.perSec = true 48 | return f 49 | } 50 | 51 | func (f *formatter) String() (out string) { 52 | switch f.unit { 53 | case U_BYTES: 54 | out = formatBytes(f.n) 55 | case U_DURATION: 56 | d := time.Duration(f.n) 57 | if d > time.Hour*24 { 58 | out = fmt.Sprintf("%dd", d/24/time.Hour) 59 | d -= (d / time.Hour / 24) * (time.Hour * 24) 60 | } 61 | out = fmt.Sprintf("%s%v", out, d) 62 | default: 63 | out = fmt.Sprintf(fmt.Sprintf("%%%dd", f.width), f.n) 64 | } 65 | if f.perSec { 66 | out += "/s" 67 | } 68 | return 69 | } 70 | 71 | // Convert bytes to human readable string. Like a 2 MB, 64.2 KB, 52 B 72 | func formatBytes(i int64) (result string) { 73 | switch { 74 | case i > (1024 * 1024 * 1024 * 1024): 75 | result = fmt.Sprintf("%.02f TB", float64(i)/1024/1024/1024/1024) 76 | case i > (1024 * 1024 * 1024): 77 | result = fmt.Sprintf("%.02f GB", float64(i)/1024/1024/1024) 78 | case i > (1024 * 1024): 79 | result = fmt.Sprintf("%.02f MB", float64(i)/1024/1024) 80 | case i > 1024: 81 | result = fmt.Sprintf("%.02f KB", float64(i)/1024) 82 | default: 83 | result = fmt.Sprintf("%d B", i) 84 | } 85 | result = strings.Trim(result, " ") 86 | return 87 | } 88 | -------------------------------------------------------------------------------- /vendor/gopkg.in/cheggaaa/pb.v1/pb_appengine.go: -------------------------------------------------------------------------------- 1 | // +build appengine 2 | 3 | package pb 4 | 5 | import "errors" 6 | 7 | // terminalWidth returns width of the terminal, which is not supported 8 | // and should always failed on appengine classic which is a sandboxed PaaS. 9 | func terminalWidth() (int, error) { 10 | return 0, errors.New("Not supported") 11 | } 12 | -------------------------------------------------------------------------------- /vendor/gopkg.in/cheggaaa/pb.v1/pb_nix.go: -------------------------------------------------------------------------------- 1 | // +build linux darwin freebsd netbsd openbsd dragonfly 2 | // +build !appengine 3 | 4 | package pb 5 | 6 | import "syscall" 7 | 8 | const sysIoctl = syscall.SYS_IOCTL 9 | -------------------------------------------------------------------------------- /vendor/gopkg.in/cheggaaa/pb.v1/pb_solaris.go: -------------------------------------------------------------------------------- 1 | // +build solaris 2 | // +build !appengine 3 | 4 | package pb 5 | 6 | const sysIoctl = 54 7 | -------------------------------------------------------------------------------- /vendor/gopkg.in/cheggaaa/pb.v1/pb_win.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package pb 4 | 5 | import ( 6 | "errors" 7 | "fmt" 8 | "os" 9 | "sync" 10 | "syscall" 11 | "unsafe" 12 | ) 13 | 14 | var tty = os.Stdin 15 | 16 | var ( 17 | kernel32 = syscall.NewLazyDLL("kernel32.dll") 18 | 19 | // GetConsoleScreenBufferInfo retrieves information about the 20 | // specified console screen buffer. 21 | // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx 22 | procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") 23 | 24 | // GetConsoleMode retrieves the current input mode of a console's 25 | // input buffer or the current output mode of a console screen buffer. 26 | // https://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx 27 | getConsoleMode = kernel32.NewProc("GetConsoleMode") 28 | 29 | // SetConsoleMode sets the input mode of a console's input buffer 30 | // or the output mode of a console screen buffer. 31 | // https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx 32 | setConsoleMode = kernel32.NewProc("SetConsoleMode") 33 | 34 | // SetConsoleCursorPosition sets the cursor position in the 35 | // specified console screen buffer. 36 | // https://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx 37 | setConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") 38 | ) 39 | 40 | type ( 41 | // Defines the coordinates of the upper left and lower right corners 42 | // of a rectangle. 43 | // See 44 | // http://msdn.microsoft.com/en-us/library/windows/desktop/ms686311(v=vs.85).aspx 45 | smallRect struct { 46 | Left, Top, Right, Bottom int16 47 | } 48 | 49 | // Defines the coordinates of a character cell in a console screen 50 | // buffer. The origin of the coordinate system (0,0) is at the top, left cell 51 | // of the buffer. 52 | // See 53 | // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682119(v=vs.85).aspx 54 | coordinates struct { 55 | X, Y int16 56 | } 57 | 58 | word int16 59 | 60 | // Contains information about a console screen buffer. 61 | // http://msdn.microsoft.com/en-us/library/windows/desktop/ms682093(v=vs.85).aspx 62 | consoleScreenBufferInfo struct { 63 | dwSize coordinates 64 | dwCursorPosition coordinates 65 | wAttributes word 66 | srWindow smallRect 67 | dwMaximumWindowSize coordinates 68 | } 69 | ) 70 | 71 | // terminalWidth returns width of the terminal. 72 | func terminalWidth() (width int, err error) { 73 | var info consoleScreenBufferInfo 74 | _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&info)), 0) 75 | if e != 0 { 76 | return 0, error(e) 77 | } 78 | return int(info.dwSize.X) - 1, nil 79 | } 80 | 81 | func getCursorPos() (pos coordinates, err error) { 82 | var info consoleScreenBufferInfo 83 | _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&info)), 0) 84 | if e != 0 { 85 | return info.dwCursorPosition, error(e) 86 | } 87 | return info.dwCursorPosition, nil 88 | } 89 | 90 | func setCursorPos(pos coordinates) error { 91 | _, _, e := syscall.Syscall(setConsoleCursorPosition.Addr(), 2, uintptr(syscall.Stdout), uintptr(uint32(uint16(pos.Y))<<16|uint32(uint16(pos.X))), 0) 92 | if e != 0 { 93 | return error(e) 94 | } 95 | return nil 96 | } 97 | 98 | var ErrPoolWasStarted = errors.New("Bar pool was started") 99 | 100 | var echoLocked bool 101 | var echoLockMutex sync.Mutex 102 | 103 | var oldState word 104 | 105 | func lockEcho() (quit chan int, err error) { 106 | echoLockMutex.Lock() 107 | defer echoLockMutex.Unlock() 108 | if echoLocked { 109 | err = ErrPoolWasStarted 110 | return 111 | } 112 | echoLocked = true 113 | 114 | if _, _, e := syscall.Syscall(getConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&oldState)), 0); e != 0 { 115 | err = fmt.Errorf("Can't get terminal settings: %v", e) 116 | return 117 | } 118 | 119 | newState := oldState 120 | const ENABLE_ECHO_INPUT = 0x0004 121 | const ENABLE_LINE_INPUT = 0x0002 122 | newState = newState & (^(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT)) 123 | if _, _, e := syscall.Syscall(setConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(newState), 0); e != 0 { 124 | err = fmt.Errorf("Can't set terminal settings: %v", e) 125 | return 126 | } 127 | return 128 | } 129 | 130 | func unlockEcho() (err error) { 131 | echoLockMutex.Lock() 132 | defer echoLockMutex.Unlock() 133 | if !echoLocked { 134 | return 135 | } 136 | echoLocked = false 137 | if _, _, e := syscall.Syscall(setConsoleMode.Addr(), 2, uintptr(syscall.Stdout), uintptr(oldState), 0); e != 0 { 138 | err = fmt.Errorf("Can't set terminal settings") 139 | } 140 | return 141 | } 142 | -------------------------------------------------------------------------------- /vendor/gopkg.in/cheggaaa/pb.v1/pb_x.go: -------------------------------------------------------------------------------- 1 | // +build linux darwin freebsd netbsd openbsd solaris dragonfly 2 | // +build !appengine 3 | 4 | package pb 5 | 6 | import ( 7 | "errors" 8 | "fmt" 9 | "os" 10 | "os/signal" 11 | "runtime" 12 | "sync" 13 | "syscall" 14 | "unsafe" 15 | ) 16 | 17 | const ( 18 | TIOCGWINSZ = 0x5413 19 | TIOCGWINSZ_OSX = 1074295912 20 | ) 21 | 22 | var tty *os.File 23 | 24 | var ErrPoolWasStarted = errors.New("Bar pool was started") 25 | 26 | var echoLocked bool 27 | var echoLockMutex sync.Mutex 28 | 29 | func init() { 30 | var err error 31 | tty, err = os.Open("/dev/tty") 32 | if err != nil { 33 | tty = os.Stdin 34 | } 35 | } 36 | 37 | // terminalWidth returns width of the terminal. 38 | func terminalWidth() (int, error) { 39 | w := new(window) 40 | tio := syscall.TIOCGWINSZ 41 | if runtime.GOOS == "darwin" { 42 | tio = TIOCGWINSZ_OSX 43 | } 44 | res, _, err := syscall.Syscall(sysIoctl, 45 | tty.Fd(), 46 | uintptr(tio), 47 | uintptr(unsafe.Pointer(w)), 48 | ) 49 | if int(res) == -1 { 50 | return 0, err 51 | } 52 | return int(w.Col), nil 53 | } 54 | 55 | var oldState syscall.Termios 56 | 57 | func lockEcho() (quit chan int, err error) { 58 | echoLockMutex.Lock() 59 | defer echoLockMutex.Unlock() 60 | if echoLocked { 61 | err = ErrPoolWasStarted 62 | return 63 | } 64 | echoLocked = true 65 | 66 | fd := tty.Fd() 67 | if _, _, e := syscall.Syscall6(sysIoctl, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); e != 0 { 68 | err = fmt.Errorf("Can't get terminal settings: %v", e) 69 | return 70 | } 71 | 72 | newState := oldState 73 | newState.Lflag &^= syscall.ECHO 74 | newState.Lflag |= syscall.ICANON | syscall.ISIG 75 | newState.Iflag |= syscall.ICRNL 76 | if _, _, e := syscall.Syscall6(sysIoctl, fd, ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); e != 0 { 77 | err = fmt.Errorf("Can't set terminal settings: %v", e) 78 | return 79 | } 80 | quit = make(chan int, 1) 81 | go catchTerminate(quit) 82 | return 83 | } 84 | 85 | func unlockEcho() (err error) { 86 | echoLockMutex.Lock() 87 | defer echoLockMutex.Unlock() 88 | if !echoLocked { 89 | return 90 | } 91 | echoLocked = false 92 | fd := tty.Fd() 93 | if _, _, e := syscall.Syscall6(sysIoctl, fd, ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); e != 0 { 94 | err = fmt.Errorf("Can't set terminal settings") 95 | } 96 | return 97 | } 98 | 99 | // listen exit signals and restore terminal state 100 | func catchTerminate(quit chan int) { 101 | sig := make(chan os.Signal, 1) 102 | signal.Notify(sig, os.Interrupt, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGKILL) 103 | defer signal.Stop(sig) 104 | select { 105 | case <-quit: 106 | unlockEcho() 107 | case <-sig: 108 | unlockEcho() 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /vendor/gopkg.in/cheggaaa/pb.v1/pool.go: -------------------------------------------------------------------------------- 1 | // +build linux darwin freebsd netbsd openbsd solaris dragonfly windows 2 | 3 | package pb 4 | 5 | import ( 6 | "sync" 7 | "time" 8 | ) 9 | 10 | // Create and start new pool with given bars 11 | // You need call pool.Stop() after work 12 | func StartPool(pbs ...*ProgressBar) (pool *Pool, err error) { 13 | pool = new(Pool) 14 | if err = pool.start(); err != nil { 15 | return 16 | } 17 | pool.Add(pbs...) 18 | return 19 | } 20 | 21 | type Pool struct { 22 | RefreshRate time.Duration 23 | bars []*ProgressBar 24 | quit chan int 25 | finishOnce sync.Once 26 | } 27 | 28 | // Add progress bars. 29 | func (p *Pool) Add(pbs ...*ProgressBar) { 30 | for _, bar := range pbs { 31 | bar.ManualUpdate = true 32 | bar.NotPrint = true 33 | bar.Start() 34 | p.bars = append(p.bars, bar) 35 | } 36 | } 37 | 38 | func (p *Pool) start() (err error) { 39 | p.RefreshRate = DefaultRefreshRate 40 | quit, err := lockEcho() 41 | if err != nil { 42 | return 43 | } 44 | p.quit = make(chan int) 45 | go p.writer(quit) 46 | return 47 | } 48 | 49 | func (p *Pool) writer(finish chan int) { 50 | var first = true 51 | for { 52 | select { 53 | case <-time.After(p.RefreshRate): 54 | if p.print(first) { 55 | p.print(false) 56 | finish <- 1 57 | return 58 | } 59 | first = false 60 | case <-p.quit: 61 | finish <- 1 62 | return 63 | } 64 | } 65 | } 66 | 67 | // Restore terminal state and close pool 68 | func (p *Pool) Stop() error { 69 | // Wait until one final refresh has passed. 70 | time.Sleep(p.RefreshRate) 71 | 72 | p.finishOnce.Do(func() { 73 | close(p.quit) 74 | }) 75 | return unlockEcho() 76 | } 77 | -------------------------------------------------------------------------------- /vendor/gopkg.in/cheggaaa/pb.v1/pool_win.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package pb 4 | 5 | import ( 6 | "fmt" 7 | "log" 8 | ) 9 | 10 | func (p *Pool) print(first bool) bool { 11 | var out string 12 | if !first { 13 | coords, err := getCursorPos() 14 | if err != nil { 15 | log.Panic(err) 16 | } 17 | coords.Y -= int16(len(p.bars)) 18 | coords.X = 0 19 | 20 | err = setCursorPos(coords) 21 | if err != nil { 22 | log.Panic(err) 23 | } 24 | } 25 | isFinished := true 26 | for _, bar := range p.bars { 27 | if !bar.isFinish { 28 | isFinished = false 29 | } 30 | bar.Update() 31 | out += fmt.Sprintf("\r%s\n", bar.String()) 32 | } 33 | fmt.Print(out) 34 | return isFinished 35 | } 36 | -------------------------------------------------------------------------------- /vendor/gopkg.in/cheggaaa/pb.v1/pool_x.go: -------------------------------------------------------------------------------- 1 | // +build linux darwin freebsd netbsd openbsd solaris dragonfly 2 | 3 | package pb 4 | 5 | import "fmt" 6 | 7 | func (p *Pool) print(first bool) bool { 8 | var out string 9 | if !first { 10 | out = fmt.Sprintf("\033[%dA", len(p.bars)) 11 | } 12 | isFinished := true 13 | for _, bar := range p.bars { 14 | if !bar.isFinish { 15 | isFinished = false 16 | } 17 | bar.Update() 18 | out += fmt.Sprintf("\r%s\n", bar.String()) 19 | } 20 | fmt.Print(out) 21 | return isFinished 22 | } 23 | -------------------------------------------------------------------------------- /vendor/gopkg.in/cheggaaa/pb.v1/reader.go: -------------------------------------------------------------------------------- 1 | package pb 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | // It's proxy reader, implement io.Reader 8 | type Reader struct { 9 | io.Reader 10 | bar *ProgressBar 11 | } 12 | 13 | func (r *Reader) Read(p []byte) (n int, err error) { 14 | n, err = r.Reader.Read(p) 15 | r.bar.Add(n) 16 | return 17 | } 18 | 19 | // Close the reader when it implements io.Closer 20 | func (r *Reader) Close() (err error) { 21 | if closer, ok := r.Reader.(io.Closer); ok { 22 | return closer.Close() 23 | } 24 | return 25 | } 26 | -------------------------------------------------------------------------------- /vendor/gopkg.in/cheggaaa/pb.v1/runecount.go: -------------------------------------------------------------------------------- 1 | package pb 2 | 3 | import ( 4 | "github.com/mattn/go-runewidth" 5 | "regexp" 6 | ) 7 | 8 | // Finds the control character sequences (like colors) 9 | var ctrlFinder = regexp.MustCompile("\x1b\x5b[0-9]+\x6d") 10 | 11 | func escapeAwareRuneCountInString(s string) int { 12 | n := runewidth.StringWidth(s) 13 | for _, sm := range ctrlFinder.FindAllString(s, -1) { 14 | n -= len(sm) 15 | } 16 | return n 17 | } 18 | -------------------------------------------------------------------------------- /vendor/gopkg.in/cheggaaa/pb.v1/termios_bsd.go: -------------------------------------------------------------------------------- 1 | // +build darwin freebsd netbsd openbsd dragonfly 2 | // +build !appengine 3 | 4 | package pb 5 | 6 | import "syscall" 7 | 8 | const ioctlReadTermios = syscall.TIOCGETA 9 | const ioctlWriteTermios = syscall.TIOCSETA 10 | -------------------------------------------------------------------------------- /vendor/gopkg.in/cheggaaa/pb.v1/termios_nix.go: -------------------------------------------------------------------------------- 1 | // +build linux solaris 2 | // +build !appengine 3 | 4 | package pb 5 | 6 | const ioctlReadTermios = 0x5401 // syscall.TCGETS 7 | const ioctlWriteTermios = 0x5402 // syscall.TCSETS 8 | -------------------------------------------------------------------------------- /vendor/vendor.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "", 3 | "ignore": "test", 4 | "package": [ 5 | { 6 | "checksumSHA1": "bOODD4Gbw3GfcuQPU2dI40crxxk=", 7 | "path": "github.com/PuerkitoBio/goquery", 8 | "revision": "c641b87978a16588afa9ab1a02e02c8b8e05b314", 9 | "revisionTime": "2017-03-08T19:48:52Z" 10 | }, 11 | { 12 | "checksumSHA1": "2rWSos1D+xqNSGkvEMOG3pntod8=", 13 | "path": "github.com/andybalholm/cascadia", 14 | "revision": "349dd0209470eabd9514242c688c403c0926d266", 15 | "revisionTime": "2016-12-24T14:14:13Z" 16 | }, 17 | { 18 | "checksumSHA1": "MzsAuz46V8uY0nNlHIY0XgL/7ys=", 19 | "path": "github.com/jroimartin/gocui", 20 | "revision": "7ac95c981b8a07b624ab197b3ad5813fdfbe4864", 21 | "revisionTime": "2016-11-24T21:23:44Z" 22 | }, 23 | { 24 | "checksumSHA1": "yf185lmVPcvXjLZuPT1s4JzxI18=", 25 | "path": "github.com/mattn/go-runewidth", 26 | "revision": "737072b4e32b7a5018b4a7125da8d12de90e8045", 27 | "revisionTime": "2016-10-12T01:35:12Z" 28 | }, 29 | { 30 | "checksumSHA1": "403/aj8ClsMSvpwL753atQuOIeU=", 31 | "path": "github.com/mauidude/go-readability", 32 | "revision": "2f30b1a346f19ddab94ffe0cb788331be9399de2", 33 | "revisionTime": "2014-12-16T01:23:17Z" 34 | }, 35 | { 36 | "checksumSHA1": "pcxo5ATo/IV/Co8/F4RmBTL+mQs=", 37 | "path": "github.com/nsf/termbox-go", 38 | "revision": "abe82ce5fb7a42fbd6784a5ceb71aff977e09ed8", 39 | "revisionTime": "2016-12-05T19:42:51Z" 40 | }, 41 | { 42 | "checksumSHA1": "vqc3a+oTUGX8PmD0TS+qQ7gmN8I=", 43 | "path": "golang.org/x/net/html", 44 | "revision": "45e771701b814666a7eb299e6c7a57d0b1799e91", 45 | "revisionTime": "2016-12-15T19:42:18Z" 46 | }, 47 | { 48 | "checksumSHA1": "00eQaGynDYrv3tL+C7l9xH0IDZg=", 49 | "path": "golang.org/x/net/html/atom", 50 | "revision": "45e771701b814666a7eb299e6c7a57d0b1799e91", 51 | "revisionTime": "2016-12-15T19:42:18Z" 52 | }, 53 | { 54 | "checksumSHA1": "mj71zOfqkkSk7YwAQHY1AiiEUmU=", 55 | "path": "gopkg.in/cheggaaa/pb.v1", 56 | "revision": "d7e6ca3010b6f084d8056847f55d7f572f180678", 57 | "revisionTime": "2016-11-21T09:29:06Z" 58 | } 59 | ], 60 | "rootPath": "github.com/drgarcia1986/gonews" 61 | } 62 | --------------------------------------------------------------------------------