aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/gizak
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/gizak')
-rw-r--r--vendor/github.com/gizak/termui/.gitignore26
-rw-r--r--vendor/github.com/gizak/termui/.travis.yml6
-rw-r--r--vendor/github.com/gizak/termui/LICENSE22
-rw-r--r--vendor/github.com/gizak/termui/README.md150
-rw-r--r--vendor/github.com/gizak/termui/barchart.go149
-rw-r--r--vendor/github.com/gizak/termui/block.go240
-rw-r--r--vendor/github.com/gizak/termui/block_common.go20
-rw-r--r--vendor/github.com/gizak/termui/block_windows.go14
-rw-r--r--vendor/github.com/gizak/termui/buffer.go106
-rw-r--r--vendor/github.com/gizak/termui/canvas.go72
-rwxr-xr-xvendor/github.com/gizak/termui/config26
-rw-r--r--vendor/github.com/gizak/termui/doc.go29
-rw-r--r--vendor/github.com/gizak/termui/events.go323
-rw-r--r--vendor/github.com/gizak/termui/gauge.go109
-rw-r--r--vendor/github.com/gizak/termui/glide.lock14
-rw-r--r--vendor/github.com/gizak/termui/glide.yaml8
-rw-r--r--vendor/github.com/gizak/termui/grid.go279
-rw-r--r--vendor/github.com/gizak/termui/helper.go222
-rw-r--r--vendor/github.com/gizak/termui/linechart.go331
-rw-r--r--vendor/github.com/gizak/termui/linechart_others.go11
-rw-r--r--vendor/github.com/gizak/termui/linechart_windows.go11
-rw-r--r--vendor/github.com/gizak/termui/list.go89
-rw-r--r--vendor/github.com/gizak/termui/mbarchart.go242
-rw-r--r--vendor/github.com/gizak/termui/mkdocs.yml28
-rw-r--r--vendor/github.com/gizak/termui/par.go73
-rw-r--r--vendor/github.com/gizak/termui/pos.go78
-rw-r--r--vendor/github.com/gizak/termui/render.go135
-rw-r--r--vendor/github.com/gizak/termui/sparkline.go167
-rw-r--r--vendor/github.com/gizak/termui/textbuilder.go278
-rw-r--r--vendor/github.com/gizak/termui/theme.go140
-rw-r--r--vendor/github.com/gizak/termui/widget.go94
31 files changed, 3492 insertions, 0 deletions
diff --git a/vendor/github.com/gizak/termui/.gitignore b/vendor/github.com/gizak/termui/.gitignore
new file mode 100644
index 000000000..8b156b020
--- /dev/null
+++ b/vendor/github.com/gizak/termui/.gitignore
@@ -0,0 +1,26 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
+.DS_Store
+/vendor
diff --git a/vendor/github.com/gizak/termui/.travis.yml b/vendor/github.com/gizak/termui/.travis.yml
new file mode 100644
index 000000000..206e88740
--- /dev/null
+++ b/vendor/github.com/gizak/termui/.travis.yml
@@ -0,0 +1,6 @@
+language: go
+
+go:
+ - tip
+
+script: go test -v ./ \ No newline at end of file
diff --git a/vendor/github.com/gizak/termui/LICENSE b/vendor/github.com/gizak/termui/LICENSE
new file mode 100644
index 000000000..311ccc74f
--- /dev/null
+++ b/vendor/github.com/gizak/termui/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Zack Guo
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/vendor/github.com/gizak/termui/README.md b/vendor/github.com/gizak/termui/README.md
new file mode 100644
index 000000000..4f3d4a419
--- /dev/null
+++ b/vendor/github.com/gizak/termui/README.md
@@ -0,0 +1,150 @@
+# termui [![Build Status](https://travis-ci.org/gizak/termui.svg?branch=master)](https://travis-ci.org/gizak/termui) [![Doc Status](https://godoc.org/github.com/gizak/termui?status.png)](https://godoc.org/github.com/gizak/termui)
+
+<img src="./_example/dashboard.gif" alt="demo cast under osx 10.10; Terminal.app; Menlo Regular 12pt.)" width="80%">
+
+`termui` is a cross-platform, easy-to-compile, and fully-customizable terminal dashboard. It is inspired by [blessed-contrib](https://github.com/yaronn/blessed-contrib), but purely in Go.
+
+Now version v2 has arrived! It brings new event system, new theme system, new `Buffer` interface and specific colour text rendering. (some docs are missing, but it will be completed soon!)
+
+## Installation
+
+`master` mirrors v2 branch, to install:
+
+ go get -u github.com/gizak/termui
+
+It is recommanded to use locked deps by using [glide](https://glide.sh): move to `termui` src directory then run `glide up`.
+
+For the compatible reason, you can choose to install the legacy version of `termui`:
+
+ go get gopkg.in/gizak/termui.v1
+
+## Usage
+
+### Layout
+
+To use `termui`, the very first thing you may want to know is how to manage layout. `termui` offers two ways of doing this, known as absolute layout and grid layout.
+
+__Absolute layout__
+
+Each widget has an underlying block structure which basically is a box model. It has border, label and padding properties. A border of a widget can be chosen to hide or display (with its border label), you can pick a different front/back colour for the border as well. To display such a widget at a specific location in terminal window, you need to assign `.X`, `.Y`, `.Height`, `.Width` values for each widget before sending it to `.Render`. Let's demonstrate these by a code snippet:
+
+`````go
+ import ui "github.com/gizak/termui" // <- ui shortcut, optional
+
+ func main() {
+ err := ui.Init()
+ if err != nil {
+ panic(err)
+ }
+ defer ui.Close()
+
+ p := ui.NewPar(":PRESS q TO QUIT DEMO")
+ p.Height = 3
+ p.Width = 50
+ p.TextFgColor = ui.ColorWhite
+ p.BorderLabel = "Text Box"
+ p.BorderFg = ui.ColorCyan
+
+ g := ui.NewGauge()
+ g.Percent = 50
+ g.Width = 50
+ g.Height = 3
+ g.Y = 11
+ g.BorderLabel = "Gauge"
+ g.BarColor = ui.ColorRed
+ g.BorderFg = ui.ColorWhite
+ g.BorderLabelFg = ui.ColorCyan
+
+ ui.Render(p, g) // feel free to call Render, it's async and non-block
+
+ // event handler...
+ }
+`````
+
+Note that components can be overlapped (I'd rather call this a feature...), `Render(rs ...Renderer)` renders its args from left to right (i.e. each component's weight is arising from left to right).
+
+__Grid layout:__
+
+<img src="./_example/grid.gif" alt="grid" width="60%">
+
+Grid layout uses [12 columns grid system](http://www.w3schools.com/bootstrap/bootstrap_grid_system.asp) with expressive syntax. To use `Grid`, all we need to do is build a widget tree consisting of `Row`s and `Col`s (Actually a `Col` is also a `Row` but with a widget endpoint attached).
+
+```go
+ import ui "github.com/gizak/termui"
+ // init and create widgets...
+
+ // build
+ ui.Body.AddRows(
+ ui.NewRow(
+ ui.NewCol(6, 0, widget0),
+ ui.NewCol(6, 0, widget1)),
+ ui.NewRow(
+ ui.NewCol(3, 0, widget2),
+ ui.NewCol(3, 0, widget30, widget31, widget32),
+ ui.NewCol(6, 0, widget4)))
+
+ // calculate layout
+ ui.Body.Align()
+
+ ui.Render(ui.Body)
+```
+
+### Events
+
+`termui` ships with a http-like event mux handling system. All events are channeled up from different sources (typing, click, windows resize, custom event) and then encoded as universal `Event` object. `Event.Path` indicates the event type and `Event.Data` stores the event data struct. Add a handler to a certain event is easy as below:
+
+```go
+ // handle key q pressing
+ ui.Handle("/sys/kbd/q", func(ui.Event) {
+ // press q to quit
+ ui.StopLoop()
+ })
+
+ ui.Handle("/sys/kbd/C-x", func(ui.Event) {
+ // handle Ctrl + x combination
+ })
+
+ ui.Handle("/sys/kbd", func(ui.Event) {
+ // handle all other key pressing
+ })
+
+ // handle a 1s timer
+ ui.Handle("/timer/1s", func(e ui.Event) {
+ t := e.Data.(ui.EvtTimer)
+ // t is a EvtTimer
+ if t.Count%2 ==0 {
+ // do something
+ }
+ })
+
+ ui.Loop() // block until StopLoop is called
+```
+
+### Widgets
+
+Click image to see the corresponding demo codes.
+
+[<img src="./_example/par.png" alt="par" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/par.go)
+[<img src="./_example/list.png" alt="list" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/list.go)
+[<img src="./_example/gauge.png" alt="gauge" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/gauge.go)
+[<img src="./_example/linechart.png" alt="linechart" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/linechart.go)
+[<img src="./_example/barchart.png" alt="barchart" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/barchart.go)
+[<img src="./_example/mbarchart.png" alt="barchart" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/mbarchart.go)
+[<img src="./_example/sparklines.png" alt="sparklines" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/sparklines.go)
+
+## GoDoc
+
+[godoc](https://godoc.org/github.com/gizak/termui)
+
+## TODO
+
+- [x] Grid layout
+- [x] Event system
+- [x] Canvas widget
+- [x] Refine APIs
+- [ ] Focusable widgets
+
+## Changelog
+
+## License
+This library is under the [MIT License](http://opensource.org/licenses/MIT)
diff --git a/vendor/github.com/gizak/termui/barchart.go b/vendor/github.com/gizak/termui/barchart.go
new file mode 100644
index 000000000..9e2a10689
--- /dev/null
+++ b/vendor/github.com/gizak/termui/barchart.go
@@ -0,0 +1,149 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import "fmt"
+
+// BarChart creates multiple bars in a widget:
+/*
+ bc := termui.NewBarChart()
+ data := []int{3, 2, 5, 3, 9, 5}
+ bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
+ bc.BorderLabel = "Bar Chart"
+ bc.Data = data
+ bc.Width = 26
+ bc.Height = 10
+ bc.DataLabels = bclabels
+ bc.TextColor = termui.ColorGreen
+ bc.BarColor = termui.ColorRed
+ bc.NumColor = termui.ColorYellow
+*/
+type BarChart struct {
+ Block
+ BarColor Attribute
+ TextColor Attribute
+ NumColor Attribute
+ Data []int
+ DataLabels []string
+ BarWidth int
+ BarGap int
+ CellChar rune
+ labels [][]rune
+ dataNum [][]rune
+ numBar int
+ scale float64
+ max int
+}
+
+// NewBarChart returns a new *BarChart with current theme.
+func NewBarChart() *BarChart {
+ bc := &BarChart{Block: *NewBlock()}
+ bc.BarColor = ThemeAttr("barchart.bar.bg")
+ bc.NumColor = ThemeAttr("barchart.num.fg")
+ bc.TextColor = ThemeAttr("barchart.text.fg")
+ bc.BarGap = 1
+ bc.BarWidth = 3
+ bc.CellChar = ' '
+ return bc
+}
+
+func (bc *BarChart) layout() {
+ bc.numBar = bc.innerArea.Dx() / (bc.BarGap + bc.BarWidth)
+ bc.labels = make([][]rune, bc.numBar)
+ bc.dataNum = make([][]rune, len(bc.Data))
+
+ for i := 0; i < bc.numBar && i < len(bc.DataLabels) && i < len(bc.Data); i++ {
+ bc.labels[i] = trimStr2Runes(bc.DataLabels[i], bc.BarWidth)
+ n := bc.Data[i]
+ s := fmt.Sprint(n)
+ bc.dataNum[i] = trimStr2Runes(s, bc.BarWidth)
+ }
+
+ //bc.max = bc.Data[0] // what if Data is nil? Sometimes when bar graph is nill it produces panic with panic: runtime error: index out of range
+ // Asign a negative value to get maxvalue auto-populates
+ if bc.max == 0 {
+ bc.max = -1
+ }
+ for i := 0; i < len(bc.Data); i++ {
+ if bc.max < bc.Data[i] {
+ bc.max = bc.Data[i]
+ }
+ }
+ bc.scale = float64(bc.max) / float64(bc.innerArea.Dy()-1)
+}
+
+func (bc *BarChart) SetMax(max int) {
+
+ if max > 0 {
+ bc.max = max
+ }
+}
+
+// Buffer implements Bufferer interface.
+func (bc *BarChart) Buffer() Buffer {
+ buf := bc.Block.Buffer()
+ bc.layout()
+
+ for i := 0; i < bc.numBar && i < len(bc.Data) && i < len(bc.DataLabels); i++ {
+ h := int(float64(bc.Data[i]) / bc.scale)
+ oftX := i * (bc.BarWidth + bc.BarGap)
+
+ barBg := bc.Bg
+ barFg := bc.BarColor
+
+ if bc.CellChar == ' ' {
+ barBg = bc.BarColor
+ barFg = ColorDefault
+ if bc.BarColor == ColorDefault { // the same as above
+ barBg |= AttrReverse
+ }
+ }
+
+ // plot bar
+ for j := 0; j < bc.BarWidth; j++ {
+ for k := 0; k < h; k++ {
+ c := Cell{
+ Ch: bc.CellChar,
+ Bg: barBg,
+ Fg: barFg,
+ }
+
+ x := bc.innerArea.Min.X + i*(bc.BarWidth+bc.BarGap) + j
+ y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 - k
+ buf.Set(x, y, c)
+ }
+ }
+ // plot text
+ for j, k := 0, 0; j < len(bc.labels[i]); j++ {
+ w := charWidth(bc.labels[i][j])
+ c := Cell{
+ Ch: bc.labels[i][j],
+ Bg: bc.Bg,
+ Fg: bc.TextColor,
+ }
+ y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 1
+ x := bc.innerArea.Min.X + oftX + k
+ buf.Set(x, y, c)
+ k += w
+ }
+ // plot num
+ for j := 0; j < len(bc.dataNum[i]); j++ {
+ c := Cell{
+ Ch: bc.dataNum[i][j],
+ Fg: bc.NumColor,
+ Bg: barBg,
+ }
+
+ if h == 0 {
+ c.Bg = bc.Bg
+ }
+ x := bc.innerArea.Min.X + oftX + (bc.BarWidth-len(bc.dataNum[i]))/2 + j
+ y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2
+ buf.Set(x, y, c)
+ }
+ }
+
+ return buf
+}
diff --git a/vendor/github.com/gizak/termui/block.go b/vendor/github.com/gizak/termui/block.go
new file mode 100644
index 000000000..418738c8d
--- /dev/null
+++ b/vendor/github.com/gizak/termui/block.go
@@ -0,0 +1,240 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import "image"
+
+// Hline is a horizontal line.
+type Hline struct {
+ X int
+ Y int
+ Len int
+ Fg Attribute
+ Bg Attribute
+}
+
+// Vline is a vertical line.
+type Vline struct {
+ X int
+ Y int
+ Len int
+ Fg Attribute
+ Bg Attribute
+}
+
+// Buffer draws a horizontal line.
+func (l Hline) Buffer() Buffer {
+ if l.Len <= 0 {
+ return NewBuffer()
+ }
+ return NewFilledBuffer(l.X, l.Y, l.X+l.Len, l.Y+1, HORIZONTAL_LINE, l.Fg, l.Bg)
+}
+
+// Buffer draws a vertical line.
+func (l Vline) Buffer() Buffer {
+ if l.Len <= 0 {
+ return NewBuffer()
+ }
+ return NewFilledBuffer(l.X, l.Y, l.X+1, l.Y+l.Len, VERTICAL_LINE, l.Fg, l.Bg)
+}
+
+// Buffer draws a box border.
+func (b Block) drawBorder(buf Buffer) {
+ if !b.Border {
+ return
+ }
+
+ min := b.area.Min
+ max := b.area.Max
+
+ x0 := min.X
+ y0 := min.Y
+ x1 := max.X - 1
+ y1 := max.Y - 1
+
+ // draw lines
+ if b.BorderTop {
+ buf.Merge(Hline{x0, y0, x1 - x0, b.BorderFg, b.BorderBg}.Buffer())
+ }
+ if b.BorderBottom {
+ buf.Merge(Hline{x0, y1, x1 - x0, b.BorderFg, b.BorderBg}.Buffer())
+ }
+ if b.BorderLeft {
+ buf.Merge(Vline{x0, y0, y1 - y0, b.BorderFg, b.BorderBg}.Buffer())
+ }
+ if b.BorderRight {
+ buf.Merge(Vline{x1, y0, y1 - y0, b.BorderFg, b.BorderBg}.Buffer())
+ }
+
+ // draw corners
+ if b.BorderTop && b.BorderLeft && b.area.Dx() > 0 && b.area.Dy() > 0 {
+ buf.Set(x0, y0, Cell{TOP_LEFT, b.BorderFg, b.BorderBg})
+ }
+ if b.BorderTop && b.BorderRight && b.area.Dx() > 1 && b.area.Dy() > 0 {
+ buf.Set(x1, y0, Cell{TOP_RIGHT, b.BorderFg, b.BorderBg})
+ }
+ if b.BorderBottom && b.BorderLeft && b.area.Dx() > 0 && b.area.Dy() > 1 {
+ buf.Set(x0, y1, Cell{BOTTOM_LEFT, b.BorderFg, b.BorderBg})
+ }
+ if b.BorderBottom && b.BorderRight && b.area.Dx() > 1 && b.area.Dy() > 1 {
+ buf.Set(x1, y1, Cell{BOTTOM_RIGHT, b.BorderFg, b.BorderBg})
+ }
+}
+
+func (b Block) drawBorderLabel(buf Buffer) {
+ maxTxtW := b.area.Dx() - 2
+ tx := DTrimTxCls(DefaultTxBuilder.Build(b.BorderLabel, b.BorderLabelFg, b.BorderLabelBg), maxTxtW)
+
+ for i, w := 0, 0; i < len(tx); i++ {
+ buf.Set(b.area.Min.X+1+w, b.area.Min.Y, tx[i])
+ w += tx[i].Width()
+ }
+}
+
+// Block is a base struct for all other upper level widgets,
+// consider it as css: display:block.
+// Normally you do not need to create it manually.
+type Block struct {
+ area image.Rectangle
+ innerArea image.Rectangle
+ X int
+ Y int
+ Border bool
+ BorderFg Attribute
+ BorderBg Attribute
+ BorderLeft bool
+ BorderRight bool
+ BorderTop bool
+ BorderBottom bool
+ BorderLabel string
+ BorderLabelFg Attribute
+ BorderLabelBg Attribute
+ Display bool
+ Bg Attribute
+ Width int
+ Height int
+ PaddingTop int
+ PaddingBottom int
+ PaddingLeft int
+ PaddingRight int
+ id string
+ Float Align
+}
+
+// NewBlock returns a *Block which inherits styles from current theme.
+func NewBlock() *Block {
+ b := Block{}
+ b.Display = true
+ b.Border = true
+ b.BorderLeft = true
+ b.BorderRight = true
+ b.BorderTop = true
+ b.BorderBottom = true
+ b.BorderBg = ThemeAttr("border.bg")
+ b.BorderFg = ThemeAttr("border.fg")
+ b.BorderLabelBg = ThemeAttr("label.bg")
+ b.BorderLabelFg = ThemeAttr("label.fg")
+ b.Bg = ThemeAttr("block.bg")
+ b.Width = 2
+ b.Height = 2
+ b.id = GenId()
+ b.Float = AlignNone
+ return &b
+}
+
+func (b Block) Id() string {
+ return b.id
+}
+
+// Align computes box model
+func (b *Block) Align() {
+ // outer
+ b.area.Min.X = 0
+ b.area.Min.Y = 0
+ b.area.Max.X = b.Width
+ b.area.Max.Y = b.Height
+
+ // float
+ b.area = AlignArea(TermRect(), b.area, b.Float)
+ b.area = MoveArea(b.area, b.X, b.Y)
+
+ // inner
+ b.innerArea.Min.X = b.area.Min.X + b.PaddingLeft
+ b.innerArea.Min.Y = b.area.Min.Y + b.PaddingTop
+ b.innerArea.Max.X = b.area.Max.X - b.PaddingRight
+ b.innerArea.Max.Y = b.area.Max.Y - b.PaddingBottom
+
+ if b.Border {
+ if b.BorderLeft {
+ b.innerArea.Min.X++
+ }
+ if b.BorderRight {
+ b.innerArea.Max.X--
+ }
+ if b.BorderTop {
+ b.innerArea.Min.Y++
+ }
+ if b.BorderBottom {
+ b.innerArea.Max.Y--
+ }
+ }
+}
+
+// InnerBounds returns the internal bounds of the block after aligning and
+// calculating the padding and border, if any.
+func (b *Block) InnerBounds() image.Rectangle {
+ b.Align()
+ return b.innerArea
+}
+
+// Buffer implements Bufferer interface.
+// Draw background and border (if any).
+func (b *Block) Buffer() Buffer {
+ b.Align()
+
+ buf := NewBuffer()
+ buf.SetArea(b.area)
+ buf.Fill(' ', ColorDefault, b.Bg)
+
+ b.drawBorder(buf)
+ b.drawBorderLabel(buf)
+
+ return buf
+}
+
+// GetHeight implements GridBufferer.
+// It returns current height of the block.
+func (b Block) GetHeight() int {
+ return b.Height
+}
+
+// SetX implements GridBufferer interface, which sets block's x position.
+func (b *Block) SetX(x int) {
+ b.X = x
+}
+
+// SetY implements GridBufferer interface, it sets y position for block.
+func (b *Block) SetY(y int) {
+ b.Y = y
+}
+
+// SetWidth implements GridBuffer interface, it sets block's width.
+func (b *Block) SetWidth(w int) {
+ b.Width = w
+}
+
+func (b Block) InnerWidth() int {
+ return b.innerArea.Dx()
+}
+
+func (b Block) InnerHeight() int {
+ return b.innerArea.Dy()
+}
+
+func (b Block) InnerX() int {
+ return b.innerArea.Min.X
+}
+
+func (b Block) InnerY() int { return b.innerArea.Min.Y }
diff --git a/vendor/github.com/gizak/termui/block_common.go b/vendor/github.com/gizak/termui/block_common.go
new file mode 100644
index 000000000..0f972cbbd
--- /dev/null
+++ b/vendor/github.com/gizak/termui/block_common.go
@@ -0,0 +1,20 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+// +build !windows
+
+package termui
+
+const TOP_RIGHT = '┐'
+const VERTICAL_LINE = '│'
+const HORIZONTAL_LINE = '─'
+const TOP_LEFT = '┌'
+const BOTTOM_RIGHT = '┘'
+const BOTTOM_LEFT = '└'
+const VERTICAL_LEFT = '┤'
+const VERTICAL_RIGHT = '├'
+const HORIZONTAL_DOWN = '┬'
+const HORIZONTAL_UP = '┴'
+const QUOTA_LEFT = '«'
+const QUOTA_RIGHT = '»'
diff --git a/vendor/github.com/gizak/termui/block_windows.go b/vendor/github.com/gizak/termui/block_windows.go
new file mode 100644
index 000000000..4dbc017de
--- /dev/null
+++ b/vendor/github.com/gizak/termui/block_windows.go
@@ -0,0 +1,14 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+// +build windows
+
+package termui
+
+const TOP_RIGHT = '+'
+const VERTICAL_LINE = '|'
+const HORIZONTAL_LINE = '-'
+const TOP_LEFT = '+'
+const BOTTOM_RIGHT = '+'
+const BOTTOM_LEFT = '+'
diff --git a/vendor/github.com/gizak/termui/buffer.go b/vendor/github.com/gizak/termui/buffer.go
new file mode 100644
index 000000000..60e77863a
--- /dev/null
+++ b/vendor/github.com/gizak/termui/buffer.go
@@ -0,0 +1,106 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import "image"
+
+// Cell is a rune with assigned Fg and Bg
+type Cell struct {
+ Ch rune
+ Fg Attribute
+ Bg Attribute
+}
+
+// Buffer is a renderable rectangle cell data container.
+type Buffer struct {
+ Area image.Rectangle // selected drawing area
+ CellMap map[image.Point]Cell
+}
+
+// At returns the cell at (x,y).
+func (b Buffer) At(x, y int) Cell {
+ return b.CellMap[image.Pt(x, y)]
+}
+
+// Set assigns a char to (x,y)
+func (b Buffer) Set(x, y int, c Cell) {
+ b.CellMap[image.Pt(x, y)] = c
+}
+
+// Bounds returns the domain for which At can return non-zero color.
+func (b Buffer) Bounds() image.Rectangle {
+ x0, y0, x1, y1 := 0, 0, 0, 0
+ for p := range b.CellMap {
+ if p.X > x1 {
+ x1 = p.X
+ }
+ if p.X < x0 {
+ x0 = p.X
+ }
+ if p.Y > y1 {
+ y1 = p.Y
+ }
+ if p.Y < y0 {
+ y0 = p.Y
+ }
+ }
+ return image.Rect(x0, y0, x1, y1)
+}
+
+// SetArea assigns a new rect area to Buffer b.
+func (b *Buffer) SetArea(r image.Rectangle) {
+ b.Area.Max = r.Max
+ b.Area.Min = r.Min
+}
+
+// Sync sets drawing area to the buffer's bound
+func (b Buffer) Sync() {
+ b.SetArea(b.Bounds())
+}
+
+// NewCell returns a new cell
+func NewCell(ch rune, fg, bg Attribute) Cell {
+ return Cell{ch, fg, bg}
+}
+
+// Merge merges bs Buffers onto b
+func (b *Buffer) Merge(bs ...Buffer) {
+ for _, buf := range bs {
+ for p, v := range buf.CellMap {
+ b.Set(p.X, p.Y, v)
+ }
+ b.SetArea(b.Area.Union(buf.Area))
+ }
+}
+
+// NewBuffer returns a new Buffer
+func NewBuffer() Buffer {
+ return Buffer{
+ CellMap: make(map[image.Point]Cell),
+ Area: image.Rectangle{}}
+}
+
+// Fill fills the Buffer b with ch,fg and bg.
+func (b Buffer) Fill(ch rune, fg, bg Attribute) {
+ for x := b.Area.Min.X; x < b.Area.Max.X; x++ {
+ for y := b.Area.Min.Y; y < b.Area.Max.Y; y++ {
+ b.Set(x, y, Cell{ch, fg, bg})
+ }
+ }
+}
+
+// NewFilledBuffer returns a new Buffer filled with ch, fb and bg.
+func NewFilledBuffer(x0, y0, x1, y1 int, ch rune, fg, bg Attribute) Buffer {
+ buf := NewBuffer()
+ buf.Area.Min = image.Pt(x0, y0)
+ buf.Area.Max = image.Pt(x1, y1)
+
+ for x := buf.Area.Min.X; x < buf.Area.Max.X; x++ {
+ for y := buf.Area.Min.Y; y < buf.Area.Max.Y; y++ {
+ buf.Set(x, y, Cell{ch, fg, bg})
+ }
+ }
+ return buf
+}
diff --git a/vendor/github.com/gizak/termui/canvas.go b/vendor/github.com/gizak/termui/canvas.go
new file mode 100644
index 000000000..4173780f3
--- /dev/null
+++ b/vendor/github.com/gizak/termui/canvas.go
@@ -0,0 +1,72 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+/*
+dots:
+ ,___,
+ |1 4|
+ |2 5|
+ |3 6|
+ |7 8|
+ `````
+*/
+
+var brailleBase = '\u2800'
+
+var brailleOftMap = [4][2]rune{
+ {'\u0001', '\u0008'},
+ {'\u0002', '\u0010'},
+ {'\u0004', '\u0020'},
+ {'\u0040', '\u0080'}}
+
+// Canvas contains drawing map: i,j -> rune
+type Canvas map[[2]int]rune
+
+// NewCanvas returns an empty Canvas
+func NewCanvas() Canvas {
+ return make(map[[2]int]rune)
+}
+
+func chOft(x, y int) rune {
+ return brailleOftMap[y%4][x%2]
+}
+
+func (c Canvas) rawCh(x, y int) rune {
+ if ch, ok := c[[2]int{x, y}]; ok {
+ return ch
+ }
+ return '\u0000' //brailleOffset
+}
+
+// return coordinate in terminal
+func chPos(x, y int) (int, int) {
+ return y / 4, x / 2
+}
+
+// Set sets a point (x,y) in the virtual coordinate
+func (c Canvas) Set(x, y int) {
+ i, j := chPos(x, y)
+ ch := c.rawCh(i, j)
+ ch |= chOft(x, y)
+ c[[2]int{i, j}] = ch
+}
+
+// Unset removes point (x,y)
+func (c Canvas) Unset(x, y int) {
+ i, j := chPos(x, y)
+ ch := c.rawCh(i, j)
+ ch &= ^chOft(x, y)
+ c[[2]int{i, j}] = ch
+}
+
+// Buffer returns un-styled points
+func (c Canvas) Buffer() Buffer {
+ buf := NewBuffer()
+ for k, v := range c {
+ buf.Set(k[0], k[1], Cell{Ch: v + brailleBase})
+ }
+ return buf
+}
diff --git a/vendor/github.com/gizak/termui/config b/vendor/github.com/gizak/termui/config
new file mode 100755
index 000000000..18fd6a401
--- /dev/null
+++ b/vendor/github.com/gizak/termui/config
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl6
+
+use v6;
+
+my $copyright = '// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+';
+
+sub MAIN('update-docstr', Str $srcp) {
+ if $srcp.IO.f {
+ $_ = $srcp.IO.slurp;
+ if m/^ \/\/\s Copyright .+? \n\n/ {
+ unless ~$/ eq $copyright {
+ s/^ \/\/\s Copyright .+? \n\n /$copyright/;
+ spurt $srcp, $_;
+ say "[updated] doc string for:"~$srcp;
+ }
+ } else {
+ say "[added] doc string for "~$srcp~" (no match found)";
+ $_ = $copyright ~ $_;
+ spurt $srcp, $_;
+ }
+ }
+}
diff --git a/vendor/github.com/gizak/termui/doc.go b/vendor/github.com/gizak/termui/doc.go
new file mode 100644
index 000000000..0d7108d30
--- /dev/null
+++ b/vendor/github.com/gizak/termui/doc.go
@@ -0,0 +1,29 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+/*
+Package termui is a library designed for creating command line UI. For more info, goto http://github.com/gizak/termui
+
+A simplest example:
+ package main
+
+ import ui "github.com/gizak/termui"
+
+ func main() {
+ if err:=ui.Init(); err != nil {
+ panic(err)
+ }
+ defer ui.Close()
+
+ g := ui.NewGauge()
+ g.Percent = 50
+ g.Width = 50
+ g.BorderLabel = "Gauge"
+
+ ui.Render(g)
+
+ ui.Loop()
+ }
+*/
+package termui
diff --git a/vendor/github.com/gizak/termui/events.go b/vendor/github.com/gizak/termui/events.go
new file mode 100644
index 000000000..6627f8942
--- /dev/null
+++ b/vendor/github.com/gizak/termui/events.go
@@ -0,0 +1,323 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import (
+ "path"
+ "strconv"
+ "sync"
+ "time"
+
+ "github.com/nsf/termbox-go"
+)
+
+type Event struct {
+ Type string
+ Path string
+ From string
+ To string
+ Data interface{}
+ Time int64
+}
+
+var sysEvtChs []chan Event
+
+type EvtKbd struct {
+ KeyStr string
+}
+
+func evtKbd(e termbox.Event) EvtKbd {
+ ek := EvtKbd{}
+
+ k := string(e.Ch)
+ pre := ""
+ mod := ""
+
+ if e.Mod == termbox.ModAlt {
+ mod = "M-"
+ }
+ if e.Ch == 0 {
+ if e.Key > 0xFFFF-12 {
+ k = "<f" + strconv.Itoa(0xFFFF-int(e.Key)+1) + ">"
+ } else if e.Key > 0xFFFF-25 {
+ ks := []string{"<insert>", "<delete>", "<home>", "<end>", "<previous>", "<next>", "<up>", "<down>", "<left>", "<right>"}
+ k = ks[0xFFFF-int(e.Key)-12]
+ }
+
+ if e.Key <= 0x7F {
+ pre = "C-"
+ k = string('a' - 1 + int(e.Key))
+ kmap := map[termbox.Key][2]string{
+ termbox.KeyCtrlSpace: {"C-", "<space>"},
+ termbox.KeyBackspace: {"", "<backspace>"},
+ termbox.KeyTab: {"", "<tab>"},
+ termbox.KeyEnter: {"", "<enter>"},
+ termbox.KeyEsc: {"", "<escape>"},
+ termbox.KeyCtrlBackslash: {"C-", "\\"},
+ termbox.KeyCtrlSlash: {"C-", "/"},
+ termbox.KeySpace: {"", "<space>"},
+ termbox.KeyCtrl8: {"C-", "8"},
+ }
+ if sk, ok := kmap[e.Key]; ok {
+ pre = sk[0]
+ k = sk[1]
+ }
+ }
+ }
+
+ ek.KeyStr = pre + mod + k
+ return ek
+}
+
+func crtTermboxEvt(e termbox.Event) Event {
+ systypemap := map[termbox.EventType]string{
+ termbox.EventKey: "keyboard",
+ termbox.EventResize: "window",
+ termbox.EventMouse: "mouse",
+ termbox.EventError: "error",
+ termbox.EventInterrupt: "interrupt",
+ }
+ ne := Event{From: "/sys", Time: time.Now().Unix()}
+ typ := e.Type
+ ne.Type = systypemap[typ]
+
+ switch typ {
+ case termbox.EventKey:
+ kbd := evtKbd(e)
+ ne.Path = "/sys/kbd/" + kbd.KeyStr
+ ne.Data = kbd
+ case termbox.EventResize:
+ wnd := EvtWnd{}
+ wnd.Width = e.Width
+ wnd.Height = e.Height
+ ne.Path = "/sys/wnd/resize"
+ ne.Data = wnd
+ case termbox.EventError:
+ err := EvtErr(e.Err)
+ ne.Path = "/sys/err"
+ ne.Data = err
+ case termbox.EventMouse:
+ m := EvtMouse{}
+ m.X = e.MouseX
+ m.Y = e.MouseY
+ ne.Path = "/sys/mouse"
+ ne.Data = m
+ }
+ return ne
+}
+
+type EvtWnd struct {
+ Width int
+ Height int
+}
+
+type EvtMouse struct {
+ X int
+ Y int
+ Press string
+}
+
+type EvtErr error
+
+func hookTermboxEvt() {
+ for {
+ e := termbox.PollEvent()
+
+ for _, c := range sysEvtChs {
+ go func(ch chan Event) {
+ ch <- crtTermboxEvt(e)
+ }(c)
+ }
+ }
+}
+
+func NewSysEvtCh() chan Event {
+ ec := make(chan Event)
+ sysEvtChs = append(sysEvtChs, ec)
+ return ec
+}
+
+var DefaultEvtStream = NewEvtStream()
+
+type EvtStream struct {
+ sync.RWMutex
+ srcMap map[string]chan Event
+ stream chan Event
+ wg sync.WaitGroup
+ sigStopLoop chan Event
+ Handlers map[string]func(Event)
+ hook func(Event)
+}
+
+func NewEvtStream() *EvtStream {
+ return &EvtStream{
+ srcMap: make(map[string]chan Event),
+ stream: make(chan Event),
+ Handlers: make(map[string]func(Event)),
+ sigStopLoop: make(chan Event),
+ }
+}
+
+func (es *EvtStream) Init() {
+ es.Merge("internal", es.sigStopLoop)
+ go func() {
+ es.wg.Wait()
+ close(es.stream)
+ }()
+}
+
+func cleanPath(p string) string {
+ if p == "" {
+ return "/"
+ }
+ if p[0] != '/' {
+ p = "/" + p
+ }
+ return path.Clean(p)
+}
+
+func isPathMatch(pattern, path string) bool {
+ if len(pattern) == 0 {
+ return false
+ }
+ n := len(pattern)
+ return len(path) >= n && path[0:n] == pattern
+}
+
+func (es *EvtStream) Merge(name string, ec chan Event) {
+ es.Lock()
+ defer es.Unlock()
+
+ es.wg.Add(1)
+ es.srcMap[name] = ec
+
+ go func(a chan Event) {
+ for n := range a {
+ n.From = name
+ es.stream <- n
+ }
+ es.wg.Done()
+ }(ec)
+}
+
+func (es *EvtStream) Handle(path string, handler func(Event)) {
+ es.Handlers[cleanPath(path)] = handler
+}
+
+func findMatch(mux map[string]func(Event), path string) string {
+ n := -1
+ pattern := ""
+ for m := range mux {
+ if !isPathMatch(m, path) {
+ continue
+ }
+ if len(m) > n {
+ pattern = m
+ n = len(m)
+ }
+ }
+ return pattern
+
+}
+// Remove all existing defined Handlers from the map
+func (es *EvtStream) ResetHandlers() {
+ for Path, _ := range es.Handlers {
+ delete(es.Handlers, Path)
+ }
+ return
+}
+
+func (es *EvtStream) match(path string) string {
+ return findMatch(es.Handlers, path)
+}
+
+func (es *EvtStream) Hook(f func(Event)) {
+ es.hook = f
+}
+
+func (es *EvtStream) Loop() {
+ for e := range es.stream {
+ switch e.Path {
+ case "/sig/stoploop":
+ return
+ }
+ go func(a Event) {
+ es.RLock()
+ defer es.RUnlock()
+ if pattern := es.match(a.Path); pattern != "" {
+ es.Handlers[pattern](a)
+ }
+ }(e)
+ if es.hook != nil {
+ es.hook(e)
+ }
+ }
+}
+
+func (es *EvtStream) StopLoop() {
+ go func() {
+ e := Event{
+ Path: "/sig/stoploop",
+ }
+ es.sigStopLoop <- e
+ }()
+}
+
+func Merge(name string, ec chan Event) {
+ DefaultEvtStream.Merge(name, ec)
+}
+
+func Handle(path string, handler func(Event)) {
+ DefaultEvtStream.Handle(path, handler)
+}
+
+func Loop() {
+ DefaultEvtStream.Loop()
+}
+
+func StopLoop() {
+ DefaultEvtStream.StopLoop()
+}
+
+type EvtTimer struct {
+ Duration time.Duration
+ Count uint64
+}
+
+func NewTimerCh(du time.Duration) chan Event {
+ t := make(chan Event)
+
+ go func(a chan Event) {
+ n := uint64(0)
+ for {
+ n++
+ time.Sleep(du)
+ e := Event{}
+ e.Type = "timer"
+ e.Path = "/timer/" + du.String()
+ e.Time = time.Now().Unix()
+ e.Data = EvtTimer{
+ Duration: du,
+ Count: n,
+ }
+ t <- e
+
+ }
+ }(t)
+ return t
+}
+
+var DefualtHandler = func(e Event) {
+}
+
+var usrEvtCh = make(chan Event)
+
+func SendCustomEvt(path string, data interface{}) {
+ e := Event{}
+ e.Path = path
+ e.Data = data
+ e.Time = time.Now().Unix()
+ usrEvtCh <- e
+}
diff --git a/vendor/github.com/gizak/termui/gauge.go b/vendor/github.com/gizak/termui/gauge.go
new file mode 100644
index 000000000..01af0ea7c
--- /dev/null
+++ b/vendor/github.com/gizak/termui/gauge.go
@@ -0,0 +1,109 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import (
+ "strconv"
+ "strings"
+)
+
+// Gauge is a progress bar like widget.
+// A simple example:
+/*
+ g := termui.NewGauge()
+ g.Percent = 40
+ g.Width = 50
+ g.Height = 3
+ g.BorderLabel = "Slim Gauge"
+ g.BarColor = termui.ColorRed
+ g.PercentColor = termui.ColorBlue
+*/
+
+const ColorUndef Attribute = Attribute(^uint16(0))
+
+type Gauge struct {
+ Block
+ Percent int
+ BarColor Attribute
+ PercentColor Attribute
+ PercentColorHighlighted Attribute
+ Label string
+ LabelAlign Align
+}
+
+// NewGauge return a new gauge with current theme.
+func NewGauge() *Gauge {
+ g := &Gauge{
+ Block: *NewBlock(),
+ PercentColor: ThemeAttr("gauge.percent.fg"),
+ BarColor: ThemeAttr("gauge.bar.bg"),
+ Label: "{{percent}}%",
+ LabelAlign: AlignCenter,
+ PercentColorHighlighted: ColorUndef,
+ }
+
+ g.Width = 12
+ g.Height = 5
+ return g
+}
+
+// Buffer implements Bufferer interface.
+func (g *Gauge) Buffer() Buffer {
+ buf := g.Block.Buffer()
+
+ // plot bar
+ w := g.Percent * g.innerArea.Dx() / 100
+ for i := 0; i < g.innerArea.Dy(); i++ {
+ for j := 0; j < w; j++ {
+ c := Cell{}
+ c.Ch = ' '
+ c.Bg = g.BarColor
+ if c.Bg == ColorDefault {
+ c.Bg |= AttrReverse
+ }
+ buf.Set(g.innerArea.Min.X+j, g.innerArea.Min.Y+i, c)
+ }
+ }
+
+ // plot percentage
+ s := strings.Replace(g.Label, "{{percent}}", strconv.Itoa(g.Percent), -1)
+ pry := g.innerArea.Min.Y + g.innerArea.Dy()/2
+ rs := str2runes(s)
+ var pos int
+ switch g.LabelAlign {
+ case AlignLeft:
+ pos = 0
+
+ case AlignCenter:
+ pos = (g.innerArea.Dx() - strWidth(s)) / 2
+
+ case AlignRight:
+ pos = g.innerArea.Dx() - strWidth(s) - 1
+ }
+ pos += g.innerArea.Min.X
+
+ for i, v := range rs {
+ c := Cell{
+ Ch: v,
+ Fg: g.PercentColor,
+ }
+
+ if w+g.innerArea.Min.X > pos+i {
+ c.Bg = g.BarColor
+ if c.Bg == ColorDefault {
+ c.Bg |= AttrReverse
+ }
+
+ if g.PercentColorHighlighted != ColorUndef {
+ c.Fg = g.PercentColorHighlighted
+ }
+ } else {
+ c.Bg = g.Block.Bg
+ }
+
+ buf.Set(1+pos+i, pry, c)
+ }
+ return buf
+}
diff --git a/vendor/github.com/gizak/termui/glide.lock b/vendor/github.com/gizak/termui/glide.lock
new file mode 100644
index 000000000..798e944d3
--- /dev/null
+++ b/vendor/github.com/gizak/termui/glide.lock
@@ -0,0 +1,14 @@
+hash: 67a478802ee1d122cf1df063c52458d074864900c96a5cc25dc6be4b7638eb1c
+updated: 2016-04-06T21:16:00.849048757-04:00
+imports:
+- name: github.com/mattn/go-runewidth
+ version: d6bea18f789704b5f83375793155289da36a3c7f
+- name: github.com/mitchellh/go-wordwrap
+ version: ad45545899c7b13c020ea92b2072220eefad42b8
+- name: github.com/nsf/termbox-go
+ version: 362329b0aa6447eadd52edd8d660ec1dff470295
+- name: golang.org/x/net
+ version: af4fee9d05b66edc24197d189e6118f8ebce8c2b
+ subpackages:
+ - websocket
+devImports: []
diff --git a/vendor/github.com/gizak/termui/glide.yaml b/vendor/github.com/gizak/termui/glide.yaml
new file mode 100644
index 000000000..d8e445254
--- /dev/null
+++ b/vendor/github.com/gizak/termui/glide.yaml
@@ -0,0 +1,8 @@
+package: github.com/gizak/termui
+import:
+- package: github.com/mattn/go-runewidth
+- package: github.com/mitchellh/go-wordwrap
+- package: github.com/nsf/termbox-go
+- package: golang.org/x/net
+ subpackages:
+ - websocket
diff --git a/vendor/github.com/gizak/termui/grid.go b/vendor/github.com/gizak/termui/grid.go
new file mode 100644
index 000000000..364442e02
--- /dev/null
+++ b/vendor/github.com/gizak/termui/grid.go
@@ -0,0 +1,279 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+// GridBufferer introduces a Bufferer that can be manipulated by Grid.
+type GridBufferer interface {
+ Bufferer
+ GetHeight() int
+ SetWidth(int)
+ SetX(int)
+ SetY(int)
+}
+
+// Row builds a layout tree
+type Row struct {
+ Cols []*Row //children
+ Widget GridBufferer // root
+ X int
+ Y int
+ Width int
+ Height int
+ Span int
+ Offset int
+}
+
+// calculate and set the underlying layout tree's x, y, height and width.
+func (r *Row) calcLayout() {
+ r.assignWidth(r.Width)
+ r.Height = r.solveHeight()
+ r.assignX(r.X)
+ r.assignY(r.Y)
+}
+
+// tell if the node is leaf in the tree.
+func (r *Row) isLeaf() bool {
+ return r.Cols == nil || len(r.Cols) == 0
+}
+
+func (r *Row) isRenderableLeaf() bool {
+ return r.isLeaf() && r.Widget != nil
+}
+
+// assign widgets' (and their parent rows') width recursively.
+func (r *Row) assignWidth(w int) {
+ r.SetWidth(w)
+
+ accW := 0 // acc span and offset
+ calcW := make([]int, len(r.Cols)) // calculated width
+ calcOftX := make([]int, len(r.Cols)) // computated start position of x
+
+ for i, c := range r.Cols {
+ accW += c.Span + c.Offset
+ cw := int(float64(c.Span*r.Width) / 12.0)
+
+ if i >= 1 {
+ calcOftX[i] = calcOftX[i-1] +
+ calcW[i-1] +
+ int(float64(r.Cols[i-1].Offset*r.Width)/12.0)
+ }
+
+ // use up the space if it is the last col
+ if i == len(r.Cols)-1 && accW == 12 {
+ cw = r.Width - calcOftX[i]
+ }
+ calcW[i] = cw
+ r.Cols[i].assignWidth(cw)
+ }
+}
+
+// bottom up calc and set rows' (and their widgets') height,
+// return r's total height.
+func (r *Row) solveHeight() int {
+ if r.isRenderableLeaf() {
+ r.Height = r.Widget.GetHeight()
+ return r.Widget.GetHeight()
+ }
+
+ maxh := 0
+ if !r.isLeaf() {
+ for _, c := range r.Cols {
+ nh := c.solveHeight()
+ // when embed rows in Cols, row widgets stack up
+ if r.Widget != nil {
+ nh += r.Widget.GetHeight()
+ }
+ if nh > maxh {
+ maxh = nh
+ }
+ }
+ }
+
+ r.Height = maxh
+ return maxh
+}
+
+// recursively assign x position for r tree.
+func (r *Row) assignX(x int) {
+ r.SetX(x)
+
+ if !r.isLeaf() {
+ acc := 0
+ for i, c := range r.Cols {
+ if c.Offset != 0 {
+ acc += int(float64(c.Offset*r.Width) / 12.0)
+ }
+ r.Cols[i].assignX(x + acc)
+ acc += c.Width
+ }
+ }
+}
+
+// recursively assign y position to r.
+func (r *Row) assignY(y int) {
+ r.SetY(y)
+
+ if r.isLeaf() {
+ return
+ }
+
+ for i := range r.Cols {
+ acc := 0
+ if r.Widget != nil {
+ acc = r.Widget.GetHeight()
+ }
+ r.Cols[i].assignY(y + acc)
+ }
+
+}
+
+// GetHeight implements GridBufferer interface.
+func (r Row) GetHeight() int {
+ return r.Height
+}
+
+// SetX implements GridBufferer interface.
+func (r *Row) SetX(x int) {
+ r.X = x
+ if r.Widget != nil {
+ r.Widget.SetX(x)
+ }
+}
+
+// SetY implements GridBufferer interface.
+func (r *Row) SetY(y int) {
+ r.Y = y
+ if r.Widget != nil {
+ r.Widget.SetY(y)
+ }
+}
+
+// SetWidth implements GridBufferer interface.
+func (r *Row) SetWidth(w int) {
+ r.Width = w
+ if r.Widget != nil {
+ r.Widget.SetWidth(w)
+ }
+}
+
+// Buffer implements Bufferer interface,
+// recursively merge all widgets buffer
+func (r *Row) Buffer() Buffer {
+ merged := NewBuffer()
+
+ if r.isRenderableLeaf() {
+ return r.Widget.Buffer()
+ }
+
+ // for those are not leaves but have a renderable widget
+ if r.Widget != nil {
+ merged.Merge(r.Widget.Buffer())
+ }
+
+ // collect buffer from children
+ if !r.isLeaf() {
+ for _, c := range r.Cols {
+ merged.Merge(c.Buffer())
+ }
+ }
+
+ return merged
+}
+
+// Grid implements 12 columns system.
+// A simple example:
+/*
+ import ui "github.com/gizak/termui"
+ // init and create widgets...
+
+ // build
+ ui.Body.AddRows(
+ ui.NewRow(
+ ui.NewCol(6, 0, widget0),
+ ui.NewCol(6, 0, widget1)),
+ ui.NewRow(
+ ui.NewCol(3, 0, widget2),
+ ui.NewCol(3, 0, widget30, widget31, widget32),
+ ui.NewCol(6, 0, widget4)))
+
+ // calculate layout
+ ui.Body.Align()
+
+ ui.Render(ui.Body)
+*/
+type Grid struct {
+ Rows []*Row
+ Width int
+ X int
+ Y int
+ BgColor Attribute
+}
+
+// NewGrid returns *Grid with given rows.
+func NewGrid(rows ...*Row) *Grid {
+ return &Grid{Rows: rows}
+}
+
+// AddRows appends given rows to Grid.
+func (g *Grid) AddRows(rs ...*Row) {
+ g.Rows = append(g.Rows, rs...)
+}
+
+// NewRow creates a new row out of given columns.
+func NewRow(cols ...*Row) *Row {
+ rs := &Row{Span: 12, Cols: cols}
+ return rs
+}
+
+// NewCol accepts: widgets are LayoutBufferer or widgets is A NewRow.
+// Note that if multiple widgets are provided, they will stack up in the col.
+func NewCol(span, offset int, widgets ...GridBufferer) *Row {
+ r := &Row{Span: span, Offset: offset}
+
+ if widgets != nil && len(widgets) == 1 {
+ wgt := widgets[0]
+ nw, isRow := wgt.(*Row)
+ if isRow {
+ r.Cols = nw.Cols
+ } else {
+ r.Widget = wgt
+ }
+ return r
+ }
+
+ r.Cols = []*Row{}
+ ir := r
+ for _, w := range widgets {
+ nr := &Row{Span: 12, Widget: w}
+ ir.Cols = []*Row{nr}
+ ir = nr
+ }
+
+ return r
+}
+
+// Align calculate each rows' layout.
+func (g *Grid) Align() {
+ h := 0
+ for _, r := range g.Rows {
+ r.SetWidth(g.Width)
+ r.SetX(g.X)
+ r.SetY(g.Y + h)
+ r.calcLayout()
+ h += r.GetHeight()
+ }
+}
+
+// Buffer implments Bufferer interface.
+func (g Grid) Buffer() Buffer {
+ buf := NewBuffer()
+
+ for _, r := range g.Rows {
+ buf.Merge(r.Buffer())
+ }
+ return buf
+}
+
+var Body *Grid
diff --git a/vendor/github.com/gizak/termui/helper.go b/vendor/github.com/gizak/termui/helper.go
new file mode 100644
index 000000000..308f6c1db
--- /dev/null
+++ b/vendor/github.com/gizak/termui/helper.go
@@ -0,0 +1,222 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import (
+ "regexp"
+ "strings"
+
+ tm "github.com/nsf/termbox-go"
+)
+import rw "github.com/mattn/go-runewidth"
+
+/* ---------------Port from termbox-go --------------------- */
+
+// Attribute is printable cell's color and style.
+type Attribute uint16
+
+// 8 basic clolrs
+const (
+ ColorDefault Attribute = iota
+ ColorBlack
+ ColorRed
+ ColorGreen
+ ColorYellow
+ ColorBlue
+ ColorMagenta
+ ColorCyan
+ ColorWhite
+)
+
+//Have a constant that defines number of colors
+const NumberofColors = 8
+
+// Text style
+const (
+ AttrBold Attribute = 1 << (iota + 9)
+ AttrUnderline
+ AttrReverse
+)
+
+var (
+ dot = "…"
+ dotw = rw.StringWidth(dot)
+)
+
+/* ----------------------- End ----------------------------- */
+
+func toTmAttr(x Attribute) tm.Attribute {
+ return tm.Attribute(x)
+}
+
+func str2runes(s string) []rune {
+ return []rune(s)
+}
+
+// Here for backwards-compatibility.
+func trimStr2Runes(s string, w int) []rune {
+ return TrimStr2Runes(s, w)
+}
+
+// TrimStr2Runes trims string to w[-1 rune], appends …, and returns the runes
+// of that string if string is grather then n. If string is small then w,
+// return the runes.
+func TrimStr2Runes(s string, w int) []rune {
+ if w <= 0 {
+ return []rune{}
+ }
+
+ sw := rw.StringWidth(s)
+ if sw > w {
+ return []rune(rw.Truncate(s, w, dot))
+ }
+ return str2runes(s)
+}
+
+// TrimStrIfAppropriate trim string to "s[:-1] + …"
+// if string > width otherwise return string
+func TrimStrIfAppropriate(s string, w int) string {
+ if w <= 0 {
+ return ""
+ }
+
+ sw := rw.StringWidth(s)
+ if sw > w {
+ return rw.Truncate(s, w, dot)
+ }
+
+ return s
+}
+
+func strWidth(s string) int {
+ return rw.StringWidth(s)
+}
+
+func charWidth(ch rune) int {
+ return rw.RuneWidth(ch)
+}
+
+var whiteSpaceRegex = regexp.MustCompile(`\s`)
+
+// StringToAttribute converts text to a termui attribute. You may specifiy more
+// then one attribute like that: "BLACK, BOLD, ...". All whitespaces
+// are ignored.
+func StringToAttribute(text string) Attribute {
+ text = whiteSpaceRegex.ReplaceAllString(strings.ToLower(text), "")
+ attributes := strings.Split(text, ",")
+ result := Attribute(0)
+
+ for _, theAttribute := range attributes {
+ var match Attribute
+ switch theAttribute {
+ case "reset", "default":
+ match = ColorDefault
+
+ case "black":
+ match = ColorBlack
+
+ case "red":
+ match = ColorRed
+
+ case "green":
+ match = ColorGreen
+
+ case "yellow":
+ match = ColorYellow
+
+ case "blue":
+ match = ColorBlue
+
+ case "magenta":
+ match = ColorMagenta
+
+ case "cyan":
+ match = ColorCyan
+
+ case "white":
+ match = ColorWhite
+
+ case "bold":
+ match = AttrBold
+
+ case "underline":
+ match = AttrUnderline
+
+ case "reverse":
+ match = AttrReverse
+ }
+
+ result |= match
+ }
+
+ return result
+}
+
+// TextCells returns a coloured text cells []Cell
+func TextCells(s string, fg, bg Attribute) []Cell {
+ cs := make([]Cell, 0, len(s))
+
+ // sequence := MarkdownTextRendererFactory{}.TextRenderer(s).Render(fg, bg)
+ // runes := []rune(sequence.NormalizedText)
+ runes := str2runes(s)
+
+ for n := range runes {
+ // point, _ := sequence.PointAt(n, 0, 0)
+ // cs = append(cs, Cell{point.Ch, point.Fg, point.Bg})
+ cs = append(cs, Cell{runes[n], fg, bg})
+ }
+ return cs
+}
+
+// Width returns the actual screen space the cell takes (usually 1 or 2).
+func (c Cell) Width() int {
+ return charWidth(c.Ch)
+}
+
+// Copy return a copy of c
+func (c Cell) Copy() Cell {
+ return c
+}
+
+// TrimTxCells trims the overflowed text cells sequence.
+func TrimTxCells(cs []Cell, w int) []Cell {
+ if len(cs) <= w {
+ return cs
+ }
+ return cs[:w]
+}
+
+// DTrimTxCls trims the overflowed text cells sequence and append dots at the end.
+func DTrimTxCls(cs []Cell, w int) []Cell {
+ l := len(cs)
+ if l <= 0 {
+ return []Cell{}
+ }
+
+ rt := make([]Cell, 0, w)
+ csw := 0
+ for i := 0; i < l && csw <= w; i++ {
+ c := cs[i]
+ cw := c.Width()
+
+ if cw+csw < w {
+ rt = append(rt, c)
+ csw += cw
+ } else {
+ rt = append(rt, Cell{'…', c.Fg, c.Bg})
+ break
+ }
+ }
+
+ return rt
+}
+
+func CellsToStr(cs []Cell) string {
+ str := ""
+ for _, c := range cs {
+ str += string(c.Ch)
+ }
+ return str
+}
diff --git a/vendor/github.com/gizak/termui/linechart.go b/vendor/github.com/gizak/termui/linechart.go
new file mode 100644
index 000000000..81834efdb
--- /dev/null
+++ b/vendor/github.com/gizak/termui/linechart.go
@@ -0,0 +1,331 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import (
+ "fmt"
+ "math"
+)
+
+// only 16 possible combinations, why bother
+var braillePatterns = map[[2]int]rune{
+ [2]int{0, 0}: '⣀',
+ [2]int{0, 1}: '⡠',
+ [2]int{0, 2}: '⡐',
+ [2]int{0, 3}: '⡈',
+
+ [2]int{1, 0}: '⢄',
+ [2]int{1, 1}: '⠤',
+ [2]int{1, 2}: '⠔',
+ [2]int{1, 3}: '⠌',
+
+ [2]int{2, 0}: '⢂',
+ [2]int{2, 1}: '⠢',
+ [2]int{2, 2}: '⠒',
+ [2]int{2, 3}: '⠊',
+
+ [2]int{3, 0}: '⢁',
+ [2]int{3, 1}: '⠡',
+ [2]int{3, 2}: '⠑',
+ [2]int{3, 3}: '⠉',
+}
+
+var lSingleBraille = [4]rune{'\u2840', '⠄', '⠂', '⠁'}
+var rSingleBraille = [4]rune{'\u2880', '⠠', '⠐', '⠈'}
+
+// LineChart has two modes: braille(default) and dot. Using braille gives 2x capicity as dot mode,
+// because one braille char can represent two data points.
+/*
+ lc := termui.NewLineChart()
+ lc.BorderLabel = "braille-mode Line Chart"
+ lc.Data = [1.2, 1.3, 1.5, 1.7, 1.5, 1.6, 1.8, 2.0]
+ lc.Width = 50
+ lc.Height = 12
+ lc.AxesColor = termui.ColorWhite
+ lc.LineColor = termui.ColorGreen | termui.AttrBold
+ // termui.Render(lc)...
+*/
+type LineChart struct {
+ Block
+ Data []float64
+ DataLabels []string // if unset, the data indices will be used
+ Mode string // braille | dot
+ DotStyle rune
+ LineColor Attribute
+ scale float64 // data span per cell on y-axis
+ AxesColor Attribute
+ drawingX int
+ drawingY int
+ axisYHeight int
+ axisXWidth int
+ axisYLabelGap int
+ axisXLabelGap int
+ topValue float64
+ bottomValue float64
+ labelX [][]rune
+ labelY [][]rune
+ labelYSpace int
+ maxY float64
+ minY float64
+ autoLabels bool
+}
+
+// NewLineChart returns a new LineChart with current theme.
+func NewLineChart() *LineChart {
+ lc := &LineChart{Block: *NewBlock()}
+ lc.AxesColor = ThemeAttr("linechart.axes.fg")
+ lc.LineColor = ThemeAttr("linechart.line.fg")
+ lc.Mode = "braille"
+ lc.DotStyle = '•'
+ lc.axisXLabelGap = 2
+ lc.axisYLabelGap = 1
+ lc.bottomValue = math.Inf(1)
+ lc.topValue = math.Inf(-1)
+ return lc
+}
+
+// one cell contains two data points
+// so the capicity is 2x as dot-mode
+func (lc *LineChart) renderBraille() Buffer {
+ buf := NewBuffer()
+
+ // return: b -> which cell should the point be in
+ // m -> in the cell, divided into 4 equal height levels, which subcell?
+ getPos := func(d float64) (b, m int) {
+ cnt4 := int((d-lc.bottomValue)/(lc.scale/4) + 0.5)
+ b = cnt4 / 4
+ m = cnt4 % 4
+ return
+ }
+ // plot points
+ for i := 0; 2*i+1 < len(lc.Data) && i < lc.axisXWidth; i++ {
+ b0, m0 := getPos(lc.Data[2*i])
+ b1, m1 := getPos(lc.Data[2*i+1])
+
+ if b0 == b1 {
+ c := Cell{
+ Ch: braillePatterns[[2]int{m0, m1}],
+ Bg: lc.Bg,
+ Fg: lc.LineColor,
+ }
+ y := lc.innerArea.Min.Y + lc.innerArea.Dy() - 3 - b0
+ x := lc.innerArea.Min.X + lc.labelYSpace + 1 + i
+ buf.Set(x, y, c)
+ } else {
+ c0 := Cell{Ch: lSingleBraille[m0],
+ Fg: lc.LineColor,
+ Bg: lc.Bg}
+ x0 := lc.innerArea.Min.X + lc.labelYSpace + 1 + i
+ y0 := lc.innerArea.Min.Y + lc.innerArea.Dy() - 3 - b0
+ buf.Set(x0, y0, c0)
+
+ c1 := Cell{Ch: rSingleBraille[m1],
+ Fg: lc.LineColor,
+ Bg: lc.Bg}
+ x1 := lc.innerArea.Min.X + lc.labelYSpace + 1 + i
+ y1 := lc.innerArea.Min.Y + lc.innerArea.Dy() - 3 - b1
+ buf.Set(x1, y1, c1)
+ }
+
+ }
+ return buf
+}
+
+func (lc *LineChart) renderDot() Buffer {
+ buf := NewBuffer()
+ for i := 0; i < len(lc.Data) && i < lc.axisXWidth; i++ {
+ c := Cell{
+ Ch: lc.DotStyle,
+ Fg: lc.LineColor,
+ Bg: lc.Bg,
+ }
+ x := lc.innerArea.Min.X + lc.labelYSpace + 1 + i
+ y := lc.innerArea.Min.Y + lc.innerArea.Dy() - 3 - int((lc.Data[i]-lc.bottomValue)/lc.scale+0.5)
+ buf.Set(x, y, c)
+ }
+
+ return buf
+}
+
+func (lc *LineChart) calcLabelX() {
+ lc.labelX = [][]rune{}
+
+ for i, l := 0, 0; i < len(lc.DataLabels) && l < lc.axisXWidth; i++ {
+ if lc.Mode == "dot" {
+ if l >= len(lc.DataLabels) {
+ break
+ }
+
+ s := str2runes(lc.DataLabels[l])
+ w := strWidth(lc.DataLabels[l])
+ if l+w <= lc.axisXWidth {
+ lc.labelX = append(lc.labelX, s)
+ }
+ l += w + lc.axisXLabelGap
+ } else { // braille
+ if 2*l >= len(lc.DataLabels) {
+ break
+ }
+
+ s := str2runes(lc.DataLabels[2*l])
+ w := strWidth(lc.DataLabels[2*l])
+ if l+w <= lc.axisXWidth {
+ lc.labelX = append(lc.labelX, s)
+ }
+ l += w + lc.axisXLabelGap
+
+ }
+ }
+}
+
+func shortenFloatVal(x float64) string {
+ s := fmt.Sprintf("%.2f", x)
+ if len(s)-3 > 3 {
+ s = fmt.Sprintf("%.2e", x)
+ }
+
+ if x < 0 {
+ s = fmt.Sprintf("%.2f", x)
+ }
+ return s
+}
+
+func (lc *LineChart) calcLabelY() {
+ span := lc.topValue - lc.bottomValue
+ lc.scale = span / float64(lc.axisYHeight)
+
+ n := (1 + lc.axisYHeight) / (lc.axisYLabelGap + 1)
+ lc.labelY = make([][]rune, n)
+ maxLen := 0
+ for i := 0; i < n; i++ {
+ s := str2runes(shortenFloatVal(lc.bottomValue + float64(i)*span/float64(n)))
+ if len(s) > maxLen {
+ maxLen = len(s)
+ }
+ lc.labelY[i] = s
+ }
+
+ lc.labelYSpace = maxLen
+}
+
+func (lc *LineChart) calcLayout() {
+ // set datalabels if it is not provided
+ if (lc.DataLabels == nil || len(lc.DataLabels) == 0) || lc.autoLabels {
+ lc.autoLabels = true
+ lc.DataLabels = make([]string, len(lc.Data))
+ for i := range lc.Data {
+ lc.DataLabels[i] = fmt.Sprint(i)
+ }
+ }
+
+ // lazy increase, to avoid y shaking frequently
+ // update bound Y when drawing is gonna overflow
+ lc.minY = lc.Data[0]
+ lc.maxY = lc.Data[0]
+
+ // valid visible range
+ vrange := lc.innerArea.Dx()
+ if lc.Mode == "braille" {
+ vrange = 2 * lc.innerArea.Dx()
+ }
+ if vrange > len(lc.Data) {
+ vrange = len(lc.Data)
+ }
+
+ for _, v := range lc.Data[:vrange] {
+ if v > lc.maxY {
+ lc.maxY = v
+ }
+ if v < lc.minY {
+ lc.minY = v
+ }
+ }
+
+ span := lc.maxY - lc.minY
+
+ if lc.minY < lc.bottomValue {
+ lc.bottomValue = lc.minY - 0.2*span
+ }
+
+ if lc.maxY > lc.topValue {
+ lc.topValue = lc.maxY + 0.2*span
+ }
+
+ lc.axisYHeight = lc.innerArea.Dy() - 2
+ lc.calcLabelY()
+
+ lc.axisXWidth = lc.innerArea.Dx() - 1 - lc.labelYSpace
+ lc.calcLabelX()
+
+ lc.drawingX = lc.innerArea.Min.X + 1 + lc.labelYSpace
+ lc.drawingY = lc.innerArea.Min.Y
+}
+
+func (lc *LineChart) plotAxes() Buffer {
+ buf := NewBuffer()
+
+ origY := lc.innerArea.Min.Y + lc.innerArea.Dy() - 2
+ origX := lc.innerArea.Min.X + lc.labelYSpace
+
+ buf.Set(origX, origY, Cell{Ch: ORIGIN, Fg: lc.AxesColor, Bg: lc.Bg})
+
+ for x := origX + 1; x < origX+lc.axisXWidth; x++ {
+ buf.Set(x, origY, Cell{Ch: HDASH, Fg: lc.AxesColor, Bg: lc.Bg})
+ }
+
+ for dy := 1; dy <= lc.axisYHeight; dy++ {
+ buf.Set(origX, origY-dy, Cell{Ch: VDASH, Fg: lc.AxesColor, Bg: lc.Bg})
+ }
+
+ // x label
+ oft := 0
+ for _, rs := range lc.labelX {
+ if oft+len(rs) > lc.axisXWidth {
+ break
+ }
+ for j, r := range rs {
+ c := Cell{
+ Ch: r,
+ Fg: lc.AxesColor,
+ Bg: lc.Bg,
+ }
+ x := origX + oft + j
+ y := lc.innerArea.Min.Y + lc.innerArea.Dy() - 1
+ buf.Set(x, y, c)
+ }
+ oft += len(rs) + lc.axisXLabelGap
+ }
+
+ // y labels
+ for i, rs := range lc.labelY {
+ for j, r := range rs {
+ buf.Set(
+ lc.innerArea.Min.X+j,
+ origY-i*(lc.axisYLabelGap+1),
+ Cell{Ch: r, Fg: lc.AxesColor, Bg: lc.Bg})
+ }
+ }
+
+ return buf
+}
+
+// Buffer implements Bufferer interface.
+func (lc *LineChart) Buffer() Buffer {
+ buf := lc.Block.Buffer()
+
+ if lc.Data == nil || len(lc.Data) == 0 {
+ return buf
+ }
+ lc.calcLayout()
+ buf.Merge(lc.plotAxes())
+
+ if lc.Mode == "dot" {
+ buf.Merge(lc.renderDot())
+ } else {
+ buf.Merge(lc.renderBraille())
+ }
+
+ return buf
+}
diff --git a/vendor/github.com/gizak/termui/linechart_others.go b/vendor/github.com/gizak/termui/linechart_others.go
new file mode 100644
index 000000000..7e2e66b3e
--- /dev/null
+++ b/vendor/github.com/gizak/termui/linechart_others.go
@@ -0,0 +1,11 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+// +build !windows
+
+package termui
+
+const VDASH = '┊'
+const HDASH = '┈'
+const ORIGIN = '└'
diff --git a/vendor/github.com/gizak/termui/linechart_windows.go b/vendor/github.com/gizak/termui/linechart_windows.go
new file mode 100644
index 000000000..1478b5ce3
--- /dev/null
+++ b/vendor/github.com/gizak/termui/linechart_windows.go
@@ -0,0 +1,11 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+// +build windows
+
+package termui
+
+const VDASH = '|'
+const HDASH = '-'
+const ORIGIN = '+'
diff --git a/vendor/github.com/gizak/termui/list.go b/vendor/github.com/gizak/termui/list.go
new file mode 100644
index 000000000..0029d2622
--- /dev/null
+++ b/vendor/github.com/gizak/termui/list.go
@@ -0,0 +1,89 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import "strings"
+
+// List displays []string as its items,
+// it has a Overflow option (default is "hidden"), when set to "hidden",
+// the item exceeding List's width is truncated, but when set to "wrap",
+// the overflowed text breaks into next line.
+/*
+ strs := []string{
+ "[0] github.com/gizak/termui",
+ "[1] editbox.go",
+ "[2] iterrupt.go",
+ "[3] keyboard.go",
+ "[4] output.go",
+ "[5] random_out.go",
+ "[6] dashboard.go",
+ "[7] nsf/termbox-go"}
+
+ ls := termui.NewList()
+ ls.Items = strs
+ ls.ItemFgColor = termui.ColorYellow
+ ls.BorderLabel = "List"
+ ls.Height = 7
+ ls.Width = 25
+ ls.Y = 0
+*/
+type List struct {
+ Block
+ Items []string
+ Overflow string
+ ItemFgColor Attribute
+ ItemBgColor Attribute
+}
+
+// NewList returns a new *List with current theme.
+func NewList() *List {
+ l := &List{Block: *NewBlock()}
+ l.Overflow = "hidden"
+ l.ItemFgColor = ThemeAttr("list.item.fg")
+ l.ItemBgColor = ThemeAttr("list.item.bg")
+ return l
+}
+
+// Buffer implements Bufferer interface.
+func (l *List) Buffer() Buffer {
+ buf := l.Block.Buffer()
+
+ switch l.Overflow {
+ case "wrap":
+ cs := DefaultTxBuilder.Build(strings.Join(l.Items, "\n"), l.ItemFgColor, l.ItemBgColor)
+ i, j, k := 0, 0, 0
+ for i < l.innerArea.Dy() && k < len(cs) {
+ w := cs[k].Width()
+ if cs[k].Ch == '\n' || j+w > l.innerArea.Dx() {
+ i++
+ j = 0
+ if cs[k].Ch == '\n' {
+ k++
+ }
+ continue
+ }
+ buf.Set(l.innerArea.Min.X+j, l.innerArea.Min.Y+i, cs[k])
+
+ k++
+ j++
+ }
+
+ case "hidden":
+ trimItems := l.Items
+ if len(trimItems) > l.innerArea.Dy() {
+ trimItems = trimItems[:l.innerArea.Dy()]
+ }
+ for i, v := range trimItems {
+ cs := DTrimTxCls(DefaultTxBuilder.Build(v, l.ItemFgColor, l.ItemBgColor), l.innerArea.Dx())
+ j := 0
+ for _, vv := range cs {
+ w := vv.Width()
+ buf.Set(l.innerArea.Min.X+j, l.innerArea.Min.Y+i, vv)
+ j += w
+ }
+ }
+ }
+ return buf
+}
diff --git a/vendor/github.com/gizak/termui/mbarchart.go b/vendor/github.com/gizak/termui/mbarchart.go
new file mode 100644
index 000000000..6828e6533
--- /dev/null
+++ b/vendor/github.com/gizak/termui/mbarchart.go
@@ -0,0 +1,242 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import (
+ "fmt"
+)
+
+// This is the implemetation of multi-colored or stacked bar graph. This is different from default barGraph which is implemented in bar.go
+// Multi-Colored-BarChart creates multiple bars in a widget:
+/*
+ bc := termui.NewMBarChart()
+ data := make([][]int, 2)
+ data[0] := []int{3, 2, 5, 7, 9, 4}
+ data[1] := []int{7, 8, 5, 3, 1, 6}
+ bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
+ bc.BorderLabel = "Bar Chart"
+ bc.Data = data
+ bc.Width = 26
+ bc.Height = 10
+ bc.DataLabels = bclabels
+ bc.TextColor = termui.ColorGreen
+ bc.BarColor = termui.ColorRed
+ bc.NumColor = termui.ColorYellow
+*/
+type MBarChart struct {
+ Block
+ BarColor [NumberofColors]Attribute
+ TextColor Attribute
+ NumColor [NumberofColors]Attribute
+ Data [NumberofColors][]int
+ DataLabels []string
+ BarWidth int
+ BarGap int
+ labels [][]rune
+ dataNum [NumberofColors][][]rune
+ numBar int
+ scale float64
+ max int
+ minDataLen int
+ numStack int
+ ShowScale bool
+ maxScale []rune
+}
+
+// NewBarChart returns a new *BarChart with current theme.
+func NewMBarChart() *MBarChart {
+ bc := &MBarChart{Block: *NewBlock()}
+ bc.BarColor[0] = ThemeAttr("mbarchart.bar.bg")
+ bc.NumColor[0] = ThemeAttr("mbarchart.num.fg")
+ bc.TextColor = ThemeAttr("mbarchart.text.fg")
+ bc.BarGap = 1
+ bc.BarWidth = 3
+ return bc
+}
+
+func (bc *MBarChart) layout() {
+ bc.numBar = bc.innerArea.Dx() / (bc.BarGap + bc.BarWidth)
+ bc.labels = make([][]rune, bc.numBar)
+ DataLen := 0
+ LabelLen := len(bc.DataLabels)
+ bc.minDataLen = 9999 //Set this to some very hight value so that we find the minimum one We want to know which array among data[][] has got the least length
+
+ // We need to know how many stack/data array data[0] , data[1] are there
+ for i := 0; i < len(bc.Data); i++ {
+ if bc.Data[i] == nil {
+ break
+ }
+ DataLen++
+ }
+ bc.numStack = DataLen
+
+ //We need to know what is the mimimum size of data array data[0] could have 10 elements data[1] could have only 5, so we plot only 5 bar graphs
+
+ for i := 0; i < DataLen; i++ {
+ if bc.minDataLen > len(bc.Data[i]) {
+ bc.minDataLen = len(bc.Data[i])
+ }
+ }
+
+ if LabelLen > bc.minDataLen {
+ LabelLen = bc.minDataLen
+ }
+
+ for i := 0; i < LabelLen && i < bc.numBar; i++ {
+ bc.labels[i] = trimStr2Runes(bc.DataLabels[i], bc.BarWidth)
+ }
+
+ for i := 0; i < bc.numStack; i++ {
+ bc.dataNum[i] = make([][]rune, len(bc.Data[i]))
+ //For each stack of bar calcualte the rune
+ for j := 0; j < LabelLen && i < bc.numBar; j++ {
+ n := bc.Data[i][j]
+ s := fmt.Sprint(n)
+ bc.dataNum[i][j] = trimStr2Runes(s, bc.BarWidth)
+ }
+ //If color is not defined by default then populate a color that is different from the prevous bar
+ if bc.BarColor[i] == ColorDefault && bc.NumColor[i] == ColorDefault {
+ if i == 0 {
+ bc.BarColor[i] = ColorBlack
+ } else {
+ bc.BarColor[i] = bc.BarColor[i-1] + 1
+ if bc.BarColor[i] > NumberofColors {
+ bc.BarColor[i] = ColorBlack
+ }
+ }
+ bc.NumColor[i] = (NumberofColors + 1) - bc.BarColor[i] //Make NumColor opposite of barColor for visibility
+ }
+ }
+
+ //If Max value is not set then we have to populate, this time the max value will be max(sum(d1[0],d2[0],d3[0]) .... sum(d1[n], d2[n], d3[n]))
+
+ if bc.max == 0 {
+ bc.max = -1
+ }
+ for i := 0; i < bc.minDataLen && i < LabelLen; i++ {
+ var dsum int
+ for j := 0; j < bc.numStack; j++ {
+ dsum += bc.Data[j][i]
+ }
+ if dsum > bc.max {
+ bc.max = dsum
+ }
+ }
+
+ //Finally Calculate max sale
+ if bc.ShowScale {
+ s := fmt.Sprintf("%d", bc.max)
+ bc.maxScale = trimStr2Runes(s, len(s))
+ bc.scale = float64(bc.max) / float64(bc.innerArea.Dy()-2)
+ } else {
+ bc.scale = float64(bc.max) / float64(bc.innerArea.Dy()-1)
+ }
+
+}
+
+func (bc *MBarChart) SetMax(max int) {
+
+ if max > 0 {
+ bc.max = max
+ }
+}
+
+// Buffer implements Bufferer interface.
+func (bc *MBarChart) Buffer() Buffer {
+ buf := bc.Block.Buffer()
+ bc.layout()
+ var oftX int
+
+ for i := 0; i < bc.numBar && i < bc.minDataLen && i < len(bc.DataLabels); i++ {
+ ph := 0 //Previous Height to stack up
+ oftX = i * (bc.BarWidth + bc.BarGap)
+ for i1 := 0; i1 < bc.numStack; i1++ {
+ h := int(float64(bc.Data[i1][i]) / bc.scale)
+ // plot bars
+ for j := 0; j < bc.BarWidth; j++ {
+ for k := 0; k < h; k++ {
+ c := Cell{
+ Ch: ' ',
+ Bg: bc.BarColor[i1],
+ }
+ if bc.BarColor[i1] == ColorDefault { // when color is default, space char treated as transparent!
+ c.Bg |= AttrReverse
+ }
+ x := bc.innerArea.Min.X + i*(bc.BarWidth+bc.BarGap) + j
+ y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 - k - ph
+ buf.Set(x, y, c)
+
+ }
+ }
+ ph += h
+ }
+ // plot text
+ for j, k := 0, 0; j < len(bc.labels[i]); j++ {
+ w := charWidth(bc.labels[i][j])
+ c := Cell{
+ Ch: bc.labels[i][j],
+ Bg: bc.Bg,
+ Fg: bc.TextColor,
+ }
+ y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 1
+ x := bc.innerArea.Max.X + oftX + ((bc.BarWidth - len(bc.labels[i])) / 2) + k
+ buf.Set(x, y, c)
+ k += w
+ }
+ // plot num
+ ph = 0 //re-initialize previous height
+ for i1 := 0; i1 < bc.numStack; i1++ {
+ h := int(float64(bc.Data[i1][i]) / bc.scale)
+ for j := 0; j < len(bc.dataNum[i1][i]) && h > 0; j++ {
+ c := Cell{
+ Ch: bc.dataNum[i1][i][j],
+ Fg: bc.NumColor[i1],
+ Bg: bc.BarColor[i1],
+ }
+ if bc.BarColor[i1] == ColorDefault { // the same as above
+ c.Bg |= AttrReverse
+ }
+ if h == 0 {
+ c.Bg = bc.Bg
+ }
+ x := bc.innerArea.Min.X + oftX + (bc.BarWidth-len(bc.dataNum[i1][i]))/2 + j
+ y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 - ph
+ buf.Set(x, y, c)
+ }
+ ph += h
+ }
+ }
+
+ if bc.ShowScale {
+ //Currently bar graph only supprts data range from 0 to MAX
+ //Plot 0
+ c := Cell{
+ Ch: '0',
+ Bg: bc.Bg,
+ Fg: bc.TextColor,
+ }
+
+ y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2
+ x := bc.X
+ buf.Set(x, y, c)
+
+ //Plot the maximum sacle value
+ for i := 0; i < len(bc.maxScale); i++ {
+ c := Cell{
+ Ch: bc.maxScale[i],
+ Bg: bc.Bg,
+ Fg: bc.TextColor,
+ }
+
+ y := bc.innerArea.Min.Y
+ x := bc.X + i
+
+ buf.Set(x, y, c)
+ }
+
+ }
+
+ return buf
+}
diff --git a/vendor/github.com/gizak/termui/mkdocs.yml b/vendor/github.com/gizak/termui/mkdocs.yml
new file mode 100644
index 000000000..2ab45f064
--- /dev/null
+++ b/vendor/github.com/gizak/termui/mkdocs.yml
@@ -0,0 +1,28 @@
+pages:
+- Home: 'index.md'
+- Quickstart: 'quickstart.md'
+- Recipes: 'recipes.md'
+- References:
+ - Layouts: 'layouts.md'
+ - Components: 'components.md'
+ - Events: 'events.md'
+ - Themes: 'themes.md'
+- Versions: 'versions.md'
+- About: 'about.md'
+
+site_name: termui
+repo_url: https://github.com/gizak/termui/
+site_description: 'termui user guide'
+site_author: gizak
+
+docs_dir: '_docs'
+
+theme: readthedocs
+
+markdown_extensions:
+ - smarty
+ - admonition
+ - toc
+
+extra:
+ version: 1.0
diff --git a/vendor/github.com/gizak/termui/par.go b/vendor/github.com/gizak/termui/par.go
new file mode 100644
index 000000000..78bffd091
--- /dev/null
+++ b/vendor/github.com/gizak/termui/par.go
@@ -0,0 +1,73 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+// Par displays a paragraph.
+/*
+ par := termui.NewPar("Simple Text")
+ par.Height = 3
+ par.Width = 17
+ par.BorderLabel = "Label"
+*/
+type Par struct {
+ Block
+ Text string
+ TextFgColor Attribute
+ TextBgColor Attribute
+ WrapLength int // words wrap limit. Note it may not work properly with multi-width char
+}
+
+// NewPar returns a new *Par with given text as its content.
+func NewPar(s string) *Par {
+ return &Par{
+ Block: *NewBlock(),
+ Text: s,
+ TextFgColor: ThemeAttr("par.text.fg"),
+ TextBgColor: ThemeAttr("par.text.bg"),
+ WrapLength: 0,
+ }
+}
+
+// Buffer implements Bufferer interface.
+func (p *Par) Buffer() Buffer {
+ buf := p.Block.Buffer()
+
+ fg, bg := p.TextFgColor, p.TextBgColor
+ cs := DefaultTxBuilder.Build(p.Text, fg, bg)
+
+ // wrap if WrapLength set
+ if p.WrapLength < 0 {
+ cs = wrapTx(cs, p.Width-2)
+ } else if p.WrapLength > 0 {
+ cs = wrapTx(cs, p.WrapLength)
+ }
+
+ y, x, n := 0, 0, 0
+ for y < p.innerArea.Dy() && n < len(cs) {
+ w := cs[n].Width()
+ if cs[n].Ch == '\n' || x+w > p.innerArea.Dx() {
+ y++
+ x = 0 // set x = 0
+ if cs[n].Ch == '\n' {
+ n++
+ }
+
+ if y >= p.innerArea.Dy() {
+ buf.Set(p.innerArea.Min.X+p.innerArea.Dx()-1,
+ p.innerArea.Min.Y+p.innerArea.Dy()-1,
+ Cell{Ch: '…', Fg: p.TextFgColor, Bg: p.TextBgColor})
+ break
+ }
+ continue
+ }
+
+ buf.Set(p.innerArea.Min.X+x, p.innerArea.Min.Y+y, cs[n])
+
+ n++
+ x += w
+ }
+
+ return buf
+}
diff --git a/vendor/github.com/gizak/termui/pos.go b/vendor/github.com/gizak/termui/pos.go
new file mode 100644
index 000000000..2046dce5a
--- /dev/null
+++ b/vendor/github.com/gizak/termui/pos.go
@@ -0,0 +1,78 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import "image"
+
+// Align is the position of the gauge's label.
+type Align uint
+
+// All supported positions.
+const (
+ AlignNone Align = 0
+ AlignLeft Align = 1 << iota
+ AlignRight
+ AlignBottom
+ AlignTop
+ AlignCenterVertical
+ AlignCenterHorizontal
+ AlignCenter = AlignCenterVertical | AlignCenterHorizontal
+)
+
+func AlignArea(parent, child image.Rectangle, a Align) image.Rectangle {
+ w, h := child.Dx(), child.Dy()
+
+ // parent center
+ pcx, pcy := parent.Min.X+parent.Dx()/2, parent.Min.Y+parent.Dy()/2
+ // child center
+ ccx, ccy := child.Min.X+child.Dx()/2, child.Min.Y+child.Dy()/2
+
+ if a&AlignLeft == AlignLeft {
+ child.Min.X = parent.Min.X
+ child.Max.X = child.Min.X + w
+ }
+
+ if a&AlignRight == AlignRight {
+ child.Max.X = parent.Max.X
+ child.Min.X = child.Max.X - w
+ }
+
+ if a&AlignBottom == AlignBottom {
+ child.Max.Y = parent.Max.Y
+ child.Min.Y = child.Max.Y - h
+ }
+
+ if a&AlignTop == AlignRight {
+ child.Min.Y = parent.Min.Y
+ child.Max.Y = child.Min.Y + h
+ }
+
+ if a&AlignCenterHorizontal == AlignCenterHorizontal {
+ child.Min.X += pcx - ccx
+ child.Max.X = child.Min.X + w
+ }
+
+ if a&AlignCenterVertical == AlignCenterVertical {
+ child.Min.Y += pcy - ccy
+ child.Max.Y = child.Min.Y + h
+ }
+
+ return child
+}
+
+func MoveArea(a image.Rectangle, dx, dy int) image.Rectangle {
+ a.Min.X += dx
+ a.Max.X += dx
+ a.Min.Y += dy
+ a.Max.Y += dy
+ return a
+}
+
+var termWidth int
+var termHeight int
+
+func TermRect() image.Rectangle {
+ return image.Rect(0, 0, termWidth, termHeight)
+}
diff --git a/vendor/github.com/gizak/termui/render.go b/vendor/github.com/gizak/termui/render.go
new file mode 100644
index 000000000..36544f063
--- /dev/null
+++ b/vendor/github.com/gizak/termui/render.go
@@ -0,0 +1,135 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import (
+ "image"
+ "sync"
+ "time"
+
+ tm "github.com/nsf/termbox-go"
+)
+
+// Bufferer should be implemented by all renderable components.
+type Bufferer interface {
+ Buffer() Buffer
+}
+
+// Init initializes termui library. This function should be called before any others.
+// After initialization, the library must be finalized by 'Close' function.
+func Init() error {
+ if err := tm.Init(); err != nil {
+ return err
+ }
+
+ sysEvtChs = make([]chan Event, 0)
+ go hookTermboxEvt()
+
+ renderJobs = make(chan []Bufferer)
+ //renderLock = new(sync.RWMutex)
+
+ Body = NewGrid()
+ Body.X = 0
+ Body.Y = 0
+ Body.BgColor = ThemeAttr("bg")
+ Body.Width = TermWidth()
+
+ DefaultEvtStream.Init()
+ DefaultEvtStream.Merge("termbox", NewSysEvtCh())
+ DefaultEvtStream.Merge("timer", NewTimerCh(time.Second))
+ DefaultEvtStream.Merge("custom", usrEvtCh)
+
+ DefaultEvtStream.Handle("/", DefualtHandler)
+ DefaultEvtStream.Handle("/sys/wnd/resize", func(e Event) {
+ w := e.Data.(EvtWnd)
+ Body.Width = w.Width
+ })
+
+ DefaultWgtMgr = NewWgtMgr()
+ DefaultEvtStream.Hook(DefaultWgtMgr.WgtHandlersHook())
+
+ go func() {
+ for bs := range renderJobs {
+ render(bs...)
+ }
+ }()
+
+ return nil
+}
+
+// Close finalizes termui library,
+// should be called after successful initialization when termui's functionality isn't required anymore.
+func Close() {
+ tm.Close()
+}
+
+var renderLock sync.Mutex
+
+func termSync() {
+ renderLock.Lock()
+ tm.Sync()
+ termWidth, termHeight = tm.Size()
+ renderLock.Unlock()
+}
+
+// TermWidth returns the current terminal's width.
+func TermWidth() int {
+ termSync()
+ return termWidth
+}
+
+// TermHeight returns the current terminal's height.
+func TermHeight() int {
+ termSync()
+ return termHeight
+}
+
+// Render renders all Bufferer in the given order from left to right,
+// right could overlap on left ones.
+func render(bs ...Bufferer) {
+
+ for _, b := range bs {
+
+ buf := b.Buffer()
+ // set cels in buf
+ for p, c := range buf.CellMap {
+ if p.In(buf.Area) {
+
+ tm.SetCell(p.X, p.Y, c.Ch, toTmAttr(c.Fg), toTmAttr(c.Bg))
+
+ }
+ }
+
+ }
+
+ renderLock.Lock()
+ // render
+ tm.Flush()
+ renderLock.Unlock()
+}
+
+func Clear() {
+ tm.Clear(tm.ColorDefault, toTmAttr(ThemeAttr("bg")))
+}
+
+func clearArea(r image.Rectangle, bg Attribute) {
+ for i := r.Min.X; i < r.Max.X; i++ {
+ for j := r.Min.Y; j < r.Max.Y; j++ {
+ tm.SetCell(i, j, ' ', tm.ColorDefault, toTmAttr(bg))
+ }
+ }
+}
+
+func ClearArea(r image.Rectangle, bg Attribute) {
+ clearArea(r, bg)
+ tm.Flush()
+}
+
+var renderJobs chan []Bufferer
+
+func Render(bs ...Bufferer) {
+ //go func() { renderJobs <- bs }()
+ renderJobs <- bs
+}
diff --git a/vendor/github.com/gizak/termui/sparkline.go b/vendor/github.com/gizak/termui/sparkline.go
new file mode 100644
index 000000000..312ad9563
--- /dev/null
+++ b/vendor/github.com/gizak/termui/sparkline.go
@@ -0,0 +1,167 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+// Sparkline is like: ▅▆▂▂▅▇▂▂▃▆▆▆▅▃. The data points should be non-negative integers.
+/*
+ data := []int{4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1}
+ spl := termui.NewSparkline()
+ spl.Data = data
+ spl.Title = "Sparkline 0"
+ spl.LineColor = termui.ColorGreen
+*/
+type Sparkline struct {
+ Data []int
+ Height int
+ Title string
+ TitleColor Attribute
+ LineColor Attribute
+ displayHeight int
+ scale float32
+ max int
+}
+
+// Sparklines is a renderable widget which groups together the given sparklines.
+/*
+ spls := termui.NewSparklines(spl0,spl1,spl2) //...
+ spls.Height = 2
+ spls.Width = 20
+*/
+type Sparklines struct {
+ Block
+ Lines []Sparkline
+ displayLines int
+ displayWidth int
+}
+
+var sparks = []rune{'▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'}
+
+// Add appends a given Sparkline to s *Sparklines.
+func (s *Sparklines) Add(sl Sparkline) {
+ s.Lines = append(s.Lines, sl)
+}
+
+// NewSparkline returns a unrenderable single sparkline that intended to be added into Sparklines.
+func NewSparkline() Sparkline {
+ return Sparkline{
+ Height: 1,
+ TitleColor: ThemeAttr("sparkline.title.fg"),
+ LineColor: ThemeAttr("sparkline.line.fg")}
+}
+
+// NewSparklines return a new *Spaklines with given Sparkline(s), you can always add a new Sparkline later.
+func NewSparklines(ss ...Sparkline) *Sparklines {
+ s := &Sparklines{Block: *NewBlock(), Lines: ss}
+ return s
+}
+
+func (sl *Sparklines) update() {
+ for i, v := range sl.Lines {
+ if v.Title == "" {
+ sl.Lines[i].displayHeight = v.Height
+ } else {
+ sl.Lines[i].displayHeight = v.Height + 1
+ }
+ }
+ sl.displayWidth = sl.innerArea.Dx()
+
+ // get how many lines gotta display
+ h := 0
+ sl.displayLines = 0
+ for _, v := range sl.Lines {
+ if h+v.displayHeight <= sl.innerArea.Dy() {
+ sl.displayLines++
+ } else {
+ break
+ }
+ h += v.displayHeight
+ }
+
+ for i := 0; i < sl.displayLines; i++ {
+ data := sl.Lines[i].Data
+
+ max := 0
+ for _, v := range data {
+ if max < v {
+ max = v
+ }
+ }
+ sl.Lines[i].max = max
+ if max != 0 {
+ sl.Lines[i].scale = float32(8*sl.Lines[i].Height) / float32(max)
+ } else { // when all negative
+ sl.Lines[i].scale = 0
+ }
+ }
+}
+
+// Buffer implements Bufferer interface.
+func (sl *Sparklines) Buffer() Buffer {
+ buf := sl.Block.Buffer()
+ sl.update()
+
+ oftY := 0
+ for i := 0; i < sl.displayLines; i++ {
+ l := sl.Lines[i]
+ data := l.Data
+
+ if len(data) > sl.innerArea.Dx() {
+ data = data[len(data)-sl.innerArea.Dx():]
+ }
+
+ if l.Title != "" {
+ rs := trimStr2Runes(l.Title, sl.innerArea.Dx())
+ oftX := 0
+ for _, v := range rs {
+ w := charWidth(v)
+ c := Cell{
+ Ch: v,
+ Fg: l.TitleColor,
+ Bg: sl.Bg,
+ }
+ x := sl.innerArea.Min.X + oftX
+ y := sl.innerArea.Min.Y + oftY
+ buf.Set(x, y, c)
+ oftX += w
+ }
+ }
+
+ for j, v := range data {
+ // display height of the data point, zero when data is negative
+ h := int(float32(v)*l.scale + 0.5)
+ if v < 0 {
+ h = 0
+ }
+
+ barCnt := h / 8
+ barMod := h % 8
+ for jj := 0; jj < barCnt; jj++ {
+ c := Cell{
+ Ch: ' ', // => sparks[7]
+ Bg: l.LineColor,
+ }
+ x := sl.innerArea.Min.X + j
+ y := sl.innerArea.Min.Y + oftY + l.Height - jj
+
+ //p.Bg = sl.BgColor
+ buf.Set(x, y, c)
+ }
+ if barMod != 0 {
+ c := Cell{
+ Ch: sparks[barMod-1],
+ Fg: l.LineColor,
+ Bg: sl.Bg,
+ }
+ x := sl.innerArea.Min.X + j
+ y := sl.innerArea.Min.Y + oftY + l.Height - barCnt
+ buf.Set(x, y, c)
+ }
+ }
+
+ oftY += l.displayHeight
+ }
+
+ return buf
+}
diff --git a/vendor/github.com/gizak/termui/textbuilder.go b/vendor/github.com/gizak/termui/textbuilder.go
new file mode 100644
index 000000000..06a019bed
--- /dev/null
+++ b/vendor/github.com/gizak/termui/textbuilder.go
@@ -0,0 +1,278 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import (
+ "regexp"
+ "strings"
+
+ "github.com/mitchellh/go-wordwrap"
+)
+
+// TextBuilder is a minimal interface to produce text []Cell using specific syntax (markdown).
+type TextBuilder interface {
+ Build(s string, fg, bg Attribute) []Cell
+}
+
+// DefaultTxBuilder is set to be MarkdownTxBuilder.
+var DefaultTxBuilder = NewMarkdownTxBuilder()
+
+// MarkdownTxBuilder implements TextBuilder interface, using markdown syntax.
+type MarkdownTxBuilder struct {
+ baseFg Attribute
+ baseBg Attribute
+ plainTx []rune
+ markers []marker
+}
+
+type marker struct {
+ st int
+ ed int
+ fg Attribute
+ bg Attribute
+}
+
+var colorMap = map[string]Attribute{
+ "red": ColorRed,
+ "blue": ColorBlue,
+ "black": ColorBlack,
+ "cyan": ColorCyan,
+ "yellow": ColorYellow,
+ "white": ColorWhite,
+ "default": ColorDefault,
+ "green": ColorGreen,
+ "magenta": ColorMagenta,
+}
+
+var attrMap = map[string]Attribute{
+ "bold": AttrBold,
+ "underline": AttrUnderline,
+ "reverse": AttrReverse,
+}
+
+func rmSpc(s string) string {
+ reg := regexp.MustCompile(`\s+`)
+ return reg.ReplaceAllString(s, "")
+}
+
+// readAttr translates strings like `fg-red,fg-bold,bg-white` to fg and bg Attribute
+func (mtb MarkdownTxBuilder) readAttr(s string) (Attribute, Attribute) {
+ fg := mtb.baseFg
+ bg := mtb.baseBg
+
+ updateAttr := func(a Attribute, attrs []string) Attribute {
+ for _, s := range attrs {
+ // replace the color
+ if c, ok := colorMap[s]; ok {
+ a &= 0xFF00 // erase clr 0 ~ 8 bits
+ a |= c // set clr
+ }
+ // add attrs
+ if c, ok := attrMap[s]; ok {
+ a |= c
+ }
+ }
+ return a
+ }
+
+ ss := strings.Split(s, ",")
+ fgs := []string{}
+ bgs := []string{}
+ for _, v := range ss {
+ subs := strings.Split(v, "-")
+ if len(subs) > 1 {
+ if subs[0] == "fg" {
+ fgs = append(fgs, subs[1])
+ }
+ if subs[0] == "bg" {
+ bgs = append(bgs, subs[1])
+ }
+ }
+ }
+
+ fg = updateAttr(fg, fgs)
+ bg = updateAttr(bg, bgs)
+ return fg, bg
+}
+
+func (mtb *MarkdownTxBuilder) reset() {
+ mtb.plainTx = []rune{}
+ mtb.markers = []marker{}
+}
+
+// parse streams and parses text into normalized text and render sequence.
+func (mtb *MarkdownTxBuilder) parse(str string) {
+ rs := str2runes(str)
+ normTx := []rune{}
+ square := []rune{}
+ brackt := []rune{}
+ accSquare := false
+ accBrackt := false
+ cntSquare := 0
+
+ reset := func() {
+ square = []rune{}
+ brackt = []rune{}
+ accSquare = false
+ accBrackt = false
+ cntSquare = 0
+ }
+ // pipe stacks into normTx and clear
+ rollback := func() {
+ normTx = append(normTx, square...)
+ normTx = append(normTx, brackt...)
+ reset()
+ }
+ // chop first and last
+ chop := func(s []rune) []rune {
+ return s[1 : len(s)-1]
+ }
+
+ for i, r := range rs {
+ switch {
+ // stacking brackt
+ case accBrackt:
+ brackt = append(brackt, r)
+ if ')' == r {
+ fg, bg := mtb.readAttr(string(chop(brackt)))
+ st := len(normTx)
+ ed := len(normTx) + len(square) - 2
+ mtb.markers = append(mtb.markers, marker{st, ed, fg, bg})
+ normTx = append(normTx, chop(square)...)
+ reset()
+ } else if i+1 == len(rs) {
+ rollback()
+ }
+ // stacking square
+ case accSquare:
+ switch {
+ // squares closed and followed by a '('
+ case cntSquare == 0 && '(' == r:
+ accBrackt = true
+ brackt = append(brackt, '(')
+ // squares closed but not followed by a '('
+ case cntSquare == 0:
+ rollback()
+ if '[' == r {
+ accSquare = true
+ cntSquare = 1
+ brackt = append(brackt, '[')
+ } else {
+ normTx = append(normTx, r)
+ }
+ // hit the end
+ case i+1 == len(rs):
+ square = append(square, r)
+ rollback()
+ case '[' == r:
+ cntSquare++
+ square = append(square, '[')
+ case ']' == r:
+ cntSquare--
+ square = append(square, ']')
+ // normal char
+ default:
+ square = append(square, r)
+ }
+ // stacking normTx
+ default:
+ if '[' == r {
+ accSquare = true
+ cntSquare = 1
+ square = append(square, '[')
+ } else {
+ normTx = append(normTx, r)
+ }
+ }
+ }
+
+ mtb.plainTx = normTx
+}
+
+func wrapTx(cs []Cell, wl int) []Cell {
+ tmpCell := make([]Cell, len(cs))
+ copy(tmpCell, cs)
+
+ // get the plaintext
+ plain := CellsToStr(cs)
+
+ // wrap
+ plainWrapped := wordwrap.WrapString(plain, uint(wl))
+
+ // find differences and insert
+ finalCell := tmpCell // finalcell will get the inserts and is what is returned
+
+ plainRune := []rune(plain)
+ plainWrappedRune := []rune(plainWrapped)
+ trigger := "go"
+ plainRuneNew := plainRune
+
+ for trigger != "stop" {
+ plainRune = plainRuneNew
+ for i := range plainRune {
+ if plainRune[i] == plainWrappedRune[i] {
+ trigger = "stop"
+ } else if plainRune[i] != plainWrappedRune[i] && plainWrappedRune[i] == 10 {
+ trigger = "go"
+ cell := Cell{10, 0, 0}
+ j := i - 0
+
+ // insert a cell into the []Cell in correct position
+ tmpCell[i] = cell
+
+ // insert the newline into plain so we avoid indexing errors
+ plainRuneNew = append(plainRune, 10)
+ copy(plainRuneNew[j+1:], plainRuneNew[j:])
+ plainRuneNew[j] = plainWrappedRune[j]
+
+ // restart the inner for loop until plain and plain wrapped are
+ // the same; yeah, it's inefficient, but the text amounts
+ // should be small
+ break
+
+ } else if plainRune[i] != plainWrappedRune[i] &&
+ plainWrappedRune[i-1] == 10 && // if the prior rune is a newline
+ plainRune[i] == 32 { // and this rune is a space
+ trigger = "go"
+ // need to delete plainRune[i] because it gets rid of an extra
+ // space
+ plainRuneNew = append(plainRune[:i], plainRune[i+1:]...)
+ break
+
+ } else {
+ trigger = "stop" // stops the outer for loop
+ }
+ }
+ }
+
+ finalCell = tmpCell
+
+ return finalCell
+}
+
+// Build implements TextBuilder interface.
+func (mtb MarkdownTxBuilder) Build(s string, fg, bg Attribute) []Cell {
+ mtb.baseFg = fg
+ mtb.baseBg = bg
+ mtb.reset()
+ mtb.parse(s)
+ cs := make([]Cell, len(mtb.plainTx))
+ for i := range cs {
+ cs[i] = Cell{Ch: mtb.plainTx[i], Fg: fg, Bg: bg}
+ }
+ for _, mrk := range mtb.markers {
+ for i := mrk.st; i < mrk.ed; i++ {
+ cs[i].Fg = mrk.fg
+ cs[i].Bg = mrk.bg
+ }
+ }
+
+ return cs
+}
+
+// NewMarkdownTxBuilder returns a TextBuilder employing markdown syntax.
+func NewMarkdownTxBuilder() TextBuilder {
+ return MarkdownTxBuilder{}
+}
diff --git a/vendor/github.com/gizak/termui/theme.go b/vendor/github.com/gizak/termui/theme.go
new file mode 100644
index 000000000..c3ccda559
--- /dev/null
+++ b/vendor/github.com/gizak/termui/theme.go
@@ -0,0 +1,140 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import "strings"
+
+/*
+// A ColorScheme represents the current look-and-feel of the dashboard.
+type ColorScheme struct {
+ BodyBg Attribute
+ BlockBg Attribute
+ HasBorder bool
+ BorderFg Attribute
+ BorderBg Attribute
+ BorderLabelTextFg Attribute
+ BorderLabelTextBg Attribute
+ ParTextFg Attribute
+ ParTextBg Attribute
+ SparklineLine Attribute
+ SparklineTitle Attribute
+ GaugeBar Attribute
+ GaugePercent Attribute
+ LineChartLine Attribute
+ LineChartAxes Attribute
+ ListItemFg Attribute
+ ListItemBg Attribute
+ BarChartBar Attribute
+ BarChartText Attribute
+ BarChartNum Attribute
+ MBarChartBar Attribute
+ MBarChartText Attribute
+ MBarChartNum Attribute
+ TabActiveBg Attribute
+}
+
+// default color scheme depends on the user's terminal setting.
+var themeDefault = ColorScheme{HasBorder: true}
+
+var themeHelloWorld = ColorScheme{
+ BodyBg: ColorBlack,
+ BlockBg: ColorBlack,
+ HasBorder: true,
+ BorderFg: ColorWhite,
+ BorderBg: ColorBlack,
+ BorderLabelTextBg: ColorBlack,
+ BorderLabelTextFg: ColorGreen,
+ ParTextBg: ColorBlack,
+ ParTextFg: ColorWhite,
+ SparklineLine: ColorMagenta,
+ SparklineTitle: ColorWhite,
+ GaugeBar: ColorRed,
+ GaugePercent: ColorWhite,
+ LineChartLine: ColorYellow | AttrBold,
+ LineChartAxes: ColorWhite,
+ ListItemBg: ColorBlack,
+ ListItemFg: ColorYellow,
+ BarChartBar: ColorRed,
+ BarChartNum: ColorWhite,
+ BarChartText: ColorCyan,
+ MBarChartBar: ColorRed,
+ MBarChartNum: ColorWhite,
+ MBarChartText: ColorCyan,
+ TabActiveBg: ColorMagenta,
+}
+
+var theme = themeDefault // global dep
+
+// Theme returns the currently used theme.
+func Theme() ColorScheme {
+ return theme
+}
+
+// SetTheme sets a new, custom theme.
+func SetTheme(newTheme ColorScheme) {
+ theme = newTheme
+}
+
+// UseTheme sets a predefined scheme. Currently available: "hello-world" and
+// "black-and-white".
+func UseTheme(th string) {
+ switch th {
+ case "helloworld":
+ theme = themeHelloWorld
+ default:
+ theme = themeDefault
+ }
+}
+*/
+
+var ColorMap = map[string]Attribute{
+ "fg": ColorWhite,
+ "bg": ColorDefault,
+ "border.fg": ColorWhite,
+ "label.fg": ColorGreen,
+ "par.fg": ColorYellow,
+ "par.label.bg": ColorWhite,
+}
+
+func ThemeAttr(name string) Attribute {
+ return lookUpAttr(ColorMap, name)
+}
+
+func lookUpAttr(clrmap map[string]Attribute, name string) Attribute {
+
+ a, ok := clrmap[name]
+ if ok {
+ return a
+ }
+
+ ns := strings.Split(name, ".")
+ for i := range ns {
+ nn := strings.Join(ns[i:len(ns)], ".")
+ a, ok = ColorMap[nn]
+ if ok {
+ break
+ }
+ }
+
+ return a
+}
+
+// 0<=r,g,b <= 5
+func ColorRGB(r, g, b int) Attribute {
+ within := func(n int) int {
+ if n < 0 {
+ return 0
+ }
+
+ if n > 5 {
+ return 5
+ }
+
+ return n
+ }
+
+ r, b, g = within(r), within(b), within(g)
+ return Attribute(0x0f + 36*r + 6*g + b)
+}
diff --git a/vendor/github.com/gizak/termui/widget.go b/vendor/github.com/gizak/termui/widget.go
new file mode 100644
index 000000000..35cf143a3
--- /dev/null
+++ b/vendor/github.com/gizak/termui/widget.go
@@ -0,0 +1,94 @@
+// Copyright 2016 Zack Guo <gizak@icloud.com>. All rights reserved.
+// Use of this source code is governed by a MIT license that can
+// be found in the LICENSE file.
+
+package termui
+
+import (
+ "fmt"
+ "sync"
+)
+
+// event mixins
+type WgtMgr map[string]WgtInfo
+
+type WgtInfo struct {
+ Handlers map[string]func(Event)
+ WgtRef Widget
+ Id string
+}
+
+type Widget interface {
+ Id() string
+}
+
+func NewWgtInfo(wgt Widget) WgtInfo {
+ return WgtInfo{
+ Handlers: make(map[string]func(Event)),
+ WgtRef: wgt,
+ Id: wgt.Id(),
+ }
+}
+
+func NewWgtMgr() WgtMgr {
+ wm := WgtMgr(make(map[string]WgtInfo))
+ return wm
+
+}
+
+func (wm WgtMgr) AddWgt(wgt Widget) {
+ wm[wgt.Id()] = NewWgtInfo(wgt)
+}
+
+func (wm WgtMgr) RmWgt(wgt Widget) {
+ wm.RmWgtById(wgt.Id())
+}
+
+func (wm WgtMgr) RmWgtById(id string) {
+ delete(wm, id)
+}
+
+func (wm WgtMgr) AddWgtHandler(id, path string, h func(Event)) {
+ if w, ok := wm[id]; ok {
+ w.Handlers[path] = h
+ }
+}
+
+func (wm WgtMgr) RmWgtHandler(id, path string) {
+ if w, ok := wm[id]; ok {
+ delete(w.Handlers, path)
+ }
+}
+
+var counter struct {
+ sync.RWMutex
+ count int
+}
+
+func GenId() string {
+ counter.Lock()
+ defer counter.Unlock()
+
+ counter.count += 1
+ return fmt.Sprintf("%d", counter.count)
+}
+
+func (wm WgtMgr) WgtHandlersHook() func(Event) {
+ return func(e Event) {
+ for _, v := range wm {
+ if k := findMatch(v.Handlers, e.Path); k != "" {
+ v.Handlers[k](e)
+ }
+ }
+ }
+}
+
+var DefaultWgtMgr WgtMgr
+
+func (b *Block) Handle(path string, handler func(Event)) {
+ if _, ok := DefaultWgtMgr[b.Id()]; !ok {
+ DefaultWgtMgr.AddWgt(b)
+ }
+
+ DefaultWgtMgr.AddWgtHandler(b.Id(), path, handler)
+}