Split examples to alone repo (#1776)
* split examples to alone repo * vendor * fix package error * add examples/README.md
This commit is contained in:
parent
ccb9e90295
commit
0feaf8cbd8
@ -34,7 +34,6 @@ go_import_path: github.com/gin-gonic/gin
|
|||||||
script:
|
script:
|
||||||
- make vet
|
- make vet
|
||||||
- make fmt-check
|
- make fmt-check
|
||||||
- make embedmd
|
|
||||||
- make misspell-check
|
- make misspell-check
|
||||||
- make test
|
- make test
|
||||||
|
|
||||||
|
9
Makefile
9
Makefile
@ -52,12 +52,6 @@ deps:
|
|||||||
@hash govendor > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
@hash govendor > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
$(GO) get -u github.com/kardianos/govendor; \
|
$(GO) get -u github.com/kardianos/govendor; \
|
||||||
fi
|
fi
|
||||||
@hash embedmd > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
|
||||||
$(GO) get -u github.com/campoy/embedmd; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
embedmd:
|
|
||||||
embedmd -d *.md
|
|
||||||
|
|
||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
lint:
|
lint:
|
||||||
@ -83,5 +77,4 @@ misspell:
|
|||||||
.PHONY: tools
|
.PHONY: tools
|
||||||
tools:
|
tools:
|
||||||
go install golang.org/x/lint/golint; \
|
go install golang.org/x/lint/golint; \
|
||||||
go install github.com/client9/misspell/cmd/misspell; \
|
go install github.com/client9/misspell/cmd/misspell;
|
||||||
go install github.com/campoy/embedmd;
|
|
||||||
|
@ -728,7 +728,6 @@ When running the above example using the above the `curl` command, it returns er
|
|||||||
|
|
||||||
It is also possible to register custom validators. See the [example code](examples/custom-validation/server.go).
|
It is also possible to register custom validators. See the [example code](examples/custom-validation/server.go).
|
||||||
|
|
||||||
[embedmd]:# (examples/custom-validation/server.go go)
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@ -1501,7 +1500,6 @@ func main() {
|
|||||||
|
|
||||||
example for 1-line LetsEncrypt HTTPS servers.
|
example for 1-line LetsEncrypt HTTPS servers.
|
||||||
|
|
||||||
[embedmd]:# (examples/auto-tls/example1/main.go go)
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@ -1526,7 +1524,6 @@ func main() {
|
|||||||
|
|
||||||
example for custom autocert manager.
|
example for custom autocert manager.
|
||||||
|
|
||||||
[embedmd]:# (examples/auto-tls/example2/main.go go)
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@ -1560,7 +1557,6 @@ func main() {
|
|||||||
|
|
||||||
See the [question](https://github.com/gin-gonic/gin/issues/346) and try the following example:
|
See the [question](https://github.com/gin-gonic/gin/issues/346) and try the following example:
|
||||||
|
|
||||||
[embedmd]:# (examples/multiple-service/main.go go)
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@ -1660,7 +1656,6 @@ An alternative to endless:
|
|||||||
|
|
||||||
If you are using Go 1.8, you may not need to use this library! Consider using http.Server's built-in [Shutdown()](https://golang.org/pkg/net/http/#Server.Shutdown) method for graceful shutdowns. See the full [graceful-shutdown](./examples/graceful-shutdown) example with gin.
|
If you are using Go 1.8, you may not need to use this library! Consider using http.Server's built-in [Shutdown()](https://golang.org/pkg/net/http/#Server.Shutdown) method for graceful shutdowns. See the full [graceful-shutdown](./examples/graceful-shutdown) example with gin.
|
||||||
|
|
||||||
[embedmd]:# (examples/graceful-shutdown/graceful-shutdown/server.go go)
|
|
||||||
```go
|
```go
|
||||||
// +build go1.8
|
// +build go1.8
|
||||||
|
|
||||||
@ -1919,7 +1914,6 @@ performance (See [#1341](https://github.com/gin-gonic/gin/pull/1341)).
|
|||||||
|
|
||||||
http.Pusher is supported only **go1.8+**. See the [golang blog](https://blog.golang.org/h2push) for detail information.
|
http.Pusher is supported only **go1.8+**. See the [golang blog](https://blog.golang.org/h2push) for detail information.
|
||||||
|
|
||||||
[embedmd]:# (examples/http-pusher/main.go go)
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
3
examples/README.md
Normal file
3
examples/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Gin Examples
|
||||||
|
|
||||||
|
## TODO
|
@ -1,8 +0,0 @@
|
|||||||
# Guide to run Gin under App Engine LOCAL Development Server
|
|
||||||
|
|
||||||
1. Download, install and setup Go in your computer. (That includes setting your `$GOPATH`.)
|
|
||||||
2. Download SDK for your platform from [here](https://cloud.google.com/appengine/docs/standard/go/download): `https://cloud.google.com/appengine/docs/standard/go/download`
|
|
||||||
3. Download Gin source code using: `$ go get github.com/gin-gonic/gin`
|
|
||||||
4. Navigate to examples folder: `$ cd $GOPATH/src/github.com/gin-gonic/gin/examples/app-engine/`
|
|
||||||
5. Run it: `$ dev_appserver.py .` (notice that you have to run this script by Python2)
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
|||||||
application: hello
|
|
||||||
version: 1
|
|
||||||
runtime: go
|
|
||||||
api_version: go1
|
|
||||||
|
|
||||||
handlers:
|
|
||||||
- url: /.*
|
|
||||||
script: _go_app
|
|
@ -1,24 +0,0 @@
|
|||||||
package hello
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This function's name is a must. App Engine uses it to drive the requests properly.
|
|
||||||
func init() {
|
|
||||||
// Starts a new Gin instance with no middle-ware
|
|
||||||
r := gin.New()
|
|
||||||
|
|
||||||
// Define your handlers
|
|
||||||
r.GET("/", func(c *gin.Context) {
|
|
||||||
c.String(http.StatusOK, "Hello World!")
|
|
||||||
})
|
|
||||||
r.GET("/ping", func(c *gin.Context) {
|
|
||||||
c.String(http.StatusOK, "pong")
|
|
||||||
})
|
|
||||||
|
|
||||||
// Handle all requests using net/http
|
|
||||||
http.Handle("/", r)
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
# Building a single binary containing templates
|
|
||||||
|
|
||||||
This is a complete example to create a single binary with the
|
|
||||||
[gin-gonic/gin][gin] Web Server with HTML templates.
|
|
||||||
|
|
||||||
[gin]: https://github.com/gin-gonic/gin
|
|
||||||
|
|
||||||
## How to use
|
|
||||||
|
|
||||||
### Prepare Packages
|
|
||||||
|
|
||||||
```
|
|
||||||
go get github.com/gin-gonic/gin
|
|
||||||
go get github.com/jessevdk/go-assets-builder
|
|
||||||
```
|
|
||||||
|
|
||||||
### Generate assets.go
|
|
||||||
|
|
||||||
```
|
|
||||||
go-assets-builder html -o assets.go
|
|
||||||
```
|
|
||||||
|
|
||||||
### Build the server
|
|
||||||
|
|
||||||
```
|
|
||||||
go build -o assets-in-binary
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run
|
|
||||||
|
|
||||||
```
|
|
||||||
./assets-in-binary
|
|
||||||
```
|
|
@ -1,34 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/jessevdk/go-assets"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _Assetsbfa8d115ce0617d89507412d5393a462f8e9b003 = "<!doctype html>\n<body>\n <p>Can you see this? → {{.Bar}}</p>\n</body>\n"
|
|
||||||
var _Assets3737a75b5254ed1f6d588b40a3449721f9ea86c2 = "<!doctype html>\n<body>\n <p>Hello, {{.Foo}}</p>\n</body>\n"
|
|
||||||
|
|
||||||
// Assets returns go-assets FileSystem
|
|
||||||
var Assets = assets.NewFileSystem(map[string][]string{"/": {"html"}, "/html": {"bar.tmpl", "index.tmpl"}}, map[string]*assets.File{
|
|
||||||
"/": {
|
|
||||||
Path: "/",
|
|
||||||
FileMode: 0x800001ed,
|
|
||||||
Mtime: time.Unix(1524365738, 1524365738517125470),
|
|
||||||
Data: nil,
|
|
||||||
}, "/html": {
|
|
||||||
Path: "/html",
|
|
||||||
FileMode: 0x800001ed,
|
|
||||||
Mtime: time.Unix(1524365491, 1524365491289799093),
|
|
||||||
Data: nil,
|
|
||||||
}, "/html/bar.tmpl": {
|
|
||||||
Path: "/html/bar.tmpl",
|
|
||||||
FileMode: 0x1a4,
|
|
||||||
Mtime: time.Unix(1524365491, 1524365491289611557),
|
|
||||||
Data: []byte(_Assetsbfa8d115ce0617d89507412d5393a462f8e9b003),
|
|
||||||
}, "/html/index.tmpl": {
|
|
||||||
Path: "/html/index.tmpl",
|
|
||||||
FileMode: 0x1a4,
|
|
||||||
Mtime: time.Unix(1524365491, 1524365491289995821),
|
|
||||||
Data: []byte(_Assets3737a75b5254ed1f6d588b40a3449721f9ea86c2),
|
|
||||||
}}, "")
|
|
@ -1,4 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<body>
|
|
||||||
<p>Can you see this? → {{.Bar}}</p>
|
|
||||||
</body>
|
|
@ -1,4 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<body>
|
|
||||||
<p>Hello, {{.Foo}}</p>
|
|
||||||
</body>
|
|
@ -1,48 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"html/template"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
r := gin.New()
|
|
||||||
t, err := loadTemplate()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
r.SetHTMLTemplate(t)
|
|
||||||
r.GET("/", func(c *gin.Context) {
|
|
||||||
c.HTML(http.StatusOK, "/html/index.tmpl", gin.H{
|
|
||||||
"Foo": "World",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
r.GET("/bar", func(c *gin.Context) {
|
|
||||||
c.HTML(http.StatusOK, "/html/bar.tmpl", gin.H{
|
|
||||||
"Bar": "World",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
r.Run(":8080")
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadTemplate() (*template.Template, error) {
|
|
||||||
t := template.New("")
|
|
||||||
for name, file := range Assets.Files {
|
|
||||||
if file.IsDir() || !strings.HasSuffix(name, ".tmpl") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
h, err := ioutil.ReadAll(file)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
t, err = t.New(name).Parse(string(h))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return t, nil
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/autotls"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
r := gin.Default()
|
|
||||||
|
|
||||||
// Ping handler
|
|
||||||
r.GET("/ping", func(c *gin.Context) {
|
|
||||||
c.String(200, "pong")
|
|
||||||
})
|
|
||||||
|
|
||||||
log.Fatal(autotls.Run(r, "example1.com", "example2.com"))
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/autotls"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"golang.org/x/crypto/acme/autocert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
r := gin.Default()
|
|
||||||
|
|
||||||
// Ping handler
|
|
||||||
r.GET("/ping", func(c *gin.Context) {
|
|
||||||
c.String(200, "pong")
|
|
||||||
})
|
|
||||||
|
|
||||||
m := autocert.Manager{
|
|
||||||
Prompt: autocert.AcceptTOS,
|
|
||||||
HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"),
|
|
||||||
Cache: autocert.DirCache("/var/www/.cache"),
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Fatal(autotls.RunWithManager(r, &m))
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
var db = make(map[string]string)
|
|
||||||
|
|
||||||
func setupRouter() *gin.Engine {
|
|
||||||
// Disable Console Color
|
|
||||||
// gin.DisableConsoleColor()
|
|
||||||
r := gin.Default()
|
|
||||||
|
|
||||||
// Ping test
|
|
||||||
r.GET("/ping", func(c *gin.Context) {
|
|
||||||
c.String(http.StatusOK, "pong")
|
|
||||||
})
|
|
||||||
|
|
||||||
// Get user value
|
|
||||||
r.GET("/user/:name", func(c *gin.Context) {
|
|
||||||
user := c.Params.ByName("name")
|
|
||||||
value, ok := db[user]
|
|
||||||
if ok {
|
|
||||||
c.JSON(http.StatusOK, gin.H{"user": user, "value": value})
|
|
||||||
} else {
|
|
||||||
c.JSON(http.StatusOK, gin.H{"user": user, "status": "no value"})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Authorized group (uses gin.BasicAuth() middleware)
|
|
||||||
// Same than:
|
|
||||||
// authorized := r.Group("/")
|
|
||||||
// authorized.Use(gin.BasicAuth(gin.Credentials{
|
|
||||||
// "foo": "bar",
|
|
||||||
// "manu": "123",
|
|
||||||
//}))
|
|
||||||
authorized := r.Group("/", gin.BasicAuth(gin.Accounts{
|
|
||||||
"foo": "bar", // user:foo password:bar
|
|
||||||
"manu": "123", // user:manu password:123
|
|
||||||
}))
|
|
||||||
|
|
||||||
authorized.POST("admin", func(c *gin.Context) {
|
|
||||||
user := c.MustGet(gin.AuthUserKey).(string)
|
|
||||||
|
|
||||||
// Parse JSON
|
|
||||||
var json struct {
|
|
||||||
Value string `json:"value" binding:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Bind(&json) == nil {
|
|
||||||
db[user] = json.Value
|
|
||||||
c.JSON(http.StatusOK, gin.H{"status": "ok"})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
r := setupRouter()
|
|
||||||
// Listen and Server in 0.0.0.0:8080
|
|
||||||
r.Run(":8080")
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPingRoute(t *testing.T) {
|
|
||||||
router := setupRouter()
|
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
req, _ := http.NewRequest("GET", "/ping", nil)
|
|
||||||
router.ServeHTTP(w, req)
|
|
||||||
|
|
||||||
assert.Equal(t, http.StatusOK, w.Code)
|
|
||||||
assert.Equal(t, "pong", w.Body.String())
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/gin-gonic/gin/binding"
|
|
||||||
"gopkg.in/go-playground/validator.v8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Booking contains binded and validated data.
|
|
||||||
type Booking struct {
|
|
||||||
CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
|
|
||||||
CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func bookableDate(
|
|
||||||
v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value,
|
|
||||||
field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string,
|
|
||||||
) bool {
|
|
||||||
if date, ok := field.Interface().(time.Time); ok {
|
|
||||||
today := time.Now()
|
|
||||||
if today.Year() > date.Year() || today.YearDay() > date.YearDay() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
route := gin.Default()
|
|
||||||
|
|
||||||
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
|
|
||||||
v.RegisterValidation("bookabledate", bookableDate)
|
|
||||||
}
|
|
||||||
|
|
||||||
route.GET("/bookable", getBookable)
|
|
||||||
route.Run(":8085")
|
|
||||||
}
|
|
||||||
|
|
||||||
func getBookable(c *gin.Context) {
|
|
||||||
var b Booking
|
|
||||||
if err := c.ShouldBindWith(&b, binding.Query); err == nil {
|
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})
|
|
||||||
} else {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB |
@ -1,17 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/thinkerou/favicon"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
app := gin.Default()
|
|
||||||
app.Use(favicon.New("./favicon.ico"))
|
|
||||||
app.GET("/ping", func(c *gin.Context) {
|
|
||||||
c.String(http.StatusOK, "Hello favicon.")
|
|
||||||
})
|
|
||||||
app.Run(":8080")
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
// +build go1.8
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
router := gin.Default()
|
|
||||||
router.GET("/", func(c *gin.Context) {
|
|
||||||
c.String(http.StatusOK, "Welcome Gin Server")
|
|
||||||
})
|
|
||||||
|
|
||||||
server := &http.Server{
|
|
||||||
Addr: ":8080",
|
|
||||||
Handler: router,
|
|
||||||
}
|
|
||||||
|
|
||||||
quit := make(chan os.Signal)
|
|
||||||
signal.Notify(quit, os.Interrupt)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
<-quit
|
|
||||||
log.Println("receive interrupt signal")
|
|
||||||
if err := server.Close(); err != nil {
|
|
||||||
log.Fatal("Server Close:", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := server.ListenAndServe(); err != nil {
|
|
||||||
if err == http.ErrServerClosed {
|
|
||||||
log.Println("Server closed under request")
|
|
||||||
} else {
|
|
||||||
log.Fatal("Server closed unexpect")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Server exiting")
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
// +build go1.8
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
router := gin.Default()
|
|
||||||
router.GET("/", func(c *gin.Context) {
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
c.String(http.StatusOK, "Welcome Gin Server")
|
|
||||||
})
|
|
||||||
|
|
||||||
srv := &http.Server{
|
|
||||||
Addr: ":8080",
|
|
||||||
Handler: router,
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
// service connections
|
|
||||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
||||||
log.Fatalf("listen: %s\n", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Wait for interrupt signal to gracefully shutdown the server with
|
|
||||||
// a timeout of 5 seconds.
|
|
||||||
quit := make(chan os.Signal)
|
|
||||||
// kill (no param) default send syscanll.SIGTERM
|
|
||||||
// kill -2 is syscall.SIGINT
|
|
||||||
// kill -9 is syscall. SIGKILL but can"t be catch, so don't need add it
|
|
||||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
|
||||||
<-quit
|
|
||||||
log.Println("Shutdown Server ...")
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
if err := srv.Shutdown(ctx); err != nil {
|
|
||||||
log.Fatal("Server Shutdown:", err)
|
|
||||||
}
|
|
||||||
// catching ctx.Done(). timeout of 5 seconds.
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
log.Println("timeout of 5 seconds.")
|
|
||||||
}
|
|
||||||
log.Println("Server exiting")
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
## How to run this example
|
|
||||||
|
|
||||||
1. run grpc server
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ go run grpc/server.go
|
|
||||||
```
|
|
||||||
|
|
||||||
2. run gin server
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ go run gin/main.go
|
|
||||||
```
|
|
||||||
|
|
||||||
3. use curl command to test it
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ curl -v 'http://localhost:8052/rest/n/thinkerou'
|
|
||||||
```
|
|
@ -1,46 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
pb "github.com/gin-gonic/gin/examples/grpc/pb"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// Set up a connection to the server.
|
|
||||||
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("did not connect: %v", err)
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
client := pb.NewGreeterClient(conn)
|
|
||||||
|
|
||||||
// Set up a http server.
|
|
||||||
r := gin.Default()
|
|
||||||
r.GET("/rest/n/:name", func(c *gin.Context) {
|
|
||||||
name := c.Param("name")
|
|
||||||
|
|
||||||
// Contact the server and print out its response.
|
|
||||||
req := &pb.HelloRequest{Name: name}
|
|
||||||
res, err := client.SayHello(c, req)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
|
||||||
"result": fmt.Sprint(res.Message),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// Run http server
|
|
||||||
if err := r.Run(":8052"); err != nil {
|
|
||||||
log.Fatalf("could not run server: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
pb "github.com/gin-gonic/gin/examples/grpc/pb"
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
"google.golang.org/grpc/reflection"
|
|
||||||
)
|
|
||||||
|
|
||||||
// server is used to implement helloworld.GreeterServer.
|
|
||||||
type server struct{}
|
|
||||||
|
|
||||||
// SayHello implements helloworld.GreeterServer
|
|
||||||
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
|
|
||||||
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
lis, err := net.Listen("tcp", ":50051")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("failed to listen: %v", err)
|
|
||||||
}
|
|
||||||
s := grpc.NewServer()
|
|
||||||
pb.RegisterGreeterServer(s, &server{})
|
|
||||||
|
|
||||||
// Register reflection service on gRPC server.
|
|
||||||
reflection.Register(s)
|
|
||||||
if err := s.Serve(lis); err != nil {
|
|
||||||
log.Fatalf("failed to serve: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,151 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go.
|
|
||||||
// source: helloworld.proto
|
|
||||||
// DO NOT EDIT!
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package helloworld is a generated protocol buffer package.
|
|
||||||
|
|
||||||
It is generated from these files:
|
|
||||||
helloworld.proto
|
|
||||||
|
|
||||||
It has these top-level messages:
|
|
||||||
HelloRequest
|
|
||||||
HelloReply
|
|
||||||
*/
|
|
||||||
package helloworld
|
|
||||||
|
|
||||||
import proto "github.com/golang/protobuf/proto"
|
|
||||||
import fmt "fmt"
|
|
||||||
import math "math"
|
|
||||||
|
|
||||||
import (
|
|
||||||
context "golang.org/x/net/context"
|
|
||||||
grpc "google.golang.org/grpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
|
||||||
var _ = proto.Marshal
|
|
||||||
var _ = fmt.Errorf
|
|
||||||
var _ = math.Inf
|
|
||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
|
||||||
// is compatible with the proto package it is being compiled against.
|
|
||||||
// A compilation error at this line likely means your copy of the
|
|
||||||
// proto package needs to be updated.
|
|
||||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
|
||||||
|
|
||||||
// The request message containing the user's name.
|
|
||||||
type HelloRequest struct {
|
|
||||||
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *HelloRequest) Reset() { *m = HelloRequest{} }
|
|
||||||
func (m *HelloRequest) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*HelloRequest) ProtoMessage() {}
|
|
||||||
func (*HelloRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
|
||||||
|
|
||||||
// The response message containing the greetings
|
|
||||||
type HelloReply struct {
|
|
||||||
Message string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *HelloReply) Reset() { *m = HelloReply{} }
|
|
||||||
func (m *HelloReply) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*HelloReply) ProtoMessage() {}
|
|
||||||
func (*HelloReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
proto.RegisterType((*HelloRequest)(nil), "helloworld.HelloRequest")
|
|
||||||
proto.RegisterType((*HelloReply)(nil), "helloworld.HelloReply")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
|
||||||
var _ context.Context
|
|
||||||
var _ grpc.ClientConn
|
|
||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
|
||||||
// is compatible with the grpc package it is being compiled against.
|
|
||||||
const _ = grpc.SupportPackageIsVersion4
|
|
||||||
|
|
||||||
// Client API for Greeter service
|
|
||||||
|
|
||||||
type GreeterClient interface {
|
|
||||||
// Sends a greeting
|
|
||||||
SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type greeterClient struct {
|
|
||||||
cc *grpc.ClientConn
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewGreeterClient(cc *grpc.ClientConn) GreeterClient {
|
|
||||||
return &greeterClient{cc}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {
|
|
||||||
out := new(HelloReply)
|
|
||||||
err := grpc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, c.cc, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server API for Greeter service
|
|
||||||
|
|
||||||
type GreeterServer interface {
|
|
||||||
// Sends a greeting
|
|
||||||
SayHello(context.Context, *HelloRequest) (*HelloReply, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {
|
|
||||||
s.RegisterService(&_Greeter_serviceDesc, srv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
||||||
in := new(HelloRequest)
|
|
||||||
if err := dec(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if interceptor == nil {
|
|
||||||
return srv.(GreeterServer).SayHello(ctx, in)
|
|
||||||
}
|
|
||||||
info := &grpc.UnaryServerInfo{
|
|
||||||
Server: srv,
|
|
||||||
FullMethod: "/helloworld.Greeter/SayHello",
|
|
||||||
}
|
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest))
|
|
||||||
}
|
|
||||||
return interceptor(ctx, in, info, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
var _Greeter_serviceDesc = grpc.ServiceDesc{
|
|
||||||
ServiceName: "helloworld.Greeter",
|
|
||||||
HandlerType: (*GreeterServer)(nil),
|
|
||||||
Methods: []grpc.MethodDesc{
|
|
||||||
{
|
|
||||||
MethodName: "SayHello",
|
|
||||||
Handler: _Greeter_SayHello_Handler,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Streams: []grpc.StreamDesc{},
|
|
||||||
Metadata: "helloworld.proto",
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { proto.RegisterFile("helloworld.proto", fileDescriptor0) }
|
|
||||||
|
|
||||||
var fileDescriptor0 = []byte{
|
|
||||||
// 174 bytes of a gzipped FileDescriptorProto
|
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0xc8, 0x48, 0xcd, 0xc9,
|
|
||||||
0xc9, 0x2f, 0xcf, 0x2f, 0xca, 0x49, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x42, 0x88,
|
|
||||||
0x28, 0x29, 0x71, 0xf1, 0x78, 0x80, 0x78, 0x41, 0xa9, 0x85, 0xa5, 0xa9, 0xc5, 0x25, 0x42, 0x42,
|
|
||||||
0x5c, 0x2c, 0x79, 0x89, 0xb9, 0xa9, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x60, 0xb6, 0x92,
|
|
||||||
0x1a, 0x17, 0x17, 0x54, 0x4d, 0x41, 0x4e, 0xa5, 0x90, 0x04, 0x17, 0x7b, 0x6e, 0x6a, 0x71, 0x71,
|
|
||||||
0x62, 0x3a, 0x4c, 0x11, 0x8c, 0x6b, 0xe4, 0xc9, 0xc5, 0xee, 0x5e, 0x94, 0x9a, 0x5a, 0x92, 0x5a,
|
|
||||||
0x24, 0x64, 0xc7, 0xc5, 0x11, 0x9c, 0x58, 0x09, 0xd6, 0x25, 0x24, 0xa1, 0x87, 0xe4, 0x02, 0x64,
|
|
||||||
0xcb, 0xa4, 0xc4, 0xb0, 0xc8, 0x00, 0xad, 0x50, 0x62, 0x70, 0x32, 0xe0, 0x92, 0xce, 0xcc, 0xd7,
|
|
||||||
0x4b, 0x2f, 0x2a, 0x48, 0xd6, 0x4b, 0xad, 0x48, 0xcc, 0x2d, 0xc8, 0x49, 0x2d, 0x46, 0x52, 0xeb,
|
|
||||||
0xc4, 0x0f, 0x56, 0x1c, 0x0e, 0x62, 0x07, 0x80, 0xbc, 0x14, 0xc0, 0x98, 0xc4, 0x06, 0xf6, 0x9b,
|
|
||||||
0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x0f, 0xb7, 0xcd, 0xf2, 0xef, 0x00, 0x00, 0x00,
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
// Copyright 2015 gRPC authors.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
option java_multiple_files = true;
|
|
||||||
option java_package = "io.grpc.examples.helloworld";
|
|
||||||
option java_outer_classname = "HelloWorldProto";
|
|
||||||
|
|
||||||
package helloworld;
|
|
||||||
|
|
||||||
// The greeting service definition.
|
|
||||||
service Greeter {
|
|
||||||
// Sends a greeting
|
|
||||||
rpc SayHello (HelloRequest) returns (HelloReply) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The request message containing the user's name.
|
|
||||||
message HelloRequest {
|
|
||||||
string name = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The response message containing the greetings
|
|
||||||
message HelloReply {
|
|
||||||
string message = 1;
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
console.log("http2 pusher");
|
|
@ -1,41 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"html/template"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
var html = template.Must(template.New("https").Parse(`
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Https Test</title>
|
|
||||||
<script src="/assets/app.js"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1 style="color:red;">Welcome, Ginner!</h1>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`))
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
r := gin.Default()
|
|
||||||
r.Static("/assets", "./assets")
|
|
||||||
r.SetHTMLTemplate(html)
|
|
||||||
|
|
||||||
r.GET("/", func(c *gin.Context) {
|
|
||||||
if pusher := c.Writer.Pusher(); pusher != nil {
|
|
||||||
// use pusher.Push() to do server push
|
|
||||||
if err := pusher.Push("/assets/app.js", nil); err != nil {
|
|
||||||
log.Printf("Failed to push: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.HTML(200, "https", gin.H{
|
|
||||||
"status": "success",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// Listen and Server in https://127.0.0.1:8080
|
|
||||||
r.RunTLS(":8080", "./testdata/server.pem", "./testdata/server.key")
|
|
||||||
}
|
|
15
examples/http-pusher/testdata/ca.pem
vendored
15
examples/http-pusher/testdata/ca.pem
vendored
@ -1,15 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
|
|
||||||
BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
|
|
||||||
aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla
|
|
||||||
Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
|
|
||||||
YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT
|
|
||||||
BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7
|
|
||||||
+L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu
|
|
||||||
g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd
|
|
||||||
Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV
|
|
||||||
HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau
|
|
||||||
sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m
|
|
||||||
oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG
|
|
||||||
Dfcog5wrJytaQ6UA0wE=
|
|
||||||
-----END CERTIFICATE-----
|
|
16
examples/http-pusher/testdata/server.key
vendored
16
examples/http-pusher/testdata/server.key
vendored
@ -1,16 +0,0 @@
|
|||||||
-----BEGIN PRIVATE KEY-----
|
|
||||||
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD
|
|
||||||
M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf
|
|
||||||
3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY
|
|
||||||
AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm
|
|
||||||
V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY
|
|
||||||
tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p
|
|
||||||
dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q
|
|
||||||
K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR
|
|
||||||
81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff
|
|
||||||
DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd
|
|
||||||
aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2
|
|
||||||
ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3
|
|
||||||
XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe
|
|
||||||
F98XJ7tIFfJq
|
|
||||||
-----END PRIVATE KEY-----
|
|
16
examples/http-pusher/testdata/server.pem
vendored
16
examples/http-pusher/testdata/server.pem
vendored
@ -1,16 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIICnDCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJBVTET
|
|
||||||
MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ
|
|
||||||
dHkgTHRkMQ8wDQYDVQQDEwZ0ZXN0Y2EwHhcNMTUxMTA0MDIyMDI0WhcNMjUxMTAx
|
|
||||||
MDIyMDI0WjBlMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
|
|
||||||
BAcTB0NoaWNhZ28xFTATBgNVBAoTDEV4YW1wbGUsIENvLjEaMBgGA1UEAxQRKi50
|
|
||||||
ZXN0Lmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOHDFSco
|
|
||||||
LCVJpYDDM4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1Bg
|
|
||||||
zkWF+slf3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd
|
|
||||||
9N8YwbBYAckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAGjazBpMAkGA1UdEwQCMAAw
|
|
||||||
CwYDVR0PBAQDAgXgME8GA1UdEQRIMEaCECoudGVzdC5nb29nbGUuZnKCGHdhdGVy
|
|
||||||
em9vaS50ZXN0Lmdvb2dsZS5iZYISKi50ZXN0LnlvdXR1YmUuY29thwTAqAEDMA0G
|
|
||||||
CSqGSIb3DQEBCwUAA4GBAJFXVifQNub1LUP4JlnX5lXNlo8FxZ2a12AFQs+bzoJ6
|
|
||||||
hM044EDjqyxUqSbVePK0ni3w1fHQB5rY9yYC5f8G7aqqTY1QOhoUk8ZTSTRpnkTh
|
|
||||||
y4jjdvTZeLDVBlueZUTDRmy2feY5aZIU18vFDK08dTG0A87pppuv1LNIR3loveU8
|
|
||||||
-----END CERTIFICATE-----
|
|
@ -1,18 +0,0 @@
|
|||||||
## How to generate RSA private key and digital certificate
|
|
||||||
|
|
||||||
1. Install Openssl
|
|
||||||
|
|
||||||
Please visit https://github.com/openssl/openssl to get pkg and install.
|
|
||||||
|
|
||||||
2. Generate RSA private key
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ mkdir testdata
|
|
||||||
$ openssl genrsa -out ./testdata/server.key 2048
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Generate digital certificate
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ openssl req -new -x509 -key ./testdata/server.key -out ./testdata/server.pem -days 365
|
|
||||||
```
|
|
@ -1,38 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"html/template"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
var html = template.Must(template.New("https").Parse(`
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Https Test</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1 style="color:red;">Welcome, Ginner!</h1>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`))
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
logger := log.New(os.Stderr, "", 0)
|
|
||||||
logger.Println("[WARNING] DON'T USE THE EMBED CERTS FROM THIS EXAMPLE IN PRODUCTION ENVIRONMENT, GENERATE YOUR OWN!")
|
|
||||||
|
|
||||||
r := gin.Default()
|
|
||||||
r.SetHTMLTemplate(html)
|
|
||||||
|
|
||||||
r.GET("/welcome", func(c *gin.Context) {
|
|
||||||
c.HTML(http.StatusOK, "https", gin.H{
|
|
||||||
"status": "success",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// Listen and Server in https://127.0.0.1:8080
|
|
||||||
r.RunTLS(":8080", "./testdata/server.pem", "./testdata/server.key")
|
|
||||||
}
|
|
15
examples/http2/testdata/ca.pem
vendored
15
examples/http2/testdata/ca.pem
vendored
@ -1,15 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
|
|
||||||
BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
|
|
||||||
aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla
|
|
||||||
Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
|
|
||||||
YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT
|
|
||||||
BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7
|
|
||||||
+L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu
|
|
||||||
g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd
|
|
||||||
Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV
|
|
||||||
HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau
|
|
||||||
sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m
|
|
||||||
oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG
|
|
||||||
Dfcog5wrJytaQ6UA0wE=
|
|
||||||
-----END CERTIFICATE-----
|
|
16
examples/http2/testdata/server.key
vendored
16
examples/http2/testdata/server.key
vendored
@ -1,16 +0,0 @@
|
|||||||
-----BEGIN PRIVATE KEY-----
|
|
||||||
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD
|
|
||||||
M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf
|
|
||||||
3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY
|
|
||||||
AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm
|
|
||||||
V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY
|
|
||||||
tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p
|
|
||||||
dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q
|
|
||||||
K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR
|
|
||||||
81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff
|
|
||||||
DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd
|
|
||||||
aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2
|
|
||||||
ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3
|
|
||||||
XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe
|
|
||||||
F98XJ7tIFfJq
|
|
||||||
-----END PRIVATE KEY-----
|
|
16
examples/http2/testdata/server.pem
vendored
16
examples/http2/testdata/server.pem
vendored
@ -1,16 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIICnDCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJBVTET
|
|
||||||
MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ
|
|
||||||
dHkgTHRkMQ8wDQYDVQQDEwZ0ZXN0Y2EwHhcNMTUxMTA0MDIyMDI0WhcNMjUxMTAx
|
|
||||||
MDIyMDI0WjBlMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
|
|
||||||
BAcTB0NoaWNhZ28xFTATBgNVBAoTDEV4YW1wbGUsIENvLjEaMBgGA1UEAxQRKi50
|
|
||||||
ZXN0Lmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOHDFSco
|
|
||||||
LCVJpYDDM4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1Bg
|
|
||||||
zkWF+slf3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd
|
|
||||||
9N8YwbBYAckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAGjazBpMAkGA1UdEwQCMAAw
|
|
||||||
CwYDVR0PBAQDAgXgME8GA1UdEQRIMEaCECoudGVzdC5nb29nbGUuZnKCGHdhdGVy
|
|
||||||
em9vaS50ZXN0Lmdvb2dsZS5iZYISKi50ZXN0LnlvdXR1YmUuY29thwTAqAEDMA0G
|
|
||||||
CSqGSIb3DQEBCwUAA4GBAJFXVifQNub1LUP4JlnX5lXNlo8FxZ2a12AFQs+bzoJ6
|
|
||||||
hM044EDjqyxUqSbVePK0ni3w1fHQB5rY9yYC5f8G7aqqTY1QOhoUk8ZTSTRpnkTh
|
|
||||||
y4jjdvTZeLDVBlueZUTDRmy2feY5aZIU18vFDK08dTG0A87pppuv1LNIR3loveU8
|
|
||||||
-----END CERTIFICATE-----
|
|
@ -1,74 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"golang.org/x/sync/errgroup"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
g errgroup.Group
|
|
||||||
)
|
|
||||||
|
|
||||||
func router01() http.Handler {
|
|
||||||
e := gin.New()
|
|
||||||
e.Use(gin.Recovery())
|
|
||||||
e.GET("/", func(c *gin.Context) {
|
|
||||||
c.JSON(
|
|
||||||
http.StatusOK,
|
|
||||||
gin.H{
|
|
||||||
"code": http.StatusOK,
|
|
||||||
"error": "Welcome server 01",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
func router02() http.Handler {
|
|
||||||
e := gin.New()
|
|
||||||
e.Use(gin.Recovery())
|
|
||||||
e.GET("/", func(c *gin.Context) {
|
|
||||||
c.JSON(
|
|
||||||
http.StatusOK,
|
|
||||||
gin.H{
|
|
||||||
"code": http.StatusOK,
|
|
||||||
"error": "Welcome server 02",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
server01 := &http.Server{
|
|
||||||
Addr: ":8080",
|
|
||||||
Handler: router01(),
|
|
||||||
ReadTimeout: 5 * time.Second,
|
|
||||||
WriteTimeout: 10 * time.Second,
|
|
||||||
}
|
|
||||||
|
|
||||||
server02 := &http.Server{
|
|
||||||
Addr: ":8081",
|
|
||||||
Handler: router02(),
|
|
||||||
ReadTimeout: 5 * time.Second,
|
|
||||||
WriteTimeout: 10 * time.Second,
|
|
||||||
}
|
|
||||||
|
|
||||||
g.Go(func() error {
|
|
||||||
return server01.ListenAndServe()
|
|
||||||
})
|
|
||||||
|
|
||||||
g.Go(func() error {
|
|
||||||
return server02.ListenAndServe()
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := g.Wait(); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
The [New Relic Go Agent](https://github.com/newrelic/go-agent) provides a nice middleware for the stdlib handler signature.
|
|
||||||
The following is an adaptation of that middleware for Gin.
|
|
||||||
|
|
||||||
```golang
|
|
||||||
const (
|
|
||||||
// NewRelicTxnKey is the key used to retrieve the NewRelic Transaction from the context
|
|
||||||
NewRelicTxnKey = "NewRelicTxnKey"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewRelicMonitoring is a middleware that starts a newrelic transaction, stores it in the context, then calls the next handler
|
|
||||||
func NewRelicMonitoring(app newrelic.Application) gin.HandlerFunc {
|
|
||||||
return func(ctx *gin.Context) {
|
|
||||||
txn := app.StartTransaction(ctx.Request.URL.Path, ctx.Writer, ctx.Request)
|
|
||||||
defer txn.End()
|
|
||||||
ctx.Set(NewRelicTxnKey, txn)
|
|
||||||
ctx.Next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
and in `main.go` or equivalent...
|
|
||||||
```golang
|
|
||||||
router := gin.Default()
|
|
||||||
cfg := newrelic.NewConfig(os.Getenv("APP_NAME"), os.Getenv("NEW_RELIC_API_KEY"))
|
|
||||||
app, err := newrelic.NewApplication(cfg)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("failed to make new_relic app: %v", err)
|
|
||||||
} else {
|
|
||||||
router.Use(adapters.NewRelicMonitoring(app))
|
|
||||||
}
|
|
||||||
```
|
|
@ -1,42 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/newrelic/go-agent"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// NewRelicTxnKey is the key used to retrieve the NewRelic Transaction from the context
|
|
||||||
NewRelicTxnKey = "NewRelicTxnKey"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewRelicMonitoring is a middleware that starts a newrelic transaction, stores it in the context, then calls the next handler
|
|
||||||
func NewRelicMonitoring(app newrelic.Application) gin.HandlerFunc {
|
|
||||||
return func(ctx *gin.Context) {
|
|
||||||
txn := app.StartTransaction(ctx.Request.URL.Path, ctx.Writer, ctx.Request)
|
|
||||||
defer txn.End()
|
|
||||||
ctx.Set(NewRelicTxnKey, txn)
|
|
||||||
ctx.Next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
router := gin.Default()
|
|
||||||
|
|
||||||
cfg := newrelic.NewConfig(os.Getenv("APP_NAME"), os.Getenv("NEW_RELIC_API_KEY"))
|
|
||||||
app, err := newrelic.NewApplication(cfg)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("failed to make new_relic app: %v", err)
|
|
||||||
} else {
|
|
||||||
router.Use(NewRelicMonitoring(app))
|
|
||||||
}
|
|
||||||
|
|
||||||
router.GET("/", func(c *gin.Context) {
|
|
||||||
c.String(http.StatusOK, "Hello World!\n")
|
|
||||||
})
|
|
||||||
router.Run()
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
all: deps build
|
|
||||||
|
|
||||||
.PHONY: deps
|
|
||||||
deps:
|
|
||||||
go get -d -v github.com/dustin/go-broadcast/...
|
|
||||||
go get -d -v github.com/manucorporat/stats/...
|
|
||||||
|
|
||||||
.PHONY: build
|
|
||||||
build: deps
|
|
||||||
go build -o realtime-advanced main.go rooms.go routes.go stats.go
|
|
@ -1,42 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
ConfigRuntime()
|
|
||||||
StartWorkers()
|
|
||||||
StartGin()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfigRuntime sets the number of operating system threads.
|
|
||||||
func ConfigRuntime() {
|
|
||||||
nuCPU := runtime.NumCPU()
|
|
||||||
runtime.GOMAXPROCS(nuCPU)
|
|
||||||
fmt.Printf("Running with %d CPUs\n", nuCPU)
|
|
||||||
}
|
|
||||||
|
|
||||||
// StartWorkers start starsWorker by goroutine.
|
|
||||||
func StartWorkers() {
|
|
||||||
go statsWorker()
|
|
||||||
}
|
|
||||||
|
|
||||||
// StartGin starts gin web server with setting router.
|
|
||||||
func StartGin() {
|
|
||||||
gin.SetMode(gin.ReleaseMode)
|
|
||||||
|
|
||||||
router := gin.New()
|
|
||||||
router.Use(rateLimit, gin.Recovery())
|
|
||||||
router.LoadHTMLGlob("resources/*.templ.html")
|
|
||||||
router.Static("/static", "resources/static")
|
|
||||||
router.GET("/", index)
|
|
||||||
router.GET("/room/:roomid", roomGET)
|
|
||||||
router.POST("/room-post/:roomid", roomPOST)
|
|
||||||
router.GET("/stream/:roomid", streamRoom)
|
|
||||||
|
|
||||||
router.Run(":80")
|
|
||||||
}
|
|
@ -1,208 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title>Server-Sent Events. Room "{{.roomid}}"</title>
|
|
||||||
<!-- jQuery -->
|
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
|
|
||||||
<script src="http://malsup.github.com/jquery.form.js"></script>
|
|
||||||
<!-- EPOCH -->
|
|
||||||
<script src="http://d3js.org/d3.v3.min.js"></script>
|
|
||||||
<script src="/static/epoch.min.js"></script>
|
|
||||||
<link rel="stylesheet" href="/static/epoch.min.css">
|
|
||||||
<script src="/static/realtime.js"></script>
|
|
||||||
<!-- Latest compiled and minified CSS -->
|
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
|
|
||||||
<!-- Optional theme -->
|
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap-theme.min.css">
|
|
||||||
<!-- Latest compiled and minified JavaScript -->
|
|
||||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
|
|
||||||
<!-- Primjs -->
|
|
||||||
<link href="/static/prismjs.min.css" rel="stylesheet">
|
|
||||||
|
|
||||||
<script>
|
|
||||||
$(document).ready(function() {
|
|
||||||
StartRealtime({{.roomid}}, {{.timestamp}});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<style>
|
|
||||||
body { padding-top: 50px; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<nav class="navbar navbar-fixed-top navbar-inverse">
|
|
||||||
<div class="container">
|
|
||||||
<div class="navbar-header">
|
|
||||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
|
||||||
<span class="sr-only">Toggle navigation</span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
</button>
|
|
||||||
<a class="navbar-brand" href="#">Server-Sent Events</a>
|
|
||||||
</div>
|
|
||||||
<div id="navbar" class="collapse navbar-collapse">
|
|
||||||
<ul class="nav navbar-nav">
|
|
||||||
<li class="active"><a href="#">Demo</a></li>
|
|
||||||
<li><a href="http://www.w3.org/TR/2009/WD-eventsource-20091029/">W3 Standard</a></li>
|
|
||||||
<li><a href="http://caniuse.com/#feat=eventsource">Browser Support</a></li>
|
|
||||||
<li><a href="http://gin-gonic.github.io/gin/">Gin Framework</a></li>
|
|
||||||
<li><a href="https://github.com/gin-gonic/gin/tree/develop/examples/realtime-advanced">GitHub</a></li>
|
|
||||||
</ul>
|
|
||||||
</div><!-- /.nav-collapse -->
|
|
||||||
</div><!-- /.container -->
|
|
||||||
</nav><!-- /.navbar -->
|
|
||||||
<!-- Main jumbotron for a primary marketing message or call to action -->
|
|
||||||
<div class="jumbotron">
|
|
||||||
<div class="container">
|
|
||||||
<h1>Server-Sent Events in Go</h1>
|
|
||||||
<p>Server-sent events (SSE) is a technology where a browser receives automatic updates from a server via HTTP connection. It is not websockets. <a href="http://www.html5rocks.com/en/tutorials/eventsource/basics/">Learn more.</a></p>
|
|
||||||
<p>The chat and the charts data is provided in realtime using the SSE implementation of <a href="https://github.com/gin-gonic/gin/blob/15b0c49da556d58a3d934b86e3aa552ff224026d/examples/realtime-chat/main.go#L23-L32">Gin Framework</a>.</p>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-8">
|
|
||||||
<div id="chat-scroll" style="overflow-y:scroll; overflow-x:scroll; height:290px">
|
|
||||||
<table id="table-style" class="table" data-show-header="false">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th data-field="nick" class="col-md-2">Nick</th>
|
|
||||||
<th data-field="message" class="col-md-8">Message</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="chat"></tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{{if .nick}}
|
|
||||||
<form autocomplete="off" class="form-inline" id="chat-form" action="/room-post/{{.roomid}}?nick={{.nick}}" method="post">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="sr-only" for="chat-message">Message</label>
|
|
||||||
<div class="input-group">
|
|
||||||
<div class="input-group-addon">{{.nick}}</div>
|
|
||||||
<input type="text" name="message" id="chat-message" class="form-control" placeholder="a message" value="">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<input type="submit" class="btn btn-primary" value="Send">
|
|
||||||
</form>
|
|
||||||
{{else}}
|
|
||||||
<form action="" method="get" class="form-inline">
|
|
||||||
<legend>Join the SSE real-time chat</legend>
|
|
||||||
<div class="form-group">
|
|
||||||
<input value='' name="nick" id="nick" placeholder="Your Name" type="text" class="form-control">
|
|
||||||
</div>
|
|
||||||
<div class="form-group text-center">
|
|
||||||
<input type="submit" class="btn btn-success btn-login-submit" value="Join">
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{{end}}
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<div id="messagesChart" class="epoch category10"></div>
|
|
||||||
<p>
|
|
||||||
<span style="font-size:20px; color:#1f77b4">◼︎</span> Users<br>
|
|
||||||
<span style="font-size:20px; color:#ff7f0e">◼︎</span> Inbound messages / sec<br>
|
|
||||||
<span style="font-size:20px; color:#2ca02c">◼︎</span> Outbound messages / sec<br>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<h2>Realtime server Go stats</h2>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<h3>Memory usage</h3>
|
|
||||||
<p>
|
|
||||||
<div id="heapChart" class="epoch category20c"></div>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<span style="font-size:20px; color:#1f77b4">◼︎</span> Heap bytes<br>
|
|
||||||
<span style="font-size:20px; color:#aec7e8">◼︎</span> Stack bytes<br>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<h3>Allocations per second</h3>
|
|
||||||
<p>
|
|
||||||
<div id="mallocsChart" class="epoch category20b"></div>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<span style="font-size:20px; color:#393b79">◼︎</span> Mallocs / sec<br>
|
|
||||||
<span style="font-size:20px; color:#5254a3">◼︎</span> Frees / sec<br>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<h2>MIT Open Sourced</h2>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://github.com/gin-gonic/gin/tree/develop/examples/realtime-advanced">This demo website (JS and Go)</a></li>
|
|
||||||
<li><a href="https://github.com/manucorporat/sse">The SSE implementation in Go</a></li>
|
|
||||||
<li><a href="https://github.com/gin-gonic/gin">The Web Framework (Gin)</a></li>
|
|
||||||
</ul>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<script src="/static/prismjs.min.js"></script>
|
|
||||||
<h3>Server-side (Go)</h3>
|
|
||||||
<pre><code class="language-go">func streamRoom(c *gin.Context) {
|
|
||||||
roomid := c.ParamValue("roomid")
|
|
||||||
listener := openListener(roomid)
|
|
||||||
statsTicker := time.NewTicker(1 * time.Second)
|
|
||||||
defer closeListener(roomid, listener)
|
|
||||||
defer statsTicker.Stop()
|
|
||||||
|
|
||||||
c.Stream(func(w io.Writer) bool {
|
|
||||||
select {
|
|
||||||
case msg := <-listener:
|
|
||||||
c.SSEvent("message", msg)
|
|
||||||
case <-statsTicker.C:
|
|
||||||
c.SSEvent("stats", Stats())
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}</code></pre>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<h3>Client-side (JS)</h3>
|
|
||||||
<pre><code class="language-javascript">function StartSSE(roomid) {
|
|
||||||
var source = new EventSource('/stream/'+roomid);
|
|
||||||
source.addEventListener('message', newChatMessage, false);
|
|
||||||
source.addEventListener('stats', stats, false);
|
|
||||||
}</code></pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<h3>SSE package</h3>
|
|
||||||
<pre><code class="language-go">import "github.com/manucorporat/sse"
|
|
||||||
|
|
||||||
func httpHandler(w http.ResponseWriter, req *http.Request) {
|
|
||||||
// data can be a primitive like a string, an integer or a float
|
|
||||||
sse.Encode(w, sse.Event{
|
|
||||||
Event: "message",
|
|
||||||
Data: "some data\nmore data",
|
|
||||||
})
|
|
||||||
|
|
||||||
// also a complex type, like a map, a struct or a slice
|
|
||||||
sse.Encode(w, sse.Event{
|
|
||||||
Id: "124",
|
|
||||||
Event: "message",
|
|
||||||
Data: map[string]interface{}{
|
|
||||||
"user": "manu",
|
|
||||||
"date": time.Now().Unix(),
|
|
||||||
"content": "hi!",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}</code></pre>
|
|
||||||
<pre>event: message
|
|
||||||
data: some data\\nmore data
|
|
||||||
|
|
||||||
id: 124
|
|
||||||
event: message
|
|
||||||
data: {"content":"hi!","date":1431540810,"user":"manu"}</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
<footer>
|
|
||||||
<p>Created with <span class="glyphicon glyphicon-heart"></span> by <a href="https://github.com/manucorporat">Manu Martinez-Almeida</a></p>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
File diff suppressed because one or more lines are too long
@ -1,114 +0,0 @@
|
|||||||
(function(){var e;null==window.Epoch&&(window.Epoch={});null==(e=window.Epoch).Chart&&(e.Chart={});null==(e=window.Epoch).Time&&(e.Time={});null==(e=window.Epoch).Util&&(e.Util={});null==(e=window.Epoch).Formats&&(e.Formats={});Epoch.warn=function(g){return(console.warn||console.log)("Epoch Warning: "+g)};Epoch.exception=function(g){throw"Epoch Error: "+g;}}).call(this);
|
|
||||||
(function(){Epoch.TestContext=function(){function e(){var c,a,d;this._log=[];a=0;for(d=g.length;a<d;a++)c=g[a],this._makeFauxMethod(c)}var g;g="arc arcTo beginPath bezierCurveTo clearRect clip closePath drawImage fill fillRect fillText moveTo quadraticCurveTo rect restore rotate save scale scrollPathIntoView setLineDash setTransform stroke strokeRect strokeText transform translate".split(" ");e.prototype._makeFauxMethod=function(c){return this[c]=function(){var a;return this._log.push(""+c+"("+function(){var d,
|
|
||||||
b,h;h=[];d=0;for(b=arguments.length;d<b;d++)a=arguments[d],h.push(a.toString());return h}.apply(this,arguments).join(",")+")")}};e.prototype.getImageData=function(){var c;this._log.push("getImageData("+function(){var a,d,b;b=[];a=0;for(d=arguments.length;a<d;a++)c=arguments[a],b.push(c.toString());return b}.apply(this,arguments).join(",")+")");return{width:0,height:0,resolution:1,data:[]}};return e}()}).call(this);
|
|
||||||
(function(){var e,g;e=function(c){return function(a){return Object.prototype.toString.call(a)==="[object "+c+"]"}};Epoch.isArray=null!=(g=Array.isArray)?g:e("Array");Epoch.isObject=e("Object");Epoch.isString=e("String");Epoch.isFunction=e("Function");Epoch.isNumber=e("Number");Epoch.isElement=function(c){return"undefined"!==typeof HTMLElement&&null!==HTMLElement?c instanceof HTMLElement:null!=c&&Epoch.isObject(c)&&1===c.nodeType&&Epoch.isString(c.nodeName)};Epoch.Util.copy=function(c){var a,d,b;if(null==
|
|
||||||
c)return null;a={};for(d in c)b=c[d],a[d]=b;return a};Epoch.Util.defaults=function(c,a){var d,b,h,k,f;f=Epoch.Util.copy(c);for(h in a)k=c[h],b=a[h],d=Epoch.isObject(k)&&Epoch.isObject(b),null!=k&&null!=b?d&&!Epoch.isArray(k)?f[h]=Epoch.Util.defaults(k,b):f[h]=k:f[h]=null!=k?k:b;return f};Epoch.Util.formatSI=function(c,a,d){var b,h,k,f;null==a&&(a=1);null==d&&(d=!1);if(1E3>c){if((c|0)!==c||d)c=c.toFixed(a);return c}f="KMGTPEZY".split("");for(h in f)if(k=f[h],b=Math.pow(10,3*((h|0)+1)),c>=b&&c<Math.pow(10,
|
|
||||||
3*((h|0)+2))){c/=b;if(0!==c%1||d)c=c.toFixed(a);return""+c+" "+k}};Epoch.Util.formatBytes=function(c,a,d){var b,h,k,f;null==a&&(a=1);null==d&&(d=!1);if(1024>c){if(0!==c%1||d)c=c.toFixed(a);return""+c+" B"}f="KB MB GB TB PB EB ZB YB".split(" ");for(h in f)if(k=f[h],b=Math.pow(1024,(h|0)+1),c>=b&&c<Math.pow(1024,(h|0)+2)){c/=b;if(0!==c%1||d)c=c.toFixed(a);return""+c+" "+k}};Epoch.Util.dasherize=function(c){return Epoch.Util.trim(c).replace("\n","").replace(/\s+/g,"-").toLowerCase()};Epoch.Util.domain=
|
|
||||||
function(c,a){var d,b,h,k,f,q,u,m;null==a&&(a="x");h={};d=[];k=0;for(q=c.length;k<q;k++)for(b=c[k],m=b.values,f=0,u=m.length;f<u;f++)b=m[f],null==h[b[a]]&&(d.push(b[a]),h[b[a]]=!0);return d};Epoch.Util.trim=function(c){return Epoch.isString(c)?c.replace(/^\s+/g,"").replace(/\s+$/g,""):null};Epoch.Util.getComputedStyle=function(c,a){if(Epoch.isFunction(window.getComputedStyle))return window.getComputedStyle(c,a);if(null!=c.currentStyle)return c.currentStyle};Epoch.Util.toRGBA=function(c,a){var d,b,
|
|
||||||
h;if(d=c.match(/^rgba\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*[0-9\.]+\)/))h=d[1],b=d[2],d=d[3],b="rgba("+h+","+b+","+d+","+a+")";else if(d=d3.rgb(c))b="rgba("+d.r+","+d.g+","+d.b+","+a+")";return b};Epoch.Util.getContext=function(c,a){null==a&&(a="2d");return null!=c.getContext?c.getContext(a):new Epoch.TestContext}}).call(this);
|
|
||||||
(function(){d3.selection.prototype.width=function(e){return null!=e&&Epoch.isString(e)?this.style("width",e):null!=e&&Epoch.isNumber(e)?this.style("width",""+e+"px"):+Epoch.Util.getComputedStyle(this.node(),null).width.replace("px","")};d3.selection.prototype.height=function(e){return null!=e&&Epoch.isString(e)?this.style("height",e):null!=e&&Epoch.isNumber(e)?this.style("height",""+e+"px"):+Epoch.Util.getComputedStyle(this.node(),null).height.replace("px","")}}).call(this);
|
|
||||||
(function(){var e;Epoch.Formats.regular=function(g){return g};Epoch.Formats.si=function(g){return Epoch.Util.formatSI(g)};Epoch.Formats.percent=function(g){return(100*g).toFixed(1)+"%"};Epoch.Formats.seconds=function(g){return e(new Date(1E3*g))};e=d3.time.format("%I:%M:%S %p");Epoch.Formats.bytes=function(g){return Epoch.Util.formatBytes(g)}}).call(this);
|
|
||||||
(function(){var e={}.hasOwnProperty,g=function(c,a){function d(){this.constructor=c}for(var b in a)e.call(a,b)&&(c[b]=a[b]);d.prototype=a.prototype;c.prototype=new d;c.__super__=a.prototype;return c};Epoch.Events=function(){function c(){this._events={}}c.prototype.on=function(a,d){var b;if(null!=d)return null==(b=this._events)[a]&&(b[a]=[]),this._events[a].push(d)};c.prototype.onAll=function(a){var d,b,h;if(Epoch.isObject(a)){h=[];for(b in a)d=a[b],h.push(this.on(b,d));return h}};c.prototype.off=
|
|
||||||
function(a,d){var b,h;if(Epoch.isArray(this._events[a])){if(null==d)return delete this._events[a];for(h=[];0<=(b=this._events[a].indexOf(d));)h.push(this._events[a].splice(b,1));return h}};c.prototype.offAll=function(a){var d,b,h,k;if(Epoch.isArray(a)){k=[];d=0;for(h=a.length;d<h;d++)b=a[d],k.push(this.off(b));return k}if(Epoch.isObject(a)){h=[];for(b in a)d=a[b],h.push(this.off(b,d));return h}};c.prototype.trigger=function(a){var d,b,h,k,f,q,c,m;if(null!=this._events[a]){d=function(){var a,f,q;q=
|
|
||||||
[];k=a=1;for(f=arguments.length;1<=f?a<f:a>f;k=1<=f?++a:--a)q.push(arguments[k]);return q}.apply(this,arguments);c=this._events[a];m=[];f=0;for(q=c.length;f<q;f++)b=c[f],h=null,Epoch.isString(b)?h=this[b]:Epoch.isFunction(b)&&(h=b),null==h&&Epoch.exception("Callback for event '"+a+"' is not a function or reference to a method."),m.push(h.apply(this,d));return m}};return c}();Epoch.Chart.Base=function(c){function a(h){this.options=null!=h?h:{};a.__super__.constructor.call(this);this.setData(this.options.data||
|
|
||||||
[]);null!=this.options.el&&(this.el=d3.select(this.options.el));this.width=this.options.width;this.height=this.options.height;null!=this.el?(null==this.width&&(this.width=this.el.width()),null==this.height&&(this.height=this.el.height())):(null==this.width&&(this.width=d.width),null==this.height&&(this.height=d.height));this.onAll(b)}var d,b;g(a,c);d={width:320,height:240};b={"option:width":"dimensionsChanged","option:height":"dimensionsChanged"};a.prototype._getAllOptions=function(){return Epoch.Util.defaults({},
|
|
||||||
this.options)};a.prototype._getOption=function(a){var k,f;a=a.split(".");for(k=this.options;a.length&&null!=k;)f=a.shift(),k=k[f];return k};a.prototype._setOption=function(a,k){var f,q,b;f=a.split(".");for(q=this.options;f.length;){b=f.shift();if(0===f.length){q[b]=k;this.trigger("option:"+a);break}null==q[b]&&(q[b]={});q=q[b]}};a.prototype._setManyOptions=function(a,k){var f,q,b;null==k&&(k="");b=[];for(f in a)q=a[f],Epoch.isObject(q)?b.push(this._setManyOptions(q,""+(k+f)+".")):b.push(this._setOption(k+
|
|
||||||
f,q));return b};a.prototype.option=function(){if(0===arguments.length)return this._getAllOptions();if(1===arguments.length&&Epoch.isString(arguments[0]))return this._getOption(arguments[0]);if(2===arguments.length&&Epoch.isString(arguments[0]))return this._setOption(arguments[0],arguments[1]);if(1===arguments.length&&Epoch.isObject(arguments[0]))return this._setManyOptions(arguments[0])};a.prototype.setData=function(a){var k,f,b,d,c;k=1;d=0;for(c=a.length;d<c;d++)b=a[d],f=["layer"],f.push("category"+
|
|
||||||
k),b.category=k,null!=b.label&&f.push(Epoch.Util.dasherize(b.label)),b.className=f.join(" "),k++;return this.data=a};a.prototype.update=function(a,k){null==k&&(k=!0);this.setData(a);if(k)return this.draw()};a.prototype.draw=function(){return this.trigger("draw")};a.prototype.extent=function(a){return[d3.min(this.data,function(k){return d3.min(k.values,a)}),d3.max(this.data,function(k){return d3.max(k.values,a)})]};a.prototype.dimensionsChanged=function(){this.width=this.option("width")||this.width;
|
|
||||||
this.height=this.option("height")||this.height;this.el.width(this.width);return this.el.height(this.height)};return a}(Epoch.Events);Epoch.Chart.SVG=function(c){function a(d){this.options=null!=d?d:{};a.__super__.constructor.call(this,this.options);this.svg=null!=this.el?this.el.append("svg"):d3.select(document.createElement("svg"));this.svg.attr({xmlns:"http://www.w3.org/2000/svg",width:this.width,height:this.height})}g(a,c);a.prototype.dimensionsChanged=function(){a.__super__.dimensionsChanged.call(this);
|
|
||||||
return this.svg.attr("width",this.width).attr("height",this.height)};return a}(Epoch.Chart.Base);Epoch.Chart.Canvas=function(c){function a(d){this.options=null!=d?d:{};a.__super__.constructor.call(this,this.options);this.pixelRatio=null!=this.options.pixelRatio?this.options.pixelRatio:null!=window.devicePixelRatio?window.devicePixelRatio:1;this.canvas=d3.select(document.createElement("CANVAS"));this.canvas.style({width:""+this.width+"px",height:""+this.height+"px"});this.canvas.attr({width:this.getWidth(),
|
|
||||||
height:this.getHeight()});null!=this.el&&this.el.node().appendChild(this.canvas.node());this.ctx=Epoch.Util.getContext(this.canvas.node())}g(a,c);a.prototype.getWidth=function(){return this.width*this.pixelRatio};a.prototype.getHeight=function(){return this.height*this.pixelRatio};a.prototype.clear=function(){return this.ctx.clearRect(0,0,this.getWidth(),this.getHeight())};a.prototype.getStyles=function(a){return Epoch.QueryCSS.getStyles(a,this.el)};a.prototype.dimensionsChanged=function(){a.__super__.dimensionsChanged.call(this);
|
|
||||||
this.canvas.style({width:""+this.width+"px",height:""+this.height+"px"});return this.canvas.attr({width:this.getWidth(),height:this.getHeight()})};return a}(Epoch.Chart.Base)}).call(this);
|
|
||||||
(function(){var e;e=function(){function g(){}var c,a,d,b,h;a=0;b=function(){return"epoch-container-"+a++};c=/^([^#. ]+)?(#[^. ]+)?(\.[^# ]+)?$/;d=!1;h=function(a){var f,b;f=a.match(c);if(null==f)return Epoch.error("Query CSS cannot match given selector: "+a);b=f[1];a=f[2];f=f[3];b=(null!=b?b:"div").toUpperCase();b=document.createElement(b);null!=a&&(b.id=a.substr(1));null!=f&&(b.className=f.substr(1).replace(/\./g," "));return b};g.log=function(a){return d=a};g.cache={};g.styleList=["fill","stroke",
|
|
||||||
"stroke-width"];g.container=null;g.purge=function(){return g.cache={}};g.getContainer=function(){var a;if(null!=g.container)return g.container;a=document.createElement("DIV");a.id="_canvas_css_reference";document.body.appendChild(a);return g.container=d3.select(a)};g.hash=function(a,f){var d;d=f.attr("data-epoch-container-id");null==d&&(d=b(),f.attr("data-epoch-container-id",d));return""+d+"__"+a};g.getStyles=function(a,f){var b,c,m,l,n,e,r;c=g.hash(a,f);b=g.cache[c];if(null!=b)return b;m=[];for(b=
|
|
||||||
f.node().parentNode;null!=b&&"body"!==b.nodeName.toLowerCase();)m.unshift(b),b=b.parentNode;m.push(f.node());b=[];e=0;for(r=m.length;e<r;e++)l=m[e],n=l.nodeName.toLowerCase(),null!=l.id&&0<l.id.length&&(n+="#"+l.id),null!=l.className&&0<l.className.length&&(n+="."+Epoch.Util.trim(l.className).replace(/\s+/g,".")),b.push(n);b.push("svg");e=Epoch.Util.trim(a).split(/\s+/);l=0;for(n=e.length;l<n;l++)m=e[l],b.push(m);d&&console.log(b);for(l=n=h(b.shift());b.length;)m=h(b.shift()),l.appendChild(m),l=m;
|
|
||||||
d&&console.log(n);g.getContainer().node().appendChild(n);m=d3.select("#_canvas_css_reference "+a);l={};r=g.styleList;n=0;for(e=r.length;n<e;n++)b=r[n],l[b]=m.style(b);g.cache[c]=l;g.getContainer().html("");return l};return g}();Epoch.QueryCSS=e}).call(this);
|
|
||||||
(function(){var e={}.hasOwnProperty,g=function(c,a){function d(){this.constructor=c}for(var b in a)e.call(a,b)&&(c[b]=a[b]);d.prototype=a.prototype;c.prototype=new d;c.__super__=a.prototype;return c};Epoch.Chart.Plot=function(c){function a(k){var f,c,u;this.options=null!=k?k:{};Epoch.Util.copy(this.options.margins);a.__super__.constructor.call(this,this.options=Epoch.Util.defaults(this.options,b));this.margins={};u=["top","right","bottom","left"];f=0;for(c=u.length;f<c;f++)k=u[f],this.margins[k]=
|
|
||||||
null!=this.options.margins&&null!=this.options.margins[k]?this.options.margins[k]:this.hasAxis(k)?d[k]:6;this.g=this.svg.append("g").attr("transform","translate("+this.margins.left+", "+this.margins.top+")");this.onAll(h)}var d,b,h;g(a,c);b={domain:null,range:null,axes:["left","bottom"],ticks:{top:14,bottom:14,left:5,right:5},tickFormats:{top:Epoch.Formats.regular,bottom:Epoch.Formats.regular,left:Epoch.Formats.si,right:Epoch.Formats.si}};d={top:25,right:50,bottom:25,left:50};h={"option:margins.top":"marginsChanged",
|
|
||||||
"option:margins.right":"marginsChanged","option:margins.bottom":"marginsChanged","option:margins.left":"marginsChanged","option:axes":"axesChanged","option:ticks.top":"ticksChanged","option:ticks.right":"ticksChanged","option:ticks.bottom":"ticksChanged","option:ticks.left":"ticksChanged","option:tickFormats.top":"tickFormatsChanged","option:tickFormats.right":"tickFormatsChanged","option:tickFormats.bottom":"tickFormatsChanged","option:tickFormats.left":"tickFormatsChanged","option:domain":"domainChanged",
|
|
||||||
"option:range":"rangeChanged"};a.prototype.setTickFormat=function(a,f){return this.options.tickFormats[a]=f};a.prototype.hasAxis=function(a){return-1<this.options.axes.indexOf(a)};a.prototype.innerWidth=function(){return this.width-(this.margins.left+this.margins.right)};a.prototype.innerHeight=function(){return this.height-(this.margins.top+this.margins.bottom)};a.prototype.x=function(){var a,f;a=null!=(f=this.options.domain)?f:this.extent(function(a){return a.x});return d3.scale.linear().domain(a).range([0,
|
|
||||||
this.innerWidth()])};a.prototype.y=function(){var a,f;a=null!=(f=this.options.range)?f:this.extent(function(a){return a.y});return d3.scale.linear().domain(a).range([this.innerHeight(),0])};a.prototype.bottomAxis=function(){return d3.svg.axis().scale(this.x()).orient("bottom").ticks(this.options.ticks.bottom).tickFormat(this.options.tickFormats.bottom)};a.prototype.topAxis=function(){return d3.svg.axis().scale(this.x()).orient("top").ticks(this.options.ticks.top).tickFormat(this.options.tickFormats.top)};
|
|
||||||
a.prototype.leftAxis=function(){return d3.svg.axis().scale(this.y()).orient("left").ticks(this.options.ticks.left).tickFormat(this.options.tickFormats.left)};a.prototype.rightAxis=function(){return d3.svg.axis().scale(this.y()).orient("right").ticks(this.options.ticks.right).tickFormat(this.options.tickFormats.right)};a.prototype.draw=function(){this._axesDrawn?this._redrawAxes():this._drawAxes();return a.__super__.draw.call(this)};a.prototype._redrawAxes=function(){this.hasAxis("bottom")&&this.g.selectAll(".x.axis.bottom").transition().duration(500).ease("linear").call(this.bottomAxis());
|
|
||||||
this.hasAxis("top")&&this.g.selectAll(".x.axis.top").transition().duration(500).ease("linear").call(this.topAxis());this.hasAxis("left")&&this.g.selectAll(".y.axis.left").transition().duration(500).ease("linear").call(this.leftAxis());if(this.hasAxis("right"))return this.g.selectAll(".y.axis.right").transition().duration(500).ease("linear").call(this.rightAxis())};a.prototype._drawAxes=function(){this.hasAxis("bottom")&&this.g.append("g").attr("class","x axis bottom").attr("transform","translate(0, "+
|
|
||||||
this.innerHeight()+")").call(this.bottomAxis());this.hasAxis("top")&&this.g.append("g").attr("class","x axis top").call(this.topAxis());this.hasAxis("left")&&this.g.append("g").attr("class","y axis left").call(this.leftAxis());this.hasAxis("right")&&this.g.append("g").attr("class","y axis right").attr("transform","translate("+this.innerWidth()+", 0)").call(this.rightAxis());return this._axesDrawn=!0};a.prototype.dimensionsChanged=function(){a.__super__.dimensionsChanged.call(this);this.g.selectAll(".axis").remove();
|
|
||||||
this._axesDrawn=!1;return this.draw()};a.prototype.marginsChanged=function(){var a,f,b;if(null!=this.options.margins){b=this.options.margins;for(a in b)f=b[a],this.margins[a]=null==f?6:f;this.g.transition().duration(750).attr("transform","translate("+this.margins.left+", "+this.margins.top+")");return this.draw()}};a.prototype.axesChanged=function(){var a,f,b,c;c=["top","right","bottom","left"];f=0;for(b=c.length;f<b;f++)if(a=c[f],null==this.options.margins||null==this.options.margins[a])this.hasAxis(a)?
|
|
||||||
this.margins[a]=d[a]:this.margins[a]=6;this.g.transition().duration(750).attr("transform","translate("+this.margins.left+", "+this.margins.top+")");this.g.selectAll(".axis").remove();this._axesDrawn=!1;return this.draw()};a.prototype.ticksChanged=function(){return this.draw()};a.prototype.tickFormatsChanged=function(){return this.draw()};a.prototype.domainChanged=function(){return this.draw()};a.prototype.rangeChanged=function(){return this.draw()};return a}(Epoch.Chart.SVG)}).call(this);
|
|
||||||
(function(){var e={}.hasOwnProperty,g=function(c,a){function d(){this.constructor=c}for(var b in a)e.call(a,b)&&(c[b]=a[b]);d.prototype=a.prototype;c.prototype=new d;c.__super__=a.prototype;return c};Epoch.Chart.Area=function(c){function a(){return a.__super__.constructor.apply(this,arguments)}g(a,c);a.prototype.y=function(){var a,b,c,k,f,q,u,m;a=[];q=this.data;k=0;for(f=q.length;k<f;k++)for(b in c=q[k],u=c.values,u)c=u[b],null!=a[b]&&(a[b]+=c.y),null==a[b]&&(a[b]=c.y);return d3.scale.linear().domain(null!=
|
|
||||||
(m=this.options.range)?m:[0,d3.max(a)]).range([this.height-this.margins.top-this.margins.bottom,0])};a.prototype.draw=function(){var d,b,c,k;b=[this.x(),this.y()];c=b[0];k=b[1];d=d3.svg.area().x(function(a){return c(a.x)}).y0(function(a){return k(a.y0)}).y1(function(a){return k(a.y0+a.y)});d3.layout.stack().values(function(a){return a.values})(this.data);this.g.selectAll(".layer").remove();b=this.g.selectAll(".layer").data(this.data,function(a){return a.category});b.select(".area").attr("d",function(a){return d(a.values)});
|
|
||||||
b.enter().append("g").attr("class",function(a){return a.className});b.append("path").attr("class","area").attr("d",function(a){return d(a.values)});return a.__super__.draw.call(this)};return a}(Epoch.Chart.Plot)}).call(this);
|
|
||||||
(function(){var e={}.hasOwnProperty,g=function(c,a){function d(){this.constructor=c}for(var b in a)e.call(a,b)&&(c[b]=a[b]);d.prototype=a.prototype;c.prototype=new d;c.__super__=a.prototype;return c};Epoch.Chart.Bar=function(c){function a(k){this.options=null!=k?k:{};this.options="horizontal"===this.options.orientation?Epoch.Util.defaults(this.options,b):Epoch.Util.defaults(this.options,d);a.__super__.constructor.call(this,this.options);this.onAll(h)}var d,b,h;g(a,c);d={style:"grouped",orientation:"vertical",
|
|
||||||
padding:{bar:0.08,group:0.1},outerPadding:{bar:0.08,group:0.1}};b=Epoch.Util.defaults({tickFormats:{top:Epoch.Formats.si,bottom:Epoch.Formats.si,left:Epoch.Formats.regular,right:Epoch.Formats.regular}},d);h={"option:orientation":"orientationChanged","option:padding":"paddingChanged","option:outerPadding":"paddingChanged","option:padding:bar":"paddingChanged","option:padding:group":"paddingChanged","option:outerPadding:bar":"paddingChanged","option:outerPadding:group":"paddingChanged"};a.prototype.x=
|
|
||||||
function(){var a;if("vertical"===this.options.orientation)return d3.scale.ordinal().domain(Epoch.Util.domain(this.data)).rangeRoundBands([0,this.innerWidth()],this.options.padding.group,this.options.outerPadding.group);a=this.extent(function(a){return a.y});a[0]=Math.min(0,a[0]);return d3.scale.linear().domain(a).range([0,this.width-this.margins.left-this.margins.right])};a.prototype.x1=function(a){var f;return d3.scale.ordinal().domain(function(){var a,k,b,d;b=this.data;d=[];a=0;for(k=b.length;a<
|
|
||||||
k;a++)f=b[a],d.push(f.category);return d}.call(this)).rangeRoundBands([0,a.rangeBand()],this.options.padding.bar,this.options.outerPadding.bar)};a.prototype.y=function(){var a;return"vertical"===this.options.orientation?(a=this.extent(function(a){return a.y}),a[0]=Math.min(0,a[0]),d3.scale.linear().domain(a).range([this.height-this.margins.top-this.margins.bottom,0])):d3.scale.ordinal().domain(Epoch.Util.domain(this.data)).rangeRoundBands([0,this.innerHeight()],this.options.padding.group,this.options.outerPadding.group)};
|
|
||||||
a.prototype.y1=function(a){var f;return d3.scale.ordinal().domain(function(){var a,k,b,d;b=this.data;d=[];a=0;for(k=b.length;a<k;a++)f=b[a],d.push(f.category);return d}.call(this)).rangeRoundBands([0,a.rangeBand()],this.options.padding.bar,this.options.outerPadding.bar)};a.prototype._remapData=function(){var a,f,b,d,c,h,n,e,g,s,t,v;c={};t=this.data;h=0;for(e=t.length;h<e;h++)for(d=t[h],a="bar "+d.className.replace(/\s*layer\s*/,""),v=d.values,n=0,g=v.length;n<g;n++)f=v[n],null==c[s=f.x]&&(c[s]=[]),
|
|
||||||
c[f.x].push({label:d.category,y:f.y,className:a});f=[];for(b in c)a=c[b],f.push({group:b,values:a});return f};a.prototype.draw=function(){"horizontal"===this.options.orientation?this._drawHorizontal():this._drawVertical();return a.__super__.draw.call(this)};a.prototype._drawVertical=function(){var a,b,d,c,h,l;a=[this.x(),this.y()];c=a[0];l=a[1];h=this.x1(c);b=this.height-this.margins.top-this.margins.bottom;a=this._remapData();a=this.g.selectAll(".layer").data(a,function(a){return a.group});a.transition().duration(750).attr("transform",
|
|
||||||
function(a){return"translate("+c(a.group)+", 0)"});a.enter().append("g").attr("class","layer").attr("transform",function(a){return"translate("+c(a.group)+", 0)"});d=a.selectAll("rect").data(function(a){return a.values});d.transition().duration(600).attr("x",function(a){return h(a.label)}).attr("y",function(a){return l(a.y)}).attr("width",h.rangeBand()).attr("height",function(a){return b-l(a.y)});d.enter().append("rect").attr("class",function(a){return a.className}).attr("x",function(a){return h(a.label)}).attr("y",
|
|
||||||
function(a){return l(a.y)}).attr("width",h.rangeBand()).attr("height",function(a){return b-l(a.y)});d.exit().transition().duration(150).style("opacity","0").remove();return a.exit().transition().duration(750).style("opacity","0").remove()};a.prototype._drawHorizontal=function(){var a,b,d,c,h;a=[this.x(),this.y()];d=a[0];c=a[1];h=this.y1(c);a=this._remapData();a=this.g.selectAll(".layer").data(a,function(a){return a.group});a.transition().duration(750).attr("transform",function(a){return"translate(0, "+
|
|
||||||
c(a.group)+")"});a.enter().append("g").attr("class","layer").attr("transform",function(a){return"translate(0, "+c(a.group)+")"});b=a.selectAll("rect").data(function(a){return a.values});b.transition().duration(600).attr("x",function(a){return 0}).attr("y",function(a){return h(a.label)}).attr("height",h.rangeBand()).attr("width",function(a){return d(a.y)});b.enter().append("rect").attr("class",function(a){return a.className}).attr("x",function(a){return 0}).attr("y",function(a){return h(a.label)}).attr("height",
|
|
||||||
h.rangeBand()).attr("width",function(a){return d(a.y)});b.exit().transition().duration(150).style("opacity","0").remove();return a.exit().transition().duration(750).style("opacity","0").remove()};a.prototype.orientationChanged=function(){var a,b,d,c;c=this.options.tickFormats.top;a=this.options.tickFormats.bottom;b=this.options.tickFormats.left;d=this.options.tickFormats.right;this.options.tickFormats.left=c;this.options.tickFormats.right=a;this.options.tickFormats.top=b;this.options.tickFormats.bottom=
|
|
||||||
d;return this.draw()};a.prototype.paddingChanged=function(){return this.draw()};return a}(Epoch.Chart.Plot)}).call(this);
|
|
||||||
(function(){var e={}.hasOwnProperty,g=function(c,a){function d(){this.constructor=c}for(var b in a)e.call(a,b)&&(c[b]=a[b]);d.prototype=a.prototype;c.prototype=new d;c.__super__=a.prototype;return c};Epoch.Chart.Line=function(c){function a(){return a.__super__.constructor.apply(this,arguments)}g(a,c);a.prototype.line=function(){var a,b,c;c=[this.x(),this.y()];a=c[0];b=c[1];return d3.svg.line().x(function(b){return function(b){return a(b.x)}}(this)).y(function(a){return function(a){return b(a.y)}}(this))};a.prototype.draw=
|
|
||||||
function(){var c,b;b=[this.x(),this.y(),this.line()][2];c=this.g.selectAll(".layer").data(this.data,function(a){return a.category});c.select(".line").transition().duration(500).attr("d",function(a){return b(a.values)});c.enter().append("g").attr("class",function(a){return a.className}).append("path").attr("class","line").attr("d",function(a){return b(a.values)});c.exit().transition().duration(750).style("opacity","0").remove();return a.__super__.draw.call(this)};return a}(Epoch.Chart.Plot)}).call(this);
|
|
||||||
(function(){var e={}.hasOwnProperty,g=function(c,a){function d(){this.constructor=c}for(var b in a)e.call(a,b)&&(c[b]=a[b]);d.prototype=a.prototype;c.prototype=new d;c.__super__=a.prototype;return c};Epoch.Chart.Pie=function(c){function a(b){this.options=null!=b?b:{};a.__super__.constructor.call(this,this.options=Epoch.Util.defaults(this.options,d));this.pie=d3.layout.pie().sort(null).value(function(a){return a.value});this.arc=d3.svg.arc().outerRadius(function(a){return function(){return Math.max(a.width,
|
|
||||||
a.height)/2-a.options.margin}}(this)).innerRadius(function(a){return function(){return a.options.inner}}(this));this.g=this.svg.append("g").attr("transform","translate("+this.width/2+", "+this.height/2+")");this.on("option:margin","marginChanged");this.on("option:inner","innerChanged")}var d;g(a,c);d={margin:10,inner:0};a.prototype.draw=function(){var b;this.g.selectAll(".arc").remove();b=this.g.selectAll(".arc").data(this.pie(this.data),function(a){return a.data.category});b.enter().append("g").attr("class",
|
|
||||||
function(a){return"arc pie "+a.data.className});b.select("path").attr("d",this.arc);b.select("text").attr("transform",function(a){return function(b){return"translate("+a.arc.centroid(b)+")"}}(this)).text(function(a){return a.data.label||a.data.category});b.append("path").attr("d",this.arc).each(function(a){return this._current=a});b.append("text").attr("transform",function(a){return function(b){return"translate("+a.arc.centroid(b)+")"}}(this)).attr("dy",".35em").style("text-anchor","middle").text(function(a){return a.data.label||
|
|
||||||
a.data.category});return a.__super__.draw.call(this)};a.prototype.marginChanged=function(){return this.draw()};a.prototype.innerChanged=function(){return this.draw()};return a}(Epoch.Chart.SVG)}).call(this);
|
|
||||||
(function(){var e={}.hasOwnProperty,g=function(c,a){function d(){this.constructor=c}for(var b in a)e.call(a,b)&&(c[b]=a[b]);d.prototype=a.prototype;c.prototype=new d;c.__super__=a.prototype;return c};Epoch.Chart.Scatter=function(c){function a(b){this.options=null!=b?b:{};a.__super__.constructor.call(this,this.options=Epoch.Util.defaults(this.options,d));this.on("option:radius","radiusChanged")}var d;g(a,c);d={radius:3.5,axes:["top","bottom","left","right"]};a.prototype.draw=function(){var b,c,k,f,
|
|
||||||
d;b=[this.x(),this.y()];f=b[0];d=b[1];k=this.options.radius;c=this.g.selectAll(".layer").data(this.data,function(a){return a.category});c.enter().append("g").attr("class",function(a){return a.className});b=c.selectAll(".dot").data(function(a){return a.values});b.transition().duration(500).attr("r",function(a){var b;return null!=(b=a.r)?b:k}).attr("cx",function(a){return f(a.x)}).attr("cy",function(a){return d(a.y)});b.enter().append("circle").attr("class","dot").attr("r",function(a){var b;return null!=
|
|
||||||
(b=a.r)?b:k}).attr("cx",function(a){return f(a.x)}).attr("cy",function(a){return d(a.y)});b.exit().transition().duration(750).style("opacity",0).remove();c.exit().transition().duration(750).style("opacity",0).remove();return a.__super__.draw.call(this)};a.prototype.radiusChanged=function(){return this.draw()};return a}(Epoch.Chart.Plot)}).call(this);
|
|
||||||
(function(){var e={}.hasOwnProperty,g=function(c,a){function d(){this.constructor=c}for(var b in a)e.call(a,b)&&(c[b]=a[b]);d.prototype=a.prototype;c.prototype=new d;c.__super__=a.prototype;return c};Epoch.Time.Plot=function(c){function a(k){var f,c,u;this.options=k;Epoch.Util.copy(this.options.margins);a.__super__.constructor.call(this,this.options=Epoch.Util.defaults(this.options,b));this._queue=[];this.margins={};u=["top","right","bottom","left"];f=0;for(c=u.length;f<c;f++)k=u[f],this.margins[k]=
|
|
||||||
null!=this.options.margins&&null!=this.options.margins[k]?this.options.margins[k]:this.hasAxis(k)?d[k]:6;this.svg=this.el.insert("svg",":first-child").attr("width",this.width).attr("height",this.height).style("z-index","1000");"absolute"!==this.el.style("position")&&"relative"!==this.el.style("position")&&this.el.style("position","relative");this.canvas.style({position:"absolute","z-index":"999"});this._sizeCanvas();this.animation={interval:null,active:!1,delta:function(a){return function(){return-(a.w()/
|
|
||||||
a.options.fps)}}(this),tickDelta:function(a){return function(){return-(a.w()/a.pixelRatio/a.options.fps)}}(this),frame:0,duration:this.options.fps};this._buildAxes();this.animationCallback=function(a){return function(){return a._animate()}}(this);this.onAll(h)}var d,b,h;g(a,c);b={fps:24,historySize:120,windowSize:40,queueSize:10,axes:["bottom"],ticks:{time:15,left:5,right:5},tickFormats:{top:Epoch.Formats.seconds,bottom:Epoch.Formats.seconds,left:Epoch.Formats.si,right:Epoch.Formats.si}};d={top:25,
|
|
||||||
right:50,bottom:25,left:50};h={"option:margins":"marginsChanged","option:margins.top":"marginsChanged","option:margins.right":"marginsChanged","option:margins.bottom":"marginsChanged","option:margins.left":"marginsChanged","option:axes":"axesChanged","option:ticks":"ticksChanged","option:ticks.top":"ticksChanged","option:ticks.right":"ticksChanged","option:ticks.bottom":"ticksChanged","option:ticks.left":"ticksChanged","option:tickFormats":"tickFormatsChanged","option:tickFormats.top":"tickFormatsChanged",
|
|
||||||
"option:tickFormats.right":"tickFormatsChanged","option:tickFormats.bottom":"tickFormatsChanged","option:tickFormats.left":"tickFormatsChanged"};a.prototype._sizeCanvas=function(){this.canvas.attr({width:this.innerWidth(),height:this.innerHeight()});return this.canvas.style({width:""+this.innerWidth()/this.pixelRatio+"px",height:""+this.innerHeight()/this.pixelRatio+"px",top:""+this.margins.top+"px",left:""+this.margins.left+"px"})};a.prototype._buildAxes=function(){this.svg.selectAll(".axis").remove();
|
|
||||||
this._prepareTimeAxes();return this._prepareRangeAxes()};a.prototype.setData=function(a){var b,c,d,h,e;this.data=[];e=[];for(d in a)h=a[d],c=Epoch.Util.copy(h),b=Math.max(0,h.values.length-this.options.historySize),c.values=h.values.slice(b),b=["layer"],b.push("category"+((d|0)+1)),null!=h.label&&b.push(Epoch.Util.dasherize(h.label)),c.className=b.join(" "),e.push(this.data.push(c));return e};a.prototype._offsetX=function(){return 0};a.prototype._prepareTimeAxes=function(){var a;this.hasAxis("bottom")&&
|
|
||||||
(a=this.bottomAxis=this.svg.append("g").attr("class","x axis bottom canvas").attr("transform","translate("+(this.margins.left-1)+", "+(this.innerHeight()/this.pixelRatio+this.margins.top)+")"),a.append("path").attr("class","domain").attr("d","M0,0H"+(this.innerWidth()/this.pixelRatio+1)));this.hasAxis("top")&&(a=this.topAxis=this.svg.append("g").attr("class","x axis top canvas").attr("transform","translate("+(this.margins.left-1)+", "+this.margins.top+")"),a.append("path").attr("class","domain").attr("d",
|
|
||||||
"M0,0H"+(this.innerWidth()/this.pixelRatio+1)));return this._resetInitialTimeTicks()};a.prototype._resetInitialTimeTicks=function(){var a,b,c,d,h;d=this.options.ticks.time;this._ticks=[];this._tickTimer=d;null!=this.bottomAxis&&this.bottomAxis.selectAll(".tick").remove();null!=this.topAxis&&this.topAxis.selectAll(".tick").remove();h=this.data;a=0;for(b=h.length;a<b;a++)if(c=h[a],null!=c.values&&0<c.values.length){b=[this.options.windowSize-1,c.values.length-1];a=b[0];for(b=b[1];0<=a&&0<=b;)this._pushTick(a,
|
|
||||||
c.values[b].time,!1,!0),a-=d,b-=d;break}return[]};a.prototype._prepareRangeAxes=function(){this.hasAxis("left")&&this.svg.append("g").attr("class","y axis left").attr("transform","translate("+(this.margins.left-1)+", "+this.margins.top+")").call(this.leftAxis());if(this.hasAxis("right"))return this.svg.append("g").attr("class","y axis right").attr("transform","translate("+(this.width-this.margins.right)+", "+this.margins.top+")").call(this.rightAxis())};a.prototype.leftAxis=function(){var a,b;b=this.options.ticks.left;
|
|
||||||
a=d3.svg.axis().scale(this.ySvg()).orient("left").tickFormat(this.options.tickFormats.left);return 2===b?a.tickValues(this.extent(function(a){return a.y})):a.ticks(b)};a.prototype.rightAxis=function(){var a,b;this.extent(function(a){return a.y});b=this.options.ticks.right;a=d3.svg.axis().scale(this.ySvg()).orient("right").tickFormat(this.options.tickFormats.left);return 2===b?a.tickValues(this.extent(function(a){return a.y})):a.ticks(b)};a.prototype.hasAxis=function(a){return-1<this.options.axes.indexOf(a)};
|
|
||||||
a.prototype.innerWidth=function(){return(this.width-(this.margins.left+this.margins.right))*this.pixelRatio};a.prototype.innerHeight=function(){return(this.height-(this.margins.top+this.margins.bottom))*this.pixelRatio};a.prototype._prepareEntry=function(a){return a};a.prototype._prepareLayers=function(a){return a};a.prototype._startTransition=function(){if(!0!==this.animation.active&&0!==this._queue.length)return this.trigger("transition:start"),this._shift(),this.animation.active=!0,this.animation.interval=
|
|
||||||
setInterval(this.animationCallback,1E3/this.options.fps)};a.prototype._stopTransition=function(){var a,b,c,d;if(this.inTransition()){d=this.data;b=0;for(c=d.length;b<c;b++)a=d[b],a.values.length>this.options.windowSize+1&&a.values.shift();b=[this._ticks[0],this._ticks[this._ticks.length-1]];a=b[0];b=b[1];null!=b&&b.enter&&(b.enter=!1,b.opacity=1);null!=a&&a.exit&&this._shiftTick();this.animation.frame=0;this.trigger("transition:end");if(0<this._queue.length)return this._shift();this.animation.active=
|
|
||||||
!1;return clearInterval(this.animation.interval)}};a.prototype.inTransition=function(){return this.animation.active};a.prototype.push=function(a){a=this._prepareLayers(a);this._queue.length>this.options.queueSize&&this._queue.splice(this.options.queueSize,this._queue.length-this.options.queueSize);if(this._queue.length===this.options.queueSize)return!1;this._queue.push(a.map(function(a){return function(b){return a._prepareEntry(b)}}(this)));this.trigger("push");if(!this.inTransition())return this._startTransition()};
|
|
||||||
a.prototype._shift=function(){var a,b,c,d;this.trigger("before:shift");a=this._queue.shift();d=this.data;for(b in d)c=d[b],c.values.push(a[b]);this._updateTicks(a[0].time);this._transitionRangeAxes();return this.trigger("after:shift")};a.prototype._transitionRangeAxes=function(){this.hasAxis("left")&&this.svg.selectAll(".y.axis.left").transition().duration(500).ease("linear").call(this.leftAxis());if(this.hasAxis("right"))return this.svg.selectAll(".y.axis.right").transition().duration(500).ease("linear").call(this.rightAxis())};
|
|
||||||
a.prototype._animate=function(){if(this.inTransition())return++this.animation.frame===this.animation.duration&&this._stopTransition(),this.draw(this.animation.frame*this.animation.delta()),this._updateTimeAxes()};a.prototype.y=function(){return d3.scale.linear().domain(this.extent(function(a){return a.y})).range([this.innerHeight(),0])};a.prototype.ySvg=function(){return d3.scale.linear().domain(this.extent(function(a){return a.y})).range([this.innerHeight()/this.pixelRatio,0])};a.prototype.w=function(){return this.innerWidth()/
|
|
||||||
this.options.windowSize};a.prototype._updateTicks=function(a){if(this.hasAxis("top")||this.hasAxis("bottom"))if(++this._tickTimer%this.options.ticks.time||this._pushTick(this.options.windowSize,a,!0),!(0<=this._ticks[0].x-this.w()/this.pixelRatio))return this._ticks[0].exit=!0};a.prototype._pushTick=function(a,b,c,d){null==c&&(c=!1);null==d&&(d=!1);if(this.hasAxis("top")||this.hasAxis("bottom"))return b={time:b,x:a*(this.w()/this.pixelRatio)+this._offsetX(),opacity:c?0:1,enter:c?!0:!1,exit:!1},this.hasAxis("bottom")&&
|
|
||||||
(a=this.bottomAxis.append("g").attr("class","tick major").attr("transform","translate("+(b.x+1)+",0)").style("opacity",b.opacity),a.append("line").attr("y2",6),a.append("text").attr("text-anchor","middle").attr("dy",19).text(this.options.tickFormats.bottom(b.time)),b.bottomEl=a),this.hasAxis("top")&&(a=this.topAxis.append("g").attr("class","tick major").attr("transform","translate("+(b.x+1)+",0)").style("opacity",b.opacity),a.append("line").attr("y2",-6),a.append("text").attr("text-anchor","middle").attr("dy",
|
|
||||||
-10).text(this.options.tickFormats.top(b.time)),b.topEl=a),d?this._ticks.unshift(b):this._ticks.push(b),b};a.prototype._shiftTick=function(){var a;if(0<this._ticks.length&&(a=this._ticks.shift(),null!=a.topEl&&a.topEl.remove(),null!=a.bottomEl))return a.bottomEl.remove()};a.prototype._updateTimeAxes=function(){var a,b,c,d,h,e,g;if(this.hasAxis("top")||this.hasAxis("bottom")){a=[this.animation.tickDelta(),1/this.options.fps];b=a[0];a=a[1];e=this._ticks;g=[];d=0;for(h=e.length;d<h;d++)c=e[d],c.x+=b,
|
|
||||||
this.hasAxis("bottom")&&c.bottomEl.attr("transform","translate("+(c.x+1)+",0)"),this.hasAxis("top")&&c.topEl.attr("transform","translate("+(c.x+1)+",0)"),c.enter?c.opacity+=a:c.exit&&(c.opacity-=a),c.enter||c.exit?(this.hasAxis("bottom")&&c.bottomEl.style("opacity",c.opacity),this.hasAxis("top")?g.push(c.topEl.style("opacity",c.opacity)):g.push(void 0)):g.push(void 0);return g}};a.prototype.draw=function(b){return a.__super__.draw.call(this)};a.prototype.dimensionsChanged=function(){a.__super__.dimensionsChanged.call(this);
|
|
||||||
this.svg.attr("width",this.width).attr("height",this.height);this._sizeCanvas();this._buildAxes();return this.draw(this.animation.frame*this.animation.delta())};a.prototype.axesChanged=function(){var a,b,c,h;h=["top","right","bottom","left"];b=0;for(c=h.length;b<c;b++)if(a=h[b],null==this.options.margins||null==this.options.margins[a])this.hasAxis(a)?this.margins[a]=d[a]:this.margins[a]=6;this._sizeCanvas();this._buildAxes();return this.draw(this.animation.frame*this.animation.delta())};a.prototype.ticksChanged=
|
|
||||||
function(){this._resetInitialTimeTicks();this._transitionRangeAxes();return this.draw(this.animation.frame*this.animation.delta())};a.prototype.tickFormatsChanged=function(){this._resetInitialTimeTicks();this._transitionRangeAxes();return this.draw(this.animation.frame*this.animation.delta())};a.prototype.marginsChanged=function(){var a,b,c;if(null!=this.options.margins){c=this.options.margins;for(a in c)b=c[a],this.margins[a]=null==b?6:b;this._sizeCanvas();return this.draw(this.animation.frame*this.animation.delta())}};
|
|
||||||
return a}(Epoch.Chart.Canvas);Epoch.Time.Stack=function(c){function a(){return a.__super__.constructor.apply(this,arguments)}g(a,c);a.prototype._prepareLayers=function(a){var b,c,k,f;k=c=0;for(f=a.length;k<f;k++)b=a[k],b.y0=c,c+=b.y;return a};a.prototype.setData=function(c){var b,h,k,f,e;a.__super__.setData.call(this,c);e=[];b=c=0;for(f=this.data[0].values.length;0<=f?c<f:c>f;b=0<=f?++c:--c)k=0,e.push(function(){var a,c,d,f;d=this.data;f=[];a=0;for(c=d.length;a<c;a++)h=d[a],h.values[b].y0=k,f.push(k+=
|
|
||||||
h.values[b].y);return f}.call(this));return e};a.prototype.extent=function(){var a,b,c,k,f,e,g,m;a=f=c=0;for(g=this.data[0].values.length;0<=g?f<g:f>g;a=0<=g?++f:--f){b=e=k=0;for(m=this.data.length;0<=m?e<m:e>m;b=0<=m?++e:--e)k+=this.data[b].values[a].y;k>c&&(c=k)}return[0,c]};return a}(Epoch.Time.Plot)}).call(this);
|
|
||||||
(function(){var e={}.hasOwnProperty,g=function(c,a){function d(){this.constructor=c}for(var b in a)e.call(a,b)&&(c[b]=a[b]);d.prototype=a.prototype;c.prototype=new d;c.__super__=a.prototype;return c};Epoch.Time.Area=function(c){function a(){return a.__super__.constructor.apply(this,arguments)}g(a,c);a.prototype.setStyles=function(a){a=null!=a.className?this.getStyles("g."+a.className.replace(/\s/g,".")+" path.area"):this.getStyles("g path.area");this.ctx.fillStyle=a.fill;null!=a.stroke&&(this.ctx.strokeStyle=
|
|
||||||
a.stroke);if(null!=a["stroke-width"])return this.ctx.lineWidth=a["stroke-width"].replace("px","")};a.prototype._drawAreas=function(a){var b,c,k,f,e,g,m,l,n,p;null==a&&(a=0);g=[this.y(),this.w()];m=g[0];g=g[1];p=[];for(c=l=n=this.data.length-1;0>=n?0>=l:0<=l;c=0>=n?++l:--l){f=this.data[c];this.setStyles(f);this.ctx.beginPath();e=[this.options.windowSize,f.values.length,this.inTransition()];c=e[0];k=e[1];for(e=e[2];-2<=--c&&0<=--k;)b=f.values[k],b=[(c+1)*g+a,m(b.y+b.y0)],e&&(b[0]+=g),c===this.options.windowSize-
|
|
||||||
1?this.ctx.moveTo.apply(this.ctx,b):this.ctx.lineTo.apply(this.ctx,b);c=e?(c+3)*g+a:(c+2)*g+a;this.ctx.lineTo(c,this.innerHeight());this.ctx.lineTo(this.width*this.pixelRatio+g+a,this.innerHeight());this.ctx.closePath();p.push(this.ctx.fill())}return p};a.prototype._drawStrokes=function(a){var b,c,k,f,e,g,m,l,n,p;null==a&&(a=0);c=[this.y(),this.w()];m=c[0];g=c[1];p=[];for(c=l=n=this.data.length-1;0>=n?0>=l:0<=l;c=0>=n?++l:--l){f=this.data[c];this.setStyles(f);this.ctx.beginPath();e=[this.options.windowSize,
|
|
||||||
f.values.length,this.inTransition()];c=e[0];k=e[1];for(e=e[2];-2<=--c&&0<=--k;)b=f.values[k],b=[(c+1)*g+a,m(b.y+b.y0)],e&&(b[0]+=g),c===this.options.windowSize-1?this.ctx.moveTo.apply(this.ctx,b):this.ctx.lineTo.apply(this.ctx,b);p.push(this.ctx.stroke())}return p};a.prototype.draw=function(c){null==c&&(c=0);this.clear();this._drawAreas(c);this._drawStrokes(c);return a.__super__.draw.call(this)};return a}(Epoch.Time.Stack)}).call(this);
|
|
||||||
(function(){var e={}.hasOwnProperty,g=function(c,a){function d(){this.constructor=c}for(var b in a)e.call(a,b)&&(c[b]=a[b]);d.prototype=a.prototype;c.prototype=new d;c.__super__=a.prototype;return c};Epoch.Time.Bar=function(c){function a(){return a.__super__.constructor.apply(this,arguments)}g(a,c);a.prototype._offsetX=function(){return 0.5*this.w()/this.pixelRatio};a.prototype.setStyles=function(a){a=this.getStyles("rect.bar."+a.replace(/\s/g,"."));this.ctx.fillStyle=a.fill;this.ctx.strokeStyle=
|
|
||||||
null==a.stroke||"none"===a.stroke?"transparent":a.stroke;if(null!=a["stroke-width"])return this.ctx.lineWidth=a["stroke-width"].replace("px","")};a.prototype.draw=function(c){var b,h,k,f,e,g,m,l,n,p,r,s,t;null==c&&(c=0);this.clear();f=[this.y(),this.w()];p=f[0];n=f[1];t=this.data;r=0;for(s=t.length;r<s;r++)if(m=t[r],0<m.values.length)for(this.setStyles(m.className),e=[this.options.windowSize,m.values.length,this.inTransition()],f=e[0],g=e[1],e=(l=e[2])?-1:0;--f>=e&&0<=--g;)b=m.values[g],k=[f*n+c,
|
|
||||||
b.y,b.y0],b=k[0],h=k[1],k=k[2],l&&(b+=n),b=[b+1,p(h+k),n-2,this.innerHeight()-p(h)+0.5*this.pixelRatio],this.ctx.fillRect.apply(this.ctx,b),this.ctx.strokeRect.apply(this.ctx,b);return a.__super__.draw.call(this)};return a}(Epoch.Time.Stack)}).call(this);
|
|
||||||
(function(){var e={}.hasOwnProperty,g=function(c,a){function d(){this.constructor=c}for(var b in a)e.call(a,b)&&(c[b]=a[b]);d.prototype=a.prototype;c.prototype=new d;c.__super__=a.prototype;return c};Epoch.Time.Gauge=function(c){function a(c){this.options=null!=c?c:{};a.__super__.constructor.call(this,this.options=Epoch.Util.defaults(this.options,d));this.value=this.options.value||0;"absolute"!==this.el.style("position")&&"relative"!==this.el.style("position")&&this.el.style("position","relative");
|
|
||||||
this.svg=this.el.insert("svg",":first-child").attr("width",this.width).attr("height",this.height).attr("class","gauge-labels");this.svg.style({position:"absolute","z-index":"1"});this.svg.append("g").attr("transform","translate("+this.textX()+", "+this.textY()+")").append("text").attr("class","value").text(this.options.format(this.value));this.animation={interval:null,active:!1,delta:0,target:0};this._animate=function(a){return function(){Math.abs(a.animation.target-a.value)<Math.abs(a.animation.delta)?
|
|
||||||
(a.value=a.animation.target,clearInterval(a.animation.interval),a.animation.active=!1):a.value+=a.animation.delta;a.svg.select("text.value").text(a.options.format(a.value));return a.draw()}}(this);this.onAll(b)}var d,b;g(a,c);d={domain:[0,1],ticks:10,tickSize:5,tickOffset:5,fps:34,format:Epoch.Formats.percent};b={"option:domain":"domainChanged","option:ticks":"ticksChanged","option:tickSize":"tickSizeChanged","option:tickOffset":"tickOffsetChanged","option:format":"formatChanged"};a.prototype.update=
|
|
||||||
function(a){this.animation.target=a;this.animation.delta=(a-this.value)/this.options.fps;if(!this.animation.active)return this.animation.interval=setInterval(this._animate,1E3/this.options.fps),this.animation.active=!0};a.prototype.push=function(a){return this.update(a)};a.prototype.radius=function(){return this.getHeight()/1.58};a.prototype.centerX=function(){return this.getWidth()/2};a.prototype.centerY=function(){return 0.68*this.getHeight()};a.prototype.textX=function(){return this.width/2};a.prototype.textY=
|
|
||||||
function(){return 0.48*this.height};a.prototype.getAngle=function(a){var b,c;c=this.options.domain;b=c[0];return(a-b)/(c[1]-b)*(Math.PI+2*Math.PI/8)-Math.PI/2-Math.PI/8};a.prototype.setStyles=function(a){a=this.getStyles(a);this.ctx.fillStyle=a.fill;this.ctx.strokeStyle=a.stroke;if(null!=a["stroke-width"])return this.ctx.lineWidth=a["stroke-width"].replace("px","")};a.prototype.draw=function(){var b,c,d,e,g,m,l,n,p,r,s,t;g=[this.centerX(),this.centerY(),this.radius()];d=g[0];e=g[1];g=g[2];l=[this.options.tickOffset,
|
|
||||||
this.options.tickSize];n=l[0];p=l[1];this.clear();l=d3.scale.linear().domain([0,this.options.ticks]).range([-1.125*Math.PI,Math.PI/8]);this.setStyles(".epoch .gauge .tick");this.ctx.beginPath();b=s=0;for(t=this.options.ticks;0<=t?s<=t:s>=t;b=0<=t?++s:--s)b=l(b),b=[Math.cos(b),Math.sin(b)],c=b[0],m=b[1],b=c*(g-n)+d,r=m*(g-n)+e,c=c*(g-n-p)+d,m=m*(g-n-p)+e,this.ctx.moveTo(b,r),this.ctx.lineTo(c,m);this.ctx.stroke();this.setStyles(".epoch .gauge .arc.outer");this.ctx.beginPath();this.ctx.arc(d,e,g,-1.125*
|
|
||||||
Math.PI,0.125*Math.PI,!1);this.ctx.stroke();this.setStyles(".epoch .gauge .arc.inner");this.ctx.beginPath();this.ctx.arc(d,e,g-10,-1.125*Math.PI,0.125*Math.PI,!1);this.ctx.stroke();this.drawNeedle();return a.__super__.draw.call(this)};a.prototype.drawNeedle=function(){var a,b,c;c=[this.centerX(),this.centerY(),this.radius()];a=c[0];b=c[1];c=c[2];this.setStyles(".epoch .gauge .needle");this.ctx.beginPath();this.ctx.save();this.ctx.translate(a,b);this.ctx.rotate(this.getAngle(this.value));this.ctx.moveTo(4*
|
|
||||||
this.pixelRatio,0);this.ctx.lineTo(-4*this.pixelRatio,0);this.ctx.lineTo(-1*this.pixelRatio,19-c);this.ctx.lineTo(1,19-c);this.ctx.fill();this.setStyles(".epoch .gauge .needle-base");this.ctx.beginPath();this.ctx.arc(0,0,this.getWidth()/25,0,2*Math.PI);this.ctx.fill();return this.ctx.restore()};a.prototype.domainChanged=function(){return this.draw()};a.prototype.ticksChanged=function(){return this.draw()};a.prototype.tickSizeChanged=function(){return this.draw()};a.prototype.tickOffsetChanged=function(){return this.draw()};
|
|
||||||
a.prototype.formatChanged=function(){return this.svg.select("text.value").text(this.options.format(this.value))};return a}(Epoch.Chart.Canvas)}).call(this);
|
|
||||||
(function(){var e={}.hasOwnProperty,g=function(c,a){function d(){this.constructor=c}for(var b in a)e.call(a,b)&&(c[b]=a[b]);d.prototype=a.prototype;c.prototype=new d;c.__super__=a.prototype;return c};Epoch.Time.Heatmap=function(c){function a(c){this.options=c;a.__super__.constructor.call(this,this.options=Epoch.Util.defaults(this.options,b));this._setOpacityFunction();this._setupPaintCanvas();this.onAll(e)}var d,b,e;g(a,c);b={buckets:10,bucketRange:[0,100],opacity:"linear",bucketPadding:2,paintZeroValues:!1,
|
|
||||||
cutOutliers:!1};d={root:function(a,b){return Math.pow(a/b,0.5)},linear:function(a,b){return a/b},quadratic:function(a,b){return Math.pow(a/b,2)},cubic:function(a,b){return Math.pow(a/b,3)},quartic:function(a,b){return Math.pow(a/b,4)},quintic:function(a,b){return Math.pow(a/b,5)}};e={"option:buckets":"bucketsChanged","option:bucketRange":"bucketRangeChanged","option:opacity":"opacityChanged","option:bucketPadding":"bucketPaddingChanged","option:paintZeroValues":"paintZeroValuesChanged","option:cutOutliers":"cutOutliersChanged"};
|
|
||||||
a.prototype._setOpacityFunction=function(){if(Epoch.isString(this.options.opacity)){if(this._opacityFn=d[this.options.opacity],null==this._opacityFn)return Epoch.exception("Unknown coloring function provided '"+this.options.opacity+"'")}else return Epoch.isFunction(this.options.opacity)?this._opacityFn=this.options.opacity:Epoch.exception("Unknown type for provided coloring function.")};a.prototype.setData=function(b){var c,d,e,g;a.__super__.setData.call(this,b);e=this.data;g=[];c=0;for(d=e.length;c<
|
|
||||||
d;c++)b=e[c],g.push(b.values=b.values.map(function(a){return function(b){return a._prepareEntry(b)}}(this)));return g};a.prototype._getBuckets=function(a){var b,c,d,e,g;e=a.time;g=[];b=0;for(d=this.options.buckets;0<=d?b<d:b>d;0<=d?++b:--b)g.push(0);e={time:e,max:0,buckets:g};b=(this.options.bucketRange[1]-this.options.bucketRange[0])/this.options.buckets;g=a.histogram;for(c in g)a=g[c],d=parseInt((c-this.options.bucketRange[0])/b),this.options.cutOutliers&&(0>d||d>=this.options.buckets)||(0>d?d=
|
|
||||||
0:d>=this.options.buckets&&(d=this.options.buckets-1),e.buckets[d]+=parseInt(a));c=a=0;for(b=e.buckets.length;0<=b?a<b:a>b;c=0<=b?++a:--a)e.max=Math.max(e.max,e.buckets[c]);return e};a.prototype.y=function(){return d3.scale.linear().domain(this.options.bucketRange).range([this.innerHeight(),0])};a.prototype.ySvg=function(){return d3.scale.linear().domain(this.options.bucketRange).range([this.innerHeight()/this.pixelRatio,0])};a.prototype.h=function(){return this.innerHeight()/this.options.buckets};
|
|
||||||
a.prototype._offsetX=function(){return 0.5*this.w()/this.pixelRatio};a.prototype._setupPaintCanvas=function(){this.paintWidth=(this.options.windowSize+1)*this.w();this.paintHeight=this.height*this.pixelRatio;this.paint=document.createElement("CANVAS");this.paint.width=this.paintWidth;this.paint.height=this.paintHeight;this.p=Epoch.Util.getContext(this.paint);this.redraw();this.on("after:shift","_paintEntry");this.on("transition:end","_shiftPaintCanvas");return this.on("transition:end",function(a){return function(){return a.draw(a.animation.frame*
|
|
||||||
a.animation.delta())}}(this))};a.prototype.redraw=function(){var a,b;b=this.data[0].values.length;a=this.options.windowSize;for(this.inTransition()&&a++;0<=--b&&0<=--a;)this._paintEntry(b,a);return this.draw(this.animation.frame*this.animation.delta())};a.prototype._computeColor=function(a,b,c){return Epoch.Util.toRGBA(c,this._opacityFn(a,b))};a.prototype._paintEntry=function(a,b){var c,d,e,g,h,p,r,s,t,v,y,w,A,z;null==a&&(a=null);null==b&&(b=null);g=[this.w(),this.h()];y=g[0];p=g[1];null==a&&(a=this.data[0].values.length-
|
|
||||||
1);null==b&&(b=this.options.windowSize);g=[];var x;x=[];h=0;for(v=this.options.buckets;0<=v?h<v:h>v;0<=v?++h:--h)x.push(0);v=0;t=this.data;d=0;for(r=t.length;d<r;d++){s=t[d];h=this._getBuckets(s.values[a]);w=h.buckets;for(c in w)e=w[c],x[c]+=e;v+=h.max;e=this.getStyles("."+s.className.split(" ").join(".")+" rect.bucket");h.color=e.fill;g.push(h)}s=b*y;this.p.clearRect(s,0,y,this.paintHeight);r=this.options.buckets;z=[];for(c in x){e=x[c];d=this._avgLab(g,c);w=t=0;for(A=g.length;w<A;w++)h=g[w],t+=
|
|
||||||
h.buckets[c]/e*v;if(0<e||this.options.paintZeroValues)this.p.fillStyle=this._computeColor(e,t,d),this.p.fillRect(s,(r-1)*p,y-this.options.bucketPadding,p-this.options.bucketPadding);z.push(r--)}return z};a.prototype._shiftPaintCanvas=function(){var a;a=this.p.getImageData(this.w(),0,this.paintWidth-this.w(),this.paintHeight);return this.p.putImageData(a,0,0)};a.prototype._avgLab=function(a,b){var c,d,e,g,h,p,r,s;r=[0,0,0,0];h=r[0];c=r[1];d=r[2];r=r[3];p=0;for(s=a.length;p<s;p++)e=a[p],null!=e.buckets[b]&&
|
|
||||||
(r+=e.buckets[b]);for(g in a)e=a[g],p=null!=e.buckets[b]?e.buckets[b]|0:0,p/=r,e=d3.lab(e.color),h+=p*e.l,c+=p*e.a,d+=p*e.b;return d3.lab(h,c,d).toString()};a.prototype.draw=function(b){null==b&&(b=0);this.clear();this.ctx.drawImage(this.paint,b,0);return a.__super__.draw.call(this)};a.prototype.bucketsChanged=function(){return this.redraw()};a.prototype.bucketRangeChanged=function(){this._transitionRangeAxes();return this.redraw()};a.prototype.opacityChanged=function(){this._setOpacityFunction();
|
|
||||||
return this.redraw()};a.prototype.bucketPaddingChanged=function(){return this.redraw()};a.prototype.paintZeroValuesChanged=function(){return this.redraw()};a.prototype.cutOutliersChanged=function(){return this.redraw()};return a}(Epoch.Time.Plot)}).call(this);
|
|
||||||
(function(){var e={}.hasOwnProperty,g=function(c,a){function d(){this.constructor=c}for(var b in a)e.call(a,b)&&(c[b]=a[b]);d.prototype=a.prototype;c.prototype=new d;c.__super__=a.prototype;return c};Epoch.Time.Line=function(c){function a(){return a.__super__.constructor.apply(this,arguments)}g(a,c);a.prototype.setStyles=function(a){a=this.getStyles("g."+a.replace(/\s/g,".")+" path.line");this.ctx.fillStyle=a.fill;this.ctx.strokeStyle=a.stroke;return this.ctx.lineWidth=this.pixelRatio*a["stroke-width"].replace("px",
|
|
||||||
"")};a.prototype.y=function(){return d3.scale.linear().domain(this.extent(function(a){return a.y})).range([this.innerHeight()-this.pixelRatio/2,this.pixelRatio])};a.prototype.draw=function(c){var b,e,g,f,q,u,m,l,n,p;null==c&&(c=0);this.clear();e=[this.y(),this.w()];m=e[0];u=e[1];p=this.data;l=0;for(n=p.length;l<n;l++)if(f=p[l],0<f.values.length){this.setStyles(f.className);this.ctx.beginPath();q=[this.options.windowSize,f.values.length,this.inTransition()];e=q[0];g=q[1];for(q=q[2];-2<=--e&&0<=--g;)b=
|
|
||||||
f.values[g],b=[(e+1)*u+c,m(b.y)],q&&(b[0]+=u),e===this.options.windowSize-1?this.ctx.moveTo.apply(this.ctx,b):this.ctx.lineTo.apply(this.ctx,b);this.ctx.stroke()}return a.__super__.draw.call(this)};return a}(Epoch.Time.Plot)}).call(this);(function(){Epoch._typeMap={area:Epoch.Chart.Area,bar:Epoch.Chart.Bar,line:Epoch.Chart.Line,pie:Epoch.Chart.Pie,scatter:Epoch.Chart.Scatter,"time.area":Epoch.Time.Area,"time.bar":Epoch.Time.Bar,"time.line":Epoch.Time.Line,"time.gauge":Epoch.Time.Gauge,"time.heatmap":Epoch.Time.Heatmap}}).call(this);
|
|
||||||
(function(){null!=window.MooTools&&function(){return Element.implement("epoch",function(e){var g,c;c=$$(this);null==(g=c.retrieve("epoch-chart")[0])&&(e.el=this,g=Epoch._typeMap[e.type],null==g&&Epoch.exception("Unknown chart type '"+e.type+"'"),c.store("epoch-chart",g=new g(e)),g.draw());return g})}()}).call(this);
|
|
||||||
(function(){var e;e=function(e){return e.fn.epoch=function(c){var a;c.el=this.get(0);null==(a=this.data("epoch-chart"))&&(a=Epoch._typeMap[c.type],null==a&&Epoch.exception("Unknown chart type '"+c.type+"'"),this.data("epoch-chart",a=new a(c)),a.draw());return a}};null!=window.jQuery&&e(jQuery)}).call(this);
|
|
||||||
(function(){var e;e=function(e){var c,a,d;a={};c=0;d=function(){return"epoch-chart-"+ ++c};return e.extend(e.fn,{epoch:function(b){var c,e;if(null!=(c=this.data("epoch-chart")))return a[c];b.el=this.get(0);e=Epoch._typeMap[b.type];null==e&&Epoch.exception("Unknown chart type '"+b.type+"'");this.data("epoch-chart",c=d());b=new e(b);a[c]=b;b.draw();return b}})};null!=window.Zepto&&e(Zepto)}).call(this);
|
|
@ -1,137 +0,0 @@
|
|||||||
/* http://prismjs.com/download.html?themes=prism&languages=clike+javascript+go */
|
|
||||||
/**
|
|
||||||
* prism.js default theme for JavaScript, CSS and HTML
|
|
||||||
* Based on dabblet (http://dabblet.com)
|
|
||||||
* @author Lea Verou
|
|
||||||
*/
|
|
||||||
|
|
||||||
code[class*="language-"],
|
|
||||||
pre[class*="language-"] {
|
|
||||||
color: black;
|
|
||||||
text-shadow: 0 1px white;
|
|
||||||
font-family: Consolas, Monaco, 'Andale Mono', monospace;
|
|
||||||
direction: ltr;
|
|
||||||
text-align: left;
|
|
||||||
white-space: pre;
|
|
||||||
word-spacing: normal;
|
|
||||||
word-break: normal;
|
|
||||||
line-height: 1.5;
|
|
||||||
|
|
||||||
-moz-tab-size: 4;
|
|
||||||
-o-tab-size: 4;
|
|
||||||
tab-size: 4;
|
|
||||||
|
|
||||||
-webkit-hyphens: none;
|
|
||||||
-moz-hyphens: none;
|
|
||||||
-ms-hyphens: none;
|
|
||||||
hyphens: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
|
|
||||||
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
|
|
||||||
text-shadow: none;
|
|
||||||
background: #b3d4fc;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
|
|
||||||
code[class*="language-"]::selection, code[class*="language-"] ::selection {
|
|
||||||
text-shadow: none;
|
|
||||||
background: #b3d4fc;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media print {
|
|
||||||
code[class*="language-"],
|
|
||||||
pre[class*="language-"] {
|
|
||||||
text-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Code blocks */
|
|
||||||
pre[class*="language-"] {
|
|
||||||
padding: 1em;
|
|
||||||
margin: .5em 0;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
:not(pre) > code[class*="language-"],
|
|
||||||
pre[class*="language-"] {
|
|
||||||
background: #f5f2f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Inline code */
|
|
||||||
:not(pre) > code[class*="language-"] {
|
|
||||||
padding: .1em;
|
|
||||||
border-radius: .3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.comment,
|
|
||||||
.token.prolog,
|
|
||||||
.token.doctype,
|
|
||||||
.token.cdata {
|
|
||||||
color: slategray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.punctuation {
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.namespace {
|
|
||||||
opacity: .7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.property,
|
|
||||||
.token.tag,
|
|
||||||
.token.boolean,
|
|
||||||
.token.number,
|
|
||||||
.token.constant,
|
|
||||||
.token.symbol,
|
|
||||||
.token.deleted {
|
|
||||||
color: #905;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.selector,
|
|
||||||
.token.attr-name,
|
|
||||||
.token.string,
|
|
||||||
.token.char,
|
|
||||||
.token.builtin,
|
|
||||||
.token.inserted {
|
|
||||||
color: #690;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.operator,
|
|
||||||
.token.entity,
|
|
||||||
.token.url,
|
|
||||||
.language-css .token.string,
|
|
||||||
.style .token.string {
|
|
||||||
color: #a67f59;
|
|
||||||
background: hsla(0, 0%, 100%, .5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.atrule,
|
|
||||||
.token.attr-value,
|
|
||||||
.token.keyword {
|
|
||||||
color: #07a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.function {
|
|
||||||
color: #DD4A68;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.regex,
|
|
||||||
.token.important,
|
|
||||||
.token.variable {
|
|
||||||
color: #e90;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.important,
|
|
||||||
.token.bold {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.token.italic {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.entity {
|
|
||||||
cursor: help;
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
|||||||
/* http://prismjs.com/download.html?themes=prism&languages=clike+javascript+go */
|
|
||||||
self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{};var Prism=function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,t.util.encode(e.content),e.alias):"Array"===t.util.type(e)?e.map(t.util.encode):e.replace(/&/g,"&").replace(/</g,"<").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).match(/\[object (\w+)\]/)[1]},clone:function(e){var n=t.util.type(e);switch(n){case"Object":var a={};for(var r in e)e.hasOwnProperty(r)&&(a[r]=t.util.clone(e[r]));return a;case"Array":return e.map(function(e){return t.util.clone(e)})}return e}},languages:{extend:function(e,n){var a=t.util.clone(t.languages[e]);for(var r in n)a[r]=n[r];return a},insertBefore:function(e,n,a,r){r=r||t.languages;var i=r[e];if(2==arguments.length){a=arguments[1];for(var l in a)a.hasOwnProperty(l)&&(i[l]=a[l]);return i}var s={};for(var o in i)if(i.hasOwnProperty(o)){if(o==n)for(var l in a)a.hasOwnProperty(l)&&(s[l]=a[l]);s[o]=i[o]}return t.languages.DFS(t.languages,function(t,n){n===r[e]&&t!=e&&(this[t]=s)}),r[e]=s},DFS:function(e,n,a){for(var r in e)e.hasOwnProperty(r)&&(n.call(e,r,e[r],a||r),"Object"===t.util.type(e[r])?t.languages.DFS(e[r],n):"Array"===t.util.type(e[r])&&t.languages.DFS(e[r],n,r))}},highlightAll:function(e,n){for(var a,r=document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'),i=0;a=r[i++];)t.highlightElement(a,e===!0,n)},highlightElement:function(a,r,i){for(var l,s,o=a;o&&!e.test(o.className);)o=o.parentNode;if(o&&(l=(o.className.match(e)||[,""])[1],s=t.languages[l]),s){a.className=a.className.replace(e,"").replace(/\s+/g," ")+" language-"+l,o=a.parentNode,/pre/i.test(o.nodeName)&&(o.className=o.className.replace(e,"").replace(/\s+/g," ")+" language-"+l);var u=a.textContent;if(u){u=u.replace(/^(?:\r?\n|\r)/,"");var g={element:a,language:l,grammar:s,code:u};if(t.hooks.run("before-highlight",g),r&&self.Worker){var c=new Worker(t.filename);c.onmessage=function(e){g.highlightedCode=n.stringify(JSON.parse(e.data),l),t.hooks.run("before-insert",g),g.element.innerHTML=g.highlightedCode,i&&i.call(g.element),t.hooks.run("after-highlight",g)},c.postMessage(JSON.stringify({language:g.language,code:g.code}))}else g.highlightedCode=t.highlight(g.code,g.grammar,g.language),t.hooks.run("before-insert",g),g.element.innerHTML=g.highlightedCode,i&&i.call(a),t.hooks.run("after-highlight",g)}}},highlight:function(e,a,r){var i=t.tokenize(e,a);return n.stringify(t.util.encode(i),r)},tokenize:function(e,n){var a=t.Token,r=[e],i=n.rest;if(i){for(var l in i)n[l]=i[l];delete n.rest}e:for(var l in n)if(n.hasOwnProperty(l)&&n[l]){var s=n[l];s="Array"===t.util.type(s)?s:[s];for(var o=0;o<s.length;++o){var u=s[o],g=u.inside,c=!!u.lookbehind,f=0,h=u.alias;u=u.pattern||u;for(var p=0;p<r.length;p++){var d=r[p];if(r.length>e.length)break e;if(!(d instanceof a)){u.lastIndex=0;var m=u.exec(d);if(m){c&&(f=m[1].length);var y=m.index-1+f,m=m[0].slice(f),v=m.length,k=y+v,b=d.slice(0,y+1),w=d.slice(k+1),N=[p,1];b&&N.push(b);var O=new a(l,g?t.tokenize(m,g):m,h);N.push(O),w&&N.push(w),Array.prototype.splice.apply(r,N)}}}}}return r},hooks:{all:{},add:function(e,n){var a=t.hooks.all;a[e]=a[e]||[],a[e].push(n)},run:function(e,n){var a=t.hooks.all[e];if(a&&a.length)for(var r,i=0;r=a[i++];)r(n)}}},n=t.Token=function(e,t,n){this.type=e,this.content=t,this.alias=n};if(n.stringify=function(e,a,r){if("string"==typeof e)return e;if("Array"===t.util.type(e))return e.map(function(t){return n.stringify(t,a,e)}).join("");var i={type:e.type,content:n.stringify(e.content,a,r),tag:"span",classes:["token",e.type],attributes:{},language:a,parent:r};if("comment"==i.type&&(i.attributes.spellcheck="true"),e.alias){var l="Array"===t.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(i.classes,l)}t.hooks.run("wrap",i);var s="";for(var o in i.attributes)s+=o+'="'+(i.attributes[o]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'" '+s+">"+i.content+"</"+i.tag+">"},!self.document)return self.addEventListener?(self.addEventListener("message",function(e){var n=JSON.parse(e.data),a=n.language,r=n.code;self.postMessage(JSON.stringify(t.util.encode(t.tokenize(r,t.languages[a])))),self.close()},!1),self.Prism):self.Prism;var a=document.getElementsByTagName("script");return a=a[a.length-1],a&&(t.filename=a.src,document.addEventListener&&!a.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)),self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism);;
|
|
||||||
Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:/("|')(\\\n|\\?.)*?\1/,"class-name":{pattern:/((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":{pattern:/[a-z0-9_]+\(/i,inside:{punctuation:/\(/}},number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/,operator:/[-+]{1,2}|!|<=?|>=?|={1,3}|&{1,2}|\|?\||\?|\*|\/|~|\^|%/,ignore:/&(lt|gt|amp);/i,punctuation:/[{}[\];(),.:]/};;
|
|
||||||
Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|function|get|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|-?Infinity)\b/,"function":/(?!\d)[a-z0-9_$]+(?=\()/i}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/<script[\w\W]*?>[\w\W]*?<\/script>/i,inside:{tag:{pattern:/<script[\w\W]*?>|<\/script>/i,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.javascript},alias:"language-javascript"}});;
|
|
||||||
Prism.languages.go=Prism.languages.extend("clike",{keyword:/\b(break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/,builtin:/\b(bool|byte|complex(64|128)|error|float(32|64)|rune|string|u?int(8|16|32|64|)|uintptr|append|cap|close|complex|copy|delete|imag|len|make|new|panic|print(ln)?|real|recover)\b/,"boolean":/\b(_|iota|nil|true|false)\b/,operator:/([(){}\[\]]|[*\/%^!]=?|\+[=+]?|-[>=-]?|\|[=|]?|>[=>]?|<(<|[=-])?|==?|&(&|=|^=?)?|\.(\.\.)?|[,;]|:=?)/,number:/\b(-?(0x[a-f\d]+|(\d+\.?\d*|\.\d+)(e[-+]?\d+)?)i?)\b/i,string:/("|'|`)(\\?.|\r|\n)*?\1/}),delete Prism.languages.go["class-name"];;
|
|
@ -1,144 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
function StartRealtime(roomid, timestamp) {
|
|
||||||
StartEpoch(timestamp);
|
|
||||||
StartSSE(roomid);
|
|
||||||
StartForm();
|
|
||||||
}
|
|
||||||
|
|
||||||
function StartForm() {
|
|
||||||
$('#chat-message').focus();
|
|
||||||
$('#chat-form').ajaxForm(function() {
|
|
||||||
$('#chat-message').val('');
|
|
||||||
$('#chat-message').focus();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function StartEpoch(timestamp) {
|
|
||||||
var windowSize = 60;
|
|
||||||
var height = 200;
|
|
||||||
var defaultData = histogram(windowSize, timestamp);
|
|
||||||
|
|
||||||
window.heapChart = $('#heapChart').epoch({
|
|
||||||
type: 'time.area',
|
|
||||||
axes: ['bottom', 'left'],
|
|
||||||
height: height,
|
|
||||||
historySize: 10,
|
|
||||||
data: [
|
|
||||||
{values: defaultData},
|
|
||||||
{values: defaultData}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
window.mallocsChart = $('#mallocsChart').epoch({
|
|
||||||
type: 'time.area',
|
|
||||||
axes: ['bottom', 'left'],
|
|
||||||
height: height,
|
|
||||||
historySize: 10,
|
|
||||||
data: [
|
|
||||||
{values: defaultData},
|
|
||||||
{values: defaultData}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
window.messagesChart = $('#messagesChart').epoch({
|
|
||||||
type: 'time.line',
|
|
||||||
axes: ['bottom', 'left'],
|
|
||||||
height: 240,
|
|
||||||
historySize: 10,
|
|
||||||
data: [
|
|
||||||
{values: defaultData},
|
|
||||||
{values: defaultData},
|
|
||||||
{values: defaultData}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function StartSSE(roomid) {
|
|
||||||
if (!window.EventSource) {
|
|
||||||
alert("EventSource is not enabled in this browser");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var source = new EventSource('/stream/'+roomid);
|
|
||||||
source.addEventListener('message', newChatMessage, false);
|
|
||||||
source.addEventListener('stats', stats, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
function stats(e) {
|
|
||||||
var data = parseJSONStats(e.data);
|
|
||||||
heapChart.push(data.heap);
|
|
||||||
mallocsChart.push(data.mallocs);
|
|
||||||
messagesChart.push(data.messages);
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseJSONStats(e) {
|
|
||||||
var data = jQuery.parseJSON(e);
|
|
||||||
var timestamp = data.timestamp;
|
|
||||||
|
|
||||||
var heap = [
|
|
||||||
{time: timestamp, y: data.HeapInuse},
|
|
||||||
{time: timestamp, y: data.StackInuse}
|
|
||||||
];
|
|
||||||
|
|
||||||
var mallocs = [
|
|
||||||
{time: timestamp, y: data.Mallocs},
|
|
||||||
{time: timestamp, y: data.Frees}
|
|
||||||
];
|
|
||||||
var messages = [
|
|
||||||
{time: timestamp, y: data.Connected},
|
|
||||||
{time: timestamp, y: data.Inbound},
|
|
||||||
{time: timestamp, y: data.Outbound}
|
|
||||||
];
|
|
||||||
|
|
||||||
return {
|
|
||||||
heap: heap,
|
|
||||||
mallocs: mallocs,
|
|
||||||
messages: messages
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function newChatMessage(e) {
|
|
||||||
var data = jQuery.parseJSON(e.data);
|
|
||||||
var nick = data.nick;
|
|
||||||
var message = data.message;
|
|
||||||
var style = rowStyle(nick);
|
|
||||||
var html = "<tr class=\""+style+"\"><td>"+nick+"</td><td>"+message+"</td></tr>";
|
|
||||||
$('#chat').append(html);
|
|
||||||
|
|
||||||
$("#chat-scroll").scrollTop($("#chat-scroll")[0].scrollHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
function histogram(windowSize, timestamp) {
|
|
||||||
var entries = new Array(windowSize);
|
|
||||||
for(var i = 0; i < windowSize; i++) {
|
|
||||||
entries[i] = {time: (timestamp-windowSize+i-1), y:0};
|
|
||||||
}
|
|
||||||
return entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
var entityMap = {
|
|
||||||
"&": "&",
|
|
||||||
"<": "<",
|
|
||||||
">": ">",
|
|
||||||
'"': '"',
|
|
||||||
"'": ''',
|
|
||||||
"/": '/'
|
|
||||||
};
|
|
||||||
|
|
||||||
function rowStyle(nick) {
|
|
||||||
var classes = ['active', 'success', 'info', 'warning', 'danger'];
|
|
||||||
var index = hashCode(nick)%5;
|
|
||||||
return classes[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
function hashCode(s){
|
|
||||||
return Math.abs(s.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0));
|
|
||||||
}
|
|
||||||
|
|
||||||
function escapeHtml(string) {
|
|
||||||
return String(string).replace(/[&<>"'\/]/g, function (s) {
|
|
||||||
return entityMap[s];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
window.StartRealtime = StartRealtime
|
|
@ -1,25 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import "github.com/dustin/go-broadcast"
|
|
||||||
|
|
||||||
var roomChannels = make(map[string]broadcast.Broadcaster)
|
|
||||||
|
|
||||||
func openListener(roomid string) chan interface{} {
|
|
||||||
listener := make(chan interface{})
|
|
||||||
room(roomid).Register(listener)
|
|
||||||
return listener
|
|
||||||
}
|
|
||||||
|
|
||||||
func closeListener(roomid string, listener chan interface{}) {
|
|
||||||
room(roomid).Unregister(listener)
|
|
||||||
close(listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
func room(roomid string) broadcast.Broadcaster {
|
|
||||||
b, ok := roomChannels[roomid]
|
|
||||||
if !ok {
|
|
||||||
b = broadcast.NewBroadcaster(10)
|
|
||||||
roomChannels[roomid] = b
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"html"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func rateLimit(c *gin.Context) {
|
|
||||||
ip := c.ClientIP()
|
|
||||||
value := int(ips.Add(ip, 1))
|
|
||||||
if value%50 == 0 {
|
|
||||||
fmt.Printf("ip: %s, count: %d\n", ip, value)
|
|
||||||
}
|
|
||||||
if value >= 200 {
|
|
||||||
if value%200 == 0 {
|
|
||||||
fmt.Println("ip blocked")
|
|
||||||
}
|
|
||||||
c.Abort()
|
|
||||||
c.String(http.StatusServiceUnavailable, "you were automatically banned :)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func index(c *gin.Context) {
|
|
||||||
c.Redirect(http.StatusMovedPermanently, "/room/hn")
|
|
||||||
}
|
|
||||||
|
|
||||||
func roomGET(c *gin.Context) {
|
|
||||||
roomid := c.Param("roomid")
|
|
||||||
nick := c.Query("nick")
|
|
||||||
if len(nick) < 2 {
|
|
||||||
nick = ""
|
|
||||||
}
|
|
||||||
if len(nick) > 13 {
|
|
||||||
nick = nick[0:12] + "..."
|
|
||||||
}
|
|
||||||
c.HTML(http.StatusOK, "room_login.templ.html", gin.H{
|
|
||||||
"roomid": roomid,
|
|
||||||
"nick": nick,
|
|
||||||
"timestamp": time.Now().Unix(),
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func roomPOST(c *gin.Context) {
|
|
||||||
roomid := c.Param("roomid")
|
|
||||||
nick := c.Query("nick")
|
|
||||||
message := c.PostForm("message")
|
|
||||||
message = strings.TrimSpace(message)
|
|
||||||
|
|
||||||
validMessage := len(message) > 1 && len(message) < 200
|
|
||||||
validNick := len(nick) > 1 && len(nick) < 14
|
|
||||||
if !validMessage || !validNick {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{
|
|
||||||
"status": "failed",
|
|
||||||
"error": "the message or nickname is too long",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
post := gin.H{
|
|
||||||
"nick": html.EscapeString(nick),
|
|
||||||
"message": html.EscapeString(message),
|
|
||||||
}
|
|
||||||
messages.Add("inbound", 1)
|
|
||||||
room(roomid).Submit(post)
|
|
||||||
c.JSON(http.StatusOK, post)
|
|
||||||
}
|
|
||||||
|
|
||||||
func streamRoom(c *gin.Context) {
|
|
||||||
roomid := c.Param("roomid")
|
|
||||||
listener := openListener(roomid)
|
|
||||||
ticker := time.NewTicker(1 * time.Second)
|
|
||||||
users.Add("connected", 1)
|
|
||||||
defer func() {
|
|
||||||
closeListener(roomid, listener)
|
|
||||||
ticker.Stop()
|
|
||||||
users.Add("disconnected", 1)
|
|
||||||
}()
|
|
||||||
|
|
||||||
c.Stream(func(w io.Writer) bool {
|
|
||||||
select {
|
|
||||||
case msg := <-listener:
|
|
||||||
messages.Add("outbound", 1)
|
|
||||||
c.SSEvent("message", msg)
|
|
||||||
case <-ticker.C:
|
|
||||||
c.SSEvent("stats", Stats())
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/manucorporat/stats"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ips = stats.New()
|
|
||||||
messages = stats.New()
|
|
||||||
users = stats.New()
|
|
||||||
mutexStats sync.RWMutex
|
|
||||||
savedStats map[string]uint64
|
|
||||||
)
|
|
||||||
|
|
||||||
func statsWorker() {
|
|
||||||
c := time.Tick(1 * time.Second)
|
|
||||||
var lastMallocs uint64
|
|
||||||
var lastFrees uint64
|
|
||||||
for range c {
|
|
||||||
var stats runtime.MemStats
|
|
||||||
runtime.ReadMemStats(&stats)
|
|
||||||
|
|
||||||
mutexStats.Lock()
|
|
||||||
savedStats = map[string]uint64{
|
|
||||||
"timestamp": uint64(time.Now().Unix()),
|
|
||||||
"HeapInuse": stats.HeapInuse,
|
|
||||||
"StackInuse": stats.StackInuse,
|
|
||||||
"Mallocs": stats.Mallocs - lastMallocs,
|
|
||||||
"Frees": stats.Frees - lastFrees,
|
|
||||||
"Inbound": uint64(messages.Get("inbound")),
|
|
||||||
"Outbound": uint64(messages.Get("outbound")),
|
|
||||||
"Connected": connectedUsers(),
|
|
||||||
}
|
|
||||||
lastMallocs = stats.Mallocs
|
|
||||||
lastFrees = stats.Frees
|
|
||||||
messages.Reset()
|
|
||||||
mutexStats.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func connectedUsers() uint64 {
|
|
||||||
connected := users.Get("connected") - users.Get("disconnected")
|
|
||||||
if connected < 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return uint64(connected)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stats returns savedStats data.
|
|
||||||
func Stats() map[string]uint64 {
|
|
||||||
mutexStats.RLock()
|
|
||||||
defer mutexStats.RUnlock()
|
|
||||||
|
|
||||||
return savedStats
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
all: deps build
|
|
||||||
|
|
||||||
.PHONY: deps
|
|
||||||
deps:
|
|
||||||
go get -d -v github.com/dustin/go-broadcast/...
|
|
||||||
|
|
||||||
.PHONY: build
|
|
||||||
build: deps
|
|
||||||
go build -o realtime-chat main.go rooms.go template.go
|
|
@ -1,59 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"math/rand"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
router := gin.Default()
|
|
||||||
router.SetHTMLTemplate(html)
|
|
||||||
|
|
||||||
router.GET("/room/:roomid", roomGET)
|
|
||||||
router.POST("/room/:roomid", roomPOST)
|
|
||||||
router.DELETE("/room/:roomid", roomDELETE)
|
|
||||||
router.GET("/stream/:roomid", stream)
|
|
||||||
|
|
||||||
router.Run(":8080")
|
|
||||||
}
|
|
||||||
|
|
||||||
func stream(c *gin.Context) {
|
|
||||||
roomid := c.Param("roomid")
|
|
||||||
listener := openListener(roomid)
|
|
||||||
defer closeListener(roomid, listener)
|
|
||||||
|
|
||||||
c.Stream(func(w io.Writer) bool {
|
|
||||||
c.SSEvent("message", <-listener)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func roomGET(c *gin.Context) {
|
|
||||||
roomid := c.Param("roomid")
|
|
||||||
userid := fmt.Sprint(rand.Int31())
|
|
||||||
c.HTML(http.StatusOK, "chat_room", gin.H{
|
|
||||||
"roomid": roomid,
|
|
||||||
"userid": userid,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func roomPOST(c *gin.Context) {
|
|
||||||
roomid := c.Param("roomid")
|
|
||||||
userid := c.PostForm("user")
|
|
||||||
message := c.PostForm("message")
|
|
||||||
room(roomid).Submit(userid + ": " + message)
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
|
||||||
"status": "success",
|
|
||||||
"message": message,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func roomDELETE(c *gin.Context) {
|
|
||||||
roomid := c.Param("roomid")
|
|
||||||
deleteBroadcast(roomid)
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import "github.com/dustin/go-broadcast"
|
|
||||||
|
|
||||||
var roomChannels = make(map[string]broadcast.Broadcaster)
|
|
||||||
|
|
||||||
func openListener(roomid string) chan interface{} {
|
|
||||||
listener := make(chan interface{})
|
|
||||||
room(roomid).Register(listener)
|
|
||||||
return listener
|
|
||||||
}
|
|
||||||
|
|
||||||
func closeListener(roomid string, listener chan interface{}) {
|
|
||||||
room(roomid).Unregister(listener)
|
|
||||||
close(listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteBroadcast(roomid string) {
|
|
||||||
b, ok := roomChannels[roomid]
|
|
||||||
if ok {
|
|
||||||
b.Close()
|
|
||||||
delete(roomChannels, roomid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func room(roomid string) broadcast.Broadcaster {
|
|
||||||
b, ok := roomChannels[roomid]
|
|
||||||
if !ok {
|
|
||||||
b = broadcast.NewBroadcaster(10)
|
|
||||||
roomChannels[roomid] = b
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import "html/template"
|
|
||||||
|
|
||||||
var html = template.Must(template.New("chat_room").Parse(`
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>{{.roomid}}</title>
|
|
||||||
<link rel="stylesheet" type="text/css" href="http://meyerweb.com/eric/tools/css/reset/reset.css">
|
|
||||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.js"></script>
|
|
||||||
<script src="http://malsup.github.com/jquery.form.js"></script>
|
|
||||||
<script>
|
|
||||||
$('#message_form').focus();
|
|
||||||
$(document).ready(function() {
|
|
||||||
// bind 'myForm' and provide a simple callback function
|
|
||||||
$('#myForm').ajaxForm(function() {
|
|
||||||
$('#message_form').val('');
|
|
||||||
$('#message_form').focus();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!!window.EventSource) {
|
|
||||||
var source = new EventSource('/stream/{{.roomid}}');
|
|
||||||
source.addEventListener('message', function(e) {
|
|
||||||
$('#messages').append(e.data + "</br>");
|
|
||||||
$('html, body').animate({scrollTop:$(document).height()}, 'slow');
|
|
||||||
|
|
||||||
}, false);
|
|
||||||
} else {
|
|
||||||
alert("NOT SUPPORTED");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Welcome to {{.roomid}} room</h1>
|
|
||||||
<div id="messages"></div>
|
|
||||||
<form id="myForm" action="/room/{{.roomid}}" method="post">
|
|
||||||
User: <input id="user_form" name="user" value="{{.userid}}">
|
|
||||||
Message: <input id="message_form" name="message">
|
|
||||||
<input type="submit" value="Submit">
|
|
||||||
</form>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`))
|
|
@ -1,50 +0,0 @@
|
|||||||
## Struct level validations
|
|
||||||
|
|
||||||
Validations can also be registered at the `struct` level when field level validations
|
|
||||||
don't make much sense. This can also be used to solve cross-field validation elegantly.
|
|
||||||
Additionally, it can be combined with tag validations. Struct Level validations run after
|
|
||||||
the structs tag validations.
|
|
||||||
|
|
||||||
### Example requests
|
|
||||||
|
|
||||||
```shell
|
|
||||||
# Validation errors are generated for struct tags as well as at the struct level
|
|
||||||
$ curl -s -X POST http://localhost:8085/user \
|
|
||||||
-H 'content-type: application/json' \
|
|
||||||
-d '{}' | jq
|
|
||||||
{
|
|
||||||
"error": "Key: 'User.Email' Error:Field validation for 'Email' failed on the 'required' tag\nKey: 'User.FirstName' Error:Field validation for 'FirstName' failed on the 'fnameorlname' tag\nKey: 'User.LastName' Error:Field validation for 'LastName' failed on the 'fnameorlname' tag",
|
|
||||||
"message": "User validation failed!"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Validation fails at the struct level because neither first name nor last name are present
|
|
||||||
$ curl -s -X POST http://localhost:8085/user \
|
|
||||||
-H 'content-type: application/json' \
|
|
||||||
-d '{"email": "george@vandaley.com"}' | jq
|
|
||||||
{
|
|
||||||
"error": "Key: 'User.FirstName' Error:Field validation for 'FirstName' failed on the 'fnameorlname' tag\nKey: 'User.LastName' Error:Field validation for 'LastName' failed on the 'fnameorlname' tag",
|
|
||||||
"message": "User validation failed!"
|
|
||||||
}
|
|
||||||
|
|
||||||
# No validation errors when either first name or last name is present
|
|
||||||
$ curl -X POST http://localhost:8085/user \
|
|
||||||
-H 'content-type: application/json' \
|
|
||||||
-d '{"fname": "George", "email": "george@vandaley.com"}'
|
|
||||||
{"message":"User validation successful."}
|
|
||||||
|
|
||||||
$ curl -X POST http://localhost:8085/user \
|
|
||||||
-H 'content-type: application/json' \
|
|
||||||
-d '{"lname": "Contanza", "email": "george@vandaley.com"}'
|
|
||||||
{"message":"User validation successful."}
|
|
||||||
|
|
||||||
$ curl -X POST http://localhost:8085/user \
|
|
||||||
-H 'content-type: application/json' \
|
|
||||||
-d '{"fname": "George", "lname": "Costanza", "email": "george@vandaley.com"}'
|
|
||||||
{"message":"User validation successful."}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Useful links
|
|
||||||
|
|
||||||
- Validator docs - https://godoc.org/gopkg.in/go-playground/validator.v8#Validate.RegisterStructValidation
|
|
||||||
- Struct level example - https://github.com/go-playground/validator/blob/v8.18.2/examples/struct-level/struct_level.go
|
|
||||||
- Validator release notes - https://github.com/go-playground/validator/releases/tag/v8.7
|
|
@ -1,64 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/gin-gonic/gin/binding"
|
|
||||||
validator "gopkg.in/go-playground/validator.v8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// User contains user information.
|
|
||||||
type User struct {
|
|
||||||
FirstName string `json:"fname"`
|
|
||||||
LastName string `json:"lname"`
|
|
||||||
Email string `binding:"required,email"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// UserStructLevelValidation contains custom struct level validations that don't always
|
|
||||||
// make sense at the field validation level. For example, this function validates that either
|
|
||||||
// FirstName or LastName exist; could have done that with a custom field validation but then
|
|
||||||
// would have had to add it to both fields duplicating the logic + overhead, this way it's
|
|
||||||
// only validated once.
|
|
||||||
//
|
|
||||||
// NOTE: you may ask why wouldn't not just do this outside of validator. Doing this way
|
|
||||||
// hooks right into validator and you can combine with validation tags and still have a
|
|
||||||
// common error output format.
|
|
||||||
func UserStructLevelValidation(v *validator.Validate, structLevel *validator.StructLevel) {
|
|
||||||
user := structLevel.CurrentStruct.Interface().(User)
|
|
||||||
|
|
||||||
if len(user.FirstName) == 0 && len(user.LastName) == 0 {
|
|
||||||
structLevel.ReportError(
|
|
||||||
reflect.ValueOf(user.FirstName), "FirstName", "fname", "fnameorlname",
|
|
||||||
)
|
|
||||||
structLevel.ReportError(
|
|
||||||
reflect.ValueOf(user.LastName), "LastName", "lname", "fnameorlname",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// plus can to more, even with different tag than "fnameorlname"
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
route := gin.Default()
|
|
||||||
|
|
||||||
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
|
|
||||||
v.RegisterStructValidation(UserStructLevelValidation, User{})
|
|
||||||
}
|
|
||||||
|
|
||||||
route.POST("/user", validateUser)
|
|
||||||
route.Run(":8085")
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateUser(c *gin.Context) {
|
|
||||||
var u User
|
|
||||||
if err := c.ShouldBindJSON(&u); err == nil {
|
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "User validation successful."})
|
|
||||||
} else {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{
|
|
||||||
"message": "User validation failed!",
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"html/template"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func formatAsDate(t time.Time) string {
|
|
||||||
year, month, day := t.Date()
|
|
||||||
return fmt.Sprintf("%d%02d/%02d", year, month, day)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
router := gin.Default()
|
|
||||||
router.Delims("{[{", "}]}")
|
|
||||||
router.SetFuncMap(template.FuncMap{
|
|
||||||
"formatAsDate": formatAsDate,
|
|
||||||
})
|
|
||||||
router.LoadHTMLFiles("../../testdata/template/raw.tmpl")
|
|
||||||
|
|
||||||
router.GET("/raw", func(c *gin.Context) {
|
|
||||||
c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{
|
|
||||||
"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
router.Run(":8080")
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
router := gin.Default()
|
|
||||||
// Set a lower memory limit for multipart forms (default is 32 MiB)
|
|
||||||
router.MaxMultipartMemory = 8 << 20 // 8 MiB
|
|
||||||
router.Static("/", "./public")
|
|
||||||
router.POST("/upload", func(c *gin.Context) {
|
|
||||||
name := c.PostForm("name")
|
|
||||||
email := c.PostForm("email")
|
|
||||||
|
|
||||||
// Multipart form
|
|
||||||
form, err := c.MultipartForm()
|
|
||||||
if err != nil {
|
|
||||||
c.String(http.StatusBadRequest, fmt.Sprintf("get form err: %s", err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
files := form.File["files"]
|
|
||||||
|
|
||||||
for _, file := range files {
|
|
||||||
filename := filepath.Base(file.Filename)
|
|
||||||
if err := c.SaveUploadedFile(file, filename); err != nil {
|
|
||||||
c.String(http.StatusBadRequest, fmt.Sprintf("upload file err: %s", err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.String(http.StatusOK, fmt.Sprintf("Uploaded successfully %d files with fields name=%s and email=%s.", len(files), name, email))
|
|
||||||
})
|
|
||||||
router.Run(":8080")
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Multiple file upload</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Upload multiple files with fields</h1>
|
|
||||||
|
|
||||||
<form action="/upload" method="post" enctype="multipart/form-data">
|
|
||||||
Name: <input type="text" name="name"><br>
|
|
||||||
Email: <input type="email" name="email"><br>
|
|
||||||
Files: <input type="file" name="files" multiple><br><br>
|
|
||||||
<input type="submit" value="Submit">
|
|
||||||
</form>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,36 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
router := gin.Default()
|
|
||||||
// Set a lower memory limit for multipart forms (default is 32 MiB)
|
|
||||||
router.MaxMultipartMemory = 8 << 20 // 8 MiB
|
|
||||||
router.Static("/", "./public")
|
|
||||||
router.POST("/upload", func(c *gin.Context) {
|
|
||||||
name := c.PostForm("name")
|
|
||||||
email := c.PostForm("email")
|
|
||||||
|
|
||||||
// Source
|
|
||||||
file, err := c.FormFile("file")
|
|
||||||
if err != nil {
|
|
||||||
c.String(http.StatusBadRequest, fmt.Sprintf("get form err: %s", err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
filename := filepath.Base(file.Filename)
|
|
||||||
if err := c.SaveUploadedFile(file, filename); err != nil {
|
|
||||||
c.String(http.StatusBadRequest, fmt.Sprintf("upload file err: %s", err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.String(http.StatusOK, fmt.Sprintf("File %s uploaded successfully with fields name=%s and email=%s.", file.Filename, name, email))
|
|
||||||
})
|
|
||||||
router.Run(":8080")
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Single file upload</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Upload single file with fields</h1>
|
|
||||||
|
|
||||||
<form action="/upload" method="post" enctype="multipart/form-data">
|
|
||||||
Name: <input type="text" name="name"><br>
|
|
||||||
Email: <input type="email" name="email"><br>
|
|
||||||
Files: <input type="file" name="file"><br><br>
|
|
||||||
<input type="submit" value="Submit">
|
|
||||||
</form>
|
|
||||||
</body>
|
|
24
go.mod
24
go.mod
@ -1,32 +1,18 @@
|
|||||||
module github.com/gin-gonic/gin
|
module github.com/gin-gonic/gin
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gin-contrib/sse v0.0.0-20190124093953-61b50c2ef482
|
github.com/gin-contrib/sse v0.0.0-20190125020943-a7658810eb74
|
||||||
github.com/golang/protobuf v1.2.0
|
github.com/golang/protobuf v1.2.0
|
||||||
github.com/json-iterator/go v1.1.5
|
github.com/json-iterator/go v1.1.5
|
||||||
github.com/mattn/go-isatty v0.0.4
|
github.com/mattn/go-isatty v0.0.4
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||||
github.com/stretchr/testify v1.3.0
|
github.com/stretchr/testify v1.3.0
|
||||||
github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2
|
github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43
|
||||||
golang.org/x/net v0.0.0-20190119204137-ed066c81e75e
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect
|
||||||
golang.org/x/sys v0.0.0-20190124100055-b90733256f2e // indirect
|
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0 // indirect
|
||||||
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
|
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
|
||||||
gopkg.in/go-playground/validator.v8 v8.18.2
|
gopkg.in/go-playground/validator.v8 v8.18.2
|
||||||
gopkg.in/yaml.v2 v2.2.2
|
gopkg.in/yaml.v2 v2.2.2
|
||||||
)
|
)
|
||||||
|
|
||||||
exclude (
|
|
||||||
github.com/campoy/embedmd v0.0.0-20181127031020-97c13d6e4160
|
|
||||||
github.com/client9/misspell v0.3.4
|
|
||||||
github.com/dustin/go-broadcast v0.0.0-20171205050544-f664265f5a66
|
|
||||||
github.com/gin-gonic/autotls v0.0.0-20190119125636-0b5f4fc15768
|
|
||||||
github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15
|
|
||||||
github.com/manucorporat/stats v0.0.0-20180402194714-3ba42d56d227
|
|
||||||
github.com/newrelic/go-agent v2.5.0+incompatible
|
|
||||||
github.com/thinkerou/favicon v0.1.0
|
|
||||||
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b
|
|
||||||
golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1
|
|
||||||
google.golang.org/grpc v1.18.0
|
|
||||||
)
|
|
||||||
|
42
go.sum
42
go.sum
@ -1,19 +1,10 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
github.com/campoy/embedmd v0.0.0-20181127031020-97c13d6e4160 h1:HJpuhXOHC4EkXDARsLjmXAV9FhlY6qFDnKI/MJM6eoE=
|
|
||||||
github.com/campoy/embedmd v0.0.0-20181127031020-97c13d6e4160/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8=
|
|
||||||
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
|
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/gin-contrib/sse v0.0.0-20190124093953-61b50c2ef482 h1:iOz5sIQUvuOlpiC7Q6+MmJQpWnlneYX98QIGf+2m50Y=
|
github.com/gin-contrib/sse v0.0.0-20190125020943-a7658810eb74 h1:FaI7wNyesdMBSkIRVUuEEYEvmzufs7EqQvRAxfEXGbQ=
|
||||||
github.com/gin-contrib/sse v0.0.0-20190124093953-61b50c2ef482/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
github.com/gin-contrib/sse v0.0.0-20190125020943-a7658810eb74/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
|
||||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
@ -22,28 +13,16 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2 h1:EICbibRW4JNKMcY+LsWmuwob+CRS1BmdRdjphAm9mH4=
|
github.com/ugorji/go v1.1.2 h1:JON3E2/GPW2iDNGoSAusl1KDf5TRQ8k8q7Tp097pZGs=
|
||||||
github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
github.com/ugorji/go v1.1.2/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3 h1:x/bBzNauLQAlE3fLku/xy92Y8QwKX5HZymrMz2IiKFc=
|
github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43 h1:BasDe+IErOQKrMVXab7UayvSlIpiyGwRvuX3EKYY7UA=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd h1:HuTn7WObtcDo9uEEU7rEqL0jYthdXAmZ6PP+meazmaU=
|
||||||
golang.org/x/net v0.0.0-20190119204137-ed066c81e75e h1:MDa3fSUp6MdYHouVmCCNz/zaH2a6CRcxY3VhT/K3C5Q=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190119204137-ed066c81e75e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0 h1:bzeyCHgoAyjZjAhvTpks+qM7sdlh4cCSitmXeCEO3B4=
|
||||||
golang.org/x/sys v0.0.0-20190124100055-b90733256f2e h1:3GIlrlVLfkoipSReOMNAgApI0ajnalyLa/EZHHca/XI=
|
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/grpc v1.18.0 h1:IZl7mfBGfbhYx2p2rKRtYgDFw6SBz+kclmxYrCksPPA=
|
|
||||||
google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||||
@ -51,4 +30,3 @@ gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2G
|
|||||||
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
|
6
vendor/vendor.json
vendored
6
vendor/vendor.json
vendored
@ -40,6 +40,12 @@
|
|||||||
"version": "v0.0",
|
"version": "v0.0",
|
||||||
"versionExact": "v0.0.4"
|
"versionExact": "v0.0.4"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "LuFv4/jlrmFNnDb/5SCSEPAM9vU=",
|
||||||
|
"path": "github.com/pmezard/go-difflib/difflib",
|
||||||
|
"revision": "5d4384ee4fb2527b0a1256a821ebfc92f91efefc",
|
||||||
|
"revisionTime": "2018-12-26T10:54:42Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "c6pbpF7eowwO59phRTpF8cQ80Z0=",
|
"checksumSHA1": "c6pbpF7eowwO59phRTpF8cQ80Z0=",
|
||||||
"path": "github.com/stretchr/testify/assert",
|
"path": "github.com/stretchr/testify/assert",
|
||||||
|
Loading…
Reference in New Issue
Block a user