├── .gitignore
├── example
└── main.go
├── go.mod
├── statsview_test.go
├── LICENSE
├── viewer
├── gcnum.go
├── gcsize.go
├── heap.go
├── stack.go
├── goroutine.go
├── gccpufraction.go
└── viewer.go
├── statics
├── macarons.go
├── westeros.go
└── jquery.go
├── go.sum
├── statsview.go
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, build with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
14 | # IDE
15 | .idea/
16 | .vscode/
17 | .history
--------------------------------------------------------------------------------
/example/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 | "os/signal"
6 |
7 | "github.com/smallnest/statsview"
8 | )
9 |
10 | func main() {
11 | mgr := statsview.New()
12 |
13 | go mgr.Start()
14 |
15 | sg := make(chan os.Signal, 1)
16 | signal.Notify(sg, os.Interrupt, os.Kill)
17 | <-sg
18 | }
19 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/smallnest/statsview
2 |
3 | go 1.20
4 |
5 | require (
6 | github.com/go-echarts/go-echarts/v2 v2.2.3
7 | github.com/rs/cors v1.7.0
8 | github.com/shirou/gopsutil/v3 v3.20.10
9 | )
10 |
11 | require (
12 | github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
13 | github.com/go-ole/go-ole v1.2.4 // indirect
14 | golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5 // indirect
15 | )
16 |
--------------------------------------------------------------------------------
/statsview_test.go:
--------------------------------------------------------------------------------
1 | package statsview
2 |
3 | import (
4 | "testing"
5 | "time"
6 | )
7 |
8 | func TestStatsViewMgr(t *testing.T) {
9 | timeout := time.After(time.Minute)
10 | done := make(chan bool)
11 |
12 | go func() {
13 | mgr := New()
14 | go mgr.Start()
15 | time.Sleep(10 * time.Second)
16 | mgr.Stop()
17 |
18 | time.Sleep(2 * time.Second)
19 |
20 | mgr = New()
21 | go mgr.Start()
22 | time.Sleep(10 * time.Second)
23 | mgr.Stop()
24 |
25 | done <- true
26 | }()
27 |
28 | select {
29 | case <-timeout:
30 | t.Fatal("Test didn't finish in time")
31 | case <-done:
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020~present chenjiandongx
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 |
--------------------------------------------------------------------------------
/viewer/gcnum.go:
--------------------------------------------------------------------------------
1 | package viewer
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 |
7 | "github.com/go-echarts/go-echarts/v2/charts"
8 | "github.com/go-echarts/go-echarts/v2/opts"
9 | )
10 |
11 | const (
12 | // VGCNum is the name of GCNumViewer
13 | VGCNum = "gcnum"
14 | )
15 |
16 | // GCNumViewer collects the GC number metric via `runtime.ReadMemStats()`
17 | type GCNumViewer struct {
18 | smgr *StatsMgr
19 | graph *charts.Line
20 | }
21 |
22 | // NewGCNumViewer returns the GCNumViewer instance
23 | // Series: GcNum
24 | func NewGCNumViewer() Viewer {
25 | graph := NewBasicView(VGCNum)
26 | graph.SetGlobalOptions(
27 | charts.WithTitleOpts(opts.Title{Title: "GC Number"}),
28 | charts.WithYAxisOpts(opts.YAxis{Name: "Num"}),
29 | )
30 | graph.AddSeries("GcNum", []opts.LineData{})
31 |
32 | return &GCNumViewer{graph: graph}
33 | }
34 |
35 | func (vr *GCNumViewer) SetStatsMgr(smgr *StatsMgr) {
36 | vr.smgr = smgr
37 | }
38 |
39 | func (vr *GCNumViewer) Name() string {
40 | return VGCNum
41 | }
42 |
43 | func (vr *GCNumViewer) View() *charts.Line {
44 | return vr.graph
45 | }
46 |
47 | func (vr *GCNumViewer) Serve(w http.ResponseWriter, _ *http.Request) {
48 | vr.smgr.Tick()
49 |
50 | metrics := Metrics{
51 | Values: []float64{float64(memstats.Stats.NumGC)},
52 | Time: memstats.T,
53 | }
54 |
55 | bs, _ := json.Marshal(metrics)
56 | w.Write(bs)
57 | }
58 |
--------------------------------------------------------------------------------
/viewer/gcsize.go:
--------------------------------------------------------------------------------
1 | package viewer
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 |
7 | "github.com/go-echarts/go-echarts/v2/charts"
8 | "github.com/go-echarts/go-echarts/v2/opts"
9 | )
10 |
11 | const (
12 | // VGCSzie is the name of GCSizeViewer
13 | VGCSize = "gcsize"
14 | )
15 |
16 | // GCSizeViewer collects the GC size metric via `runtime.ReadMemStats()`
17 | type GCSizeViewer struct {
18 | smgr *StatsMgr
19 | graph *charts.Line
20 | }
21 |
22 | // NewGCSizeViewer returns the GCSizeViewer instance
23 | // Series: GCSys / NextGC
24 | func NewGCSizeViewer() Viewer {
25 | graph := NewBasicView(VGCSize)
26 | graph.SetGlobalOptions(
27 | charts.WithTitleOpts(opts.Title{Title: "GC Size"}),
28 | charts.WithYAxisOpts(opts.YAxis{Name: "Size", AxisLabel: &opts.AxisLabel{Show: true, Formatter: "{value} MB"}}),
29 | )
30 | graph.AddSeries("GCSys", []opts.LineData{}).
31 | AddSeries("NextGC", []opts.LineData{})
32 |
33 | return &GCSizeViewer{graph: graph}
34 | }
35 |
36 | func (vr *GCSizeViewer) SetStatsMgr(smgr *StatsMgr) {
37 | vr.smgr = smgr
38 | }
39 |
40 | func (vr *GCSizeViewer) Name() string {
41 | return VGCSize
42 | }
43 |
44 | func (vr *GCSizeViewer) View() *charts.Line {
45 | return vr.graph
46 | }
47 |
48 | func (vr *GCSizeViewer) Serve(w http.ResponseWriter, _ *http.Request) {
49 | vr.smgr.Tick()
50 |
51 | metrics := Metrics{
52 | Values: []float64{
53 | FixedPrecision(float64(memstats.Stats.GCSys)/1024/1024, 2),
54 | FixedPrecision(float64(memstats.Stats.NextGC)/1024/1024, 2),
55 | },
56 | Time: memstats.T,
57 | }
58 |
59 | bs, _ := json.Marshal(metrics)
60 | w.Write(bs)
61 | }
62 |
--------------------------------------------------------------------------------
/viewer/heap.go:
--------------------------------------------------------------------------------
1 | package viewer
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 |
7 | "github.com/go-echarts/go-echarts/v2/charts"
8 | "github.com/go-echarts/go-echarts/v2/opts"
9 | )
10 |
11 | const (
12 | // VHeap is the name of HeapViewer
13 | VHeap = "heap"
14 | )
15 |
16 | // HeapViewer collects the heap-stats metrics via `runtime.ReadMemStats()`
17 | type HeapViewer struct {
18 | smgr *StatsMgr
19 | graph *charts.Line
20 | }
21 |
22 | // NewHeapViewer returns the HeapViewer instance
23 | // Series: Alloc / Inuse / Sys / Idle
24 | func NewHeapViewer() Viewer {
25 | graph := NewBasicView(VHeap)
26 | graph.SetGlobalOptions(
27 | charts.WithTitleOpts(opts.Title{Title: "Heap"}),
28 | charts.WithYAxisOpts(opts.YAxis{Name: "Size", AxisLabel: &opts.AxisLabel{Show: true, Formatter: "{value} MB"}}),
29 | )
30 | graph.AddSeries("Alloc", []opts.LineData{}).
31 | AddSeries("Inuse", []opts.LineData{}).
32 | AddSeries("Sys", []opts.LineData{}).
33 | AddSeries("Idle", []opts.LineData{})
34 |
35 | return &HeapViewer{graph: graph}
36 | }
37 | func (vr *HeapViewer) SetStatsMgr(smgr *StatsMgr) {
38 | vr.smgr = smgr
39 | }
40 | func (vr *HeapViewer) Name() string {
41 | return VHeap
42 | }
43 |
44 | func (vr *HeapViewer) View() *charts.Line {
45 | return vr.graph
46 | }
47 |
48 | func (vr *HeapViewer) Serve(w http.ResponseWriter, _ *http.Request) {
49 | vr.smgr.Tick()
50 |
51 | metrics := Metrics{
52 | Values: []float64{
53 | FixedPrecision(float64(memstats.Stats.HeapAlloc)/1024/1024, 2),
54 | FixedPrecision(float64(memstats.Stats.HeapInuse)/1024/1024, 2),
55 | FixedPrecision(float64(memstats.Stats.HeapSys)/1024/1024, 2),
56 | FixedPrecision(float64(memstats.Stats.HeapIdle)/1024/1024, 2),
57 | },
58 | Time: memstats.T,
59 | }
60 |
61 | bs, _ := json.Marshal(metrics)
62 | w.Write(bs)
63 | }
64 |
--------------------------------------------------------------------------------
/viewer/stack.go:
--------------------------------------------------------------------------------
1 | package viewer
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 |
7 | "github.com/go-echarts/go-echarts/v2/charts"
8 | "github.com/go-echarts/go-echarts/v2/opts"
9 | )
10 |
11 | const (
12 | // VCStack is the name of StackViewer
13 | VCStack = "stack"
14 | )
15 |
16 | // StackViewer collects the stack-stats metrics via `runtime.ReadMemStats()`
17 | type StackViewer struct {
18 | smgr *StatsMgr
19 | graph *charts.Line
20 | }
21 |
22 | // NewStackViewer returns the StackViewer instance
23 | // Series: StackSys / StackInuse / MSpanSys / MSpanInuse
24 | func NewStackViewer() Viewer {
25 | graph := NewBasicView(VCStack)
26 | graph.SetGlobalOptions(
27 | charts.WithTitleOpts(opts.Title{Title: "Stack"}),
28 | charts.WithYAxisOpts(opts.YAxis{Name: "Size", AxisLabel: &opts.AxisLabel{Show: true, Formatter: "{value} MB"}}),
29 | )
30 | graph.AddSeries("Sys", []opts.LineData{}).
31 | AddSeries("Inuse", []opts.LineData{}).
32 | AddSeries("MSpan Sys", []opts.LineData{}).
33 | AddSeries("MSpan Inuse", []opts.LineData{})
34 |
35 | return &StackViewer{graph: graph}
36 | }
37 |
38 | func (vr *StackViewer) SetStatsMgr(smgr *StatsMgr) {
39 | vr.smgr = smgr
40 | }
41 |
42 | func (vr *StackViewer) Name() string {
43 | return VCStack
44 | }
45 |
46 | func (vr *StackViewer) View() *charts.Line {
47 | return vr.graph
48 | }
49 |
50 | func (vr *StackViewer) Serve(w http.ResponseWriter, _ *http.Request) {
51 | vr.smgr.Tick()
52 |
53 | metrics := Metrics{
54 | Values: []float64{
55 | FixedPrecision(float64(memstats.Stats.StackSys)/1024/1024, 2),
56 | FixedPrecision(float64(memstats.Stats.StackInuse)/1024/1024, 2),
57 | FixedPrecision(float64(memstats.Stats.MSpanSys)/1024/1024, 2),
58 | FixedPrecision(float64(memstats.Stats.MSpanInuse)/1024/1024, 2),
59 | },
60 | Time: memstats.T,
61 | }
62 |
63 | bs, _ := json.Marshal(metrics)
64 | w.Write(bs)
65 | }
66 |
--------------------------------------------------------------------------------
/viewer/goroutine.go:
--------------------------------------------------------------------------------
1 | package viewer
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 | "runtime"
7 | "runtime/pprof"
8 | "time"
9 |
10 | "github.com/go-echarts/go-echarts/v2/charts"
11 | "github.com/go-echarts/go-echarts/v2/opts"
12 | )
13 |
14 | const (
15 | // VGoroutine is the name of GoroutinesViewer
16 | VGoroutine = "goroutine"
17 | )
18 |
19 | // GoroutinesViewer collects the goroutine number metric via `runtime.NumGoroutine()`
20 | type GoroutinesViewer struct {
21 | smgr *StatsMgr
22 | graph *charts.Line
23 | }
24 |
25 | // NewGoroutinesViewer returns the GoroutinesViewer instance
26 | // Series: Goroutines
27 | func NewGoroutinesViewer() Viewer {
28 | graph := NewBasicView(VGoroutine)
29 | graph.SetGlobalOptions(
30 | charts.WithYAxisOpts(opts.YAxis{Name: "Num"}),
31 | charts.WithTitleOpts(opts.Title{Title: "Goroutines"}),
32 | )
33 | graph.AddSeries("Goroutines", []opts.LineData{})
34 | graph.AddSeries("Threads", []opts.LineData{})
35 | graph.AddSeries("NumCPU", []opts.LineData{})
36 | graph.AddSeries("GOMAXPROCS", []opts.LineData{})
37 |
38 | return &GoroutinesViewer{graph: graph}
39 | }
40 |
41 | func (vr *GoroutinesViewer) SetStatsMgr(smgr *StatsMgr) {
42 | vr.smgr = smgr
43 | }
44 |
45 | func (vr *GoroutinesViewer) Name() string {
46 | return VGoroutine
47 | }
48 |
49 | func (vr *GoroutinesViewer) View() *charts.Line {
50 | return vr.graph
51 | }
52 |
53 | var threadProfile = pprof.Lookup("threadcreate")
54 |
55 | func (vr *GoroutinesViewer) Serve(w http.ResponseWriter, _ *http.Request) {
56 | vr.smgr.Tick()
57 |
58 | metrics := Metrics{
59 | Values: []float64{
60 | float64(runtime.NumGoroutine()),
61 | float64(threadProfile.Count()),
62 | float64(runtime.NumCPU()),
63 | float64(runtime.GOMAXPROCS(0)),
64 | },
65 | Time: time.Now().Format(DefaultCfg.TimeFormat),
66 | }
67 |
68 | bs, _ := json.Marshal(metrics)
69 | w.Write(bs)
70 | }
71 |
--------------------------------------------------------------------------------
/statics/macarons.go:
--------------------------------------------------------------------------------
1 | package statics
2 |
3 | // MacaronsJS contains the Macarons theme configuration
4 | const MacaronsJS = `
5 | !function(e,o){"function"==typeof define&&define.amd?define(["exports","echarts"],o):"object"==typeof exports&&"string"!=typeof exports.nodeName?o(exports,require("echarts")):o({},e.echarts)}(this,function(e,o){var l;if(!o)return l="ECharts is not Loaded",void("undefined"!=typeof console&&console&&console.error&&console.error(l));var r=["#2ec7c9","#b6a2de","#5ab1ef","#ffb980","#d87a80","#8d98b3","#e5cf0d","#97b552","#95706d","#dc69aa","#07a2a4","#9a7fd1","#588dd5","#f5994e","#c05050","#59678c","#c9ab00","#7eb00a","#6f5553","#c14089"],a={color:r,title:{textStyle:{fontWeight:"normal",color:"#008acd"}},visualMap:{itemWidth:15,color:["#5ab1ef","#e0ffff"]},toolbox:{iconStyle:{normal:{borderColor:r[0]}}},tooltip:{backgroundColor:"rgba(50,50,50,0.5)",axisPointer:{type:"line",lineStyle:{color:"#008acd"},crossStyle:{color:"#008acd"},shadowStyle:{color:"rgba(200,200,200,0.2)"}}},dataZoom:{dataBackgroundColor:"#efefff",fillerColor:"rgba(182,162,222,0.2)",handleColor:"#008acd"},grid:{borderColor:"#eee"},categoryAxis:{axisLine:{lineStyle:{color:"#008acd"}},splitLine:{lineStyle:{color:["#eee"]}}},valueAxis:{axisLine:{lineStyle:{color:"#008acd"}},splitArea:{show:!0,areaStyle:{color:["rgba(250,250,250,0.1)","rgba(200,200,200,0.1)"]}},splitLine:{lineStyle:{color:["#eee"]}}},timeline:{lineStyle:{color:"#008acd"},controlStyle:{normal:{color:"#008acd"},emphasis:{color:"#008acd"}},symbol:"emptyCircle",symbolSize:3},line:{smooth:!0,symbol:"emptyCircle",symbolSize:3},candlestick:{itemStyle:{normal:{color:"#d87a80",color0:"#2ec7c9",lineStyle:{color:"#d87a80",color0:"#2ec7c9"}}}},scatter:{symbol:"circle",symbolSize:4},map:{label:{normal:{textStyle:{color:"#d87a80"}}},itemStyle:{normal:{borderColor:"#eee",areaColor:"#ddd"},emphasis:{areaColor:"#fe994e"}}},graph:{color:r},gauge:{axisLine:{lineStyle:{color:[[.2,"#2ec7c9"],[.8,"#5ab1ef"],[1,"#d87a80"]],width:10}},axisTick:{splitNumber:10,length:15,lineStyle:{color:"auto"}},splitLine:{length:22,lineStyle:{color:"auto"}},pointer:{width:5}}};o.registerTheme("macarons",a)});
6 | `
7 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
2 | github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6 | github.com/go-echarts/go-echarts/v2 v2.2.3 h1:H8oPdUpzuiV2K8S4xYZa1JRNjP3U0h7HVqvhPrmCk1A=
7 | github.com/go-echarts/go-echarts/v2 v2.2.3/go.mod h1:6TOomEztzGDVDkOSCFBq3ed7xOYfbOqhaBzD0YV771A=
8 | github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
9 | github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
10 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
11 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
12 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
13 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
14 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
15 | github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
16 | github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
17 | github.com/shirou/gopsutil/v3 v3.20.10 h1:7zomV9HJv6UGk225YtvEa5+camNLpbua3MAz/GqiVJY=
18 | github.com/shirou/gopsutil/v3 v3.20.10/go.mod h1:igHnfak0qnw1biGeI2qKQvu0ZkwvEkUcCLlYhZzdr/4=
19 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
20 | github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
21 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
22 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
23 | golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5 h1:iCaAy5bMeEvwANu3YnJfWwI0kWAGkEa2RXPdweI/ysk=
24 | golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
25 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
26 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
27 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
28 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
29 |
--------------------------------------------------------------------------------
/viewer/gccpufraction.go:
--------------------------------------------------------------------------------
1 | package viewer
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "net/http"
7 | "os"
8 | "time"
9 |
10 | "github.com/go-echarts/go-echarts/v2/charts"
11 | "github.com/go-echarts/go-echarts/v2/opts"
12 | "github.com/shirou/gopsutil/v3/cpu"
13 | "github.com/shirou/gopsutil/v3/process"
14 | )
15 |
16 | const (
17 | // VGCCPUFraction is the name of GCCPUFractionViewer
18 | VGCCPUFraction = "gccpufraction"
19 | )
20 |
21 | // GCCPUFractionViewer collects the GC-CPU fraction metric via `runtime.ReadMemStats()`
22 | type GCCPUFractionViewer struct {
23 | smgr *StatsMgr
24 | graph *charts.Line
25 | p *process.Process
26 | }
27 |
28 | // NewGCCPUFractionViewer returns the GCCPUFractionViewer instance
29 | // Series: Fraction
30 | func NewGCCPUFractionViewer() Viewer {
31 | return NewGCCPUFractionViewerWithNumCPU()
32 | }
33 |
34 | // NewGCCPUFractionViewer returns the GCCPUFractionViewer instance
35 | // Series: Fraction
36 | func NewGCCPUFractionViewerWithNumCPU() Viewer {
37 | p, _ := process.NewProcess(int32(os.Getpid()))
38 |
39 | graph := NewBasicView(VGCCPUFraction)
40 | graph.SetGlobalOptions(
41 | charts.WithTitleOpts(opts.Title{Title: "CPUFraction"}),
42 | charts.WithYAxisOpts(opts.YAxis{Name: "Percent", AxisLabel: &opts.AxisLabel{Show: true, Formatter: "{value} %", Rotate: 35}}),
43 | )
44 | graph.AddSeries("GC CPUFraction", []opts.LineData{})
45 | graph.AddSeries("Server CPUFraction", []opts.LineData{})
46 | graph.AddSeries("App CPUFraction", []opts.LineData{})
47 |
48 | return &GCCPUFractionViewer{graph: graph, p: p}
49 | }
50 |
51 | func (vr *GCCPUFractionViewer) SetStatsMgr(smgr *StatsMgr) {
52 | vr.smgr = smgr
53 | }
54 |
55 | func (vr *GCCPUFractionViewer) Name() string {
56 | return VGCCPUFraction
57 | }
58 |
59 | func (vr *GCCPUFractionViewer) View() *charts.Line {
60 | return vr.graph
61 | }
62 |
63 | func (vr *GCCPUFractionViewer) Serve(w http.ResponseWriter, _ *http.Request) {
64 | vr.smgr.Tick()
65 |
66 | metrics := Metrics{
67 | Values: []float64{
68 | FixedPrecision(memstats.Stats.GCCPUFraction, 6),
69 | FixedPrecision(vr.getServerCPUFraction(), 6),
70 | FixedPrecision(vr.getAppCPUFraction(), 6),
71 | },
72 | Time: memstats.T,
73 | }
74 |
75 | bs, _ := json.Marshal(metrics)
76 | w.Write(bs)
77 | }
78 |
79 | func (vr *GCCPUFractionViewer) getAppCPUFraction() float64 {
80 | p := vr.p
81 | if p == nil {
82 | return 0.0
83 | }
84 | percent, _ := p.Percent(0)
85 | return percent / 100
86 | }
87 |
88 | func (vr *GCCPUFractionViewer) getServerCPUFraction() float64 {
89 | totalUsage, _ := cpu.PercentWithContext(context.Background(), time.Second, false)
90 | if len(totalUsage) == 0 {
91 | return 0.0
92 | }
93 |
94 | return totalUsage[0] / 100
95 | }
96 |
--------------------------------------------------------------------------------
/statsview.go:
--------------------------------------------------------------------------------
1 | package statsview
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "net/http"
7 | "net/http/pprof"
8 | "time"
9 |
10 | "github.com/go-echarts/go-echarts/v2/components"
11 | "github.com/go-echarts/go-echarts/v2/templates"
12 | "github.com/rs/cors"
13 |
14 | "github.com/smallnest/statsview/statics"
15 | "github.com/smallnest/statsview/viewer"
16 | )
17 |
18 | // ViewManager
19 | type ViewManager struct {
20 | srv *http.Server
21 |
22 | Smgr *viewer.StatsMgr
23 | Ctx context.Context
24 | Cancel context.CancelFunc
25 | Views []viewer.Viewer
26 | }
27 |
28 | // Register registers views to the ViewManager
29 | func (vm *ViewManager) Register(views ...viewer.Viewer) {
30 | vm.Views = append(vm.Views, views...)
31 |
32 | }
33 |
34 | // Start runs a http server and begin to collect metrics
35 | func (vm *ViewManager) Start() error {
36 | return vm.srv.ListenAndServe()
37 | }
38 |
39 | // Stop shutdown the http server gracefully
40 | func (vm *ViewManager) Stop() {
41 | ctx, cancel := context.WithTimeout(context.Background(), time.Second)
42 | defer cancel()
43 | vm.srv.Shutdown(ctx)
44 | vm.Cancel()
45 | }
46 |
47 | func init() {
48 | templates.PageTpl = `
49 | {{- define "page" }}
50 |
51 |
52 | {{- template "header" . }}
53 |
54 | 🚀 StatsView is a real-time Golang runtime stats visualization profiler
55 |
56 | {{- range .Charts }} {{ template "base" . }} {{- end }}
57 |
58 |
59 | {{ end }}
60 | `
61 | }
62 |
63 | // New creates a new ViewManager instance
64 | func New() *ViewManager {
65 | page := components.NewPage()
66 | page.PageTitle = "Statsview"
67 | page.AssetsHost = fmt.Sprintf("http://%s/debug/statsview/statics/", viewer.LinkAddr())
68 | page.Assets.JSAssets.Add("jquery.min.js")
69 |
70 | mgr := &ViewManager{
71 | srv: &http.Server{
72 | Addr: viewer.Addr(),
73 | ReadTimeout: time.Minute,
74 | WriteTimeout: time.Minute,
75 | MaxHeaderBytes: 1 << 20,
76 | },
77 | }
78 | mgr.Ctx, mgr.Cancel = context.WithCancel(context.Background())
79 | mgr.Register(
80 | viewer.NewGoroutinesViewer(),
81 | viewer.NewHeapViewer(),
82 | viewer.NewStackViewer(),
83 | viewer.NewGCNumViewer(),
84 | viewer.NewGCSizeViewer(),
85 | viewer.NewGCCPUFractionViewer(),
86 | )
87 | smgr := viewer.NewStatsMgr(mgr.Ctx)
88 | for _, v := range mgr.Views {
89 | v.SetStatsMgr(smgr)
90 | }
91 |
92 | mux := http.NewServeMux()
93 | mux.HandleFunc("/debug/pprof/", pprof.Index)
94 | mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
95 | mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
96 | mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
97 | mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
98 |
99 | for _, v := range mgr.Views {
100 | page.AddCharts(v.View())
101 | mux.HandleFunc("/debug/statsview/view/"+v.Name(), v.Serve)
102 | }
103 |
104 | mux.HandleFunc("/debug/statsview", func(w http.ResponseWriter, _ *http.Request) {
105 | page.Render(w)
106 | })
107 |
108 | staticsPrev := "/debug/statsview/statics/"
109 | mux.HandleFunc(staticsPrev+"echarts.min.js", func(w http.ResponseWriter, _ *http.Request) {
110 | w.Write([]byte(statics.EchartJS))
111 | })
112 |
113 | mux.HandleFunc(staticsPrev+"jquery.min.js", func(w http.ResponseWriter, _ *http.Request) {
114 | w.Write([]byte(statics.JqueryJS))
115 | })
116 |
117 | mux.HandleFunc(staticsPrev+"themes/westeros.js", func(w http.ResponseWriter, _ *http.Request) {
118 | w.Write([]byte(statics.WesterosJS))
119 | })
120 |
121 | mux.HandleFunc(staticsPrev+"themes/macarons.js", func(w http.ResponseWriter, _ *http.Request) {
122 | w.Write([]byte(statics.MacaronsJS))
123 | })
124 |
125 | mgr.srv.Handler = cors.AllowAll().Handler(mux)
126 | return mgr
127 | }
128 |
--------------------------------------------------------------------------------
/statics/westeros.go:
--------------------------------------------------------------------------------
1 | package statics
2 |
3 | // WesterosJS contains the Westeros theme configuration
4 | const WesterosJS = `
5 | !function(e,o){"function"==typeof define&&define.amd?define(["exports","echarts"],o):"object"==typeof exports&&"string"!=typeof exports.nodeName?o(exports,require("echarts")):o({},e.echarts)}(this,function(e,o){var r;if(!o)return r="ECharts is not Loaded",void("undefined"!=typeof console&&console&&console.error&&console.error(r));o.registerTheme("westeros",{color:["#516b91","#59c4e6","#edafda","#93b7e3","#a5e7f0","#cbb0e3"],backgroundColor:"rgba(0,0,0,0)",textStyle:{},title:{textStyle:{color:"#516b91"},subtextStyle:{color:"#93b7e3"}},line:{itemStyle:{normal:{borderWidth:"2"}},lineStyle:{normal:{width:"2"}},symbolSize:"1",symbol:"emptyCircle",smooth:!0},radar:{itemStyle:{normal:{borderWidth:"2"}},lineStyle:{normal:{width:"2"}},symbolSize:"1",symbol:"emptyCircle",smooth:!0},bar:{itemStyle:{normal:{barBorderWidth:0,barBorderColor:"#ccc"},emphasis:{barBorderWidth:0,barBorderColor:"#ccc"}}},pie:{itemStyle:{normal:{borderWidth:0,borderColor:"#ccc"},emphasis:{borderWidth:0,borderColor:"#ccc"}}},scatter:{itemStyle:{normal:{borderWidth:0,borderColor:"#ccc"},emphasis:{borderWidth:0,borderColor:"#ccc"}}},boxplot:{itemStyle:{normal:{borderWidth:0,borderColor:"#ccc"},emphasis:{borderWidth:0,borderColor:"#ccc"}}},parallel:{itemStyle:{normal:{borderWidth:0,borderColor:"#ccc"},emphasis:{borderWidth:0,borderColor:"#ccc"}}},sankey:{itemStyle:{normal:{borderWidth:0,borderColor:"#ccc"},emphasis:{borderWidth:0,borderColor:"#ccc"}}},funnel:{itemStyle:{normal:{borderWidth:0,borderColor:"#ccc"},emphasis:{borderWidth:0,borderColor:"#ccc"}}},gauge:{itemStyle:{normal:{borderWidth:0,borderColor:"#ccc"},emphasis:{borderWidth:0,borderColor:"#ccc"}}},candlestick:{itemStyle:{normal:{color:"#edafda",color0:"transparent",borderColor:"#d680bc",borderColor0:"#8fd3e8",borderWidth:"2"}}},graph:{itemStyle:{normal:{borderWidth:0,borderColor:"#ccc"}},lineStyle:{normal:{width:1,color:"#aaaaaa"}},symbolSize:"1",symbol:"emptyCircle",smooth:!0,color:["#516b91","#59c4e6","#edafda","#93b7e3","#a5e7f0","#cbb0e3"],label:{normal:{textStyle:{color:"#eeeeee"}}}},map:{itemStyle:{normal:{areaColor:"#f3f3f3",borderColor:"#516b91",borderWidth:.5},emphasis:{areaColor:"rgba(165,231,240,1)",borderColor:"#516b91",borderWidth:1}},label:{normal:{textStyle:{color:"#000000"}},emphasis:{textStyle:{color:"rgb(81,107,145)"}}}},geo:{itemStyle:{normal:{areaColor:"#f3f3f3",borderColor:"#516b91",borderWidth:.5},emphasis:{areaColor:"rgba(165,231,240,1)",borderColor:"#516b91",borderWidth:1}},label:{normal:{textStyle:{color:"#000000"}},emphasis:{textStyle:{color:"rgb(81,107,145)"}}}},categoryAxis:{axisLine:{show:!0,lineStyle:{color:"#cccccc"}},axisTick:{show:!1,lineStyle:{color:"#333"}},axisLabel:{show:!0,textStyle:{color:"#999999"}},splitLine:{show:!0,lineStyle:{color:["#eeeeee"]}},splitArea:{show:!1,areaStyle:{color:["rgba(250,250,250,0.05)","rgba(200,200,200,0.02)"]}}},valueAxis:{axisLine:{show:!0,lineStyle:{color:"#cccccc"}},axisTick:{show:!1,lineStyle:{color:"#333"}},axisLabel:{show:!0,textStyle:{color:"#999999"}},splitLine:{show:!0,lineStyle:{color:["#eeeeee"]}},splitArea:{show:!1,areaStyle:{color:["rgba(250,250,250,0.05)","rgba(200,200,200,0.02)"]}}},logAxis:{axisLine:{show:!0,lineStyle:{color:"#cccccc"}},axisTick:{show:!1,lineStyle:{color:"#333"}},axisLabel:{show:!0,textStyle:{color:"#999999"}},splitLine:{show:!0,lineStyle:{color:["#eeeeee"]}},splitArea:{show:!1,areaStyle:{color:["rgba(250,250,250,0.05)","rgba(200,200,200,0.02)"]}}},timeAxis:{axisLine:{show:!0,lineStyle:{color:"#cccccc"}},axisTick:{show:!1,lineStyle:{color:"#333"}},axisLabel:{show:!0,textStyle:{color:"#999999"}},splitLine:{show:!0,lineStyle:{color:["#eeeeee"]}},splitArea:{show:!1,areaStyle:{color:["rgba(250,250,250,0.05)","rgba(200,200,200,0.02)"]}}},toolbox:{iconStyle:{normal:{borderColor:"#999999"},emphasis:{borderColor:"#666666"}}},legend:{textStyle:{color:"#999999"}},tooltip:{axisPointer:{lineStyle:{color:"#cccccc",width:1},crossStyle:{color:"#cccccc",width:1}}},timeline:{lineStyle:{color:"#8fd3e8",width:1},itemStyle:{normal:{color:"#8fd3e8",borderWidth:1},emphasis:{color:"#8fd3e8"}},controlStyle:{normal:{color:"#8fd3e8",borderColor:"#8fd3e8",borderWidth:.5},emphasis:{color:"#8fd3e8",borderColor:"#8fd3e8",borderWidth:.5}},checkpointStyle:{color:"#8fd3e8",borderColor:"rgba(138,124,168,0.37)"},label:{normal:{textStyle:{color:"#8fd3e8"}},emphasis:{textStyle:{color:"#8fd3e8"}}}},visualMap:{color:["#516b91","#59c4e6","#a5e7f0"]},dataZoom:{backgroundColor:"rgba(0,0,0,0)",dataBackgroundColor:"rgba(255,255,255,0.3)",fillerColor:"rgba(167,183,204,0.4)",handleColor:"#a7b7cc",handleSize:"100%",textStyle:{color:"#333333"}},markPoint:{label:{normal:{textStyle:{color:"#eeeeee"}},emphasis:{textStyle:{color:"#eeeeee"}}}}})});
6 | `
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🚀 Statsview
2 |
3 | cloned from [statsview](https://github.com/go-echarts/statsview).
4 |
5 |
6 | Statsview is a real-time Golang runtime stats visualization profiler. It is built top on another open-source project, [go-echarts](https://github.com/go-echarts/go-echarts), which helps statsview to show its graphs on the browser.
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | ## 🔰 Installation
22 |
23 | ```shell
24 | $ go get -u github.com/smallnest/statsview/...
25 | ```
26 |
27 | ## 📝 Usage
28 |
29 | Statsview is quite simple to use and all static assets have been packaged into the project which makes it possible to run offline. It's worth pointing out that statsview has integrated the standard `net/http/pprof` hence statsview will be the only profiler you need.
30 |
31 | ```golang
32 | package main
33 |
34 | import (
35 | "time"
36 |
37 | "github.com/smallnest/statsview"
38 | )
39 |
40 | func main() {
41 | mgr := statsview.New()
42 |
43 | // Start() runs a HTTP server at `localhost:18066` by default.
44 | go mgr.Start()
45 |
46 | // Stop() will shutdown the http server gracefully
47 | // mgr.Stop()
48 |
49 | // busy working....
50 | time.Sleep(time.Minute)
51 | }
52 |
53 | // Visit your browser at http://localhost:18066/debug/statsview
54 | // Or debug as always via http://localhost:18066/debug/pprof, http://localhost:18066/debug/pprof/heap, ...
55 | ```
56 |
57 | ## ⚙️ Configuration
58 |
59 | Statsview gets a variety of configurations for the users. Everyone could customize their favorite charts style with `viewer.SetConfiguration(...)` method.
60 |
61 | ```golang
62 | // WithInterval sets the interval(in Millisecond) of collecting and pulling metrics
63 | // default -> 2000
64 | WithInterval(interval int)
65 |
66 | // WithMaxPoints sets the maximum points of each chart series
67 | // default -> 30
68 | WithMaxPoints(n int)
69 |
70 | // WithTemplate sets the rendered template which fetching stats from the server and
71 | // handling the metrics data
72 | WithTemplate(t string)
73 |
74 | // WithAddr sets the listening address and link address
75 | // default -> "localhost:18066"
76 | WithAddr(addr string)
77 |
78 | // WithLinkAddr sets the html link address
79 | // default -> "localhost:18066"
80 | WithLinkAddr(addr string)
81 |
82 | // WithTimeFormat sets the time format for the line-chart Y-axis label
83 | // default -> "15:04:05"
84 | WithTimeFormat(s string)
85 |
86 | // WithTheme sets the theme of the charts
87 | // default -> Macarons
88 | //
89 | // Optional:
90 | // * ThemeWesteros
91 | // * ThemeMacarons
92 | WithTheme(theme Theme)
93 | ```
94 |
95 | #### Set the options
96 |
97 | ```golang
98 | import (
99 | "github.com/smallnest/statsview"
100 | "github.com/smallnest/statsview/viewer"
101 | )
102 |
103 | // set configurations before calling `statsview.New()` method
104 | viewer.SetConfiguration(viewer.WithTheme(viewer.ThemeWesteros), viewer.WithAddr("localhost:8087"))
105 |
106 | mgr := statsview.New()
107 | go mgr.Start()
108 | ```
109 |
110 | ## 🗂 Viewers
111 |
112 | Viewer is the abstraction of a Graph which in charge of collecting metrics from Runtime. Statsview provides some default viewers as below.
113 |
114 | * `GCCPUFractionViewer`
115 | * `GCNumViewer`
116 | * `GCSizeViewer`
117 | * `GoroutinesViewer`
118 | * `HeapViewer`
119 | * `StackViewer`
120 |
121 | Viewer wraps a go-echarts [*charts.Line](https://github.com/go-echarts/go-echarts/blob/master/charts/line.go) instance that means all options/features on it could be used. To be honest, I think that is the most charming thing about this project.
122 |
123 | ## 🔖 Snapshot
124 |
125 | #### ThemeMacarons(default)
126 |
127 | 
128 |
129 | #### ThemeWesteros
130 |
131 | 
132 |
133 | ## 📄 License
134 |
135 | MIT [©chenjiandongx](https://github.com/chenjiandongx)
136 |
--------------------------------------------------------------------------------
/viewer/viewer.go:
--------------------------------------------------------------------------------
1 | package viewer
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "fmt"
7 | "net/http"
8 | "runtime"
9 | "strconv"
10 | "text/template"
11 | "time"
12 |
13 | "github.com/go-echarts/go-echarts/v2/charts"
14 | "github.com/go-echarts/go-echarts/v2/opts"
15 | "github.com/go-echarts/go-echarts/v2/types"
16 | )
17 |
18 | // Metrics
19 | type Metrics struct {
20 | Values []float64 `json:"values"`
21 | Time string `json:"time"`
22 | }
23 |
24 | type config struct {
25 | Interval int
26 | MaxPoints int
27 | Template string
28 | ListenAddr string
29 | LinkAddr string
30 | TimeFormat string
31 | Theme Theme
32 | }
33 |
34 | type Theme string
35 |
36 | const (
37 | ThemeWesteros Theme = types.ThemeWesteros
38 | ThemeMacarons Theme = types.ThemeMacarons
39 | )
40 |
41 | const (
42 | DefaultTemplate = `
43 | $(function () { setInterval({{ .ViewID }}_sync, {{ .Interval }}); });
44 | function {{ .ViewID }}_sync() {
45 | $.ajax({
46 | type: "GET",
47 | url: "http://{{ .Addr }}/debug/statsview/view/{{ .Route }}",
48 | dataType: "json",
49 | success: function (result) {
50 | let opt = goecharts_{{ .ViewID }}.getOption();
51 |
52 | let x = opt.xAxis[0].data;
53 | x.push(result.time);
54 | if (x.length > {{ .MaxPoints }}) {
55 | x = x.slice(1);
56 | }
57 | opt.xAxis[0].data = x;
58 |
59 | for (let i = 0; i < result.values.length; i++) {
60 | let y = opt.series[i].data;
61 | y.push({ value: result.values[i] });
62 | if (y.length > {{ .MaxPoints }}) {
63 | y = y.slice(1);
64 | }
65 | opt.series[i].data = y;
66 |
67 | goecharts_{{ .ViewID }}.setOption(opt);
68 | }
69 | }
70 | });
71 | }`
72 | DefaultMaxPoints = 30
73 | DefaultTimeFormat = "15:04:05"
74 | DefaultInterval = 2000
75 | DefaultAddr = "localhost:18066"
76 | DefaultTheme = ThemeMacarons
77 | )
78 |
79 | var DefaultCfg = &config{
80 | Interval: DefaultInterval,
81 | MaxPoints: DefaultMaxPoints,
82 | Template: DefaultTemplate,
83 | ListenAddr: DefaultAddr,
84 | LinkAddr: DefaultAddr,
85 | TimeFormat: DefaultTimeFormat,
86 | Theme: DefaultTheme,
87 | }
88 |
89 | type Option func(c *config)
90 |
91 | // Addr returns the default server listening address
92 | func Addr() string {
93 | return DefaultCfg.ListenAddr
94 | }
95 |
96 | // LinkAddr returns the default html link address
97 | func LinkAddr() string {
98 | return DefaultCfg.LinkAddr
99 | }
100 |
101 | // Interval returns the default collecting interval of ViewManager
102 | func Interval() int {
103 | return DefaultCfg.Interval
104 | }
105 |
106 | // WithInterval sets the interval of collecting and pulling metrics
107 | func WithInterval(interval int) Option {
108 | return func(c *config) {
109 | c.Interval = interval
110 | }
111 | }
112 |
113 | // WithMaxPoints sets the maximum points of each chart series
114 | func WithMaxPoints(n int) Option {
115 | return func(c *config) {
116 | c.MaxPoints = n
117 | }
118 | }
119 |
120 | // WithTemplate sets the rendered template which fetching stats from the server and
121 | // handling the metrics data
122 | func WithTemplate(t string) Option {
123 | return func(c *config) {
124 | c.Template = t
125 | }
126 | }
127 |
128 | // WithAddr sets the listening address and link address
129 | func WithAddr(addr string) Option {
130 | return func(c *config) {
131 | c.ListenAddr = addr
132 | c.LinkAddr = addr
133 | }
134 | }
135 |
136 | // WithLinkAddr sets the html link address
137 | func WithLinkAddr(addr string) Option {
138 | return func(c *config) {
139 | c.LinkAddr = addr
140 | }
141 | }
142 |
143 | // WithTimeFormat sets the time format for the line-chart Y-axis label
144 | func WithTimeFormat(s string) Option {
145 | return func(c *config) {
146 | c.TimeFormat = s
147 | }
148 | }
149 |
150 | // WithTheme sets the theme of the charts
151 | func WithTheme(theme Theme) Option {
152 | return func(c *config) {
153 | c.Theme = theme
154 | }
155 | }
156 |
157 | func SetConfiguration(opts ...Option) {
158 | for _, opt := range opts {
159 | opt(DefaultCfg)
160 | }
161 | }
162 |
163 | // Viewer is the abstraction of a Graph which in charge of collecting metrics from somewhere
164 | type Viewer interface {
165 | Name() string
166 | View() *charts.Line
167 | Serve(w http.ResponseWriter, _ *http.Request)
168 | SetStatsMgr(smgr *StatsMgr)
169 | }
170 |
171 | // StatsEntity is the entity of the metrics with timestamp.
172 | type StatsEntity struct {
173 | Stats *runtime.MemStats
174 | T string
175 | }
176 |
177 | var memstats = &StatsEntity{Stats: &runtime.MemStats{}}
178 |
179 | // MemStats returns the runtime.MemStats and T.
180 | func MemStats() *StatsEntity {
181 | return memstats
182 | }
183 |
184 | type StatsMgr struct {
185 | last int64
186 | Ctx context.Context
187 | Cancel context.CancelFunc
188 | }
189 |
190 | func NewStatsMgr(ctx context.Context) *StatsMgr {
191 | s := &StatsMgr{}
192 | s.Ctx, s.Cancel = context.WithCancel(ctx)
193 | go s.polling()
194 |
195 | return s
196 | }
197 |
198 | func (s *StatsMgr) Tick() {
199 | s.last = time.Now().Unix() + int64(float64(Interval())/1000.0)*2
200 | }
201 |
202 | func (s *StatsMgr) polling() {
203 | ticker := time.NewTicker(time.Duration(Interval()) * time.Millisecond)
204 | defer ticker.Stop()
205 |
206 | for {
207 | select {
208 | case <-ticker.C:
209 | if s.last > time.Now().Unix() {
210 | runtime.ReadMemStats(memstats.Stats)
211 | memstats.T = time.Now().Format(DefaultCfg.TimeFormat)
212 | }
213 | case <-s.Ctx.Done():
214 | return
215 | }
216 | }
217 | }
218 |
219 | func genViewTemplate(vid, route string) string {
220 | tpl, err := template.New("view").Parse(DefaultCfg.Template)
221 | if err != nil {
222 | panic("statsview: failed to parse template " + err.Error())
223 | }
224 |
225 | var c = struct {
226 | Interval int
227 | MaxPoints int
228 | Addr string
229 | Route string
230 | ViewID string
231 | }{
232 | Interval: DefaultCfg.Interval,
233 | MaxPoints: DefaultCfg.MaxPoints,
234 | Addr: DefaultCfg.LinkAddr,
235 | Route: route,
236 | ViewID: vid,
237 | }
238 |
239 | buf := bytes.Buffer{}
240 | if err := tpl.Execute(&buf, c); err != nil {
241 | panic("statsview: failed to execute template " + err.Error())
242 | }
243 |
244 | return buf.String()
245 | }
246 |
247 | // FixedPrecision returns the fixed precision float64
248 | func FixedPrecision(n float64, p int) float64 {
249 | var r float64
250 | switch p {
251 | case 2:
252 | r, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", n), 64)
253 | case 6:
254 | r, _ = strconv.ParseFloat(fmt.Sprintf("%.6f", n), 64)
255 | }
256 | return r
257 | }
258 |
259 | // NewBasicView returns a basic line-chart with default configurations
260 | func NewBasicView(route string) *charts.Line {
261 | graph := charts.NewLine()
262 | graph.SetGlobalOptions(
263 | charts.WithLegendOpts(opts.Legend{Show: true}),
264 | charts.WithTooltipOpts(opts.Tooltip{Show: true, Trigger: "axis"}),
265 | charts.WithXAxisOpts(opts.XAxis{Name: "Time"}),
266 | charts.WithInitializationOpts(opts.Initialization{
267 | Width: "600px",
268 | Height: "400px",
269 | Theme: string(DefaultCfg.Theme),
270 | }),
271 | )
272 | graph.SetXAxis([]string{}).SetSeriesOptions(charts.WithLineChartOpts(opts.LineChart{Smooth: true}))
273 | graph.AddJSFuncs(genViewTemplate(graph.ChartID, route))
274 | return graph
275 | }
276 |
--------------------------------------------------------------------------------
/statics/jquery.go:
--------------------------------------------------------------------------------
1 | package statics
2 |
3 | // JqueryJS is the jquery.min.js asset
4 | const JqueryJS = `
5 | /*! jQuery v3.0.0 | (c) jQuery Foundation | jquery.org/license */
6 | !function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.0.0",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:f.call(this)},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:h,sort:c.sort,splice:c.splice},r.extend=r.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||r.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(r.isPlainObject(d)||(e=r.isArray(d)))?(e?(e=!1,f=c&&r.isArray(c)?c:[]):f=c&&r.isPlainObject(c)?c:{},g[b]=r.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},r.extend({expando:"jQuery"+(q+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===r.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=r.type(a);return("number"===b||"string"===b)&&!isNaN(a-parseFloat(a))},isPlainObject:function(a){var b,c;return a&&"[object Object]"===k.call(a)?(b=e(a))?(c=l.call(b,"constructor")&&b.constructor,"function"==typeof c&&m.call(c)===n):!0:!1},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?j[k.call(a)]||"object":typeof a},globalEval:function(a){p(a)},camelCase:function(a){return a.replace(t,"ms-").replace(u,v)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(w(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(s,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(w(Object(a))?r.merge(c,"string"==typeof a?[a]:a):h.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:i.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,f=0,h=[];if(w(a))for(d=a.length;d>f;f++)e=b(a[f],f,c),null!=e&&h.push(e);else for(f in a)e=b(a[f],f,c),null!=e&&h.push(e);return g.apply([],h)},guid:1,proxy:function(a,b){var c,d,e;return"string"==typeof b&&(c=a[b],b=a,a=c),r.isFunction(a)?(d=f.call(arguments,2),e=function(){return a.apply(b||this,d.concat(f.call(arguments)))},e.guid=a.guid=a.guid||r.guid++,e):void 0},now:Date.now,support:o}),"function"==typeof Symbol&&(r.fn[Symbol.iterator]=c[Symbol.iterator]),r.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){j["[object "+b+"]"]=b.toLowerCase()});function w(a){var b=!!a&&"length"in a&&a.length,c=r.type(a);return"function"===c||r.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\x00-\\xa0])+",M="\\["+K+"*("+L+")(?:"+K+"*([*^$|!~]?=)"+K+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+L+"))|)"+K+"*\\]",N=":("+L+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+M+")*)|.*)\\)|)",O=new RegExp(K+"+","g"),P=new RegExp("^"+K+"+|((?:^|[^\\\\])(?:\\\\.)*)"+K+"+$","g"),Q=new RegExp("^"+K+"*,"+K+"*"),R=new RegExp("^"+K+"*([>+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g,ca=function(a,b){return b?"\x00"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"label"in b&&b.disabled===a||"form"in b&&b.disabled===a||"form"in b&&b.disabled===!1&&(b.isDisabled===a||b.isDisabled!==!a&&("label"in b||!ea(b))!==a)}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[0>c?c+b:c]}),even:pa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:pa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:pa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function ta(a,b,c){var d=b.dir,e=b.next,f=e||d,g=c&&"parentNode"===f,h=x++;return b.first?function(b,c,e){while(b=b[d])if(1===b.nodeType||g)return a(b,c,e)}:function(b,c,i){var j,k,l,m=[w,h];if(i){while(b=b[d])if((1===b.nodeType||g)&&a(b,c,i))return!0}else while(b=b[d])if(1===b.nodeType||g)if(l=b[u]||(b[u]={}),k=l[b.uniqueID]||(l[b.uniqueID]={}),e&&e===b.nodeName.toLowerCase())b=b[d]||b;else{if((j=k[f])&&j[0]===w&&j[1]===h)return m[2]=j[2];if(k[f]=m,m[2]=a(b,c,i))return!0}}}function ua(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function wa(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function xa(a,b,c,d,e,f){return d&&!d[u]&&(d=xa(d)),e&&!e[u]&&(e=xa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||va(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:wa(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=wa(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?I(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ta(ua(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return xa(i>1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,e>i&&ya(a.slice(i,e)),f>e&&ya(a=a.slice(e)),f>e&&sa(a))}m.push(c)}return ua(m)}function za(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(_,aa),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=V.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(_,aa),$.test(j[0].type)&&qa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&sa(j),!a)return G.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||$.test(a)&&qa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){if(r.isFunction(b))return r.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return r.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(C.test(b))return r.filter(b,a,c);b=r.filter(b,a)}return r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType})}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;d>b;b++)if(r.contains(e[b],this))return!0}));for(c=this.pushStack([]),b=0;d>b;b++)r.find(a,e[b],c);return d>1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(r.contains(this,b[a]))return!0})},closest:function(a,b){var c,d=0,e=this.length,f=[],g="string"!=typeof a&&r(a);if(!A.test(a))for(;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/\S+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(f>b)){if(a=d.apply(h,i),a===c.promise())throw new TypeError("Thenable self-resolution");j=a&&("object"==typeof a||"function"==typeof a)&&a.then,r.isFunction(j)?e?j.call(a,g(f,c,M,e),g(f,c,N,e)):(f++,j.call(a,g(f,c,M,e),g(f,c,N,e),g(f,c,M,c.notifyWith))):(d!==M&&(h=void 0,i=[a]),(e||c.resolveWith)(h,i))}},k=e?j:function(){try{j()}catch(a){r.Deferred.exceptionHook&&r.Deferred.exceptionHook(a,k.stackTrace),b+1>=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(1>=b&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R),a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){
7 | return j.call(r(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},T=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function U(){this.expando=r.expando+U.uid++}U.uid=1,U.prototype={cache:function(a){var b=a[this.expando];return b||(b={},T(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[r.camelCase(b)]=c;else for(d in b)e[r.camelCase(d)]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][r.camelCase(b)]},access:function(a,b,c){return void 0===b||b&&"string"==typeof b&&void 0===c?this.get(a,b):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d=a[this.expando];if(void 0!==d){if(void 0!==b){r.isArray(b)?b=b.map(r.camelCase):(b=r.camelCase(b),b=b in d?[b]:b.match(K)||[]),c=b.length;while(c--)delete d[b[c]]}(void 0===b||r.isEmptyObject(d))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!r.isEmptyObject(b)}};var V=new U,W=new U,X=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Y=/[A-Z]/g;function Z(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Y,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:X.test(c)?JSON.parse(c):c}catch(e){}W.set(a,b,c)}else c=void 0;return c}r.extend({hasData:function(a){return W.hasData(a)||V.hasData(a)},data:function(a,b,c){return W.access(a,b,c)},removeData:function(a,b){W.remove(a,b)},_data:function(a,b,c){return V.access(a,b,c)},_removeData:function(a,b){V.remove(a,b)}}),r.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=W.get(f),1===f.nodeType&&!V.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=r.camelCase(d.slice(5)),Z(f,d,e[d])));V.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){W.set(this,a)}):S(this,function(b){var c;if(f&&void 0===b){if(c=W.get(f,a),void 0!==c)return c;if(c=Z(f,a),void 0!==c)return c}else this.each(function(){W.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthf;f++)d=a[f],d.style&&(c=d.style.display,b?("none"===c&&(e[f]=V.get(d,"display")||null,e[f]||(d.style.display="")),""===d.style.display&&ba(d)&&(e[f]=fa(d))):"none"!==c&&(e[f]="none",V.set(d,"display",c)));for(f=0;g>f;f++)null!=e[f]&&(a[f].style.display=e[f]);return a}r.fn.extend({show:function(){return ga(this,!0)},hide:function(){return ga(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){ba(this)?r(this).show():r(this).hide()})}});var ha=/^(?:checkbox|radio)$/i,ia=/<([a-z][^\/\0>\x20\t\r\n\f]+)/i,ja=/^$|\/(?:java|ecma)script/i,ka={option:[1,""],thead:[1,""],col:[2,""],tr:[2,""],td:[3,""],_default:[0,"",""]};ka.optgroup=ka.option,ka.tbody=ka.tfoot=ka.colgroup=ka.caption=ka.thead,ka.th=ka.td;function la(a,b){var c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function ma(a,b){for(var c=0,d=a.length;d>c;c++)V.set(a[c],"globalEval",!b||V.get(b[c],"globalEval"))}var na=/<|?\w+;/;function oa(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],n=0,o=a.length;o>n;n++)if(f=a[n],f||0===f)if("object"===r.type(f))r.merge(m,f.nodeType?[f]:f);else if(na.test(f)){g=g||l.appendChild(b.createElement("div")),h=(ia.exec(f)||["",""])[1].toLowerCase(),i=ka[h]||ka._default,g.innerHTML=i[1]+r.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;r.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",n=0;while(f=m[n++])if(d&&r.inArray(f,d)>-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=la(l.appendChild(f),"script"),j&&ma(g),c){k=0;while(f=g[k++])ja.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var pa=d.documentElement,qa=/^key/,ra=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,sa=/^([^.]*)(?:\.(.+)|)/;function ta(){return!0}function ua(){return!1}function va(){try{return d.activeElement}catch(a){}}function wa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)wa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=ua;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(pa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=sa.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=sa.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;cc;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?r(e,this).index(i)>-1:r.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h\x20\t\r\n\f]*)[^>]*)\/>/gi,ya=/