1
0
mirror of https://github.com/labstack/echo.git synced 2025-03-03 14:52:47 +02:00

Moved examples to recipes

Signed-off-by: Vishal Rana <vr@labstack.com>
This commit is contained in:
Vishal Rana 2015-10-08 13:54:31 -07:00
parent 5587974933
commit 0176385654
44 changed files with 410 additions and 914 deletions

3
.gitattributes vendored
View File

@ -14,8 +14,7 @@
*.json text eol=lf
LICENSE text eol=lf
# Exclude `website` and `examples` from Github's language statistics
# Exclude `website` and `recipes` from Github's language statistics
# https://github.com/github/linguist#using-gitattributes
examples/* linguist-documentation
recipes/* linguist-documentation
website/* linguist-documentation

View File

@ -1,4 +1,4 @@
# [Echo](http://echo.labstack.com) [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/labstack/echo) [![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/labstack/echo/master/LICENSE) [![Build Status](http://img.shields.io/travis/labstack/echo.svg?style=flat-square)](https://travis-ci.org/labstack/echo) [![Coverage Status](http://img.shields.io/coveralls/labstack/echo.svg?style=flat-square)](https://coveralls.io/r/labstack/echo) [![Join the chat at https://gitter.im/labstack/echo](https://img.shields.io/badge/gitter-join%20chat-brightgreen.svg?style=flat-square)](https://gitter.im/labstack/echo)
# [Echo](http://labstack.com/echo) [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/labstack/echo) [![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/labstack/echo/master/LICENSE) [![Build Status](http://img.shields.io/travis/labstack/echo.svg?style=flat-square)](https://travis-ci.org/labstack/echo) [![Coverage Status](http://img.shields.io/coveralls/labstack/echo.svg?style=flat-square)](https://coveralls.io/r/labstack/echo) [![Join the chat at https://gitter.im/labstack/echo](https://img.shields.io/badge/gitter-join%20chat-brightgreen.svg?style=flat-square)](https://gitter.im/labstack/echo)
A fast and unfancy micro web framework for Go.
@ -87,17 +87,9 @@ BenchmarkZeus_GithubAll 2000 748827 ns/op 30068
$ go get github.com/labstack/echo
```
## [Recipes](https://github.com/labstack/echo/tree/master/recipes)
## [Recipes](http://labstack.com/echo/recipes/hello-world)
- [File Upload](http://echo.labstack.com/recipes/file-upload)
- [Streaming File Upload](http://echo.labstack.com/recipes/streaming-file-upload)
- [Streaming Response](http://echo.labstack.com/recipes/streaming-response)
- [WebSocket](http://echo.labstack.com/recipes/websocket)
- [Subdomains](http://echo.labstack.com/recipes/subdomains)
- [JWT Authentication](http://echo.labstack.com/recipes/jwt-authentication)
- [Graceful Shutdown](http://echo.labstack.com/recipes/graceful-shutdown)
##[Guide](http://echo.labstack.com/guide)
## [Guide](http://labstack.com/echo/guide)
## Echo System

View File

@ -43,7 +43,7 @@ func TestEcho(t *testing.T) {
func TestEchoIndex(t *testing.T) {
e := New()
e.Index("examples/website/public/index.html")
e.Index("recipes/website/public/index.html")
c, b := request(GET, "/", e)
assert.Equal(t, http.StatusOK, c)
assert.NotEmpty(t, b)
@ -51,7 +51,7 @@ func TestEchoIndex(t *testing.T) {
func TestEchoFavicon(t *testing.T) {
e := New()
e.Favicon("examples/website/public/favicon.ico")
e.Favicon("recipes/website/public/favicon.ico")
c, b := request(GET, "/favicon.ico", e)
assert.Equal(t, http.StatusOK, c)
assert.NotEmpty(t, b)
@ -61,23 +61,23 @@ func TestEchoStatic(t *testing.T) {
e := New()
// OK
e.Static("/scripts", "examples/website/public/scripts")
e.Static("/scripts", "recipes/website/public/scripts")
c, b := request(GET, "/scripts/main.js", e)
assert.Equal(t, http.StatusOK, c)
assert.NotEmpty(t, b)
// No file
e.Static("/scripts", "examples/website/public/scripts")
e.Static("/scripts", "recipes/website/public/scripts")
c, _ = request(GET, "/scripts/index.js", e)
assert.Equal(t, http.StatusNotFound, c)
// Directory
e.Static("/scripts", "examples/website/public/scripts")
e.Static("/scripts", "recipes/website/public/scripts")
c, _ = request(GET, "/scripts", e)
assert.Equal(t, http.StatusForbidden, c)
// Directory with index.html
e.Static("/", "examples/website/public")
e.Static("/", "recipes/website/public")
c, r := request(GET, "/", e)
assert.Equal(t, http.StatusOK, c)
assert.Equal(t, true, strings.HasPrefix(r, "<!doctype html>"))

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -5,16 +5,16 @@
"canonifyurls": true,
"menu": {
"main": [{
"Name": "Guide",
"Pre": "<i class='fa fa-book'></i>",
"Weight": 10,
"Identifier": "guide",
"URL": "guide"
"side": [{
"name": "Guide",
"pre": "<i class='fa fa-book'></i>",
"weight": 1,
"identifier": "guide",
"url": "guide"
}, {
"Name": "Recipes",
"Pre": "<i class='fa fa-code'></i>",
"Weight": 20,
"Weight": 2,
"Identifier": "recipes",
"URL": "recipes"
}]

View File

@ -1,7 +1,7 @@
---
title: Customization
menu:
main:
side:
parent: guide
weight: 2
---

View File

@ -1,7 +1,7 @@
---
title: Error Handling
menu:
main:
side:
parent: guide
weight: 7
---

View File

@ -1,7 +1,7 @@
---
title: Installation
menu:
main:
side:
parent: guide
weight: 1
---

View File

@ -1,7 +1,7 @@
---
title: Middleware
menu:
main:
side:
parent: guide
weight: 4
---
@ -64,4 +64,4 @@ control to the centralized
e.Use(mw.Recover())
```
[Examples](https://github.com/labstack/echo/tree/master/examples/middleware)
### [Recipes](https://github.com/labstack/echo/tree/master/recipes/middleware)

View File

@ -1,7 +1,7 @@
---
title: Request
menu:
main:
side:
parent: guide
weight: 5
---

View File

@ -1,7 +1,7 @@
---
title: Response
menu:
main:
side:
parent: guide
weight: 6
---

View File

@ -1,7 +1,7 @@
---
title: Routing
menu:
main:
side:
parent: guide
weight: 3
---

View File

@ -105,7 +105,7 @@ Hello, World! on the page.
### Next?
- Browse [recipes](https://github.com/labstack/echo/tree/master/recipes)
- Head over to [Guide]({{< relref "guide/installation.md" >}})
- Head over to [guide]({{< relref "guide/installation.md" >}})
## Contribute

View File

@ -0,0 +1,19 @@
---
title: CRUD
menu:
side:
parent: recipes
weight: 2
---
### Server
`server.go`
{{< embed "crud/server.go" >}}
### Maintainers
- [vishr](https://github.com/vishr)
### [Source Code](https://github.com/labstack/echo/blob/master/recipes/crud)

View File

@ -1,8 +1,9 @@
---
title: File Upload
menu:
main:
side:
parent: recipes
weight: 7
---
- Multipart/form-data file upload
@ -15,89 +16,16 @@ us an option to specify the maximum memory used while parsing the request body.
`server.go`
```go
package main
import (
"io"
"os"
"net/http"
"github.com/labstack/echo"
mw "github.com/labstack/echo/middleware"
)
func upload(c *echo.Context) error {
req := c.Request()
req.ParseMultipartForm(16 << 20) // Max memory 16 MiB
// Read form fields
name := c.Form("name")
email := c.Form("email")
// Read files
files := req.MultipartForm.File["files"]
for _, f := range files {
// Source file
src, err := f.Open()
if err != nil {
return err
}
defer src.Close()
// Destination file
dst, err := os.Create(f.Filename)
if err != nil {
return err
}
defer dst.Close()
if _, err = io.Copy(dst, src); err != nil {
return err
}
}
return c.String(http.StatusOK, "Thank You! %s <%s>, %d files uploaded successfully.",
name, email, len(files))
}
func main() {
e := echo.New()
e.Use(mw.Logger())
e.Use(mw.Recover())
e.Static("/", "public")
e.Post("/upload", upload)
e.Run(":1323")
}
```
{{< embed "file-upload/server.go" >}}
### Client
`index.html`
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>File Upload</title>
</head>
<body>
<h1>Upload Files</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>
```
{{< embed "file-upload/public/index.html" >}}
### Maintainers
- [vishr](http://github.com/vishr)
- [vishr](https://github.com/vishr)
### [Source Code](https://github.com/labstack/echo/blob/master/recipes/file-upload)

View File

@ -1,9 +1,9 @@
---
title: Google App Engine
draft: false
menu:
main:
side:
parent: recipes
weight: 12
---
Google App Engine (GAE) provides a range of hosting options from pure PaaS (App Engine Classic)
@ -30,12 +30,8 @@ and because it is a `var` this will happen _before_ any `init()` functions run -
use to connect our handlers later.
`app.go`
```go
package main
// referecnce our echo instance and create it early
var e = createMux()
```
{{< embed "google-app-engine/app.go" >}}
A separate source file contains the function to create the Echo instance and add the static
file handlers and middleware. Note the build tag on the first line which says to use this when _not_
@ -43,33 +39,8 @@ bulding with appengine or appenginevm tags (which thoese platforms automatically
have the `main()` function to start serving our app as normal. This should all be very familiar.
`app-standalone.go`
```go
// +build !appengine,!appenginevm
package main
import (
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
)
func createMux() *echo.Echo {
e := echo.New()
e.Use(middleware.Recover())
e.Use(middleware.Logger())
e.Use(middleware.Gzip())
e.Index("public/index.html")
e.Static("/public", "public")
return e
}
func main() {
e.Run(":8080")
}
```
{{< embed "google-app-engine/app-standalone.go" >}}
The handler-wireup that would normally also be a part of this Echo setup moves to separate files which
take advantage of the ability to have multiple `init()` functions which run _after_ the `e` Echo var is
@ -79,26 +50,8 @@ per REST endpoint, often with a higher-level `api` group created that they attac
Echo instance directly (so things like CORS middleware can be added at this higher common-level).
`some-endpoint.go`
```go
package main
import "github.com/labstack/echo"
func init() {
// our endpoint registers itself with the echo instance
// and could add it's own middleware if it needed
g := e.Group("/some-endpoint")
g.Get("", listHandler)
g.Get("/:id", getHandler)
g.Patch("/:id", updateHandler)
g.Post("/:id", addHandler)
g.Put("/:id", replaceHandler)
g.Delete("/:id", deleteHandler)
}
func listHandler(c *echo.Context) error {
// actual handler implementations ...
```
{{< embed "google-app-engine/some-endpoint.go" >}}
If we run our app it should execute as it did before when everything was in one file although we have
at least gained the ability to organize our handlers a little more cleanly.
@ -125,45 +78,9 @@ The yaml file also contains other options to control instance size and auto-scal
deployment freedom you would likely have separate `app-classic.yaml` and `app-vm.yaml` files and
this can help when making the transition from AppEngine Classic to Managed VMs.
`app.yaml`
```yaml
application: my-application-id # defined when you create your app using google dev console
module: default # see https://cloud.google.com/appengine/docs/go/
version: alpha # you can run multiple versions of an app and A/B test
runtime: go # see https://cloud.google.com/appengine/docs/go/
api_version: go1 # used when appengine supports different go versions
`app-engine.yaml`
default_expiration: "1d" # for CDN serving of static files (use url versioning if long!)
handlers:
# all the static files that we normally serve ourselves are defined here and Google will handle
# serving them for us from it's own CDN / edge locations. For all the configuration options see:
# https://cloud.google.com/appengine/docs/go/config/appconfig#Go_app_yaml_Static_file_handlers
- url: /
mime_type: text/html
static_files: public/index.html
upload: public/index.html
- url: /favicon.ico
mime_type: image/x-icon
static_files: public/favicon.ico
upload: public/favicon.ico
- url: /scripts
mime_type: text/javascript
static_dir: public/scripts
# static files normally don't touch the server that the app runs on but server-side template files
# needs to be readable by the app. The application_readable option makes sure they are available as
# part of the app deployment onto the instance.
- url: /templates
static_dir: /templates
application_readable: true
# finally, we route all other requests to our application. The script name just means "the go app"
- url: /.*
script: _go_app
```
{{< embed "google-app-engine/app-engine.yaml" >}}
#### Router configuration
@ -177,26 +94,8 @@ that GAE classic provides a wrapper to handle serving the app so instead of a `m
we run the server, we instead wire up the router to the default `http.Handler` instead.
`app-engine.go`
```go
// +build appengine
package main
import (
"net/http"
"github.com/labstack/echo"
)
func createMux() *echo.Echo {
e := echo.New()
// note: we don't need to provide the middleware or static handlers, that's taken care of by the platform
// app engine has it's own "main" wrapper - we just need to hook echo into the default handler
http.Handle("/", e)
return e
}
```
{{< embed "google-app-engine/app-engine.go" >}}
Managed VMs are slightly different. They are expected to respond to requests on port 8080 as well
as special health-check requests used by the service to detect if an instance is still running in
@ -204,37 +103,8 @@ order to provide automated failover and instance replacement. The `google.golang
package provides this for us so we have a slightly different version for Managed VMs:
`app-managed.go`
```go
// +build appenginevm
package main
import (
"runtime"
"net/http"
"github.com/labstack/echo"
"google.golang.org/appengine"
)
func createMux() *echo.Echo {
// we're in a container on a Google Compute Engine instance so are not sandboxed anymore ...
runtime.GOMAXPROCS(runtime.NumCPU())
e := echo.New()
// note: we don't need to provide the middleware or static handlers
// for the appengine vm version - that's taken care of by the platform
return e
}
func main() {
// the appengine package provides a convenient method to handle the health-check requests
// and also run the app on the correct port. We just need to add Echo to the default handler
http.Handle("/", e)
appengine.Main()
}
```
{{< embed "google-app-engine/app-managed.go" >}}
So now we have three different configurations. We can build and run our app as normal so it can
be executed locally, on a full Compute Engine instance or any other traditional hosting provider

View File

@ -1,60 +1,22 @@
---
title: Graceful Shutdown
menu:
main:
side:
parent: recipes
weight: 13
---
### With [graceful](https://github.com/tylerb/graceful)
`server.go`
```go
package main
import (
"net/http"
"time"
"github.com/labstack/echo"
"github.com/tylerb/graceful"
)
func main() {
// Setup
e := echo.New()
e.Get("/", func(c *echo.Context) error {
return c.String(http.StatusOK, "Sue sews rose on slow jor crows nose")
})
graceful.ListenAndServe(e.Server(":1323"), 5*time.Second)
}
```
{{< embed "graceful-shutdown/grace/server.go" >}}
### With [grace](https://github.com/facebookgo/grace)
`server.go`
```go
package main
import (
"net/http"
"github.com/facebookgo/grace/gracehttp"
"github.com/labstack/echo"
)
func main() {
// Setup
e := echo.New()
e.Get("/", func(c *echo.Context) error {
return c.String(http.StatusOK, "Six sick bricks tick")
})
gracehttp.Serve(e.Server(":1323"))
}
```
{{< embed "graceful-shutdown/graceful/server.go" >}}
### Maintainers

View File

@ -0,0 +1,19 @@
---
title: Hello World
menu:
side:
parent: recipes
weight: 1
---
### Server
`server.go`
{{< embed "hello-world/server.go" >}}
### Maintainers
- [vishr](https://github.com/vishr)
### [Source Code](https://github.com/labstack/echo/blob/master/recipes/hello-world)

View File

@ -1,8 +1,9 @@
---
title: JSONP
menu:
main:
side:
parent: recipes
weight: 6
---
JSONP is a method that allows cross-domain server calls. You can read more about it at the JSON versus JSONP Tutorial.
@ -11,85 +12,16 @@ JSONP is a method that allows cross-domain server calls. You can read more about
`server.go`
```go
package main
import (
"math/rand"
"net/http"
"time"
"github.com/labstack/echo"
)
func main() {
// Setup
e := echo.New()
e.ServeDir("/", "public")
e.Get("/jsonp", func(c *echo.Context) error {
callback := c.Query("callback")
var content struct {
Response string `json:"response"`
Timestamp time.Time `json:"timestamp"`
Random int `json:"random"`
}
content.Response = "Sent via JSONP"
content.Timestamp = time.Now().UTC()
content.Random = rand.Intn(1000)
return c.JSONP(http.StatusOK, callback, &content)
})
// Start server
e.Run(":3999")
}
```
{{< embed "jsonp/server.go" >}}
### Client
`index.html`
```html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<title>JSONP</title>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript">
var host_prefix = 'http://localhost:3999';
$(document).ready(function() {
// JSONP version - add 'callback=?' to the URL - fetch the JSONP response to the request
$("#jsonp-button").click(function(e) {
e.preventDefault();
// The only difference on the client end is the addition of 'callback=?' to the URL
var url = host_prefix + '/jsonp?callback=?';
$.getJSON(url, function(jsonp) {
console.log(jsonp);
$("#jsonp-response").html(JSON.stringify(jsonp, null, 2));
});
});
});
</script>
</head>
<body>
<div class="container" style="margin-top: 50px;">
<input type="button" class="btn btn-primary btn-lg" id="jsonp-button" value="Get JSONP response">
<p>
<pre id="jsonp-response"></pre>
</p>
</div>
</body>
</html>
```
{{< embed "jsonp/public/index.html" >}}
### Maintainers
- [willf](http://github.com/willf)
- [willf](https://github.com/willf)
### [Source Code](https://github.com/labstack/echo/blob/master/recipes/jsonp)

View File

@ -1,8 +1,9 @@
---
title: JWT Authentication
menu:
main:
side:
parent: recipes
weight: 11
---
Most applications dealing with client authentication will require a more secure
@ -19,85 +20,7 @@ other algorithms are available.
`server.go`
```go
package main
import (
"fmt"
"net/http"
"github.com/dgrijalva/jwt-go"
"github.com/labstack/echo"
mw "github.com/labstack/echo/middleware"
)
const (
Bearer = "Bearer"
SigningKey = "somethingsupersecret"
)
// A JSON Web Token middleware
func JWTAuth(key string) echo.HandlerFunc {
return func(c *echo.Context) error {
// Skip WebSocket
if (c.Request().Header.Get(echo.Upgrade)) == echo.WebSocket {
return nil
}
auth := c.Request().Header.Get("Authorization")
l := len(Bearer)
he := echo.NewHTTPError(http.StatusUnauthorized)
if len(auth) > l+1 && auth[:l] == Bearer {
t, err := jwt.Parse(auth[l+1:], func(token *jwt.Token) (interface{}, error) {
// Always check the signing method
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
// Return the key for validation
return []byte(key), nil
})
if err == nil && t.Valid {
// Store token claims in echo.Context
c.Set("claims", t.Claims)
return nil
}
}
return he
}
}
func accessible(c *echo.Context) error {
return c.String(http.StatusOK, "No auth required for this route.\n")
}
func restricted(c *echo.Context) error {
return c.String(http.StatusOK, "Access granted with JWT.\n")
}
func main() {
// Echo instance
e := echo.New()
// Logger
e.Use(mw.Logger())
// Unauthenticated route
e.Get("/", accessible)
// Restricted group
r := e.Group("/restricted")
r.Use(JWTAuth(SigningKey))
r.Get("", restricted)
// Start server
e.Run(":1323")
}
```
{{< embed "jwt-authentication/server.go" >}}
Run `server.go` and making a request to the root path `/` returns a 200 OK response,
as this route does not use our JWT authentication middleware. Sending requests to
@ -121,32 +44,7 @@ $ curl localhost:1323/restricted -H "Authorization: Bearer InvalidToken" => Una
Running `token.go` (source) will print JWT that is valid against this middleware
to stdout. You can use this token to test succesful authentication on the `/restricted` path.
```go
package main
import (
"fmt"
"time"
"github.com/dgrijalva/jwt-go"
)
const SigningKey = "somethingsupersecret"
func main() {
// New web token.
token := jwt.New(jwt.SigningMethodHS256)
// Set a header and a claim
token.Header["typ"] = "JWT"
token.Claims["exp"] = time.Now().Add(time.Hour * 96).Unix()
// Generate encoded token
t, _ := token.SignedString([]byte(SigningKey))
fmt.Println(t)
}
```
{{< embed "jwt-authentication/token/token.go" >}}
```sh
# Valid token
@ -155,6 +53,6 @@ $ curl localhost:1323/restricted -H "Authorization: Bearer <token>" => Access g
### Maintainers
- [axdg](http://github.com/axdg)
- [axdg](https://github.com/axdg)
### [Source Code](https://github.com/labstack/echo/blob/master/recipes/jwt-authentication)

View File

@ -0,0 +1,20 @@
---
title: Middleware
menu:
side:
identifier: "recipe-miiddleware"
parent: recipes
weight: 3
---
### Server
`server.go`
{{< embed "middleware/server.go" >}}
### Maintainers
- [vishr](https://github.com/vishr)
### [Source Code](https://github.com/labstack/echo/blob/master/recipes/middleware)

View File

@ -1,8 +1,9 @@
---
title: Streaming File Upload
menu:
main:
side:
parent: recipes
weight: 8
---
- Streaming multipart/form-data file upload
@ -12,114 +13,16 @@ menu:
`server.go`
```go
package main
import (
"io/ioutil"
"github.com/labstack/echo"
mw "github.com/labstack/echo/middleware"
"io"
"net/http"
"os"
)
func upload(c *echo.Context) error {
mr, err := c.Request().MultipartReader()
if err != nil {
return err
}
// Read form field `name`
part, err := mr.NextPart()
if err != nil {
return err
}
defer part.Close()
b, err := ioutil.ReadAll(part)
if err != nil {
return err
}
name := string(b)
// Read form field `email`
part, err = mr.NextPart()
if err != nil {
return err
}
defer part.Close()
b, err = ioutil.ReadAll(part)
if err != nil {
return err
}
email := string(b)
// Read files
i := 0
for {
part, err := mr.NextPart()
if err != nil {
if err == io.EOF {
break
}
return err
}
defer part.Close()
file, err := os.Create(part.FileName())
if err != nil {
return err
}
defer file.Close()
if _, err := io.Copy(file, part); err != nil {
return err
}
i++
}
return c.String(http.StatusOK, "Thank You! %s <%s>, %d files uploaded successfully.",
name, email, i)
}
func main() {
e := echo.New()
e.Use(mw.Logger())
e.Use(mw.Recover())
e.Static("/", "public")
e.Post("/upload", upload)
e.Run(":1323")
}
```
{{< embed "streaming-file-upload/server.go" >}}
### Client
`index.html`
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>File Upload</title>
</head>
<body>
<h1>Upload Files</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>
```
{{< embed "streaming-file-upload/public/index.html" >}}
### Maintainers
- [vishr](http://github.com/vishr)
- [vishr](https://github.com/vishr)
### [Source Code](https://github.com/labstack/echo/blob/master/recipes/streaming-file-upload)

View File

@ -1,8 +1,9 @@
---
title: Streaming Response
menu:
main:
side:
parent: recipes
weight: 9
---
- Send data as it is produced
@ -12,53 +13,7 @@ menu:
`server.go`
```go
package main
import (
"net/http"
"time"
"encoding/json"
"github.com/labstack/echo"
)
type (
Geolocation struct {
Altitude float64
Latitude float64
Longitude float64
}
)
var (
locations = []Geolocation{
{-97, 37.819929, -122.478255},
{1899, 39.096849, -120.032351},
{2619, 37.865101, -119.538329},
{42, 33.812092, -117.918974},
{15, 37.77493, -122.419416},
}
)
func main() {
e := echo.New()
e.Get("/", func(c *echo.Context) error {
c.Response().Header().Set(echo.ContentType, echo.ApplicationJSON)
c.Response().WriteHeader(http.StatusOK)
for _, l := range locations {
if err := json.NewEncoder(c.Response()).Encode(l); err != nil {
return err
}
c.Response().Flush()
time.Sleep(1 * time.Second)
}
return nil
})
e.Run(":1323")
}
```
{{< embed "streaming-response/server.go" >}}
### Client
@ -78,6 +33,6 @@ $ curl localhost:1323
### Maintainers
- [vishr](http://github.com/vishr)
- [vishr](https://github.com/vishr)
### [Source Code](https://github.com/labstack/echo/blob/master/recipes/streaming-response)

View File

@ -1,85 +1,18 @@
---
title: Subdomains
menu:
main:
side:
parent: recipes
weight: 10
---
`server.go`
```go
package main
import (
"net/http"
"github.com/labstack/echo"
mw "github.com/labstack/echo/middleware"
)
type Hosts map[string]http.Handler
func (h Hosts) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if handler := h[r.Host]; handler != nil {
handler.ServeHTTP(w, r)
} else {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
}
}
func main() {
// Host map
hosts := make(Hosts)
//-----
// API
//-----
api := echo.New()
api.Use(mw.Logger())
api.Use(mw.Recover())
hosts["api.localhost:1323"] = api
api.Get("/", func(c *echo.Context) error {
return c.String(http.StatusOK, "API")
})
//------
// Blog
//------
blog := echo.New()
blog.Use(mw.Logger())
blog.Use(mw.Recover())
hosts["blog.localhost:1323"] = blog
blog.Get("/", func(c *echo.Context) error {
return c.String(http.StatusOK, "Blog")
})
//---------
// Website
//---------
site := echo.New()
site.Use(mw.Logger())
site.Use(mw.Recover())
hosts["localhost:1323"] = site
site.Get("/", func(c *echo.Context) error {
return c.String(http.StatusOK, "Welcome!")
})
http.ListenAndServe(":1323", hosts)
}
```
{{< embed "subdomains/server.go" >}}
### Maintainers
- [axdg](http://github.com/axdg)
- [vishr](http://github.com/axdg)
- [axdg](https://github.com/axdg)
- [vishr](https://github.com/axdg)
### [Source Code](https://github.com/labstack/echo/blob/master/recipes/subdomains)

View File

@ -0,0 +1,25 @@
---
title: Website
menu:
side:
parent: recipes
weight: 4
---
### Server
`server.go`
{{< embed "website/server.go" >}}
### Client
`index.html`
{{< embed "website/public/index.html" >}}
### Maintainers
- [vishr](https://github.com/vishr)
### [Source Code](https://github.com/labstack/echo/blob/master/recipes/website)

View File

@ -1,94 +1,22 @@
---
title: WebSocket
menu:
main:
side:
parent: recipes
weight: 5
---
### Server
`server.go`
```go
package main
import (
"fmt"
"github.com/labstack/echo"
mw "github.com/labstack/echo/middleware"
"golang.org/x/net/websocket"
)
func main() {
e := echo.New()
e.Use(mw.Logger())
e.Use(mw.Recover())
e.Static("/", "public")
e.WebSocket("/ws", func(c *echo.Context) (err error) {
ws := c.Socket()
msg := ""
for {
if err = websocket.Message.Send(ws, "Hello, Client!"); err != nil {
return
}
if err = websocket.Message.Receive(ws, &msg); err != nil {
return
}
fmt.Println(msg)
}
return
})
e.Run(":1323")
}
```
{{< embed "websocket/server.go" >}}
### Client
`index.html`
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>WebSocket</title>
</head>
<body>
<p id="output"></p>
<script>
var loc = window.location;
var uri = 'ws:';
if (loc.protocol === 'https:') {
uri = 'wss:';
}
uri += '//' + loc.host;
uri += loc.pathname + 'ws';
ws = new WebSocket(uri)
ws.onopen = function() {
console.log('Connected')
}
ws.onmessage = function(evt) {
var out = document.getElementById('output');
out.innerHTML += evt.data + '<br>';
}
setInterval(function() {
ws.send('Hello, Server!');
}, 1000);
</script>
</body>
</html>
```
{{< embed "websocket/public/index.html" >}}
### Output
@ -114,6 +42,6 @@ Hello, Server!
### Maintainers
- [vishr](http://github.com/vishr)
- [vishr](https://github.com/vishr)
### [Source Code](https://github.com/labstack/echo/blob/master/recipes/websocket)

View File

@ -17,7 +17,7 @@
<section class="content">
{{ .Content }}
</section>
<footer>
<footer style="margin-top: 80px;">
<div id="disqus_thread"></div>
<script type="text/javascript">
/* * * CONFIGURATION VARIABLES * * */

View File

@ -27,9 +27,10 @@
</ul>
</div>
</footer>
<script src="/scripts/highlight.pack.min.js"></script>
<!-- <script src="/scripts/highlight.pack.min.js"></script> -->
<script src="/scripts/prism.js"></script>
<script src="/scripts/echo.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<!-- <script>hljs.initHighlightingOnLoad();</script> -->
<script>
(function(i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r;

View File

@ -16,7 +16,8 @@
<link rel="stylesheet" href="//fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Roboto:300,400,500,700">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
<link rel="stylesheet" href="/styles/monokai.css">
<!-- <link rel="stylesheet" href="/styles/monokai.css"> -->
<link rel="stylesheet" href="/styles/prism.css">
<link rel="stylesheet" href="/styles/echo.css">
<script src="//storage.googleapis.com/code.getmdl.io/1.0.5/material.min.js"></script>
</head>

View File

@ -1,6 +1,6 @@
<aside class="menu">
<div>
{{ range .Site.Menus.main }}
{{ range .Site.Menus.side }}
{{ if .HasChildren }}
<h4>
{{ .Pre }}

View File

@ -0,0 +1,2 @@
<pre data-src="https://raw.githubusercontent.com/labstack/echo/master/recipes/{{ .Get 0 }}">
</pre>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
/* http://prismjs.com/download.html?themes=prism-coy&languages=markup+css+clike+javascript+go&plugins=file-highlight */
var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},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,"&amp;").replace(/</g,"&lt;").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&&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 l=r[e];if(2==arguments.length){a=arguments[1];for(var i in a)a.hasOwnProperty(i)&&(l[i]=a[i]);return l}var o={};for(var s in l)if(l.hasOwnProperty(s)){if(s==n)for(var i in a)a.hasOwnProperty(i)&&(o[i]=a[i]);o[s]=l[s]}return t.languages.DFS(t.languages,function(t,n){n===r[e]&&t!=e&&(this[t]=o)}),r[e]=o},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))}},plugins:{},highlightAll:function(e,n){for(var a,r=document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'),l=0;a=r[l++];)t.highlightElement(a,e===!0,n)},highlightElement:function(n,a,r){for(var l,i,o=n;o&&!e.test(o.className);)o=o.parentNode;o&&(l=(o.className.match(e)||[,""])[1],i=t.languages[l]),n.className=n.className.replace(e,"").replace(/\s+/g," ")+" language-"+l,o=n.parentNode,/pre/i.test(o.nodeName)&&(o.className=o.className.replace(e,"").replace(/\s+/g," ")+" language-"+l);var s=n.textContent,u={element:n,language:l,grammar:i,code:s};if(!s||!i)return t.hooks.run("complete",u),void 0;if(t.hooks.run("before-highlight",u),a&&_self.Worker){var g=new Worker(t.filename);g.onmessage=function(e){u.highlightedCode=e.data,t.hooks.run("before-insert",u),u.element.innerHTML=u.highlightedCode,r&&r.call(u.element),t.hooks.run("after-highlight",u),t.hooks.run("complete",u)},g.postMessage(JSON.stringify({language:u.language,code:u.code,immediateClose:!0}))}else u.highlightedCode=t.highlight(u.code,u.grammar,u.language),t.hooks.run("before-insert",u),u.element.innerHTML=u.highlightedCode,r&&r.call(n),t.hooks.run("after-highlight",u),t.hooks.run("complete",u)},highlight:function(e,a,r){var l=t.tokenize(e,a);return n.stringify(t.util.encode(l),r)},tokenize:function(e,n){var a=t.Token,r=[e],l=n.rest;if(l){for(var i in l)n[i]=l[i];delete n.rest}e:for(var i in n)if(n.hasOwnProperty(i)&&n[i]){var o=n[i];o="Array"===t.util.type(o)?o:[o];for(var s=0;s<o.length;++s){var u=o[s],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),P=[p,1];b&&P.push(b);var A=new a(i,g?t.tokenize(m,g):m,h);P.push(A),w&&P.push(w),Array.prototype.splice.apply(r,P)}}}}}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,l=0;r=a[l++];)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 l={type:e.type,content:n.stringify(e.content,a,r),tag:"span",classes:["token",e.type],attributes:{},language:a,parent:r};if("comment"==l.type&&(l.attributes.spellcheck="true"),e.alias){var i="Array"===t.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(l.classes,i)}t.hooks.run("wrap",l);var o="";for(var s in l.attributes)o+=(o?" ":"")+s+'="'+(l.attributes[s]||"")+'"';return"<"+l.tag+' class="'+l.classes.join(" ")+'" '+o+">"+l.content+"</"+l.tag+">"},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var n=JSON.parse(e.data),a=n.language,r=n.code,l=n.immediateClose;_self.postMessage(t.highlight(r,t.languages[a],a)),l&&_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),"undefined"!=typeof global&&(global.Prism=Prism);
Prism.languages.markup={comment:/<!--[\w\W]*?-->/,prolog:/<\?[\w\W]+?\?>/,doctype:/<!DOCTYPE[\w\W]+?>/,cdata:/<!\[CDATA\[[\w\W]*?]]>/i,tag:{pattern:/<\/?[^\s>\/=.]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,inside:{punctuation:/[=>"']/}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&amp;/,"&"))}),Prism.languages.xml=Prism.languages.markup,Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup;
Prism.languages.css={comment:/\/\*[\w\W]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^\{\}\s][^\{\};]*?(?=\s*\{)/,string:/("|')(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1/,property:/(\b|\B)[\w-]+(?=\s*:)/i,important:/\B!important\b/i,"function":/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},Prism.languages.css.atrule.inside.rest=Prism.util.clone(Prism.languages.css),Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/(<style[\w\W]*?>)[\w\W]*?(?=<\/style>)/i,lookbehind:!0,inside:Prism.languages.css,alias:"language-css"}}),Prism.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|').*?\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:Prism.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:Prism.languages.css}},alias:"language-css"}},Prism.languages.markup.tag));
Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:/(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,"class-name":{pattern:/((?:\b(?: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":/[a-z0-9_]+(?=\()/i,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/};
Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|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]+|0b[01]+|0o[0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/,"function":/[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0}}),Prism.languages.insertBefore("javascript","class-name",{"template-string":{pattern:/`(?:\\`|\\?[^`])*`/,inside:{interpolation:{pattern:/\$\{[^}]+\}/,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/(<script[\w\W]*?>)[\w\W]*?(?=<\/script>)/i,lookbehind:!0,inside:Prism.languages.javascript,alias:"language-javascript"}}),Prism.languages.js=Prism.languages.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"];
!function(){"undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector&&(self.Prism.fileHighlight=function(){var e={js:"javascript",html:"markup",svg:"markup",xml:"markup",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell"};Array.prototype.forEach&&Array.prototype.slice.call(document.querySelectorAll("pre[data-src]")).forEach(function(t){for(var r,a=t.getAttribute("data-src"),s=t,n=/\blang(?:uage)?-(?!\*)(\w+)\b/i;s&&!n.test(s.className);)s=s.parentNode;if(s&&(r=(t.className.match(n)||[,""])[1]),!r){var o=(a.match(/\.(\w+)$/)||[,""])[1];r=e[o]||o}var l=document.createElement("code");l.className="language-"+r,t.textContent="",l.textContent="Loading…",t.appendChild(l);var i=new XMLHttpRequest;i.open("GET",a,!0),i.onreadystatechange=function(){4==i.readyState&&(i.status<400&&i.responseText?(l.textContent=i.responseText,Prism.highlightElement(l)):l.textContent=i.status>=400?"✖ Error "+i.status+" while fetching file: "+i.statusText:"✖ Error: File does not exist or is empty")},i.send(null)})},self.Prism.fileHighlight())}();

View File

@ -14,7 +14,7 @@ footer {
.github a:hover {
color: #333;
}
code {
:not(pre) > code {
padding: 2px 4px;
background: #EEE;
color: #424242;

View File

@ -1,126 +0,0 @@
/*
Monokai style - ported by Luigi Maselli - http://grigio.org
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
background: #272822;
-webkit-text-size-adjust: none;
}
.hljs-tag,
.hljs-tag .hljs-title,
.hljs-keyword,
.hljs-literal,
.hljs-strong,
.hljs-change,
.hljs-winutils,
.hljs-flow,
.nginx .hljs-title,
.tex .hljs-special {
color: #f92672;
}
.hljs {
color: #ddd;
}
.hljs .hljs-constant,
.asciidoc .hljs-code,
.markdown .hljs-code {
color: #66d9ef;
}
.hljs-code,
.hljs-class .hljs-title,
.hljs-header {
color: white;
}
.hljs-link_label,
.hljs-attribute,
.hljs-symbol,
.hljs-symbol .hljs-string,
.hljs-value,
.hljs-regexp {
color: #bf79db;
}
.hljs-link_url,
.hljs-tag .hljs-value,
.hljs-string,
.hljs-bullet,
.hljs-subst,
.hljs-title,
.hljs-emphasis,
.hljs-type,
.hljs-preprocessor,
.hljs-pragma,
.ruby .hljs-class .hljs-parent,
.hljs-built_in,
.django .hljs-template_tag,
.django .hljs-variable,
.smalltalk .hljs-class,
.django .hljs-filter .hljs-argument,
.smalltalk .hljs-localvars,
.smalltalk .hljs-array,
.hljs-attr_selector,
.hljs-pseudo,
.hljs-addition,
.hljs-stream,
.hljs-envvar,
.apache .hljs-tag,
.apache .hljs-cbracket,
.tex .hljs-command,
.hljs-prompt,
.hljs-name {
color: #a6e22e;
}
.hljs-comment,
.hljs-annotation,
.smartquote,
.hljs-blockquote,
.hljs-horizontal_rule,
.hljs-decorator,
.hljs-pi,
.hljs-doctype,
.hljs-deletion,
.hljs-shebang,
.apache .hljs-sqbracket,
.tex .hljs-formula {
color: #75715e;
}
.hljs-keyword,
.hljs-literal,
.css .hljs-id,
.hljs-doctag,
.hljs-title,
.hljs-header,
.hljs-type,
.vbscript .hljs-built_in,
.rsl .hljs-built_in,
.smalltalk .hljs-class,
.diff .hljs-header,
.hljs-chunk,
.hljs-winutils,
.bash .hljs-variable,
.apache .hljs-tag,
.tex .hljs-special,
.hljs-request,
.hljs-status {
font-weight: bold;
}
.coffeescript .javascript,
.javascript .xml,
.tex .hljs-formula,
.xml .javascript,
.xml .vbscript,
.xml .css,
.xml .hljs-cdata {
opacity: 0.5;
}

View File

@ -0,0 +1,236 @@
/* http://prismjs.com/download.html?themes=prism-coy&languages=markup+css+clike+javascript+go&plugins=file-highlight */
/**
* prism.js Coy theme for JavaScript, CoffeeScript, CSS and HTML
* Based on https://github.com/tshedor/workshop-wp-theme (Example: http://workshop.kansan.com/category/sessions/basics or http://workshop.timshedor.com/category/sessions/basics);
* @author Tim Shedor
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: 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;
}
/* Code blocks */
pre[class*="language-"] {
position: relative;
margin: .5em 0;
-webkit-box-shadow: -1px 0px 0px 0px #358ccb, 0px 0px 0px 1px #dfdfdf;
-moz-box-shadow: -1px 0px 0px 0px #358ccb, 0px 0px 0px 1px #dfdfdf;
box-shadow: -1px 0px 0px 0px #358ccb, 0px 0px 0px 1px #dfdfdf;
border-left: 10px solid #358ccb;
background-color: #fdfdfd;
background-image: -webkit-linear-gradient(transparent 50%, rgba(69, 142, 209, 0.04) 50%);
background-image: -moz-linear-gradient(transparent 50%, rgba(69, 142, 209, 0.04) 50%);
background-image: -ms-linear-gradient(transparent 50%, rgba(69, 142, 209, 0.04) 50%);
background-image: -o-linear-gradient(transparent 50%, rgba(69, 142, 209, 0.04) 50%);
background-image: linear-gradient(transparent 50%, rgba(69, 142, 209, 0.04) 50%);
background-size: 3em 3em;
background-origin: content-box;
overflow: visible;
max-height: 30em;
padding: 0;
}
code[class*="language"] {
max-height: inherit;
height: 100%;
padding: 0 1em;
display: block;
overflow: auto;
}
/* Margin bottom to accomodate shadow */
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background-color: #fdfdfd;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
margin-bottom: 1em;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
position: relative;
padding: .2em;
-webkit-border-radius: 0.3em;
-moz-border-radius: 0.3em;
-ms-border-radius: 0.3em;
-o-border-radius: 0.3em;
border-radius: 0.3em;
color: #c92c2c;
border: 1px solid rgba(0, 0, 0, 0.1);
display: inline;
}
pre[class*="language-"]:before,
pre[class*="language-"]:after {
content: '';
z-index: -2;
display: block;
position: absolute;
bottom: 0.75em;
left: 0.18em;
width: 40%;
height: 20%;
-webkit-box-shadow: 0px 13px 8px #979797;
-moz-box-shadow: 0px 13px 8px #979797;
box-shadow: 0px 13px 8px #979797;
-webkit-transform: rotate(-2deg);
-moz-transform: rotate(-2deg);
-ms-transform: rotate(-2deg);
-o-transform: rotate(-2deg);
transform: rotate(-2deg);
}
:not(pre) > code[class*="language-"]:after,
pre[class*="language-"]:after {
right: 0.75em;
left: auto;
-webkit-transform: rotate(2deg);
-moz-transform: rotate(2deg);
-ms-transform: rotate(2deg);
-o-transform: rotate(2deg);
transform: rotate(2deg);
}
.token.comment,
.token.block-comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #7D8B99;
}
.token.punctuation {
color: #5F6364;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.function-name,
.token.constant,
.token.symbol,
.token.deleted {
color: #c92c2c;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.function,
.token.builtin,
.token.inserted {
color: #2f9c0a;
}
.token.operator,
.token.entity,
.token.url,
.token.variable {
color: #a67f59;
background: rgba(255, 255, 255, 0.5);
}
.token.atrule,
.token.attr-value,
.token.keyword,
.token.class-name {
color: #1990b8;
}
.token.regex,
.token.important {
color: #e90;
}
.language-css .token.string,
.style .token.string {
color: #a67f59;
background: rgba(255, 255, 255, 0.5);
}
.token.important {
font-weight: normal;
}
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
.namespace {
opacity: .7;
}
@media screen and (max-width: 767px) {
pre[class*="language-"]:before,
pre[class*="language-"]:after {
bottom: 14px;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
}
/* Plugin styles */
.token.tab:not(:empty):before,
.token.cr:before,
.token.lf:before {
color: #e0d7d1;
}
/* Plugin styles: Line Numbers */
pre[class*="language-"].line-numbers {
padding-left: 0;
}
pre[class*="language-"].line-numbers code {
padding-left: 3.8em;
}
pre[class*="language-"].line-numbers .line-numbers-rows {
left: 0;
}
/* Plugin styles: Line Highlight */
pre[class*="language-"][data-line] {
padding-top: 0;
padding-bottom: 0;
padding-left: 0;
}
pre[data-line] code {
position: relative;
padding-left: 4em;
}
pre .line-highlight {
margin-top: 0;
}