mirror of
				https://github.com/labstack/echo.git
				synced 2025-10-30 23:57:38 +02:00 
			
		
		
		
	| @@ -21,7 +21,7 @@ | ||||
|  | ||||
| ## Performance | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| ## Quick Start | ||||
|  | ||||
|   | ||||
							
								
								
									
										40
									
								
								context.go
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								context.go
									
									
									
									
									
								
							| @@ -13,8 +13,6 @@ import ( | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
|  | ||||
| 	"golang.org/x/net/websocket" | ||||
| ) | ||||
|  | ||||
| type ( | ||||
| @@ -175,16 +173,15 @@ type ( | ||||
| 	} | ||||
|  | ||||
| 	context struct { | ||||
| 		request   *http.Request | ||||
| 		response  *Response | ||||
| 		webSocket *websocket.Conn | ||||
| 		path      string | ||||
| 		pnames    []string | ||||
| 		pvalues   []string | ||||
| 		query     url.Values | ||||
| 		handler   HandlerFunc | ||||
| 		store     Map | ||||
| 		echo      *Echo | ||||
| 		request  *http.Request | ||||
| 		response *Response | ||||
| 		path     string | ||||
| 		pnames   []string | ||||
| 		pvalues  []string | ||||
| 		query    url.Values | ||||
| 		handler  HandlerFunc | ||||
| 		store    Map | ||||
| 		echo     *Echo | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| @@ -238,15 +235,22 @@ func (c *context) SetPath(p string) { | ||||
| 	c.path = p | ||||
| } | ||||
|  | ||||
| func (c *context) Param(name string) (value string) { | ||||
| 	l := len(c.pnames) | ||||
| func (c *context) Param(name string) string { | ||||
| 	for i, n := range c.pnames { | ||||
| 		if n == name && i < l { | ||||
| 			value = c.pvalues[i] | ||||
| 			break | ||||
| 		if i < len(c.pnames) { | ||||
| 			if strings.HasPrefix(n, name) { | ||||
| 				return c.pvalues[i] | ||||
| 			} | ||||
|  | ||||
| 			// Param name with aliases | ||||
| 			for _, p := range strings.Split(n, ",") { | ||||
| 				if p == name { | ||||
| 					return c.pvalues[i] | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (c *context) ParamNames() []string { | ||||
|   | ||||
| @@ -250,6 +250,18 @@ func TestContextPathParam(t *testing.T) { | ||||
| 	assert.Equal(t, "501", c.Param("fid")) | ||||
| } | ||||
|  | ||||
| func TestContextPathParamNamesAlais(t *testing.T) { | ||||
| 	e := New() | ||||
| 	req, _ := http.NewRequest(GET, "/", nil) | ||||
| 	c := e.NewContext(req, nil) | ||||
|  | ||||
| 	c.SetParamNames("id,name") | ||||
| 	c.SetParamValues("joe") | ||||
|  | ||||
| 	assert.Equal(t, "joe", c.Param("id")) | ||||
| 	assert.Equal(t, "joe", c.Param("name")) | ||||
| } | ||||
|  | ||||
| func TestContextFormValue(t *testing.T) { | ||||
| 	f := make(url.Values) | ||||
| 	f.Set("name", "Jon Snow") | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| package echo | ||||
|  | ||||
| import "strings" | ||||
|  | ||||
| type ( | ||||
| 	// Router is the registry of all registered routes for an `Echo` instance for | ||||
| 	// request matching and URL path parameter parsing. | ||||
| @@ -170,7 +172,12 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string | ||||
| 			if h != nil { | ||||
| 				cn.addHandler(method, h) | ||||
| 				cn.ppath = ppath | ||||
| 				cn.pnames = pnames | ||||
| 				for i, n := range cn.pnames { | ||||
| 					// Param name aliases | ||||
| 					if !strings.Contains(n, pnames[i]) { | ||||
| 						cn.pnames[i] += "," + pnames[i] | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		return | ||||
|   | ||||
| @@ -3,6 +3,7 @@ package echo | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| @@ -823,9 +824,11 @@ func TestRouterAPI(t *testing.T) { | ||||
| 	c := e.NewContext(nil, nil).(*context) | ||||
| 	for _, route := range gitHubAPI { | ||||
| 		r.Find(route.Method, route.Path, c) | ||||
| 		for i, n := range c.pnames { | ||||
| 			if assert.NotEmpty(t, n) { | ||||
| 				assert.Equal(t, n, c.pnames[i]) | ||||
| 		for _, n := range c.pnames { | ||||
| 			for _, p := range strings.Split(n, ",") { | ||||
| 				if assert.NotEmpty(t, p) { | ||||
| 					assert.Equal(t, c.Param(p), ":"+p) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
| @@ -1,2 +1,2 @@ | ||||
| build: | ||||
| 	rm -rf public && hugo | ||||
| 	rm -rf public/v3 && hugo | ||||
|   | ||||
| @@ -1,9 +1,10 @@ | ||||
| { | ||||
|   "baseurl": "https://echo.labstack.com", | ||||
|   "baseurl": "https://echo.labstack.com/", | ||||
|   "languageCode": "en-us", | ||||
|   "title": "Echo - Fast and unfancy HTTP server framework for Go (Golang)", | ||||
|   "canonifyurls": true, | ||||
|   "googleAnalytics": "UA-85059636-2", | ||||
|   "publishdir": "public/v3", | ||||
|   "permalinks": { | ||||
|     "guide": "/guide/:filename", | ||||
|     "middleware": "/middleware/:filename", | ||||
|   | ||||
| @@ -11,8 +11,6 @@ description = "Customizing Echo" | ||||
|  | ||||
| ### HTTP Error Handler | ||||
|  | ||||
| `Echo#SetHTTPErrorHandler(h HTTPErrorHandler)` registers a custom `Echo#HTTPErrorHandler`. | ||||
|  | ||||
| Default HTTP error handler rules: | ||||
|  | ||||
| - If error is of type `Echo#HTTPError` it sends HTTP response with status code `HTTPError.Code` | ||||
| @@ -20,79 +18,32 @@ and message `HTTPError.Message`. | ||||
| - Else it sends `500 - Internal Server Error`. | ||||
| - If debug mode is enabled, it uses `error.Error()` as status message. | ||||
|  | ||||
| ### Debug | ||||
| You can also set a custom HTTP error handler using `Echo#HTTPErrorHandler`. | ||||
|  | ||||
| `Echo#SetDebug(on bool)` enable/disable debug mode. | ||||
| ### Debugging | ||||
|  | ||||
| `Echo#Debug` enables/disables debug mode. | ||||
|  | ||||
| ### Logging | ||||
|  | ||||
| #### Custom Logger | ||||
|  | ||||
| `Echo#SetLogger(l log.Logger)` | ||||
|  | ||||
| SetLogger defines a custom logger. | ||||
|  | ||||
| #### Log Output | ||||
|  | ||||
| `Echo#SetLogOutput(w io.Writer)` sets the output destination for the logger. Default | ||||
| value `os.Stdout` | ||||
| `Echo#Logger.SetOutput(io.Writer)` sets the output destination for the logger. | ||||
| Default value `os.Stdout` | ||||
|  | ||||
| To completely disable logs use `Echo#SetLogOutput(io.Discard)` | ||||
| To completely disable logs use `Echo#Logger.SetOutput(io.Discard)` or `Echo#Logger.SetLevel(log.OFF)` | ||||
|  | ||||
| #### Log Level | ||||
|  | ||||
| `Echo#SetLogLevel(l log.Level)` | ||||
| `Echo#Logger.SetLevel(log.Lvl)` | ||||
|  | ||||
| SetLogLevel sets the log level for the logger. Default value `5` (OFF). | ||||
| SetLogLevel sets the log level for the logger. Default value `OFF`. | ||||
| Possible values: | ||||
|  | ||||
| - `0` (DEBUG) | ||||
| - `1` (INFO) | ||||
| - `2` (WARN) | ||||
| - `3`	(ERROR) | ||||
| - `4`	(FATAL) | ||||
| - `5` (OFF) | ||||
| - `DEBUG` | ||||
| - `INFO` | ||||
| - `WARN` | ||||
| - `ERROR` | ||||
| - `OFF` | ||||
|  | ||||
| ### HTTP Engine | ||||
|  | ||||
| Echo currently supports standard and [fasthttp](https://github.com/valyala/fasthttp) | ||||
| server engines. Echo utilizes interfaces to abstract the internal implementation | ||||
| of these servers so you can seamlessly switch from one engine to another based on | ||||
| your preference. | ||||
|  | ||||
| #### Running a standard HTTP server | ||||
|  | ||||
| `e.Run(standard.New(":1323"))` | ||||
|  | ||||
| #### Running a fasthttp server | ||||
|  | ||||
| `e.Run(fasthttp.New(":1323"))` | ||||
|  | ||||
| #### Running a server with TLS configuration | ||||
|  | ||||
| `e.Run(<engine>.WithTLS(":1323", "<certFile>", "<keyFile>"))` | ||||
|  | ||||
| #### Running a server with engine configuration | ||||
|  | ||||
| `e.Run(<engine>.WithConfig(<config>))` | ||||
|  | ||||
| ##### Configuration | ||||
|  | ||||
| ```go | ||||
| Config struct { | ||||
|   Address      string        // TCP address to listen on. | ||||
|   Listener     net.Listener  // Custom `net.Listener`. If set, server accepts connections on it. | ||||
|   TLSCertFile  string        // TLS certificate file path. | ||||
|   TLSKeyFile   string        // TLS key file path. | ||||
|   ReadTimeout  time.Duration // Maximum duration before timing out read of the request. | ||||
|   WriteTimeout time.Duration // Maximum duration before timing out write of the response. | ||||
| } | ||||
| ``` | ||||
|  | ||||
| #### Access internal server instance and configure its properties | ||||
|  | ||||
| ```go | ||||
| s := standard.New(":1323") | ||||
| s.MaxHeaderBytes = 1 << 20 | ||||
| e.Run(s) | ||||
| ``` | ||||
| You can also set a custom logger using `Echo#Logger`. | ||||
|   | ||||
| @@ -9,82 +9,47 @@ description = "Frequently asked questions in Echo" | ||||
|  | ||||
| ## FAQ | ||||
|  | ||||
| Q: **How to retrieve `*http.Request` and `http.ResponseWriter` from `echo.Context`?** | ||||
| Q: How to retrieve `*http.Request` and `http.ResponseWriter` from `echo.Context`? | ||||
|  | ||||
| - `http.Request` > `c.Request().(*standard.Request).Request` | ||||
| - `http.Request` > `c.Request()` | ||||
| - `http.ResponseWriter` > `c.Response()` | ||||
|  | ||||
| >  Standard engine only | ||||
|  | ||||
| Q: **How to use standard handler `func(http.ResponseWriter, *http.Request)` with Echo?** | ||||
| Q: How to use standard handler `func(http.ResponseWriter, *http.Request)` with Echo? | ||||
|  | ||||
| ```go | ||||
| func handler(w http.ResponseWriter, r *http.Request) { | ||||
| 	io.WriteString(w, "Handler!") | ||||
| 	io.WriteString(w, "Echo!") | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	e := echo.New() | ||||
| 	e.GET("/", standard.WrapHandler(http.HandlerFunc(handler))) | ||||
| 	e.Run(standard.New(":1323")) | ||||
| 	e.GET("/", echo.WrapHandler(http.HandlerFunc(handler))) | ||||
| 	e.Start(":1323") | ||||
| } | ||||
| ``` | ||||
|  | ||||
| Q: **How to use fasthttp handler `func(fasthttp.RequestCtx)` with Echo?** | ||||
|  | ||||
| ```go | ||||
| func handler(c *fh.RequestCtx) { | ||||
| 	io.WriteString(c, "Handler!") | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	e := echo.New() | ||||
| 	e.GET("/", fasthttp.WrapHandler(handler)) | ||||
| 	e.Run(fasthttp.New(":1323")) | ||||
| } | ||||
| ``` | ||||
|  | ||||
| Q: **How to use standard middleware `func(http.Handler) http.Handler` with Echo?** | ||||
| Q: How to use standard middleware `func(http.Handler) http.Handler` with Echo? | ||||
|  | ||||
| ```go | ||||
| func middleware(h http.Handler) http.Handler { | ||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		println("Middleware!") | ||||
| 		println("middleware") | ||||
| 		h.ServeHTTP(w, r) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	e := echo.New() | ||||
| 	e.Use(standard.WrapMiddleware(middleware)) | ||||
| 	e.Use(echo.WrapMiddleware(middleware)) | ||||
| 	e.GET("/", func(c echo.Context) error { | ||||
| 		return c.String(http.StatusOK, "OK") | ||||
| 		return c.String(http.StatusOK, "Echo!") | ||||
| 	}) | ||||
| 	e.Run(standard.New(":1323")) | ||||
| 	e.Start(":1323") | ||||
| } | ||||
| ``` | ||||
|  | ||||
| Q: **How to use fasthttp middleware `func(http.Handler) http.Handler` with Echo?** | ||||
| Q: How to run Echo on a specific IP address? | ||||
|  | ||||
| ```go | ||||
| func middleware(h fh.RequestHandler) fh.RequestHandler { | ||||
| 	return func(ctx *fh.RequestCtx) { | ||||
| 		println("Middleware!") | ||||
| 		h(ctx) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	e := echo.New() | ||||
| 	e.Use(fasthttp.WrapMiddleware(middleware)) | ||||
| 	e.GET("/", func(c echo.Context) error { | ||||
| 		return c.String(http.StatusOK, "OK") | ||||
| 	}) | ||||
| 	e.Run(fasthttp.New(":1323")) | ||||
| } | ||||
| e.Start("<ip>:<port>") | ||||
| ``` | ||||
|  | ||||
| <!-- ### Q: How to run Echo on specific IP and port? | ||||
|  | ||||
| ```go | ||||
| ``` --> | ||||
|   | ||||
| @@ -15,9 +15,9 @@ Echo is developed and tested using Go `1.6.x` and `1.7.x` | ||||
| $ go get -u github.com/labstack/echo | ||||
| ``` | ||||
|  | ||||
| > Ideally, you should rely on a [package manager](https://github.com/avelino/awesome-go#package-management) like glide or govendor to use a specific [version](https://github.com/labstack/echo/releases) of Echo. | ||||
| > Ideally you should rely on a [package manager](https://github.com/avelino/awesome-go#package-management) like glide or govendor to use a specific [version](https://github.com/labstack/echo/releases) of Echo. | ||||
|  | ||||
| ### [Migrating from v1](/guide/migrating) | ||||
| ### [Migrating Guide](/guide/migration) | ||||
|  | ||||
| Echo follows [semantic versioning](http://semver.org) managed through GitHub releases. | ||||
| Specific version of Echo can be installed using a [package manager](https://github.com/avelino/awesome-go#package-management). | ||||
|   | ||||
| @@ -1,93 +0,0 @@ | ||||
| +++ | ||||
| title = "Migrating" | ||||
| description = "Migrating from Echo v1 to v2" | ||||
| [menu.side] | ||||
|   name = "Migrating" | ||||
|   parent = "guide" | ||||
|   weight = 2 | ||||
| +++ | ||||
|  | ||||
| ## Migrating from v1 | ||||
|  | ||||
| ### Change Log | ||||
|  | ||||
| - Good news, 85% of the API remains the same. | ||||
| - `Engine` interface to abstract `HTTP` server implementation, allowing | ||||
| us to use HTTP servers beyond Go standard library. It currently supports standard and [fasthttp](https://github.com/valyala/fasthttp) server. | ||||
| - Context, Request and Response are converted to interfaces. [More...](https://github.com/labstack/echo/issues/146) | ||||
| - Handler signature is changed to `func (c echo.Context) error`. | ||||
| - Dropped auto wrapping of handler and middleware to enforce compile time check. | ||||
| - APIs to run middleware before or after the router, which doesn't require `Echo#Hook` API now. | ||||
| - Ability to define middleware at route level. | ||||
| - `Echo#HTTPError` exposed it's fields `Code` and `Message`. | ||||
| - Option to specify log format in logger middleware and default logger. | ||||
|  | ||||
| #### API | ||||
|  | ||||
| v1 | v2 | ||||
| --- | --- | ||||
| `Context#Query()` | `Context#QueryParam()` | ||||
| `Context#Form()`  | `Context#FormValue()` | ||||
|  | ||||
| ### FAQ | ||||
|  | ||||
| Q. How to access original objects from interfaces? | ||||
|  | ||||
| A. Only if you need to... | ||||
|  | ||||
| ```go | ||||
| // `*http.Request` | ||||
| c.Request().(*standard.Request).Request | ||||
|  | ||||
| // `*http.URL` | ||||
| c.Request().URL().(*standard.URL).URL | ||||
|  | ||||
| // Request `http.Header` | ||||
| c.Request().Header().(*standard.Header).Header | ||||
|  | ||||
| // `http.ResponseWriter` | ||||
| c.Response().(*standard.Response).ResponseWriter | ||||
|  | ||||
| // Response `http.Header` | ||||
| c.Response().Header().(*standard.Header).Header | ||||
| ``` | ||||
|  | ||||
| Q. How to use standard handler and middleware? | ||||
|  | ||||
| A. | ||||
|  | ||||
| ```go | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/labstack/echo" | ||||
| 	"github.com/labstack/echo/engine/standard" | ||||
| ) | ||||
|  | ||||
| // Standard middleware | ||||
| func middleware(next http.Handler) http.Handler { | ||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		println("standard middleware") | ||||
| 		next.ServeHTTP(w, r) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // Standard handler | ||||
| func handler(w http.ResponseWriter, r *http.Request) { | ||||
| 	println("standard handler") | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	e := echo.New() | ||||
| 	e.Use(standard.WrapMiddleware(middleware)) | ||||
| 	e.GET("/", standard.WrapHandler(http.HandlerFunc(handler))) | ||||
| 	e.Run(standard.New(":1323")) | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ### Next? | ||||
|  | ||||
| - Browse through [recipes](/recipes/hello-world) freshly converted to v2. | ||||
| - Read documentation and dig into test cases. | ||||
							
								
								
									
										50
									
								
								website/content/guide/migration.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								website/content/guide/migration.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| +++ | ||||
| title = "Migration" | ||||
| description = "Migration" | ||||
| [menu.side] | ||||
|   name = "Migration" | ||||
|   parent = "guide" | ||||
|   weight = 2 | ||||
| +++ | ||||
|  | ||||
| ## V3 | ||||
|  | ||||
| ### Change Log | ||||
|  | ||||
| - Automatic TLS certificates via [Let's Encrypt](https://letsencrypt.org/) | ||||
| - Built-in support for graceful shutdown | ||||
| - Dropped static middleware in favor of `Echo#Static` | ||||
| - Utility functions to wrap standard handler and middleware | ||||
| - `Map` type as shorthand for `map[string]interface{}` | ||||
| - Context now wraps standard net/http Request and Response | ||||
| - New configuration | ||||
| 	- `Echo#ShutdownTimeout` | ||||
| 	- `Echo#DisableHTTP2` | ||||
| - New API | ||||
| 	- `Echo#Start()` | ||||
| 	- `Echo#StartTLS()` | ||||
| 	- `Echo#StartAutoTLS()` | ||||
| 	- `Echo#StartServer()` | ||||
|     - `Echo#Shutdown()` | ||||
|     - `Echo#ShutdownTLS()` | ||||
|     - `Context#Scheme()` | ||||
|     - `Context#RealIP()` | ||||
|     - `Context#IsTLS()` | ||||
| - Exposed the following properties instead of setter / getter functions on `Echo` instance: | ||||
| 	- `Binder` | ||||
| 	- `Renderer` | ||||
| 	- `HTTPErrorHandler` | ||||
| 	- `Debug` | ||||
| 	- `Logger` | ||||
| - Enhanced redirect and CORS middleware | ||||
| - Dropped API | ||||
| 	- `Echo#Run()` | ||||
| 	- `Context#P()` | ||||
| - Dropped standard `Context` support  | ||||
| - Dropped support for `fasthttp` | ||||
| - Dropped deprecated API | ||||
| - Moved `Logger` interface to root level | ||||
| - Moved website and recipes to the main repo | ||||
| - Updated docs and fixed numerous issues | ||||
|  | ||||
| ### [Recipes](/recipes/hello-world) | ||||
| @@ -8,7 +8,6 @@ title = "Index" | ||||
|  | ||||
| - Optimized HTTP router which smartly prioritize routes | ||||
| - Build robust and scalable RESTful APIs | ||||
| - Run with standard HTTP server or FastHTTP server | ||||
| - Group APIs | ||||
| - Extensible middleware framework | ||||
| - Define middleware at root, group or route level | ||||
| @@ -18,11 +17,12 @@ title = "Index" | ||||
| - Template rendering with any template engine | ||||
| - Define your format for the logger | ||||
| - Highly customizable | ||||
|  | ||||
| - Automatic TLS via Let’s Encrypt | ||||
| - Built-in graceful shutdown | ||||
|  | ||||
| ## Performance | ||||
|  | ||||
| <img style="width: 75%;" src="http://i.imgur.com/F2V7TfO.png" alt="Performance"> | ||||
| <img style="width: 75%;" src="https://i.imgur.com/F2V7TfO.png" alt="Performance"> | ||||
|  | ||||
| ## Quick Start | ||||
|  | ||||
|   | ||||
| @@ -40,3 +40,4 @@ | ||||
| <script async defer id="github-bjs" src="//buttons.github.io/buttons.js"></script> | ||||
| <script src="https://cdn.labstack.com/scripts/prism.js"></script> | ||||
| <script src="https://cdn.labstack.com/scripts/doc.js"></script> | ||||
| <script src="{{ .Site.BaseURL }}scripts/main.js"></script> | ||||
|   | ||||
| @@ -24,7 +24,7 @@ | ||||
|   <link rel="stylesheet" href="https://cdn.labstack.com/styles/prism.css"> | ||||
|   <link rel="stylesheet" href="https://cdn.labstack.com/styles/base.css"> | ||||
|   <link rel="stylesheet" href="https://cdn.labstack.com/styles/doc.css"> | ||||
|   <link rel="stylesheet" href="{{ .Site.BaseURL }}/styles/main.css"> | ||||
|   <link rel="stylesheet" href="{{ .Site.BaseURL}}/styles/main.css"> | ||||
|   <script> | ||||
|     (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ | ||||
|     (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), | ||||
|   | ||||
| @@ -5,9 +5,15 @@ | ||||
|   <a class="support w3-btn w3-white w3-border w3-border-theme w3-round-xlarge" href="/support-echo"> | ||||
|     <i class="fa fa-heart" aria-hidden="true"></i> Support Echo | ||||
|   </a> | ||||
| 	<h4> | ||||
| 		<select class="w3-select w3-border" onchange="version(this);"> | ||||
| 			<option value="/v2">v2</option> | ||||
| 			<option value="/" selected>v3</option> | ||||
|     </select> | ||||
| 	</h4> | ||||
|   {{ $currentNode := . }} | ||||
|   {{ range .Site.Menus.side }} | ||||
|     <h4>{{ .Pre }} {{ .Name }}</h4> | ||||
|     <h3>{{ .Pre }} {{ .Name }}</h3> | ||||
|       {{ range .Children }} | ||||
|         <a{{ if $currentNode.IsMenuCurrent "side" . }} class="active"{{ end }} href="{{ .URL }}"> | ||||
|           {{ .Name }} | ||||
|   | ||||
							
								
								
									
										3
									
								
								website/static/scripts/main.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								website/static/scripts/main.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| function version(e) { | ||||
| 	window.location = e.value; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user