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:
parent
5587974933
commit
0176385654
3
.gitattributes
vendored
3
.gitattributes
vendored
@ -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
|
||||
|
14
README.md
14
README.md
@ -1,4 +1,4 @@
|
||||
# [Echo](http://echo.labstack.com) [](http://godoc.org/github.com/labstack/echo) [](https://raw.githubusercontent.com/labstack/echo/master/LICENSE) [](https://travis-ci.org/labstack/echo) [](https://coveralls.io/r/labstack/echo) [](https://gitter.im/labstack/echo)
|
||||
# [Echo](http://labstack.com/echo) [](http://godoc.org/github.com/labstack/echo) [](https://raw.githubusercontent.com/labstack/echo/master/LICENSE) [](https://travis-ci.org/labstack/echo) [](https://coveralls.io/r/labstack/echo) [](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
|
||||
|
||||
|
12
echo_test.go
12
echo_test.go
@ -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>"))
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@ -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"
|
||||
}]
|
||||
|
@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Customization
|
||||
menu:
|
||||
main:
|
||||
side:
|
||||
parent: guide
|
||||
weight: 2
|
||||
---
|
||||
|
@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Error Handling
|
||||
menu:
|
||||
main:
|
||||
side:
|
||||
parent: guide
|
||||
weight: 7
|
||||
---
|
||||
|
@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Installation
|
||||
menu:
|
||||
main:
|
||||
side:
|
||||
parent: guide
|
||||
weight: 1
|
||||
---
|
||||
|
@ -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)
|
||||
|
@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Request
|
||||
menu:
|
||||
main:
|
||||
side:
|
||||
parent: guide
|
||||
weight: 5
|
||||
---
|
||||
|
@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Response
|
||||
menu:
|
||||
main:
|
||||
side:
|
||||
parent: guide
|
||||
weight: 6
|
||||
---
|
||||
|
@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Routing
|
||||
menu:
|
||||
main:
|
||||
side:
|
||||
parent: guide
|
||||
weight: 3
|
||||
---
|
||||
|
@ -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
|
||||
|
||||
|
19
website/content/recipes/crud.md
Normal file
19
website/content/recipes/crud.md
Normal 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)
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
19
website/content/recipes/hello-world.md
Normal file
19
website/content/recipes/hello-world.md
Normal 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)
|
@ -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)
|
||||
|
@ -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)
|
||||
|
20
website/content/recipes/middleware.md
Normal file
20
website/content/recipes/middleware.md
Normal 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)
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
25
website/content/recipes/website.md
Normal file
25
website/content/recipes/website.md
Normal 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)
|
@ -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)
|
||||
|
@ -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 * * */
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<aside class="menu">
|
||||
<div>
|
||||
{{ range .Site.Menus.main }}
|
||||
{{ range .Site.Menus.side }}
|
||||
{{ if .HasChildren }}
|
||||
<h4>
|
||||
{{ .Pre }}
|
||||
|
2
website/layouts/shortcodes/embed.html
Normal file
2
website/layouts/shortcodes/embed.html
Normal file
@ -0,0 +1,2 @@
|
||||
<pre data-src="https://raw.githubusercontent.com/labstack/echo/master/recipes/{{ .Get 0 }}">
|
||||
</pre>
|
9
website/static/scripts/highlight.pack.min.js
vendored
9
website/static/scripts/highlight.pack.min.js
vendored
File diff suppressed because one or more lines are too long
8
website/static/scripts/prism.js
Normal file
8
website/static/scripts/prism.js
Normal 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,"&").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&&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(/&/,"&"))}),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())}();
|
@ -14,7 +14,7 @@ footer {
|
||||
.github a:hover {
|
||||
color: #333;
|
||||
}
|
||||
code {
|
||||
:not(pre) > code {
|
||||
padding: 2px 4px;
|
||||
background: #EEE;
|
||||
color: #424242;
|
||||
|
@ -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;
|
||||
}
|
236
website/static/styles/prism.css
Normal file
236
website/static/styles/prism.css
Normal 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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user