replace UI with single page application (#1704)
							
								
								
									
										14
									
								
								.drone.yml
									
									
									
									
									
								
							
							
						
						| @@ -1,23 +1,21 @@ | ||||
| workspace: | ||||
|   base: /drone | ||||
|   base: /go | ||||
|   path: src/github.com/drone/drone | ||||
|  | ||||
| pipeline: | ||||
|   test: | ||||
|     image: drone/golang:1.5 | ||||
|   backend: | ||||
|     image: golang:1.6 | ||||
|     environment: | ||||
|       - GO15VENDOREXPERIMENT=1 | ||||
|       - GOPATH=/drone | ||||
|     commands: | ||||
|       - export PATH=$PATH:$GOPATH/bin | ||||
|       - make deps gen | ||||
|       - make test test_postgres test_mysql | ||||
|  | ||||
|   build: | ||||
|     image: drone/golang:1.5 | ||||
|   compile: | ||||
|     image: golang:1.6 | ||||
|     environment: | ||||
|       - GO15VENDOREXPERIMENT=1 | ||||
|       - GOPATH=/drone | ||||
|       - GOPATH=/go | ||||
|     commands: | ||||
|       - export PATH=$PATH:$GOPATH/bin | ||||
|       - make build | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| eyJhbGciOiJIUzI1NiJ9.d29ya3NwYWNlOgogIGJhc2U6IC9kcm9uZQogIHBhdGg6IHNyYy9naXRodWIuY29tL2Ryb25lL2Ryb25lCgpwaXBlbGluZToKICB0ZXN0OgogICAgaW1hZ2U6IGRyb25lL2dvbGFuZzoxLjUKICAgIGVudmlyb25tZW50OgogICAgICAtIEdPMTVWRU5ET1JFWFBFUklNRU5UPTEKICAgICAgLSBHT1BBVEg9L2Ryb25lCiAgICBjb21tYW5kczoKICAgICAgLSBleHBvcnQgUEFUSD0kUEFUSDokR09QQVRIL2JpbgogICAgICAtIG1ha2UgZGVwcyBnZW4KICAgICAgLSBtYWtlIHRlc3QgdGVzdF9wb3N0Z3JlcyB0ZXN0X215c3FsCgogIGJ1aWxkOgogICAgaW1hZ2U6IGRyb25lL2dvbGFuZzoxLjUKICAgIGVudmlyb25tZW50OgogICAgICAtIEdPMTVWRU5ET1JFWFBFUklNRU5UPTEKICAgICAgLSBHT1BBVEg9L2Ryb25lCiAgICBjb21tYW5kczoKICAgICAgLSBleHBvcnQgUEFUSD0kUEFUSDokR09QQVRIL2JpbgogICAgICAtIG1ha2UgYnVpbGQKICAgIHdoZW46CiAgICAgIGV2ZW50OiBwdXNoCgogIHB1Ymxpc2g6CiAgICBpbWFnZTogczMKICAgIGFjbDogcHVibGljLXJlYWQKICAgIGJ1Y2tldDogZG93bmxvYWRzLmRyb25lLmlvCiAgICBzb3VyY2U6IHJlbGVhc2UvKiovKi4qCiAgICB3aGVuOgogICAgICBldmVudDogcHVzaAogICAgICBicmFuY2g6IG1hc3RlcgoKICBkb2NrZXI6CiAgICByZXBvOiBkcm9uZS9kcm9uZQogICAgdGFnOiBbICIwLjUuMCIsICIwLjUiIF0KICAgIHN0b3JhZ2VfZHJpdmVyOiBvdmVybGF5CiAgICB3aGVuOgogICAgICBicmFuY2g6IG1hc3RlcgogICAgICBldmVudDogcHVzaAoKc2VydmljZXM6CiAgcG9zdGdyZXM6CiAgICBpbWFnZTogcG9zdGdyZXM6OS40LjUKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9cG9zdGdyZXMKICBteXNxbDoKICAgIGltYWdlOiBteXNxbDo1LjYuMjcKICAgIGVudmlyb25tZW50OgogICAgICAtIE1ZU1FMX0RBVEFCQVNFPXRlc3QKICAgICAgLSBNWVNRTF9BTExPV19FTVBUWV9QQVNTV09SRD15ZXMK.PFyNsvPRNRtL_9Tlgsqa0IDMIgzUrlk53eV4QJjZ3hU | ||||
| eyJhbGciOiJIUzI1NiJ9.d29ya3NwYWNlOgogIGJhc2U6IC9nbwogIHBhdGg6IHNyYy9naXRodWIuY29tL2Ryb25lL2Ryb25lCgpwaXBlbGluZToKICBiYWNrZW5kOgogICAgaW1hZ2U6IGdvbGFuZzoxLjYKICAgIGVudmlyb25tZW50OgogICAgICAtIEdPMTVWRU5ET1JFWFBFUklNRU5UPTEKICAgIGNvbW1hbmRzOgogICAgICAtIG1ha2UgZGVwcyBnZW4KICAgICAgLSBtYWtlIHRlc3QgdGVzdF9wb3N0Z3JlcyB0ZXN0X215c3FsCgogIGNvbXBpbGU6CiAgICBpbWFnZTogZ29sYW5nOjEuNgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gR08xNVZFTkRPUkVYUEVSSU1FTlQ9MQogICAgICAtIEdPUEFUSD0vZ28KICAgIGNvbW1hbmRzOgogICAgICAtIGV4cG9ydCBQQVRIPSRQQVRIOiRHT1BBVEgvYmluCiAgICAgIC0gbWFrZSBidWlsZAogICAgd2hlbjoKICAgICAgZXZlbnQ6IHB1c2gKCiAgcHVibGlzaDoKICAgIGltYWdlOiBzMwogICAgYWNsOiBwdWJsaWMtcmVhZAogICAgYnVja2V0OiBkb3dubG9hZHMuZHJvbmUuaW8KICAgIHNvdXJjZTogcmVsZWFzZS8qKi8qLioKICAgIHdoZW46CiAgICAgIGV2ZW50OiBwdXNoCiAgICAgIGJyYW5jaDogbWFzdGVyCgogIGRvY2tlcjoKICAgIHJlcG86IGRyb25lL2Ryb25lCiAgICB0YWc6IFsgIjAuNS4wIiwgIjAuNSIgXQogICAgc3RvcmFnZV9kcml2ZXI6IG92ZXJsYXkKICAgIHdoZW46CiAgICAgIGJyYW5jaDogbWFzdGVyCiAgICAgIGV2ZW50OiBwdXNoCgpzZXJ2aWNlczoKICBwb3N0Z3JlczoKICAgIGltYWdlOiBwb3N0Z3Jlczo5LjQuNQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfVVNFUj1wb3N0Z3JlcwogIG15c3FsOgogICAgaW1hZ2U6IG15c3FsOjUuNi4yNwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTVlTUUxfREFUQUJBU0U9dGVzdAogICAgICAtIE1ZU1FMX0FMTE9XX0VNUFRZX1BBU1NXT1JEPXllcwo.kQIwqIgs7PnoKIGmzJ6hlbWTbV5zK0w4HVWsux79P3s | ||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,7 +1,6 @@ | ||||
| drone/drone | ||||
| *.sqlite | ||||
| *_gen.go | ||||
| *.html | ||||
| #*.css | ||||
| *.txt | ||||
| *.zip | ||||
| @@ -13,6 +12,8 @@ drone/drone | ||||
| temp/ | ||||
| release/ | ||||
|  | ||||
| server/frontend/bower_components | ||||
| server/frontend/build | ||||
| server/swagger/files/*.json | ||||
|  | ||||
| # vendored repositories that we don't actually need | ||||
|   | ||||
							
								
								
									
										18
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						| @@ -10,22 +10,20 @@ endif | ||||
|  | ||||
| all: gen build_static | ||||
|  | ||||
| deps: | ||||
| deps: deps_backend deps_frontend | ||||
|  | ||||
| deps_frontend: | ||||
| 	go get -u github.com/drone/drone-ui/dist | ||||
|  | ||||
| deps_backend: | ||||
| 	go get -u golang.org/x/tools/cmd/cover | ||||
| 	go get -u github.com/eknkc/amber/... | ||||
| 	go get -u github.com/eknkc/amber | ||||
| 	go get -u github.com/jteeuwen/go-bindata/... | ||||
| 	go get -u github.com/elazarl/go-bindata-assetfs/... | ||||
| 	go get -u github.com/dchest/jsmin | ||||
| 	go get -u github.com/franela/goblin | ||||
|  | ||||
| gen: gen_static gen_template gen_migrations | ||||
|  | ||||
| gen_static: | ||||
| 	go generate github.com/drone/drone/static | ||||
| gen: gen_template gen_migrations | ||||
|  | ||||
| gen_template: | ||||
| 	go generate github.com/drone/drone/template | ||||
| 	go generate github.com/drone/drone/server/template | ||||
|  | ||||
| gen_migrations: | ||||
| 	go generate github.com/drone/drone/store/datastore/ddl | ||||
|   | ||||
							
								
								
									
										14
									
								
								cache/helper.go
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -53,6 +53,20 @@ func GetRepos(c context.Context, user *model.User) ([]*model.RepoLite, error) { | ||||
| 	return repos, nil | ||||
| } | ||||
|  | ||||
| // GetRepoMap returns the list of user repositories from the cache | ||||
| // associated with the current context in a map structure. | ||||
| func GetRepoMap(c context.Context, user *model.User) (map[string]bool, error) { | ||||
| 	repos, err := GetRepos(c, user) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	repom := map[string]bool{} | ||||
| 	for _, repo := range repos { | ||||
| 		repom[repo.FullName] = true | ||||
| 	} | ||||
| 	return repom, nil | ||||
| } | ||||
|  | ||||
| // DeleteRepos evicts the cached user repositories from the cache associated | ||||
| // with the current context. | ||||
| func DeleteRepos(c context.Context, user *model.User) error { | ||||
|   | ||||
| @@ -43,6 +43,9 @@ type Client interface { | ||||
| 	// RepoPatch updates a repository. | ||||
| 	RepoPatch(*model.Repo) (*model.Repo, error) | ||||
|  | ||||
| 	// RepoChown updates a repository owner. | ||||
| 	RepoChown(string, string) (*model.Repo, error) | ||||
|  | ||||
| 	// RepoDel deletes a repository. | ||||
| 	RepoDel(string, string) error | ||||
|  | ||||
|   | ||||
| @@ -32,6 +32,7 @@ const ( | ||||
| 	pathFeed       = "%s/api/user/feed" | ||||
| 	pathRepos      = "%s/api/user/repos" | ||||
| 	pathRepo       = "%s/api/repos/%s/%s" | ||||
| 	pathChown      = "%s/api/repos/%s/%s/chown" | ||||
| 	pathEncrypt    = "%s/api/repos/%s/%s/encrypt" | ||||
| 	pathBuilds     = "%s/api/repos/%s/%s/builds" | ||||
| 	pathBuild      = "%s/api/repos/%s/%s/builds/%v" | ||||
| @@ -153,6 +154,14 @@ func (c *client) RepoPost(owner string, name string) (*model.Repo, error) { | ||||
| 	return out, err | ||||
| } | ||||
|  | ||||
| // RepoChow updates a repository owner. | ||||
| func (c *client) RepoChown(owner string, name string) (*model.Repo, error) { | ||||
| 	out := new(model.Repo) | ||||
| 	uri := fmt.Sprintf(pathChown, c.base, owner, name) | ||||
| 	err := c.post(uri, nil, out) | ||||
| 	return out, err | ||||
| } | ||||
|  | ||||
| // RepoPatch updates a repository. | ||||
| func (c *client) RepoPatch(in *model.Repo) (*model.Repo, error) { | ||||
| 	out := new(model.Repo) | ||||
|   | ||||
| @@ -1,2 +0,0 @@ | ||||
| /etc/init/drone.conf | ||||
| /etc/drone/dronerc | ||||
| @@ -1,7 +0,0 @@ | ||||
| Package: drone | ||||
| Version: 0.4 | ||||
| Section: base | ||||
| Priority: optional | ||||
| Architecture: amd64 | ||||
| Maintainer: Brad Rydzewski <brad@drone.io> | ||||
| Description: Drone continuous integration server | ||||
| @@ -1,24 +0,0 @@ | ||||
| #!/bin/sh | ||||
| set -e | ||||
|  | ||||
| case "$1" in | ||||
|   abort-upgrade|abort-remove|abort-deconfigure|configure) | ||||
|     ;; | ||||
|  | ||||
|   *) | ||||
|     echo "postinst called with unknown argument \`$1'" >&2 | ||||
|     exit 1 | ||||
|     ;; | ||||
| esac | ||||
|  | ||||
| echo "Starting drone ..." | ||||
| if [ -f /etc/init/drone.conf ]; then | ||||
|     if pidof /usr/local/bin/drone >/dev/null; then | ||||
|         service drone stop || exit $? | ||||
|     fi | ||||
|     service drone start && echo "Drone started." | ||||
| fi | ||||
|  | ||||
| #DEBHELPER# | ||||
|  | ||||
| exit 0 | ||||
| @@ -1,26 +0,0 @@ | ||||
| #!/bin/sh | ||||
|  | ||||
| set -e | ||||
| set -u | ||||
|  | ||||
| case "$1" in | ||||
|   remove|remove-in-favour|deconfigure|deconfigure-in-favour) | ||||
|     if [ -f /etc/init/drone.conf ]; then | ||||
|       echo "Stopping drone ..." | ||||
|       service drone stop || exit $? | ||||
|       echo "Drone Stopped." | ||||
|     fi | ||||
|     ;; | ||||
|  | ||||
|   upgrade|failed-upgrade) | ||||
|     ;; | ||||
|  | ||||
|   *) | ||||
|     echo "prerm called with unknown argument \`$1'" >&2 | ||||
|     exit 1 | ||||
|     ;; | ||||
| esac | ||||
|  | ||||
| #DEBHELPER# | ||||
|  | ||||
| exit 0 | ||||
| @@ -1,31 +0,0 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| # server configuration | ||||
|  | ||||
| SERVER_ADDR=":80" | ||||
| #SERVER_CERT="" | ||||
| #SERVER_KEY="" | ||||
|  | ||||
| # database configuration | ||||
|  | ||||
| DATABASE_DRIVER="sqlite3" | ||||
| DATABASE_CONFIG="/var/lib/drone/drone.sqlite" | ||||
|  | ||||
| # remote configuration | ||||
|  | ||||
| CLIENT="" # oauth2 client. REQUIRED | ||||
| SECRET="" # oauth2 secret. REQUIRED | ||||
|  | ||||
| REMOTE_DRIVER="github" | ||||
| REMOTE_CONFIG="https://github.com?client_id=$CLIENT&client_secret=$SECRET" | ||||
|  | ||||
| # docker configuration | ||||
|  | ||||
| DOCKER_HOST="unix:///var/run/docker.sock" | ||||
| #DOCKER_CERT="" | ||||
| #DOCKER_KEY="" | ||||
| #DOCKER_CA="" | ||||
|  | ||||
| # plugin configuration | ||||
|  | ||||
| PLUGIN_FILTER="plugins/*" | ||||
| @@ -1,12 +0,0 @@ | ||||
| start on (filesystem and net-device-up) | ||||
|  | ||||
| chdir /var/lib/drone | ||||
| console log | ||||
|  | ||||
| script | ||||
| 	set -a | ||||
| 	if [ -f /etc/drone/dronerc ]; then | ||||
| 		. /etc/drone/dronerc | ||||
| 	fi | ||||
| 	/usr/local/bin/drone | ||||
| end script | ||||
| @@ -1,19 +0,0 @@ | ||||
| # /etc/nsswitch.conf | ||||
| # | ||||
| # Example configuration of GNU Name Service Switch functionality. | ||||
| # If you have the `glibc-doc-reference' and `info' packages installed, try: | ||||
| # `info libc "Name Service Switch"' for information about this file. | ||||
|  | ||||
| passwd:         compat | ||||
| group:          compat | ||||
| shadow:         compat | ||||
|  | ||||
| hosts:          files dns | ||||
| networks:       files | ||||
|  | ||||
| protocols:      files | ||||
| services:       files | ||||
| ethers:         files | ||||
| rpc:            files | ||||
|  | ||||
| netgroup:       nis | ||||
| @@ -1,6 +0,0 @@ | ||||
| // +build ignore | ||||
|  | ||||
| // This program converts amber templates to standard | ||||
| // Go template files. | ||||
|  | ||||
| package main | ||||
| @@ -1,60 +0,0 @@ | ||||
| // +build ignore | ||||
|  | ||||
| // This program minifies JavaScript files | ||||
| // $ go run generate-js.go -dir scripts/ -out scripts/drone.min.js | ||||
|  | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	"github.com/dchest/jsmin" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	dir = flag.String("dir", "scripts/", "") | ||||
| 	out = flag.String("o", "scripts/drone.min.js", "") | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	flag.Parse() | ||||
|  | ||||
| 	var buf bytes.Buffer | ||||
|  | ||||
| 	// walk the directory tree and write all | ||||
| 	// javascript files to the buffer. | ||||
| 	filepath.Walk(*dir, func(path string, info os.FileInfo, err error) error { | ||||
| 		if filepath.Ext(path) != ".js" { | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		f, err := os.Open(path) | ||||
| 		if err != nil { | ||||
| 			return nil | ||||
| 		} | ||||
| 		defer f.Close() | ||||
|  | ||||
| 		// write the file name to the minified output | ||||
| 		fmt.Fprintf(&buf, "// %s\n", path) | ||||
|  | ||||
| 		// copy the file to the buffer | ||||
| 		_, err = io.Copy(&buf, f) | ||||
| 		return err | ||||
| 	}) | ||||
|  | ||||
| 	// minifies the javascript | ||||
| 	data, err := jsmin.Minify(buf.Bytes()) | ||||
| 	if err != nil { | ||||
| 		fmt.Println(err) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
|  | ||||
| 	// write the minified output | ||||
| 	ioutil.WriteFile(*out, data, 0700) | ||||
| } | ||||
| @@ -1,21 +0,0 @@ | ||||
| #!/bin/bash | ||||
| set -e | ||||
|  | ||||
| cd /tmp | ||||
|  | ||||
| # cleanup previously downloaded and unpacked files. | ||||
| rm -rf libsass | ||||
| rm -rf sassc | ||||
|  | ||||
| # download the latest build of sassc | ||||
| git clone --depth=1 git://github.com/sass/libsass.git | ||||
| git clone --depth=1 git://github.com/sass/sassc.git | ||||
|  | ||||
| export SASS_LIBSASS_PATH=/tmp/libsass | ||||
|  | ||||
| # build the sassc binary | ||||
| cd sassc | ||||
| make | ||||
|  | ||||
| # isntall the sassc binary | ||||
| install -t /usr/local/bin bin/sassc | ||||
| @@ -1,18 +0,0 @@ | ||||
| #!/bin/bash | ||||
| set -e | ||||
|  | ||||
| cd /tmp | ||||
|  | ||||
| # cleanup previously downloaded and unpacked files. | ||||
| rm -rf sqlite-autoconf-3081101.tar.gz | ||||
| rm -rf sqlite-autoconf-3081101 | ||||
|  | ||||
| # download sqlite | ||||
| curl -O https://www.sqlite.org/2015/sqlite-autoconf-3081101.tar.gz | ||||
| tar xzf sqlite-autoconf-3081101.tar.gz | ||||
|  | ||||
| # build and install | ||||
| cd sqlite-autoconf-3081101 | ||||
| ./configure -prefix=/scratch/usr/local | ||||
| make | ||||
| make install | ||||
| @@ -2,7 +2,6 @@ package router | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/gin-gonic/gin" | ||||
|  | ||||
| @@ -10,17 +9,19 @@ import ( | ||||
| 	"github.com/drone/drone/router/middleware/session" | ||||
| 	"github.com/drone/drone/router/middleware/token" | ||||
| 	"github.com/drone/drone/server" | ||||
| 	"github.com/drone/drone/static" | ||||
| 	"github.com/drone/drone/template" | ||||
| 	"github.com/drone/drone/server/template" | ||||
|  | ||||
| 	"github.com/drone/drone-ui/dist" | ||||
| ) | ||||
|  | ||||
| // Load loads the router | ||||
| func Load(middleware ...gin.HandlerFunc) http.Handler { | ||||
|  | ||||
| 	e := gin.New() | ||||
| 	e.Use(gin.Recovery()) | ||||
|  | ||||
| 	e.SetHTMLTemplate(template.Load()) | ||||
| 	e.StaticFS("/static", static.FileSystem()) | ||||
| 	e.StaticFS("/static", dist.AssetFS()) | ||||
|  | ||||
| 	e.Use(header.NoCache) | ||||
| 	e.Use(header.Options) | ||||
| @@ -29,35 +30,11 @@ func Load(middleware ...gin.HandlerFunc) http.Handler { | ||||
| 	e.Use(session.SetUser()) | ||||
| 	e.Use(token.Refresh) | ||||
|  | ||||
| 	e.GET("/", server.ShowIndex) | ||||
| 	e.GET("/repos", server.ShowAllRepos) | ||||
| 	e.GET("/login", server.ShowLogin) | ||||
| 	e.GET("/login/form", server.ShowLoginForm) | ||||
| 	e.GET("/logout", server.GetLogout) | ||||
| 	e.NoRoute(server.ShowIndex) | ||||
|  | ||||
| 	// TODO below will Go away with React UI | ||||
| 	settings := e.Group("/settings") | ||||
| 	{ | ||||
| 		settings.Use(session.MustUser()) | ||||
| 		settings.GET("/profile", server.ShowUser) | ||||
| 	} | ||||
| 	repo := e.Group("/repos/:owner/:name") | ||||
| 	{ | ||||
| 		repo.Use(session.SetRepo()) | ||||
| 		repo.Use(session.SetPerm()) | ||||
| 		repo.Use(session.MustPull) | ||||
|  | ||||
| 		repo.GET("", server.ShowRepo) | ||||
| 		repo.GET("/builds/:number", server.ShowBuild) | ||||
| 		repo.GET("/builds/:number/:job", server.ShowBuild) | ||||
|  | ||||
| 		repo_settings := repo.Group("/settings") | ||||
| 		{ | ||||
| 			repo_settings.GET("", session.MustPush, server.ShowRepoConf) | ||||
| 			repo_settings.GET("/encrypt", session.MustPush, server.ShowRepoEncrypt) | ||||
| 			repo_settings.GET("/badges", server.ShowRepoBadges) | ||||
| 		} | ||||
| 	} | ||||
| 	// TODO above will Go away with React UI | ||||
|  | ||||
| 	user := e.Group("/api/user") | ||||
| @@ -129,6 +106,16 @@ func Load(middleware ...gin.HandlerFunc) http.Handler { | ||||
| 		stream.GET("/:owner/:name", server.GetRepoEvents) | ||||
| 		stream.GET("/:owner/:name/:build/:number", server.GetStream) | ||||
| 	} | ||||
| 	ws := e.Group("/ws") | ||||
| 	{ | ||||
| 		ws.GET("/feed", server.EventStream) | ||||
| 		ws.GET("/logs/:owner/:name/:build/:number", | ||||
| 			session.SetRepo(), | ||||
| 			session.SetPerm(), | ||||
| 			session.MustPull, | ||||
| 			server.LogStream, | ||||
| 		) | ||||
| 	} | ||||
|  | ||||
| 	auth := e.Group("/authorize") | ||||
| 	{ | ||||
| @@ -184,34 +171,5 @@ func Load(middleware ...gin.HandlerFunc) http.Handler { | ||||
| 	// 	bots.POST("/slack/:command", Slack) | ||||
| 	// } | ||||
|  | ||||
| 	return normalize(e) | ||||
| } | ||||
|  | ||||
| // THIS HACK JOB IS GOING AWAY SOON. | ||||
| // | ||||
| // normalize is a helper function to work around the following | ||||
| // issue with gin. https://github.com/gin-gonic/gin/issues/388 | ||||
| func normalize(h http.Handler) http.Handler { | ||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
|  | ||||
| 		parts := strings.Split(r.URL.Path, "/")[1:] | ||||
| 		switch parts[0] { | ||||
| 		case "settings", "bots", "repos", "api", "login", "logout", "", "authorize", "hook", "static", "gitlab": | ||||
| 			// no-op | ||||
| 		default: | ||||
|  | ||||
| 			if len(parts) > 2 && parts[2] != "settings" { | ||||
| 				parts = append(parts[:2], append([]string{"builds"}, parts[2:]...)...) | ||||
| 			} | ||||
|  | ||||
| 			// prefix the URL with /repo so that it | ||||
| 			// can be effectively routed. | ||||
| 			parts = append([]string{"", "repos"}, parts...) | ||||
|  | ||||
| 			// reconstruct the path | ||||
| 			r.URL.Path = strings.Join(parts, "/") | ||||
| 		} | ||||
|  | ||||
| 		h.ServeHTTP(w, r) | ||||
| 	}) | ||||
| 	return e | ||||
| } | ||||
|   | ||||
| @@ -110,11 +110,7 @@ func GetLogin(c *gin.Context) { | ||||
| 	} | ||||
|  | ||||
| 	httputil.SetCookie(c.Writer, c.Request, "user_sess", tokenstr) | ||||
| 	redirect := httputil.GetCookie(c.Request, "user_last") | ||||
| 	if len(redirect) == 0 { | ||||
| 		redirect = "/" | ||||
| 	} | ||||
| 	c.Redirect(303, redirect) | ||||
| 	c.Redirect(303, "/") | ||||
|  | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										201
									
								
								server/pages.go
									
									
									
									
									
								
							
							
						
						| @@ -1,192 +1,15 @@ | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/gin-gonic/gin" | ||||
|  | ||||
| 	"github.com/drone/drone/cache" | ||||
| 	"github.com/drone/drone/model" | ||||
| 	"github.com/drone/drone/router/middleware/session" | ||||
| 	"github.com/drone/drone/shared/httputil" | ||||
| 	"github.com/drone/drone/shared/token" | ||||
| 	"github.com/drone/drone/store" | ||||
| ) | ||||
|  | ||||
| // ShowIndex serves the main Drone application page. | ||||
| func ShowIndex(c *gin.Context) { | ||||
| 	user := session.User(c) | ||||
| 	if user == nil { | ||||
| 		c.Redirect(http.StatusSeeOther, "/login") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// get the repository list from the cache | ||||
| 	repos, err := cache.GetRepos(c, user) | ||||
| 	if err != nil { | ||||
| 		c.String(400, err.Error()) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// filter to only show the currently active ones | ||||
| 	activeRepos, err := store.GetRepoListOf(c, repos) | ||||
| 	if err != nil { | ||||
| 		c.String(400, err.Error()) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	c.HTML(200, "index.html", gin.H{ | ||||
| 		"User":  user, | ||||
| 		"Repos": activeRepos, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func ShowAllRepos(c *gin.Context) { | ||||
| 	user := session.User(c) | ||||
| 	if user == nil { | ||||
| 		c.Redirect(http.StatusSeeOther, "/login") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// get the repository list from the cache | ||||
| 	repos, err := cache.GetRepos(c, user) | ||||
| 	if err != nil { | ||||
| 		c.String(400, err.Error()) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	c.HTML(200, "repos.html", gin.H{ | ||||
| 		"User":  user, | ||||
| 		"Repos": repos, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func ShowLogin(c *gin.Context) { | ||||
| 	c.HTML(200, "login.html", gin.H{"Error": c.Query("error")}) | ||||
| } | ||||
|  | ||||
| func ShowLoginForm(c *gin.Context) { | ||||
| 	c.HTML(200, "login_form.html", gin.H{}) | ||||
| } | ||||
|  | ||||
| func ShowUser(c *gin.Context) { | ||||
| 	user := session.User(c) | ||||
| 	token, _ := token.New( | ||||
| 		token.CsrfToken, | ||||
| 		user.Login, | ||||
| 	).Sign(user.Hash) | ||||
|  | ||||
| 	c.HTML(200, "user.html", gin.H{ | ||||
| 		"User": user, | ||||
| 		"Csrf": token, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func ShowRepo(c *gin.Context) { | ||||
| 	user := session.User(c) | ||||
| 	repo := session.Repo(c) | ||||
|  | ||||
| 	builds, _ := store.GetBuildList(c, repo) | ||||
| 	groups := []*model.BuildGroup{} | ||||
|  | ||||
| 	var curr *model.BuildGroup | ||||
| 	for _, build := range builds { | ||||
| 		date := time.Unix(build.Created, 0).Format("Jan 2 2006") | ||||
| 		if curr == nil || curr.Date != date { | ||||
| 			curr = &model.BuildGroup{} | ||||
| 			curr.Date = date | ||||
| 			groups = append(groups, curr) | ||||
| 		} | ||||
| 		curr.Builds = append(curr.Builds, build) | ||||
| 	} | ||||
|  | ||||
| 	httputil.SetCookie(c.Writer, c.Request, "user_last", repo.FullName) | ||||
|  | ||||
| 	c.HTML(200, "repo.html", gin.H{ | ||||
| 		"User":   user, | ||||
| 		"Repo":   repo, | ||||
| 		"Builds": builds, | ||||
| 		"Groups": groups, | ||||
| 	}) | ||||
|  | ||||
| } | ||||
|  | ||||
| func ShowRepoConf(c *gin.Context) { | ||||
|  | ||||
| 	user := session.User(c) | ||||
| 	repo := session.Repo(c) | ||||
|  | ||||
| 	token, _ := token.New( | ||||
| 		token.CsrfToken, | ||||
| 		user.Login, | ||||
| 	).Sign(user.Hash) | ||||
|  | ||||
| 	c.HTML(200, "repo_config.html", gin.H{ | ||||
| 		"User": user, | ||||
| 		"Repo": repo, | ||||
| 		"Csrf": token, | ||||
| 		"Link": httputil.GetURL(c.Request), | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func ShowRepoEncrypt(c *gin.Context) { | ||||
| 	user := session.User(c) | ||||
| 	repo := session.Repo(c) | ||||
|  | ||||
| 	token, _ := token.New( | ||||
| 		token.CsrfToken, | ||||
| 		user.Login, | ||||
| 	).Sign(user.Hash) | ||||
|  | ||||
| 	c.HTML(200, "repo_secret.html", gin.H{ | ||||
| 		"User": user, | ||||
| 		"Repo": repo, | ||||
| 		"Csrf": token, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func ShowRepoBadges(c *gin.Context) { | ||||
| 	user := session.User(c) | ||||
| 	repo := session.Repo(c) | ||||
|  | ||||
| 	c.HTML(200, "repo_badge.html", gin.H{ | ||||
| 		"User": user, | ||||
| 		"Repo": repo, | ||||
| 		"Link": httputil.GetURL(c.Request), | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func ShowBuild(c *gin.Context) { | ||||
| 	user := session.User(c) | ||||
| 	repo := session.Repo(c) | ||||
| 	num, _ := strconv.Atoi(c.Param("number")) | ||||
| 	seq, _ := strconv.Atoi(c.Param("job")) | ||||
| 	if seq == 0 { | ||||
| 		seq = 1 | ||||
| 	} | ||||
|  | ||||
| 	build, err := store.GetBuildNumber(c, repo, num) | ||||
| 	if err != nil { | ||||
| 		c.AbortWithError(404, err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	jobs, err := store.GetJobList(c, build) | ||||
| 	if err != nil { | ||||
| 		c.AbortWithError(404, err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	var job *model.Job | ||||
| 	for _, j := range jobs { | ||||
| 		if j.Number == seq { | ||||
| 			job = j | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	httputil.SetCookie(c.Writer, c.Request, "user_last", repo.FullName) | ||||
|  | ||||
| 	var csrf string | ||||
| 	if user != nil { | ||||
| @@ -196,12 +19,20 @@ func ShowBuild(c *gin.Context) { | ||||
| 		).Sign(user.Hash) | ||||
| 	} | ||||
|  | ||||
| 	c.HTML(200, "build.html", gin.H{ | ||||
| 		"User":  user, | ||||
| 		"Repo":  repo, | ||||
| 		"Build": build, | ||||
| 		"Jobs":  jobs, | ||||
| 		"Job":   job, | ||||
| 		"Csrf":  csrf, | ||||
| 	c.HTML(200, "index.html", gin.H{ | ||||
| 		"user": user, | ||||
| 		"csrf": csrf, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // ShowLogin is a legacy endpoint that now redirects to | ||||
| // initiliaze the oauth flow | ||||
| func ShowLogin(c *gin.Context) { | ||||
| 	c.Redirect(303, "/authorize") | ||||
| } | ||||
|  | ||||
| // ShowLoginForm displays a login form for systems like Gogs that do not | ||||
| // yet support oauth workflows. | ||||
| func ShowLoginForm(c *gin.Context) { | ||||
| 	c.HTML(200, "login.html", gin.H{}) | ||||
| } | ||||
|   | ||||
							
								
								
									
										188
									
								
								server/stream.go
									
									
									
									
									
								
							
							
						
						| @@ -5,17 +5,18 @@ import ( | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| 	"strconv" | ||||
|  | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/drone/drone/bus" | ||||
| 	"github.com/drone/drone/cache" | ||||
| 	"github.com/drone/drone/model" | ||||
| 	"github.com/drone/drone/router/middleware/session" | ||||
| 	"github.com/drone/drone/store" | ||||
| 	"github.com/drone/drone/stream" | ||||
|  | ||||
| 	log "github.com/Sirupsen/logrus" | ||||
|  | ||||
| 	"github.com/Sirupsen/logrus" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/gorilla/websocket" | ||||
| 	"github.com/manucorporat/sse" | ||||
| ) | ||||
|  | ||||
| @@ -30,14 +31,14 @@ func GetRepoEvents(c *gin.Context) { | ||||
| 	defer func() { | ||||
| 		bus.Unsubscribe(c, eventc) | ||||
| 		close(eventc) | ||||
| 		log.Infof("closed event stream") | ||||
| 		logrus.Infof("closed event stream") | ||||
| 	}() | ||||
|  | ||||
| 	c.Stream(func(w io.Writer) bool { | ||||
| 		select { | ||||
| 		case event := <-eventc: | ||||
| 			if event == nil { | ||||
| 				log.Infof("nil event received") | ||||
| 				logrus.Infof("nil event received") | ||||
| 				return false | ||||
| 			} | ||||
|  | ||||
| @@ -75,13 +76,13 @@ func GetStream(c *gin.Context) { | ||||
|  | ||||
| 	build, err := store.GetBuildNumber(c, repo, buildn) | ||||
| 	if err != nil { | ||||
| 		log.Debugln("stream cannot get build number.", err) | ||||
| 		logrus.Debugln("stream cannot get build number.", err) | ||||
| 		c.AbortWithError(404, err) | ||||
| 		return | ||||
| 	} | ||||
| 	job, err := store.GetJobNumber(c, build, jobn) | ||||
| 	if err != nil { | ||||
| 		log.Debugln("stream cannot get job number.", err) | ||||
| 		logrus.Debugln("stream cannot get job number.", err) | ||||
| 		c.AbortWithError(404, err) | ||||
| 		return | ||||
| 	} | ||||
| @@ -112,5 +113,174 @@ func GetStream(c *gin.Context) { | ||||
| 		c.Writer.Flush() | ||||
| 	} | ||||
|  | ||||
| 	log.Debugf("Closed stream %s#%d", repo.FullName, build.Number) | ||||
| 	logrus.Debugf("Closed stream %s#%d", repo.FullName, build.Number) | ||||
| } | ||||
|  | ||||
| var ( | ||||
| 	// Time allowed to write the file to the client. | ||||
| 	writeWait = 5 * time.Second | ||||
|  | ||||
| 	// Time allowed to read the next pong message from the client. | ||||
| 	pongWait = 60 * time.Second | ||||
|  | ||||
| 	// Send pings to client with this period. Must be less than pongWait. | ||||
| 	pingPeriod = 30 * time.Second | ||||
| ) | ||||
|  | ||||
| // LogStream streams the build log output to the client. | ||||
| func LogStream(c *gin.Context) { | ||||
| 	repo := session.Repo(c) | ||||
| 	buildn, _ := strconv.Atoi(c.Param("build")) | ||||
| 	jobn, _ := strconv.Atoi(c.Param("number")) | ||||
|  | ||||
| 	c.Writer.Header().Set("Content-Type", "text/event-stream") | ||||
|  | ||||
| 	build, err := store.GetBuildNumber(c, repo, buildn) | ||||
| 	if err != nil { | ||||
| 		logrus.Debugln("stream cannot get build number.", err) | ||||
| 		c.AbortWithError(404, err) | ||||
| 		return | ||||
| 	} | ||||
| 	job, err := store.GetJobNumber(c, build, jobn) | ||||
| 	if err != nil { | ||||
| 		logrus.Debugln("stream cannot get job number.", err) | ||||
| 		c.AbortWithError(404, err) | ||||
| 		return | ||||
| 	} | ||||
| 	if job.Status != model.StatusRunning { | ||||
| 		logrus.Debugln("stream not found.") | ||||
| 		c.AbortWithStatus(404) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	ws, err := upgrader.Upgrade(c.Writer, c.Request, nil) | ||||
| 	if err != nil { | ||||
| 		if _, ok := err.(websocket.HandshakeError); !ok { | ||||
| 			logrus.Errorf("Cannot upgrade websocket. %s", err) | ||||
| 		} | ||||
| 		return | ||||
| 	} | ||||
| 	logrus.Debugf("Successfull upgraded websocket") | ||||
|  | ||||
| 	ticker := time.NewTicker(pingPeriod) | ||||
| 	defer ticker.Stop() | ||||
|  | ||||
| 	rc, err := stream.Reader(c, stream.ToKey(job.ID)) | ||||
| 	if err != nil { | ||||
| 		c.AbortWithError(404, err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	quitc := make(chan bool) | ||||
| 	defer func() { | ||||
| 		quitc <- true | ||||
| 		close(quitc) | ||||
| 		rc.Close() | ||||
| 		ws.Close() | ||||
| 		logrus.Debug("Successfully closed websocket") | ||||
| 	}() | ||||
|  | ||||
| 	go func() { | ||||
| 		defer func() { | ||||
| 			recover() | ||||
| 		}() | ||||
| 		for { | ||||
| 			select { | ||||
| 			case <-quitc: | ||||
| 				return | ||||
| 			case <-ticker.C: | ||||
| 				err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait)) | ||||
| 				if err != nil { | ||||
| 					return | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	var scanner = bufio.NewScanner(rc) | ||||
| 	var b []byte | ||||
| 	for scanner.Scan() { | ||||
| 		b = scanner.Bytes() | ||||
| 		if len(b) == 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 		ws.SetWriteDeadline(time.Now().Add(writeWait)) | ||||
| 		ws.WriteMessage(websocket.TextMessage, b) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // EventStream produces the User event stream, sending all repository, build | ||||
| // and agent events to the client. | ||||
| func EventStream(c *gin.Context) { | ||||
| 	ws, err := upgrader.Upgrade(c.Writer, c.Request, nil) | ||||
| 	if err != nil { | ||||
| 		if _, ok := err.(websocket.HandshakeError); !ok { | ||||
| 			logrus.Errorf("Cannot upgrade websocket. %s", err) | ||||
| 		} | ||||
| 		return | ||||
| 	} | ||||
| 	logrus.Debugf("Successfull upgraded websocket") | ||||
|  | ||||
| 	user := session.User(c) | ||||
| 	repo := map[string]bool{} | ||||
| 	if user != nil { | ||||
| 		repo, _ = cache.GetRepoMap(c, user) | ||||
| 	} | ||||
|  | ||||
| 	ticker := time.NewTicker(pingPeriod) | ||||
| 	quitc := make(chan bool) | ||||
| 	eventc := make(chan *bus.Event, 10) | ||||
| 	bus.Subscribe(c, eventc) | ||||
| 	defer func() { | ||||
| 		ticker.Stop() | ||||
| 		bus.Unsubscribe(c, eventc) | ||||
| 		quitc <- true | ||||
| 		close(quitc) | ||||
| 		close(eventc) | ||||
| 		ws.Close() | ||||
| 		logrus.Debug("Successfully closed websocket") | ||||
| 	}() | ||||
|  | ||||
| 	go func() { | ||||
| 		defer func() { | ||||
| 			recover() | ||||
| 		}() | ||||
| 		for { | ||||
| 			select { | ||||
| 			case <-quitc: | ||||
| 				return | ||||
| 			case event := <-eventc: | ||||
| 				if event == nil { | ||||
| 					return | ||||
| 				} | ||||
| 				if repo[event.Repo.FullName] || !event.Repo.IsPrivate { | ||||
| 					ws.SetWriteDeadline(time.Now().Add(writeWait)) | ||||
| 					ws.WriteJSON(event) | ||||
| 				} | ||||
| 			case <-ticker.C: | ||||
| 				err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait)) | ||||
| 				if err != nil { | ||||
| 					return | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	reader(ws) | ||||
| } | ||||
|  | ||||
| func reader(ws *websocket.Conn) { | ||||
| 	defer ws.Close() | ||||
| 	ws.SetReadLimit(512) | ||||
| 	ws.SetReadDeadline(time.Now().Add(pongWait)) | ||||
| 	ws.SetPongHandler(func(string) error { | ||||
| 		ws.SetReadDeadline(time.Now().Add(pongWait)) | ||||
| 		return nil | ||||
| 	}) | ||||
| 	for { | ||||
| 		_, _, err := ws.ReadMessage() | ||||
| 		if err != nil { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										22
									
								
								server/template/files/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,22 @@ | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
| <head> | ||||
|   <meta charset="utf-8"/> | ||||
|   <meta content="width=device-width, initial-scale=1" name="viewport"/> | ||||
|   <meta content="ie=edge" http-equiv="x-ua-compatible"/> | ||||
| 	{{ if .csrf }}<meta name="csrf-token" content="{{ .csrf }}" />{{ end }} | ||||
|   <link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet"/> | ||||
|   <link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet"/> | ||||
|   <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/> | ||||
|   <link href="/static/app.css" rel="stylesheet"/> | ||||
|   <link href="/static/favicon.ico" rel="icon" type="image/x-icon"/> | ||||
| </head> | ||||
| <body> | ||||
| <div id="app"></div> | ||||
| <script> | ||||
|   window.STATE_FROM_SERVER={{ . | json }}; | ||||
| </script> | ||||
| <script src="https://code.getmdl.io/1.1.3/material.min.js"></script> | ||||
| <script src="/static/app.js"></script> | ||||
| </body> | ||||
| </html> | ||||
							
								
								
									
										20
									
								
								server/template/files/login.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,20 @@ | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
| <head> | ||||
|   <meta charset="utf-8"/> | ||||
|   <meta content="width=device-width, initial-scale=1" name="viewport"/> | ||||
|   <meta content="ie=edge" http-equiv="x-ua-compatible"/> | ||||
|   <link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet"/> | ||||
|   <link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet"/> | ||||
|   <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/> | ||||
|   <link href="/static/favicon.ico" rel="icon" type="image/x-icon"/> | ||||
| </head> | ||||
| <body> | ||||
|   <form actin="/authorize" method="post"> | ||||
|     <input type="text" placeholder="Username" name="username" /> | ||||
|     <input type="password" placeholder="Password" name="password" /> | ||||
|     <input type="submit" value="Login" /> | ||||
|   </form> | ||||
|   <script src="https://code.getmdl.io/1.1.3/material.min.js"></script> | ||||
| </body> | ||||
| </html> | ||||
							
								
								
									
										1
									
								
								server/template/files/logout.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| LOGOUT | ||||
							
								
								
									
										31
									
								
								server/template/template.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,31 @@ | ||||
| package template | ||||
|  | ||||
| //go:generate go-bindata -pkg template -o template_gen.go files/ | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"html/template" | ||||
| 	"path/filepath" | ||||
| ) | ||||
|  | ||||
| // Load loads the templates from the embedded file map. This function will not | ||||
| // compile if go generate is not executed before. | ||||
| func Load() *template.Template { | ||||
| 	dir, _ := AssetDir("files") | ||||
| 	tmpl := template.New("_").Funcs(template.FuncMap{"json": marshal}) | ||||
| 	for _, name := range dir { | ||||
| 		path := filepath.Join("files", name) | ||||
| 		src := MustAsset(path) | ||||
| 		tmpl = template.Must( | ||||
| 			tmpl.New(name).Parse(string(src)), | ||||
| 		) | ||||
| 	} | ||||
|  | ||||
| 	return tmpl | ||||
| } | ||||
|  | ||||
| // marshal is a helper function to render data as JSON inside the template. | ||||
| func marshal(v interface{}) template.JS { | ||||
| 	a, _ := json.Marshal(v) | ||||
| 	return template.JS(a) | ||||
| } | ||||
| @@ -1,70 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||
| <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> | ||||
| <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||
| 	 width="466px" height="423.891px" viewBox="0 0 466 423.891" enable-background="new 0 0 466 423.891" xml:space="preserve"> | ||||
| <rect x="-43.5" y="37.005" display="none" fill-rule="evenodd" clip-rule="evenodd" fill="#E7E7E7" width="792" height="612"/> | ||||
| <g> | ||||
| 	<path id="outline_7_" fill-rule="evenodd" clip-rule="evenodd" d="M242.133,168.481h47.146v48.194h23.837 | ||||
| 		c11.008,0,22.33-1.962,32.755-5.494c5.123-1.736,10.872-4.154,15.926-7.193c-6.656-8.689-10.053-19.661-11.054-30.476 | ||||
| 		c-1.358-14.71,1.609-33.855,11.564-45.368l4.956-5.732l5.905,4.747c14.867,11.946,27.372,28.638,29.577,47.665 | ||||
| 		c17.901-5.266,38.921-4.02,54.701,5.088l6.475,3.734l-3.408,6.652c-13.345,26.046-41.246,34.113-68.524,32.687 | ||||
| 		c-40.817,101.663-129.68,149.794-237.428,149.794c-55.666,0-106.738-20.81-135.821-70.197l-0.477-0.807l-4.238-8.621 | ||||
| 		C4.195,271.415,0.93,247.6,3.145,223.803l0.664-7.127h40.315v-48.194h47.143v-47.145h94.292V74.191h56.574V168.481z"/> | ||||
| 	<g display="none"> | ||||
| 		<path display="inline" fill="#394D54" d="M61.093,319.89c6.023,0,11.763-0.157,17.219-0.464c0.476-0.026,0.932-0.063,1.402-0.092 | ||||
| 			c0.005-0.002,0.008-0.002,0.012-0.002c13.872-0.855,25.876-2.708,35.902-5.57c0.002-0.002,0.004-0.002,0.006-0.002 | ||||
| 			c1.823-0.521,3.588-1.07,5.282-1.656c1.894-0.657,2.896-2.725,2.241-4.618c-0.656-1.895-2.722-2.899-4.618-2.24 | ||||
| 			c-12.734,4.412-29.535,6.842-50.125,7.298c-0.002,0-0.004,0-0.005,0c-10.477,0.232-21.93-0.044-34.352-0.843c0,0,0,0-0.001,0 | ||||
| 			c-0.635-0.038-1.259-0.075-1.9-0.118c-1.995-0.128-3.731,1.374-3.869,3.375c-0.136,1.999,1.376,3.73,3.375,3.866 | ||||
| 			c2.537,0.173,5.03,0.321,7.49,0.453c0.392,0.021,0.77,0.034,1.158,0.054l0,0C47.566,319.697,54.504,319.89,61.093,319.89z"/> | ||||
| 	</g> | ||||
| 	<g id="Containers_8_"> | ||||
| 		<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M86.209,179.744h3.227v34.052h-3.227V179.744z M80.02,179.744 | ||||
| 			h3.354v34.052H80.02V179.744z M73.828,179.744h3.354v34.052h-3.354V179.744z M67.636,179.744h3.354v34.052h-3.354V179.744z | ||||
| 			 M61.446,179.744H64.8v34.052h-3.354V179.744z M55.384,179.744h3.224v34.052h-3.224V179.744z M51.981,176.338h40.858v40.86H51.981 | ||||
| 			V176.338z"/> | ||||
| 		<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M133.354,132.598h3.229v34.051h-3.229V132.598z M127.165,132.598 | ||||
| 			h3.354v34.051h-3.354V132.598z M120.973,132.598h3.354v34.051h-3.354V132.598z M114.781,132.598h3.354v34.051h-3.354V132.598z | ||||
| 			 M108.593,132.598h3.352v34.051h-3.352V132.598z M102.531,132.598h3.222v34.051h-3.222V132.598z M99.124,129.193h40.863v40.859 | ||||
| 			H99.124V129.193z"/> | ||||
| 		<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M133.354,179.744h3.229v34.052h-3.229V179.744z M127.165,179.744 | ||||
| 			h3.354v34.052h-3.354V179.744z M120.973,179.744h3.354v34.052h-3.354V179.744z M114.781,179.744h3.354v34.052h-3.354V179.744z | ||||
| 			 M108.593,179.744h3.352v34.052h-3.352V179.744z M102.531,179.744h3.222v34.052h-3.222V179.744z M99.124,176.338h40.863v40.86 | ||||
| 			H99.124V176.338z"/> | ||||
| 		<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M180.501,179.744h3.225v34.052h-3.225V179.744z M174.31,179.744 | ||||
| 			h3.355v34.052h-3.355V179.744z M168.12,179.744h3.354v34.052h-3.354V179.744z M161.928,179.744h3.354v34.052h-3.354V179.744z | ||||
| 			 M155.736,179.744h3.354v34.052h-3.354V179.744z M149.676,179.744h3.222v34.052h-3.222V179.744z M146.271,176.338h40.861v40.86 | ||||
| 			h-40.861V176.338z"/> | ||||
| 		<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M180.501,132.598h3.225v34.051h-3.225V132.598z M174.31,132.598 | ||||
| 			h3.355v34.051h-3.355V132.598z M168.12,132.598h3.354v34.051h-3.354V132.598z M161.928,132.598h3.354v34.051h-3.354V132.598z | ||||
| 			 M155.736,132.598h3.354v34.051h-3.354V132.598z M149.676,132.598h3.222v34.051h-3.222V132.598z M146.271,129.193h40.861v40.859 | ||||
| 			h-40.861V129.193z"/> | ||||
| 		<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M227.647,179.744h3.226v34.052h-3.226V179.744z M221.457,179.744 | ||||
| 			h3.354v34.052h-3.354V179.744z M215.265,179.744h3.354v34.052h-3.354V179.744z M209.073,179.744h3.354v34.052h-3.354V179.744z | ||||
| 			 M202.884,179.744h3.354v34.052h-3.354V179.744z M196.821,179.744h3.224v34.052h-3.224V179.744z M193.416,176.338h40.861v40.86 | ||||
| 			h-40.861V176.338z"/> | ||||
| 		<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M227.647,132.598h3.226v34.051h-3.226V132.598z M221.457,132.598 | ||||
| 			h3.354v34.051h-3.354V132.598z M215.265,132.598h3.354v34.051h-3.354V132.598z M209.073,132.598h3.354v34.051h-3.354V132.598z | ||||
| 			 M202.884,132.598h3.354v34.051h-3.354V132.598z M196.821,132.598h3.224v34.051h-3.224V132.598z M193.416,129.193h40.861v40.859 | ||||
| 			h-40.861V129.193z"/> | ||||
| 		<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M227.647,85.451h3.226v34.053h-3.226V85.451z M221.457,85.451 | ||||
| 			h3.354v34.053h-3.354V85.451z M215.265,85.451h3.354v34.053h-3.354V85.451z M209.073,85.451h3.354v34.053h-3.354V85.451z | ||||
| 			 M202.884,85.451h3.354v34.053h-3.354V85.451z M196.821,85.451h3.224v34.053h-3.224V85.451z M193.416,82.048h40.861v40.86h-40.861 | ||||
| 			V82.048z"/> | ||||
| 		<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M274.792,179.744h3.224v34.052h-3.224V179.744z M268.602,179.744 | ||||
| 			h3.352v34.052h-3.352V179.744z M262.408,179.744h3.354v34.052h-3.354V179.744z M256.218,179.744h3.354v34.052h-3.354V179.744z | ||||
| 			 M250.026,179.744h3.354v34.052h-3.354V179.744z M243.964,179.744h3.227v34.052h-3.227V179.744z M240.561,176.338h40.86v40.86 | ||||
| 			h-40.86V176.338z"/> | ||||
| 	</g> | ||||
| 	<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M137.428,283.445c6.225,0,11.271,5.049,11.271,11.272 | ||||
| 		c0,6.225-5.046,11.271-11.271,11.271c-6.226,0-11.272-5.046-11.272-11.271C126.156,288.494,131.202,283.445,137.428,283.445"/> | ||||
| 	<path fill-rule="evenodd" clip-rule="evenodd" d="M137.428,286.644c1.031,0,2.015,0.194,2.923,0.546 | ||||
| 		c-0.984,0.569-1.65,1.635-1.65,2.854c0,1.82,1.476,3.293,3.296,3.293c1.247,0,2.329-0.693,2.89-1.715 | ||||
| 		c0.395,0.953,0.615,1.999,0.615,3.097c0,4.458-3.615,8.073-8.073,8.073c-4.458,0-8.074-3.615-8.074-8.073 | ||||
| 		C129.354,290.258,132.971,286.644,137.428,286.644"/> | ||||
| 	<path fill="#FFFFFF" d="M167.394,364.677c-27.916-13.247-43.239-31.256-51.765-50.915c-10.37,2.961-22.835,4.852-37.317,5.664 | ||||
| 		c-5.457,0.307-11.196,0.464-17.219,0.464c-6.942,0-14.26-0.205-21.94-0.613c25.6,25.585,57.094,45.283,115.408,45.645 | ||||
| 		C158.866,364.921,163.14,364.837,167.394,364.677z"/> | ||||
| </g> | ||||
| </svg> | ||||
| Before Width: | Height: | Size: 6.4 KiB | 
| @@ -1,70 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||
| <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> | ||||
| <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||
| 	 width="466px" height="423.891px" viewBox="0 0 466 423.891" enable-background="new 0 0 466 423.891" xml:space="preserve"> | ||||
| <rect x="-43.5" y="37.005" display="none" fill-rule="evenodd" clip-rule="evenodd" fill="#E7E7E7" width="792" height="612"/> | ||||
| <g> | ||||
| 	<path fill="#6699cc" id="outline_7_" fill-rule="evenodd" clip-rule="evenodd" d="M242.133,168.481h47.146v48.194h23.837 | ||||
| 		c11.008,0,22.33-1.962,32.755-5.494c5.123-1.736,10.872-4.154,15.926-7.193c-6.656-8.689-10.053-19.661-11.054-30.476 | ||||
| 		c-1.358-14.71,1.609-33.855,11.564-45.368l4.956-5.732l5.905,4.747c14.867,11.946,27.372,28.638,29.577,47.665 | ||||
| 		c17.901-5.266,38.921-4.02,54.701,5.088l6.475,3.734l-3.408,6.652c-13.345,26.046-41.246,34.113-68.524,32.687 | ||||
| 		c-40.817,101.663-129.68,149.794-237.428,149.794c-55.666,0-106.738-20.81-135.821-70.197l-0.477-0.807l-4.238-8.621 | ||||
| 		C4.195,271.415,0.93,247.6,3.145,223.803l0.664-7.127h40.315v-48.194h47.143v-47.145h94.292V74.191h56.574V168.481z"/> | ||||
| 	<g display="none"> | ||||
| 		<path display="inline" fill="#394D54" d="M61.093,319.89c6.023,0,11.763-0.157,17.219-0.464c0.476-0.026,0.932-0.063,1.402-0.092 | ||||
| 			c0.005-0.002,0.008-0.002,0.012-0.002c13.872-0.855,25.876-2.708,35.902-5.57c0.002-0.002,0.004-0.002,0.006-0.002 | ||||
| 			c1.823-0.521,3.588-1.07,5.282-1.656c1.894-0.657,2.896-2.725,2.241-4.618c-0.656-1.895-2.722-2.899-4.618-2.24 | ||||
| 			c-12.734,4.412-29.535,6.842-50.125,7.298c-0.002,0-0.004,0-0.005,0c-10.477,0.232-21.93-0.044-34.352-0.843c0,0,0,0-0.001,0 | ||||
| 			c-0.635-0.038-1.259-0.075-1.9-0.118c-1.995-0.128-3.731,1.374-3.869,3.375c-0.136,1.999,1.376,3.73,3.375,3.866 | ||||
| 			c2.537,0.173,5.03,0.321,7.49,0.453c0.392,0.021,0.77,0.034,1.158,0.054l0,0C47.566,319.697,54.504,319.89,61.093,319.89z"/> | ||||
| 	</g> | ||||
| 	<g id="Containers_8_"> | ||||
| 		<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M86.209,179.744h3.227v34.052h-3.227V179.744z M80.02,179.744 | ||||
| 			h3.354v34.052H80.02V179.744z M73.828,179.744h3.354v34.052h-3.354V179.744z M67.636,179.744h3.354v34.052h-3.354V179.744z | ||||
| 			 M61.446,179.744H64.8v34.052h-3.354V179.744z M55.384,179.744h3.224v34.052h-3.224V179.744z M51.981,176.338h40.858v40.86H51.981 | ||||
| 			V176.338z"/> | ||||
| 		<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M133.354,132.598h3.229v34.051h-3.229V132.598z M127.165,132.598 | ||||
| 			h3.354v34.051h-3.354V132.598z M120.973,132.598h3.354v34.051h-3.354V132.598z M114.781,132.598h3.354v34.051h-3.354V132.598z | ||||
| 			 M108.593,132.598h3.352v34.051h-3.352V132.598z M102.531,132.598h3.222v34.051h-3.222V132.598z M99.124,129.193h40.863v40.859 | ||||
| 			H99.124V129.193z"/> | ||||
| 		<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M133.354,179.744h3.229v34.052h-3.229V179.744z M127.165,179.744 | ||||
| 			h3.354v34.052h-3.354V179.744z M120.973,179.744h3.354v34.052h-3.354V179.744z M114.781,179.744h3.354v34.052h-3.354V179.744z | ||||
| 			 M108.593,179.744h3.352v34.052h-3.352V179.744z M102.531,179.744h3.222v34.052h-3.222V179.744z M99.124,176.338h40.863v40.86 | ||||
| 			H99.124V176.338z"/> | ||||
| 		<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M180.501,179.744h3.225v34.052h-3.225V179.744z M174.31,179.744 | ||||
| 			h3.355v34.052h-3.355V179.744z M168.12,179.744h3.354v34.052h-3.354V179.744z M161.928,179.744h3.354v34.052h-3.354V179.744z | ||||
| 			 M155.736,179.744h3.354v34.052h-3.354V179.744z M149.676,179.744h3.222v34.052h-3.222V179.744z M146.271,176.338h40.861v40.86 | ||||
| 			h-40.861V176.338z"/> | ||||
| 		<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M180.501,132.598h3.225v34.051h-3.225V132.598z M174.31,132.598 | ||||
| 			h3.355v34.051h-3.355V132.598z M168.12,132.598h3.354v34.051h-3.354V132.598z M161.928,132.598h3.354v34.051h-3.354V132.598z | ||||
| 			 M155.736,132.598h3.354v34.051h-3.354V132.598z M149.676,132.598h3.222v34.051h-3.222V132.598z M146.271,129.193h40.861v40.859 | ||||
| 			h-40.861V129.193z"/> | ||||
| 		<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M227.647,179.744h3.226v34.052h-3.226V179.744z M221.457,179.744 | ||||
| 			h3.354v34.052h-3.354V179.744z M215.265,179.744h3.354v34.052h-3.354V179.744z M209.073,179.744h3.354v34.052h-3.354V179.744z | ||||
| 			 M202.884,179.744h3.354v34.052h-3.354V179.744z M196.821,179.744h3.224v34.052h-3.224V179.744z M193.416,176.338h40.861v40.86 | ||||
| 			h-40.861V176.338z"/> | ||||
| 		<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M227.647,132.598h3.226v34.051h-3.226V132.598z M221.457,132.598 | ||||
| 			h3.354v34.051h-3.354V132.598z M215.265,132.598h3.354v34.051h-3.354V132.598z M209.073,132.598h3.354v34.051h-3.354V132.598z | ||||
| 			 M202.884,132.598h3.354v34.051h-3.354V132.598z M196.821,132.598h3.224v34.051h-3.224V132.598z M193.416,129.193h40.861v40.859 | ||||
| 			h-40.861V129.193z"/> | ||||
| 		<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M227.647,85.451h3.226v34.053h-3.226V85.451z M221.457,85.451 | ||||
| 			h3.354v34.053h-3.354V85.451z M215.265,85.451h3.354v34.053h-3.354V85.451z M209.073,85.451h3.354v34.053h-3.354V85.451z | ||||
| 			 M202.884,85.451h3.354v34.053h-3.354V85.451z M196.821,85.451h3.224v34.053h-3.224V85.451z M193.416,82.048h40.861v40.86h-40.861 | ||||
| 			V82.048z"/> | ||||
| 		<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M274.792,179.744h3.224v34.052h-3.224V179.744z M268.602,179.744 | ||||
| 			h3.352v34.052h-3.352V179.744z M262.408,179.744h3.354v34.052h-3.354V179.744z M256.218,179.744h3.354v34.052h-3.354V179.744z | ||||
| 			 M250.026,179.744h3.354v34.052h-3.354V179.744z M243.964,179.744h3.227v34.052h-3.227V179.744z M240.561,176.338h40.86v40.86 | ||||
| 			h-40.86V176.338z"/> | ||||
| 	</g> | ||||
| 	<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M137.428,283.445c6.225,0,11.271,5.049,11.271,11.272 | ||||
| 		c0,6.225-5.046,11.271-11.271,11.271c-6.226,0-11.272-5.046-11.272-11.271C126.156,288.494,131.202,283.445,137.428,283.445"/> | ||||
| 	<path fill="#6699cc" fill-rule="evenodd" clip-rule="evenodd" d="M137.428,286.644c1.031,0,2.015,0.194,2.923,0.546 | ||||
| 		c-0.984,0.569-1.65,1.635-1.65,2.854c0,1.82,1.476,3.293,3.296,3.293c1.247,0,2.329-0.693,2.89-1.715 | ||||
| 		c0.395,0.953,0.615,1.999,0.615,3.097c0,4.458-3.615,8.073-8.073,8.073c-4.458,0-8.074-3.615-8.074-8.073 | ||||
| 		C129.354,290.258,132.971,286.644,137.428,286.644"/> | ||||
| 	<path fill="#FFFFFF" d="M167.394,364.677c-27.916-13.247-43.239-31.256-51.765-50.915c-10.37,2.961-22.835,4.852-37.317,5.664 | ||||
| 		c-5.457,0.307-11.196,0.464-17.219,0.464c-6.942,0-14.26-0.205-21.94-0.613c25.6,25.585,57.094,45.283,115.408,45.645 | ||||
| 		C158.866,364.921,163.14,364.837,167.394,364.677z"/> | ||||
| </g> | ||||
| </svg> | ||||
| Before Width: | Height: | Size: 6.4 KiB | 
| @@ -1,13 +0,0 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" width="466px" height="423.891px" viewBox="0 0 466 423.891" enable-background="new 0 0 466 423.891" xml:space="preserve"> | ||||
| <rect x="-43.5" y="37.005" display="none" fill-rule="evenodd" clip-rule="evenodd" fill="#E7E7E7" width="792" height="612"/> | ||||
| <g> | ||||
| 	<path fill="#FFF" id="outline_7_" fill-rule="evenodd" clip-rule="evenodd" d="M242.133,168.481h47.146v48.194h23.837   c11.008,0,22.33-1.962,32.755-5.494c5.123-1.736,10.872-4.154,15.926-7.193c-6.656-8.689-10.053-19.661-11.054-30.476   c-1.358-14.71,1.609-33.855,11.564-45.368l4.956-5.732l5.905,4.747c14.867,11.946,27.372,28.638,29.577,47.665   c17.901-5.266,38.921-4.02,54.701,5.088l6.475,3.734l-3.408,6.652c-13.345,26.046-41.246,34.113-68.524,32.687   c-40.817,101.663-129.68,149.794-237.428,149.794c-55.666,0-106.738-20.81-135.821-70.197l-0.477-0.807l-4.238-8.621   C4.195,271.415,0.93,247.6,3.145,223.803l0.664-7.127h40.315v-48.194h47.143v-47.145h94.292V74.191h56.574V168.481z"/> | ||||
| 	<g display="none"> | ||||
| 		<path display="inline" fill="#FFF" d="M61.093,319.89c6.023,0,11.763-0.157,17.219-0.464c0.476-0.026,0.932-0.063,1.402-0.092    c0.005-0.002,0.008-0.002,0.012-0.002c13.872-0.855,25.876-2.708,35.902-5.57c0.002-0.002,0.004-0.002,0.006-0.002    c1.823-0.521,3.588-1.07,5.282-1.656c1.894-0.657,2.896-2.725,2.241-4.618c-0.656-1.895-2.722-2.899-4.618-2.24    c-12.734,4.412-29.535,6.842-50.125,7.298c-0.002,0-0.004,0-0.005,0c-10.477,0.232-21.93-0.044-34.352-0.843c0,0,0,0-0.001,0    c-0.635-0.038-1.259-0.075-1.9-0.118c-1.995-0.128-3.731,1.374-3.869,3.375c-0.136,1.999,1.376,3.73,3.375,3.866    c2.537,0.173,5.03,0.321,7.49,0.453c0.392,0.021,0.77,0.034,1.158,0.054l0,0C47.566,319.697,54.504,319.89,61.093,319.89z"/> | ||||
| 	</g> | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 	<path fill="#FFF" d="M167.394,364.677c-27.916-13.247-43.239-31.256-51.765-50.915c-10.37,2.961-22.835,4.852-37.317,5.664   c-5.457,0.307-11.196,0.464-17.219,0.464c-6.942,0-14.26-0.205-21.94-0.613c25.6,25.585,57.094,45.283,115.408,45.645   C158.866,364.921,163.14,364.837,167.394,364.677z"/> | ||||
| </g> | ||||
| </svg> | ||||
| Before Width: | Height: | Size: 2.1 KiB | 
| Before Width: | Height: | Size: 5.3 KiB | 
| Before Width: | Height: | Size: 1.1 KiB | 
| Before Width: | Height: | Size: 366 B | 
| @@ -1,93 +0,0 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||
|  | ||||
| <svg | ||||
|    xmlns:dc="http://purl.org/dc/elements/1.1/" | ||||
|    xmlns:cc="http://creativecommons.org/ns#" | ||||
|    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    width="43" | ||||
|    height="36.350704" | ||||
|    id="svg2" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.48.3.1 r9886" | ||||
|    sodipodi:docname="drone-logo-no-circle.svg"> | ||||
|   <defs | ||||
|      id="defs4" /> | ||||
|   <sodipodi:namedview | ||||
|      id="base" | ||||
|      pagecolor="#ffffff" | ||||
|      bordercolor="#2b303b" | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageopacity="0.0" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="2.8" | ||||
|      inkscape:cx="26.576205" | ||||
|      inkscape:cy="-72.54425" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="layer1" | ||||
|      showgrid="true" | ||||
|      inkscape:snap-global="false" | ||||
|      inkscape:window-width="1295" | ||||
|      inkscape:window-height="744" | ||||
|      inkscape:window-x="65" | ||||
|      inkscape:window-y="24" | ||||
|      inkscape:window-maximized="1" | ||||
|      fit-margin-top="0" | ||||
|      fit-margin-left="0" | ||||
|      fit-margin-right="0" | ||||
|      fit-margin-bottom="0"> | ||||
|     <inkscape:grid | ||||
|        type="xygrid" | ||||
|        id="grid2996" | ||||
|        empspacing="5" | ||||
|        visible="true" | ||||
|        enabled="true" | ||||
|        snapvisiblegridlinesonly="true" | ||||
|        originx="-21.720779px" | ||||
|        originy="-990.37188px" /> | ||||
|   </sodipodi:namedview> | ||||
|   <metadata | ||||
|      id="metadata7"> | ||||
|     <rdf:RDF> | ||||
|       <cc:Work | ||||
|          rdf:about=""> | ||||
|         <dc:format>image/svg+xml</dc:format> | ||||
|         <dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||
|         <dc:title></dc:title> | ||||
|       </cc:Work> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
|   <g | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1" | ||||
|      transform="translate(-21.720779,-25.639593)"> | ||||
|     <path | ||||
|        sodipodi:type="arc" | ||||
|        style="fill:#2b303b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" | ||||
|        id="path2998" | ||||
|        sodipodi:cx="172.10474" | ||||
|        sodipodi:cy="458.39249" | ||||
|        sodipodi:rx="5.4295697" | ||||
|        sodipodi:ry="5.0507627" | ||||
|        d="m 177.53431,458.39249 c 0,2.78946 -2.43091,5.05076 -5.42957,5.05076 -2.99867,0 -5.42957,-2.2613 -5.42957,-5.05076 0,-2.78946 2.4309,-5.05077 5.42957,-5.05077 2.99866,0 5.42957,2.26131 5.42957,5.05077 z" | ||||
|        transform="matrix(1.0129716,0,0,1.0889445,-131.11643,-452.42373)" /> | ||||
|     <path | ||||
|        style="fill:#2b303b;fill-opacity:1;stroke-width:0;stroke-miterlimit:4" | ||||
|        d="m 43.220779,25.640247 c 9.60163,0.0752 20.51786,6.8438 21.5,19.6 l -13,0 c 0,0 -1.67472,-7.04733 -8.5,-7 -6.82528,0.0473 -8.5,7 -8.5,7 l -13,0 c 0.63161,-12.53073 11.36576,-19.67935 21.5,-19.6 z" | ||||
|        id="rect3810" | ||||
|        inkscape:connector-curvature="0" | ||||
|        sodipodi:nodetypes="scczccs" /> | ||||
|     <path | ||||
|        style="fill:#2b303b;fill-opacity:1;stroke-width:0;stroke-miterlimit:4" | ||||
|        d="m 43.310069,61.990247 c -7.159395,0.01905 -13.847588,-5.383347 -16.58929,-13.75 l 8,0 c 0,0 1.72575,6.96782 8.55103,6.92049 6.82528,-0.0473 8.44897,-6.92049 8.44897,-6.92049 l 8,0 c -1.783351,8.850973 -9.251314,13.730946 -16.41071,13.75 z" | ||||
|        id="rect3810-1" | ||||
|        inkscape:connector-curvature="0" | ||||
|        sodipodi:nodetypes="scczccs" /> | ||||
|   </g> | ||||
| </svg> | ||||
| Before Width: | Height: | Size: 3.4 KiB | 
| @@ -1,93 +0,0 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||
|  | ||||
| <svg | ||||
|    xmlns:dc="http://purl.org/dc/elements/1.1/" | ||||
|    xmlns:cc="http://creativecommons.org/ns#" | ||||
|    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    width="43" | ||||
|    height="36.350704" | ||||
|    id="svg2" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.48.3.1 r9886" | ||||
|    sodipodi:docname="drone-logo-no-circle.svg"> | ||||
|   <defs | ||||
|      id="defs4" /> | ||||
|   <sodipodi:namedview | ||||
|      id="base" | ||||
|      pagecolor="#ffffff" | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageopacity="0.0" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="2.8" | ||||
|      inkscape:cx="26.576205" | ||||
|      inkscape:cy="-72.54425" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="layer1" | ||||
|      showgrid="true" | ||||
|      inkscape:snap-global="false" | ||||
|      inkscape:window-width="1295" | ||||
|      inkscape:window-height="744" | ||||
|      inkscape:window-x="65" | ||||
|      inkscape:window-y="24" | ||||
|      inkscape:window-maximized="1" | ||||
|      fit-margin-top="0" | ||||
|      fit-margin-left="0" | ||||
|      fit-margin-right="0" | ||||
|      fit-margin-bottom="0"> | ||||
|     <inkscape:grid | ||||
|        type="xygrid" | ||||
|        id="grid2996" | ||||
|        empspacing="5" | ||||
|        visible="true" | ||||
|        enabled="true" | ||||
|        snapvisiblegridlinesonly="true" | ||||
|        originx="-21.720779px" | ||||
|        originy="-990.37188px" /> | ||||
|   </sodipodi:namedview> | ||||
|   <metadata | ||||
|      id="metadata7"> | ||||
|     <rdf:RDF> | ||||
|       <cc:Work | ||||
|          rdf:about=""> | ||||
|         <dc:format>image/svg+xml</dc:format> | ||||
|         <dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||
|         <dc:title></dc:title> | ||||
|       </cc:Work> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
|   <g | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1" | ||||
|      transform="translate(-21.720779,-25.639593)"> | ||||
|     <path | ||||
|        sodipodi:type="arc" | ||||
|        style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" | ||||
|        id="path2998" | ||||
|        sodipodi:cx="172.10474" | ||||
|        sodipodi:cy="458.39249" | ||||
|        sodipodi:rx="5.4295697" | ||||
|        sodipodi:ry="5.0507627" | ||||
|        d="m 177.53431,458.39249 c 0,2.78946 -2.43091,5.05076 -5.42957,5.05076 -2.99867,0 -5.42957,-2.2613 -5.42957,-5.05076 0,-2.78946 2.4309,-5.05077 5.42957,-5.05077 2.99866,0 5.42957,2.26131 5.42957,5.05077 z" | ||||
|        transform="matrix(1.0129716,0,0,1.0889445,-131.11643,-452.42373)" /> | ||||
|     <path | ||||
|        style="fill:#ffffff;fill-opacity:1;stroke-width:0;stroke-miterlimit:4" | ||||
|        d="m 43.220779,25.640247 c 9.60163,0.0752 20.51786,6.8438 21.5,19.6 l -13,0 c 0,0 -1.67472,-7.04733 -8.5,-7 -6.82528,0.0473 -8.5,7 -8.5,7 l -13,0 c 0.63161,-12.53073 11.36576,-19.67935 21.5,-19.6 z" | ||||
|        id="rect3810" | ||||
|        inkscape:connector-curvature="0" | ||||
|        sodipodi:nodetypes="scczccs" /> | ||||
|     <path | ||||
|        style="fill:#ffffff;fill-opacity:1;stroke-width:0;stroke-miterlimit:4" | ||||
|        d="m 43.310069,61.990247 c -7.159395,0.01905 -13.847588,-5.383347 -16.58929,-13.75 l 8,0 c 0,0 1.72575,6.96782 8.55103,6.92049 6.82528,-0.0473 8.44897,-6.92049 8.44897,-6.92049 l 8,0 c -1.783351,8.850973 -9.251314,13.730946 -16.41071,13.75 z" | ||||
|        id="rect3810-1" | ||||
|        inkscape:connector-curvature="0" | ||||
|        sodipodi:nodetypes="scczccs" /> | ||||
|   </g> | ||||
| </svg> | ||||
| Before Width: | Height: | Size: 3.4 KiB | 
| @@ -1,7 +0,0 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <svg width="256px" height="256px" viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid"> | ||||
| 	<g> | ||||
| 		<path d="M255.637396,127.683191 C255.637396,198.196551 198.47207,255.363378 127.954205,255.363378 C57.4348387,255.363378 0.27026393,198.196551 0.27026393,127.683191 C0.27026393,57.1653255 57.4355894,0 127.954205,0 C198.472821,0 255.637396,57.1653255 255.637396,127.683191 L255.637396,127.683191 Z" fill="#DD4814"></path> | ||||
| 		<path d="M41.1334194,110.63254 C31.7139707,110.63254 24.0827683,118.264493 24.0827683,127.683191 C24.0827683,137.097384 31.7139707,144.728587 41.1334194,144.728587 C50.5476129,144.728587 58.1788152,137.097384 58.1788152,127.683191 C58.1788152,118.264493 50.5476129,110.63254 41.1334194,110.63254 L41.1334194,110.63254 Z M162.848282,188.111202 C154.694569,192.820551 151.898839,203.240727 156.608938,211.389935 C161.313032,219.543648 171.733208,222.338628 179.886921,217.629279 C188.039883,212.925185 190.835613,202.505009 186.126264,194.350545 C181.42217,186.202088 170.995988,183.407109 162.848282,188.111202 L162.848282,188.111202 Z M78.1618299,127.683191 C78.1618299,110.836739 86.5295015,95.9534545 99.3332551,86.9409032 L86.8703343,66.0667683 C71.9555191,76.0365044 60.8581818,91.271132 56.2464282,109.113806 C61.6276833,113.504845 65.0720469,120.189372 65.0720469,127.68244 C65.0720469,135.171003 61.6276833,141.855531 56.2464282,146.246569 C60.852176,164.094499 71.9495132,179.329877 86.8703343,189.299613 L99.3332551,168.420223 C86.5295015,159.412927 78.1618299,144.530393 78.1618299,127.683191 L78.1618299,127.683191 Z M127.954205,77.8855601 C153.967109,77.8855601 175.30895,97.8302874 177.549138,123.265877 L201.839859,122.907777 C200.644692,104.129689 192.441431,87.2719765 179.836622,74.875871 C173.354792,77.3247625 165.86773,76.9501466 159.396411,73.2197537 C152.91383,69.4788504 148.849361,63.1681877 147.738276,56.3177478 C141.438123,54.5790499 134.807648,53.6271202 127.952704,53.6271202 C116.168446,53.6271202 105.026815,56.3950733 95.1344047,61.2913548 L106.979472,82.5175836 C113.351695,79.5521877 120.460387,77.8855601 127.954205,77.8855601 L127.954205,77.8855601 Z M127.954205,177.475566 C120.460387,177.475566 113.351695,175.808188 106.980223,172.843543 L95.1351554,194.069021 C105.027566,198.971308 116.169196,201.740012 127.954205,201.740012 C134.80915,201.740012 141.439625,200.787331 147.739026,199.043378 C148.850111,192.192938 152.916082,185.888282 159.397161,182.140622 C165.872985,178.404223 173.355543,178.036364 179.837372,180.485255 C192.442182,168.08915 200.645443,151.231437 201.84061,132.453349 L177.543883,132.095249 C175.30895,157.537595 153.967859,177.475566 127.954205,177.475566 L127.954205,177.475566 Z M162.842276,67.2446686 C170.995988,71.9532669 181.416915,69.1642933 186.121009,61.0105806 C190.830358,52.856868 188.041384,42.4359413 179.886921,37.7258416 C171.733208,33.0217478 161.313032,35.8167273 156.602182,43.9704399 C151.898839,52.1196481 154.693818,62.5405748 162.842276,67.2446686 L162.842276,67.2446686 Z" fill="#FFFFFF"></path> | ||||
| 	</g> | ||||
| </svg> | ||||
| Before Width: | Height: | Size: 3.1 KiB | 
| @@ -1,200 +0,0 @@ | ||||
|  | ||||
|  | ||||
| function JobViewModel(repo, build, job, status) { | ||||
| 	var self = this; | ||||
| 	self.status = status; | ||||
|  | ||||
| 	self.stream = function() { | ||||
| 		$("#output").html(""); | ||||
| 		$("#restart").hide(); | ||||
| 		$("#cancel").show(); | ||||
|  | ||||
| 		var buf = new Drone.Buffer(); | ||||
| 		buf.start(document.getElementById("output")); | ||||
|  | ||||
| 		$( "#tail" ).show(); | ||||
| 		$( "#tail" ).click(function() { | ||||
| 			buf.autoFollow = !buf.autoFollow; | ||||
| 			if (buf.autoFollow) { | ||||
| 				$( "#tail i" ).text("pause"); | ||||
| 				$( "#tail" ).show(); | ||||
|  | ||||
| 				// scroll to the bottom of the page | ||||
| 				window.scrollTo(0, document.body.scrollHeight); | ||||
| 			} else { | ||||
| 				$( "#tail i" ).text("expand_more"); | ||||
| 				$( "#tail" ).show(); | ||||
| 			} | ||||
| 		}) | ||||
|  | ||||
| 		Stream(repo, build, job, function(out){ | ||||
| 			buf.write(out); | ||||
| 		}); | ||||
| 	}; | ||||
|  | ||||
| 	if (status !== "running" && status !== "pending") { | ||||
| 		Logs(repo, build, job); | ||||
| 		$("#restart").show(); | ||||
| 	} | ||||
|  | ||||
| 	if (status === "running") { | ||||
| 		self.stream(); | ||||
| 	} | ||||
|  | ||||
| 	$("#restart").click(function() { | ||||
| 		$("#restart").hide(); | ||||
| 		$("#output").html(""); | ||||
| 		$(".status").attr("class", "status pending").text("pending"); | ||||
|  | ||||
| 		$.ajax({ | ||||
| 			url: "/api/repos/"+repo+"/builds/"+build, | ||||
| 			type: "POST", | ||||
| 			success: function( data ) { }, | ||||
| 			error: function( data ) { | ||||
| 				console.log(data); | ||||
| 			} | ||||
| 		}); | ||||
| 	}) | ||||
|  | ||||
| 	$("#cancel").click(function() { | ||||
| 		$("#cancel").hide(); | ||||
|  | ||||
| 		$.ajax({ | ||||
| 			url: "/api/repos/"+repo+"/builds/"+build+"/"+job, | ||||
| 			type: "DELETE", | ||||
| 			success: function( data ) { }, | ||||
| 			error: function( data ) { | ||||
| 				console.log(data); | ||||
| 			} | ||||
| 		}); | ||||
| 	}) | ||||
|  | ||||
|  | ||||
| 	Subscribe(repo, function(data){ | ||||
| 		if (!data.jobs) { | ||||
| 			return; | ||||
| 		} | ||||
| 		if (data.number !== build) { | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		var before = self.status; | ||||
| 		self.status = data.jobs[job-1].status; | ||||
|  | ||||
| 		// update the status for each job in the view | ||||
| 		for (var i=0;i<data.jobs.length;i++) { | ||||
| 			var job_ = data.jobs[i]; | ||||
| 			var el = $("[data-job="+job_.number+"]"); | ||||
|  | ||||
| 			el.find(".status") | ||||
| 				.attr("class", "status "+job_.status).text(job_.status); | ||||
|  | ||||
| 			switch (job_.status) { | ||||
| 			case "running": | ||||
| 				el.find(".msg-running").find("span").attr("data-livestamp", job_.started_at); | ||||
|  | ||||
| 				el.find(".msg-pending").hide(); | ||||
| 				el.find(".msg-running").show(); | ||||
| 				el.find(".msg-finished").hide(); | ||||
| 				el.find(".msg-exited").hide(); | ||||
| 				break; | ||||
| 			case "pending": | ||||
| 				el.find(".msg-pending").show(); | ||||
| 				el.find(".msg-running").hide(); | ||||
| 				el.find(".msg-finished").hide(); | ||||
| 				el.find(".msg-exited").hide(); | ||||
| 				break; | ||||
| 			default: | ||||
| 				el.find(".msg-finished").find("span").attr("data-livestamp", job_.finished_at); | ||||
| 				el.find(".msg-exited").find("span").text(job_.exit_code); | ||||
|  | ||||
| 				el.find(".msg-pending").hide(); | ||||
| 				el.find(".msg-running").hide(); | ||||
| 				el.find(".msg-finished").show(); | ||||
| 				el.find(".msg-exited").show(); | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		var after = self.status; | ||||
|  | ||||
| 		// if the status has changed we should start | ||||
| 		// streaming the build contents. | ||||
| 		if (before !== after && after === "running") { | ||||
| 			self.stream(); | ||||
| 		} | ||||
|  | ||||
| 		// if the status is changes to complete, we can show | ||||
| 		// the restart button and hide the tail button. | ||||
| 		if (after !== "pending" && after !== "running") { | ||||
| 			$("#restart").show(); | ||||
| 			$("#cancel").hide(); | ||||
| 			$("#tail").hide(); | ||||
| 		} | ||||
| 	}.bind(this)); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| function Logs(repo, build, job) { | ||||
|  | ||||
| 	$.get( "/api/repos/"+repo+"/logs/"+build+"/"+job, function( data ) { | ||||
|  | ||||
| 		var lines = JSON.parse(data); | ||||
|  | ||||
| 		var groups = {} | ||||
| 		for (var i=0; i<lines.length; i++) { | ||||
| 			var line = lines[i]; | ||||
|  | ||||
| 			if (!line.proc) { | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			var group = groups[line.proc]; | ||||
|  | ||||
| 			if (!group) { | ||||
|  | ||||
| 				// create an element to hold the group of output | ||||
| 				var pre = $("<pre>").attr("data-title", line.proc); | ||||
| 				$("#output").append(pre); | ||||
|  | ||||
| 				// create the buffer for the group of output | ||||
| 				var buf = new Drone.Buffer(); | ||||
| 				buf.start(pre[0]); | ||||
|  | ||||
| 				// add items to the group | ||||
| 				group = { | ||||
| 					pre: pre, | ||||
| 					buf: buf, | ||||
| 				}; | ||||
| 				groups[line.proc]=group; | ||||
| 			} | ||||
|  | ||||
| 			group.buf.write(line.out+"\n"); | ||||
| 		} | ||||
|  | ||||
| 		for (var i=0; i<groups.length; i++) { | ||||
| 			groups[i].buf.stop(); | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| function Stream(repo, build, job, _callback) { | ||||
| 	var callback = _callback; | ||||
|  | ||||
| 	var events = new EventSource("/api/stream/" + repo + "/" + build + "/" + job, {withCredentials: true}); | ||||
| 	events.onmessage = function (event) { | ||||
| 		if (callback !== undefined) { | ||||
| 			callback(event.data); | ||||
| 		} | ||||
| 	}; | ||||
| 	events.onerror = function (event) { | ||||
| 		callback = undefined; | ||||
| 		if (events !== undefined) { | ||||
| 			events.close(); | ||||
| 			events = undefined; | ||||
| 		} | ||||
| 		console.log('user event stream closed due to error.', event); | ||||
| 	}; | ||||
| }; | ||||
| @@ -1,11 +0,0 @@ | ||||
| $(function () { | ||||
|  | ||||
| 	// fetch the CSRF token from the meta tag | ||||
| 	var token = $("meta[name='_csrf']").attr("content"); | ||||
|  | ||||
| 	// ensure every Ajax request has the CSRF token | ||||
| 	// included in the request's header. | ||||
| 	$(document).ajaxSend(function(e, xhr, options) { | ||||
| 		xhr.setRequestHeader("X-CSRF-TOKEN", token); | ||||
| 	}); | ||||
| }); | ||||
| @@ -1,90 +0,0 @@ | ||||
| /** | ||||
|  * Creates an observable build job. | ||||
|  */ | ||||
| function Job(data) { | ||||
| 	this.number	  = ko.observable(data.number); | ||||
| 	this.commit	  = ko.observable(data.commit); | ||||
| 	this.started_at  = ko.observable(data.started_at); | ||||
| 	this.finished_at = ko.observable(data.finished_at); | ||||
| 	this.exit_code   = ko.observable(data.exit_code); | ||||
| 	this.status	  = ko.observable(data.status); | ||||
| 	this.environment = ko.observable(data.environment); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Creates an observable build. | ||||
|  */ | ||||
| function Build(data) { | ||||
| 	this.number      = ko.observable(data.number); | ||||
| 	this.commit      = ko.observable(data.commit); | ||||
| 	this.branch      = ko.observable(data.branch); | ||||
| 	this.author      = ko.observable(data.author); | ||||
| 	this.message     = ko.observable(data.message); | ||||
| 	this.status      = ko.observable(data.status); | ||||
| 	this.event       = ko.observable(data.event); | ||||
| 	this.started_at  = ko.observable(data.started_at); | ||||
| 	this.finished_at = ko.observable(data.finished_at); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Creates an observable repository. | ||||
|  */ | ||||
| function Repo(data) { | ||||
| 	this.full_name  = ko.observable(data.full_name); | ||||
| 	this.owner      = ko.observable(data.owner); | ||||
| 	this.name       = ko.observable(data.name); | ||||
| 	this.private    = ko.observable(data.private); | ||||
| 	this.trusted    = ko.observable(data.trusted); | ||||
| 	this.timeout    = ko.observable(data.timeout); | ||||
| 	this.avatar_url = ko.observable(data.avatar_url); | ||||
| 	this.clone_url  = ko.observable(data.clone_url); | ||||
| 	this.link_url   = ko.observable(data.link_url); | ||||
| 	this.starred	= ko.observable(data.starred || false); | ||||
| 	this.events     = ko.observable(data.events); | ||||
| 	this.hook       = ko.observable(new Hook(data)); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Compares two repository objects by name. Used to sort | ||||
|  * a list of repositories. | ||||
|  */ | ||||
| function RepoCompare(a, b) { | ||||
| 	return a.full_name().toLowerCase() > b.full_name().toLowerCase() ? 1 : -1;  | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Creates an observable object that stores a list of hook event | ||||
|  * types (push, pull request, etc) and true or false if enabled. | ||||
|  */ | ||||
| function Hook(repo) { | ||||
| 	var data = { | ||||
| 		"pull_request" : repo.events.indexOf("pull_request") !== -1, | ||||
| 		"push"         : repo.events.indexOf("push")         !== -1, | ||||
| 		"tag"          : repo.events.indexOf("tag")          !== -1, | ||||
| 		"deploy"       : repo.events.indexOf("deploy")       !== -1 | ||||
| 	}; | ||||
|  | ||||
| 	this.pull_request = ko.observable(data.pull_request); | ||||
| 	this.push         = ko.observable(data.push); | ||||
| 	this.tag          = ko.observable(data.tag); | ||||
| 	this.deploy       = ko.observable(data.deploy); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Creates an observable user. | ||||
|  */ | ||||
| function User(data) { | ||||
| 	this.login = ko.observable(data.login); | ||||
| 	this.email = ko.observable(data.email); | ||||
| 	this.avatar_url = ko.observable(data.avatar_url); | ||||
| 	this.active = ko.observable(data.active); | ||||
| 	this.admin = ko.observable(data.admin); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Compares two user objects by login. Used to sort | ||||
|  * a list of users. | ||||
|  */ | ||||
| function UserCompare(a, b) { | ||||
| 	return a.login().toLowerCase() > b.login().toLowerCase() ? 1 : -1;   | ||||
| } | ||||
| @@ -1,74 +0,0 @@ | ||||
|  | ||||
| function NodeViewModel() { | ||||
| 	var self = this; | ||||
|  | ||||
| 	// handle requests to create a new node. | ||||
| 	$(".modal-node button").click(function(e) { | ||||
|  | ||||
| 		var node = { | ||||
| 			address : $("#addr").val(), | ||||
| 			key     : $("#key").val(), | ||||
| 			cert    : $("#cert").val(), | ||||
| 			ca      : $("#ca").val() | ||||
| 		}; | ||||
|  | ||||
| 		$.ajax({ | ||||
| 			url: "/api/nodes", | ||||
| 			type: "POST", | ||||
| 			contentType: "application/json", | ||||
| 			data: JSON.stringify(node), | ||||
| 			success: function( data ) { | ||||
| 				// clears the form value | ||||
| 				$(".modal-node input").val(""); | ||||
|  | ||||
| 				var el = $("<div>").attr("class", "col-sm-4").append( | ||||
| 					$("<div>").attr("class", "card").attr("data-id", data.id).append( | ||||
| 						$("<div>").attr("class", "card-header").append( | ||||
| 							$("<i>").attr("class", "linux_amd64") | ||||
| 						) | ||||
| 					).append( | ||||
| 						$("<div>").attr("class", "card-block").append( | ||||
| 							$("<h3>").text(data.address) | ||||
| 						).append( | ||||
| 							$("<p>").attr("class", "card-text").text(data.architecture) | ||||
| 						).append( | ||||
| 							$("<div>").attr("class", "btn-group").append( | ||||
| 								$("<button>").attr("class","btn btn-danger").text("Delete") | ||||
| 							) | ||||
| 						) | ||||
| 					) | ||||
| 				) | ||||
|  | ||||
| 				$( ".node-row" ).prepend(el); | ||||
| 			}, | ||||
| 			error: function( data ) { | ||||
|  				console.log(data); | ||||
| 			} | ||||
| 		}); | ||||
| 	}); | ||||
|  | ||||
|  | ||||
| 	$(".node-row").on('click', '.btn-group .btn-danger', function(){ | ||||
| 		// gets the unique identifier for the click row, which is | ||||
| 		// the user login id. | ||||
| 		var id = $( this ).context | ||||
| 					.parentNode | ||||
| 					.parentNode | ||||
| 					.parentNode.dataset.id; | ||||
| 		 | ||||
| 		var r = confirm("Are you sure you want to delete node "+id+"?"); | ||||
| 		if (r === false) { | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		// makes an API call to delete the user and then, if successful, | ||||
| 		// removes from the list. | ||||
| 		$.ajax({ | ||||
| 			url: "/api/nodes/"+id, | ||||
| 			type: "DELETE", | ||||
| 			success: function( data ) { | ||||
| 				$("[data-id='"+id+"']").parent().remove(); | ||||
| 			} | ||||
| 		}); | ||||
| 	}); | ||||
| } | ||||
| @@ -1,155 +0,0 @@ | ||||
|  | ||||
|  | ||||
|  | ||||
| function RepoViewModel(repo) { | ||||
| 	var self = this; | ||||
|  | ||||
| 	Subscribe(repo, function(data){ | ||||
| 		var el = $("[data-build="+data.number+"]"); | ||||
|  | ||||
| 		if (el && el.length !== 0) { | ||||
| 			// find the status label and adjust the | ||||
| 			// build status accordingly. | ||||
| 			var status = el.find(".status"); | ||||
| 			status.attr("class", "status "+data.status); | ||||
| 			status.text(data.status); | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		// construct the build entry if it doesn't already exist | ||||
| 		// so that we can append to the DOM. The code may not be | ||||
| 		// pretty, but it is simple enough and it works. | ||||
| 		var authoredOrDeployed = "authored" | ||||
| 		var branchOrDeploy = data.branch | ||||
| 		if ( data.event == "deployment" ) { | ||||
| 			authoredOrDeployed = "deployed" | ||||
| 			branchOrDeploy = data.deploy_to | ||||
| 		} | ||||
|  | ||||
| 		el = $("<a>").attr("class", "card").attr("href", "/"+repo+"/"+data.number).attr("data-build", data.number) | ||||
| 				.append( | ||||
| 					$("<div>").attr("class", "card-header").append( | ||||
| 						$("<img>").attr("src", data.author_avatar) | ||||
| 					) | ||||
| 				) | ||||
| 				.append( | ||||
| 					$("<div>").attr("class", "card-block").append( | ||||
| 						$("<div>").append( | ||||
| 							$("<div>").attr("class", "status "+ data.status).text(data.status) | ||||
| 						).append( | ||||
| 							$("<h3>").text(data.message) | ||||
| 						) | ||||
| 					).append( | ||||
| 						$("<p>").attr("class","card-text").append( | ||||
| 							$("<em>").text(data.author) | ||||
| 						).append( | ||||
| 							$("<span>").text(authoredOrDeployed) | ||||
| 						).append( | ||||
| 							$("<em>").attr("data-livestamp", data.created_at) | ||||
| 						).append( | ||||
| 							$("<span>").text("to") | ||||
| 						).append( | ||||
| 							$("<em>").text(branchOrDeploy) | ||||
| 						) | ||||
| 					) | ||||
| 				).css("display", "flex").hide().fadeIn(1000); | ||||
|  | ||||
| 		// TODO it is very possible that the group may not | ||||
| 		// exist, in which case the we'll need to create the | ||||
| 		// gropu as well. | ||||
|  | ||||
| 		// append to the latest group in the timeline. | ||||
| 		$(".card").first().before(el); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| function Subscribe(repo, _callback) { | ||||
| 	var callback = _callback; | ||||
| 			 | ||||
| 	var events = new EventSource("/api/stream/" + repo, {withCredentials: true}); | ||||
| 	events.onmessage = function (event) { | ||||
| 		if (callback !== undefined) { | ||||
| 			callback(JSON.parse(event.data)); | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	events.onerror = function (event) { | ||||
| 		callback = undefined; | ||||
| 		if (events !== undefined) { | ||||
| 			events.close(); | ||||
| 			events = undefined; | ||||
| 		} | ||||
| 		console.log('user event stream closed due to error.', event); | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
|  | ||||
|  | ||||
| function RepoConfigViewModel(repo) { | ||||
| 	var self = this; | ||||
|  | ||||
| 	var timeoutLabel = $(".timeout-label") | ||||
|  | ||||
| 	$("input[type='range']").change(function(e) { | ||||
| 		var timeout =  parseInt(e.target.value); | ||||
| 		timeoutLabel.text(timeout + " minutes"); | ||||
| 		patchRepo(repo, { timeout: timeout }) | ||||
| 	}) | ||||
|  | ||||
| 	$("#push").change(function(e) { | ||||
| 		patchRepo(repo, { | ||||
| 			allow_push: e.target.checked, | ||||
| 		}) | ||||
| 	}) | ||||
|  | ||||
| 	$("#pull").change(function(e) { | ||||
| 		patchRepo(repo, { | ||||
| 			allow_pr: e.target.checked, | ||||
| 		}) | ||||
| 	}) | ||||
|  | ||||
| 	$("#tag").change(function(e) { | ||||
| 		patchRepo(repo, { | ||||
| 			allow_tag: e.target.checked, | ||||
| 		}) | ||||
| 	}) | ||||
|  | ||||
| 	$("#deploy").change(function(e) { | ||||
| 		patchRepo(repo, { | ||||
| 			allow_deploy: e.target.checked, | ||||
| 		}) | ||||
| 	}) | ||||
|  | ||||
| 	$("#trusted").change(function(e) { | ||||
| 		patchRepo(repo, { | ||||
| 			trusted:  e.target.checked, | ||||
| 		}) | ||||
| 	}) | ||||
|  | ||||
| 	$(".btn-danger").click(function(e) { | ||||
| 		var r = confirm("Are you sure you want to delete this repository?"); | ||||
| 		if (r !== false) { | ||||
| 			deleteRepo(repo); | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| function deleteRepo(repo) { | ||||
| 	$.ajax({ | ||||
| 		url: "/api/repos/"+repo, | ||||
| 		type: "DELETE", | ||||
| 		contentType: "application/json", | ||||
| 		success: function() { | ||||
| 			window.location.href="/"; | ||||
| 		}, | ||||
| 	});	 | ||||
| } | ||||
|  | ||||
| function patchRepo(repo, data) { | ||||
| 	$.ajax({ | ||||
| 		url: "/api/repos/"+repo, | ||||
| 		type: "PATCH", | ||||
| 		contentType: "application/json", | ||||
| 		data: JSON.stringify(data) | ||||
| 	});	 | ||||
| } | ||||
| @@ -1,31 +0,0 @@ | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| function RepoListViewModel(repos) { | ||||
| 	var self = this; | ||||
|  | ||||
| 	var mapped = $.map(repos, function(repo) { | ||||
| 		return new Repo(repo) | ||||
| 	}); | ||||
|  | ||||
| 	self.repos = ko.observableArray(mapped); | ||||
| 	self.newRepo = ko.observable(); | ||||
|  | ||||
| 	self.addRepo = function() { | ||||
| 		$.ajax({ | ||||
| 			url: "/api/repos/"+self.newRepo(), | ||||
| 			type: "POST", | ||||
| 			contentType: "application/json", | ||||
| 			success: function( data ) { | ||||
| 				self.repos.push(new Repo(data)); | ||||
| 				self.repos.sort(RepoCompare); | ||||
| 				self.newRepo(""); | ||||
| 			}, | ||||
| 			error: function( data ) { | ||||
| 				console.log(data); | ||||
| 			} | ||||
| 		}); | ||||
| 	}; | ||||
| } | ||||
| @@ -1,66 +0,0 @@ | ||||
| var repoExpr = /.+\/.+/; | ||||
|  | ||||
| var remoteRepos = new Bloodhound({ | ||||
|     queryTokenizer: Bloodhound.tokenizers.whitespace, | ||||
|     datumTokenizer: Bloodhound.tokenizers.obj.whitespace("full_name"), | ||||
|  | ||||
| 	identify: function(obj) { return obj.full_name; }, | ||||
| 	prefetch: '/api/user/repos/remote' | ||||
| }); | ||||
|  | ||||
|  | ||||
| function reposWithDefaults(q, sync) { | ||||
|   if (q === "") { | ||||
|   	sync(remoteRepos.all()) | ||||
|   } else { | ||||
|     remoteRepos.search(q, sync); | ||||
|   } | ||||
| } | ||||
|  | ||||
| $('.typeahead').typeahead({ | ||||
| 	hint: true, | ||||
| 	highlight: true, | ||||
| 	minLength: 0 | ||||
| }, | ||||
| { | ||||
| 	name: "repos", | ||||
| 	display: "full_name", | ||||
| 	source: reposWithDefaults, | ||||
| 	templates: { | ||||
| 		empty: function(obj) { | ||||
| 			if (obj.query.match(repoExpr) !== null) { | ||||
| 				return [ | ||||
| 					"<div>", | ||||
| 						"<div class='not-indexed-message'>", | ||||
| 							"<p>", | ||||
| 								"No matches found for", | ||||
| 								"<em>", | ||||
| 								obj.query, | ||||
| 								"</em>", | ||||
| 							"</p>", | ||||
| 							"<p>", | ||||
| 								"This repository may not be indexed yet.", | ||||
| 								"<a href='/"+obj.query+"'>", | ||||
| 								"Click here", | ||||
| 								"</a>", | ||||
| 								"to visit this repository page directly.", | ||||
| 							"</p>", | ||||
| 						"</div>", | ||||
| 					"</div>" | ||||
| 				].join("\n"); | ||||
| 			} | ||||
| 			return [ | ||||
| 				"<div>", | ||||
| 					"<div class='no-matches-message'>", | ||||
| 						"No matches found", | ||||
| 					"</div>", | ||||
| 				"</div>" | ||||
| 			].join("\n"); | ||||
| 		}, | ||||
| 		suggestion: function(obj) { | ||||
| 			return "<div><div><img src='"+obj.avatar_url+"' width='32px' height='32px' /></div><div>"+ obj.full_name +"</div></div>"; | ||||
| 		} | ||||
| 	} | ||||
| }).bind('typeahead:select', function(ev, suggestion) { | ||||
| 	window.location.href="/"+suggestion.full_name; | ||||
| }); | ||||
| @@ -1,397 +0,0 @@ | ||||
| var Filter, STYLES, defaults, entities, extend, toHexString, _i, _results, | ||||
| 	__slice = [].slice; | ||||
|  | ||||
| // theme stuff: | ||||
| //   http://ciembor.github.io/4bit | ||||
| //   https://github.com/lysyi3m/osx-terminal-themes | ||||
|  | ||||
| STYLES = { | ||||
| 	'ef0': 'color:#000', | ||||
| 	'ef1': 'color:#b87a7a', | ||||
| 	'ef2': 'color:#7ab87a', | ||||
| 	'ef3': 'color:#b8b87a', | ||||
| 	'ef4': 'color:#7a7ab8', | ||||
| 	'ef5': 'color:#b87ab8', | ||||
| 	'ef6': 'color:#7ab8b8', | ||||
| 	'ef7': 'color:#d9d9d9', | ||||
| 	'ef8': 'color:#262626', | ||||
| 	'ef9': 'color:#dbbdbd', | ||||
| 	'ef10': 'color:#bddbbd', | ||||
| 	'ef11': 'color:#dbdbbd', | ||||
| 	'ef12': 'color:#bdbddb', | ||||
| 	'ef13': 'color:#dbbddb', | ||||
| 	'ef14': 'color:#bddbdb', | ||||
| 	'ef15': 'color:#FFF', | ||||
| 	'eb0': 'background-color:#000', | ||||
| 	'eb1': 'background-color:#b87a7a', | ||||
| 	'eb2': 'background-color:#7ab87a', | ||||
| 	'eb3': 'background-color:#b8b87a', | ||||
| 	'eb4': 'background-color:#7a7ab8', | ||||
| 	'eb5': 'background-color:#b87ab8', | ||||
| 	'eb6': 'background-color:#7ab8b8', | ||||
| 	'eb7': 'background-color:#d9d9d9', | ||||
| 	'eb8': 'background-color:#262626', | ||||
| 	'eb9': 'background-color:#dbbdbd', | ||||
| 	'eb10': 'background-color:#bddbbd', | ||||
| 	'eb11': 'background-color:#dbdbbd', | ||||
| 	'eb12': 'background-color:#bdbddb', | ||||
| 	'eb13': 'background-color:#dbbddb', | ||||
| 	'eb14': 'background-color:#bddbdb', | ||||
| 	'eb15': 'background-color:#FFF' | ||||
| }; | ||||
|  | ||||
| toHexString = function(num) { | ||||
| 	num = num.toString(16); | ||||
| 	while (num.length < 2) { | ||||
| 		num = "0" + num; | ||||
| 	} | ||||
| 	return num; | ||||
| }; | ||||
|  | ||||
| [0, 1, 2, 3, 4, 5].forEach(function(red) { | ||||
| 	return [0, 1, 2, 3, 4, 5].forEach(function(green) { | ||||
| 		return [0, 1, 2, 3, 4, 5].forEach(function(blue) { | ||||
| 			var b, c, g, n, r, rgb; | ||||
| 			c = 16 + (red * 36) + (green * 6) + blue; | ||||
| 			r = red > 0 ? red * 40 + 55 : 0; | ||||
| 			g = green > 0 ? green * 40 + 55 : 0; | ||||
| 			b = blue > 0 ? blue * 40 + 55 : 0; | ||||
| 			rgb = ((function() { | ||||
| 				var _i, _len, _ref, _results; | ||||
| 				_ref = [r, g, b]; | ||||
| 				_results = []; | ||||
| 				for (_i = 0, _len = _ref.length; _i < _len; _i++) { | ||||
| 					n = _ref[_i]; | ||||
| 					_results.push(toHexString(n)); | ||||
| 				} | ||||
| 				return _results; | ||||
| 			})()).join(''); | ||||
| 			STYLES["ef" + c] = "color:#" + rgb; | ||||
| 			return STYLES["eb" + c] = "background-color:#" + rgb; | ||||
| 		}); | ||||
| 	}); | ||||
| }); | ||||
|  | ||||
| (function() { | ||||
| 	_results = []; | ||||
| 	for (_i = 0; _i <= 23; _i++){ _results.push(_i); } | ||||
| 	return _results; | ||||
| }).apply(this).forEach(function(gray) { | ||||
| 	var c, l; | ||||
| 	c = gray + 232; | ||||
| 	l = toHexString(gray * 10 + 8); | ||||
| 	STYLES["ef" + c] = "color:#" + l + l + l; | ||||
| 	return STYLES["eb" + c] = "background-color:#" + l + l + l; | ||||
| }); | ||||
|  | ||||
| extend = function() { | ||||
| 	var dest, k, obj, objs, v, _j, _len; | ||||
| 	dest = arguments[0], objs = 2 <= arguments.length ? __slice.call(arguments, 1) : []; | ||||
| 	for (_j = 0, _len = objs.length; _j < _len; _j++) { | ||||
| 		obj = objs[_j]; | ||||
| 		for (k in obj) { | ||||
| 			v = obj[k]; | ||||
| 			dest[k] = v; | ||||
| 		} | ||||
| 	} | ||||
| 	return dest; | ||||
| }; | ||||
|  | ||||
| defaults = { | ||||
| 	fg: '#FFF', | ||||
| 	bg: '#000', | ||||
| 	newline: false, | ||||
| 	escapeXML: false, | ||||
| 	stream: false | ||||
| }; | ||||
|  | ||||
| Filter = (function() { | ||||
| 	function Filter(options) { | ||||
| 		if (options == null) { | ||||
| 			options = {}; | ||||
| 		} | ||||
| 		this.opts = extend({}, defaults, options); | ||||
| 		this.input = []; | ||||
| 		this.stack = []; | ||||
| 		this.stickyStack = []; | ||||
| 	} | ||||
|  | ||||
| 	Filter.prototype.toHtml = function(input) { | ||||
| 		var buf; | ||||
| 		this.input = typeof input === 'string' ? [input] : input; | ||||
| 		buf = []; | ||||
| 		this.stickyStack.forEach((function(_this) { | ||||
| 			return function(element) { | ||||
| 				return _this.generateOutput(element.token, element.data, function(chunk) { | ||||
| 					return buf.push(chunk); | ||||
| 				}); | ||||
| 			}; | ||||
| 		})(this)); | ||||
| 		this.forEach(function(chunk) { | ||||
| 			return buf.push(chunk); | ||||
| 		}); | ||||
| 		this.input = []; | ||||
| 		return buf.join(''); | ||||
| 	}; | ||||
|  | ||||
| 	Filter.prototype.forEach = function(callback) { | ||||
| 		var buf; | ||||
| 		buf = ''; | ||||
| 		this.input.forEach((function(_this) { | ||||
| 			return function(chunk) { | ||||
| 				buf += chunk; | ||||
| 				return _this.tokenize(buf, function(token, data) { | ||||
| 					_this.generateOutput(token, data, callback); | ||||
| 					if (_this.opts.stream) { | ||||
| 						return _this.updateStickyStack(token, data); | ||||
| 					} | ||||
| 				}); | ||||
| 			}; | ||||
| 		})(this)); | ||||
| 		if (this.stack.length) { | ||||
| 			return callback(this.resetStyles()); | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	Filter.prototype.generateOutput = function(token, data, callback) { | ||||
| 		switch (token) { | ||||
| 			case 'text': | ||||
| 				return callback(this.pushText(data)); | ||||
| 			case 'display': | ||||
| 				return this.handleDisplay(data, callback); | ||||
| 			case 'xterm256': | ||||
| 				return callback(this.pushStyle("ef" + data)); | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	Filter.prototype.updateStickyStack = function(token, data) { | ||||
| 		var notCategory; | ||||
| 		notCategory = function(category) { | ||||
| 			return function(e) { | ||||
| 				return (category === null || e.category !== category) && category !== 'all'; | ||||
| 			}; | ||||
| 		}; | ||||
| 		if (token !== 'text') { | ||||
| 			this.stickyStack = this.stickyStack.filter(notCategory(this.categoryForCode(data))); | ||||
| 			return this.stickyStack.push({ | ||||
| 				token: token, | ||||
| 				data: data, | ||||
| 				category: this.categoryForCode(data) | ||||
| 			}); | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	Filter.prototype.handleDisplay = function(code, callback) { | ||||
| 		code = parseInt(code, 10); | ||||
| 		if (code === -1) { | ||||
| 			callback('<br/>'); | ||||
| 		} | ||||
| 		if (code === 0) { | ||||
| 			if (this.stack.length) { | ||||
| 				callback(this.resetStyles()); | ||||
| 			} | ||||
| 		} | ||||
| 		if (code === 1) { | ||||
| 			callback(this.pushTag('b')); | ||||
| 		} | ||||
| 		if (code === 2) { | ||||
|  | ||||
| 		} | ||||
| 		if ((2 < code && code < 5)) { | ||||
| 			callback(this.pushTag('u')); | ||||
| 		} | ||||
| 		if ((4 < code && code < 7)) { | ||||
| 			callback(this.pushTag('blink')); | ||||
| 		} | ||||
| 		if (code === 7) { | ||||
|  | ||||
| 		} | ||||
| 		if (code === 8) { | ||||
| 			callback(this.pushStyle('display:none')); | ||||
| 		} | ||||
| 		if (code === 9) { | ||||
| 			callback(this.pushTag('strike')); | ||||
| 		} | ||||
| 		if (code === 24) { | ||||
| 			callback(this.closeTag('u')); | ||||
| 		} | ||||
| 		if ((29 < code && code < 38)) { | ||||
| 			callback(this.pushStyle("ef" + (code - 30))); | ||||
| 		} | ||||
| 		if (code === 39) { | ||||
| 			callback(this.pushStyle("color:" + this.opts.fg)); | ||||
| 		} | ||||
| 		if ((39 < code && code < 48)) { | ||||
| 			callback(this.pushStyle("eb" + (code - 40))); | ||||
| 		} | ||||
| 		if (code === 49) { | ||||
| 			callback(this.pushStyle("background-color:" + this.opts.bg)); | ||||
| 		} | ||||
| 		if ((89 < code && code < 98)) { | ||||
| 			callback(this.pushStyle("ef" + (8 + (code - 90)))); | ||||
| 		} | ||||
| 		if ((99 < code && code < 108)) { | ||||
| 			return callback(this.pushStyle("eb" + (8 + (code - 100)))); | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	Filter.prototype.categoryForCode = function(code) { | ||||
| 		code = parseInt(code, 10); | ||||
| 		if (code === 0) { | ||||
| 			return 'all'; | ||||
| 		} else if (code === 1) { | ||||
| 			return 'bold'; | ||||
| 		} else if ((2 < code && code < 5)) { | ||||
| 			return 'underline'; | ||||
| 		} else if ((4 < code && code < 7)) { | ||||
| 			return 'blink'; | ||||
| 		} else if (code === 8) { | ||||
| 			return 'hide'; | ||||
| 		} else if (code === 9) { | ||||
| 			return 'strike'; | ||||
| 		} else if ((29 < code && code < 38) || code === 39 || (89 < code && code < 98)) { | ||||
| 			return 'foreground-color'; | ||||
| 		} else if ((39 < code && code < 48) || code === 49 || (99 < code && code < 108)) { | ||||
| 			return 'background-color'; | ||||
| 		} else { | ||||
| 			return null; | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	Filter.prototype.pushTag = function(tag, style) { | ||||
| 		if (style == null) { | ||||
| 			style = ''; | ||||
| 		} | ||||
| 		if (style.length && style.indexOf(':') === -1) { | ||||
| 			style = STYLES[style]; | ||||
| 		} | ||||
| 		this.stack.push(tag); | ||||
| 		return ["<" + tag, (style ? " style=\"" + style + "\"" : void 0), ">"].join(''); | ||||
| 	}; | ||||
|  | ||||
| 	Filter.prototype.pushText = function(text) { | ||||
| 		if (this.opts.escapeXML) { | ||||
| 			return entities.encodeXML(text); | ||||
| 		} else { | ||||
| 			return text; | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	Filter.prototype.pushStyle = function(style) { | ||||
| 		return this.pushTag("span", style); | ||||
| 	}; | ||||
|  | ||||
| 	Filter.prototype.closeTag = function(style) { | ||||
| 		var last; | ||||
| 		if (this.stack.slice(-1)[0] === style) { | ||||
| 			last = this.stack.pop(); | ||||
| 		} | ||||
| 		if (last != null) { | ||||
| 			return "</" + style + ">"; | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	Filter.prototype.resetStyles = function() { | ||||
| 		var stack, _ref; | ||||
| 		_ref = [this.stack, []], stack = _ref[0], this.stack = _ref[1]; | ||||
| 		return stack.reverse().map(function(tag) { | ||||
| 			return "</" + tag + ">"; | ||||
| 		}).join(''); | ||||
| 	}; | ||||
|  | ||||
| 	Filter.prototype.tokenize = function(text, callback) { | ||||
| 		var ansiHandler, ansiMatch, ansiMess, handler, i, length, newline, process, realText, remove, removeXterm256, tokens, _j, _len, _results1; | ||||
| 		ansiMatch = false; | ||||
| 		ansiHandler = 3; | ||||
| 		remove = function(m) { | ||||
| 			return ''; | ||||
| 		}; | ||||
| 		removeXterm256 = function(m, g1) { | ||||
| 			callback('xterm256', g1); | ||||
| 			return ''; | ||||
| 		}; | ||||
| 		newline = (function(_this) { | ||||
| 			return function(m) { | ||||
| 				if (_this.opts.newline) { | ||||
| 					callback('display', -1); | ||||
| 				} else { | ||||
| 					callback('text', m); | ||||
| 				} | ||||
| 				return ''; | ||||
| 			}; | ||||
| 		})(this); | ||||
| 		ansiMess = function(m, g1) { | ||||
| 			var code, _j, _len; | ||||
| 			ansiMatch = true; | ||||
| 			if (g1.trim().length === 0) { | ||||
| 				g1 = '0'; | ||||
| 			} | ||||
| 			g1 = g1.trimRight(';').split(';'); | ||||
| 			for (_j = 0, _len = g1.length; _j < _len; _j++) { | ||||
| 				code = g1[_j]; | ||||
| 				callback('display', code); | ||||
| 			} | ||||
| 			return ''; | ||||
| 		}; | ||||
| 		realText = function(m) { | ||||
| 			callback('text', m); | ||||
| 			return ''; | ||||
| 		}; | ||||
| 		tokens = [ | ||||
| 			{ | ||||
| 				pattern: /^\x08+/, | ||||
| 				sub: remove | ||||
| 			}, { | ||||
| 				pattern: /^\x1b\[[012]?K/, | ||||
| 				sub: remove | ||||
| 			}, { | ||||
| 				pattern: /^\x1b\[38;5;(\d+)m/, | ||||
| 				sub: removeXterm256 | ||||
| 			}, { | ||||
| 				pattern: /^\n+/, | ||||
| 				sub: newline | ||||
| 			}, { | ||||
| 				pattern: /^\r+/, | ||||
| 				sub: newline | ||||
| 			}, { | ||||
| 				pattern: /^\x1b\[((?:\d{1,3};?)+|)m/, | ||||
| 				sub: ansiMess | ||||
| 			}, { | ||||
| 				pattern: /^\x1b\[?[\d;]{0,3}/, | ||||
| 				sub: remove | ||||
| 			}, { | ||||
| 				pattern: /^([^\x1b\x08\n]+)/, | ||||
| 				sub: realText | ||||
| 			} | ||||
| 		]; | ||||
| 		process = function(handler, i) { | ||||
| 			var matches; | ||||
| 			if (i > ansiHandler && ansiMatch) { | ||||
| 				return; | ||||
| 			} else { | ||||
| 				ansiMatch = false; | ||||
| 			} | ||||
| 			matches = text.match(handler.pattern); | ||||
| 			text = text.replace(handler.pattern, handler.sub); | ||||
| 			if (matches == null) { | ||||
|  | ||||
| 			} | ||||
| 		}; | ||||
| 		_results1 = []; | ||||
| 		while ((length = text.length) > 0) { | ||||
| 			for (i = _j = 0, _len = tokens.length; _j < _len; i = ++_j) { | ||||
| 				handler = tokens[i]; | ||||
| 				process(handler, i); | ||||
| 			} | ||||
| 			if (text.length === length) { | ||||
| 				break; | ||||
| 			} else { | ||||
| 				_results1.push(void 0); | ||||
| 			} | ||||
| 		} | ||||
| 		return _results1; | ||||
| 	}; | ||||
|  | ||||
| 	return Filter; | ||||
|  | ||||
| })(); | ||||
| @@ -1,52 +0,0 @@ | ||||
| ;// Live commit updates | ||||
|  | ||||
| if(typeof(Drone) === 'undefined') { Drone = {}; } | ||||
|  | ||||
| (function () { | ||||
| 	Drone.Buffer = function() { | ||||
| 		this.lineFormatter = new Filter({stream: true, newline: false}); | ||||
| 	} | ||||
|  | ||||
| 	Drone.Buffer.prototype = { | ||||
| 		lineBuffer: "", | ||||
| 		autoFollow: false, | ||||
| 		stoppingRefresh: false, | ||||
|  | ||||
| 		start: function(el) { | ||||
| 			if(typeof(el) === 'string') { | ||||
| 				this.el = document.getElementById(el); | ||||
| 			} else { | ||||
| 				this.el = el; | ||||
| 			} | ||||
|  | ||||
| 			this.el.innerHTML=""; | ||||
| 			this.update(); | ||||
| 		}, | ||||
|  | ||||
| 		stop: function() { | ||||
| 			this.stoppingRefresh = true; | ||||
| 		}, | ||||
|  | ||||
| 		update: function() { | ||||
| 			if(this.lineBuffer.length > 0) { | ||||
| 				this.el.innerHTML += this.lineFormatter.toHtml(escapeHTML(this.lineBuffer)); | ||||
| 				this.lineBuffer = ''; | ||||
|  | ||||
| 				if (this.autoFollow) { | ||||
| 					window.scrollTo(0, document.body.scrollHeight); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if(this.stoppingRefresh) { | ||||
| 				this.stoppingRefresh = false; | ||||
| 			} else { | ||||
| 				window.requestAnimationFrame(this.update.bind(this)); | ||||
| 			} | ||||
| 		}, | ||||
|  | ||||
| 		write: function(data) { | ||||
| 			this.lineBuffer += data; | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| })(); | ||||
| @@ -1,78 +0,0 @@ | ||||
|  | ||||
| function UserViewModel() { | ||||
| 	var self = this; | ||||
|  | ||||
| 	// handle requests to create a new user. | ||||
| 	$(".modal-user button").click(function(e) { | ||||
| 		var login = $(".modal-user input").val(); | ||||
| 		var user = { login: login }; | ||||
|  | ||||
| 		$(".alert-danger").hide(); | ||||
|  | ||||
| 		$.ajax({ | ||||
| 			url: "/api/users", | ||||
| 			type: "POST", | ||||
| 			data: JSON.stringify(user), | ||||
| 			contentType: "application/json", | ||||
| 			success: function( data ) { | ||||
| 				// clears the form value | ||||
| 				$(".modal-user input").val(""); | ||||
|  | ||||
| 				// find an existing item in the list and clone it. | ||||
| 				var el = $(".col-sm-4").first().clone(); | ||||
| 				el.find("img").attr("src", data.avatar_url); | ||||
| 				el.find("h3").text(data.login); | ||||
| 				el.find("p").text(data.email); | ||||
| 				el.find(".card").attr("data-id", data.login); | ||||
| 				el.find(".card").attr("data-admin", data.admin); | ||||
|  | ||||
| 				$( ".user-row" ).prepend(el); | ||||
| 			}, | ||||
| 			error: function(data) { | ||||
| 				$(".alert-danger").text(data.responseText); | ||||
| 				$(".alert-danger").show(); | ||||
| 			} | ||||
| 		}); | ||||
| 	}); | ||||
|  | ||||
|  | ||||
| 	$(".user-row").on('click', '.btn-group .btn-info', function(){  | ||||
| 		// gets the unique identifier for the click row, which is | ||||
| 		// the user login id. | ||||
| 		var row   = $( this ).closest("[data-id]"); | ||||
| 		var id    = row.data("id"); | ||||
| 		var admin = row.data("admin") == true; | ||||
|  | ||||
| 		row.attr("data-admin", !admin); | ||||
|  | ||||
| 		$.ajax({ | ||||
| 			url: "/api/users/"+id, | ||||
| 			type: "PATCH", | ||||
| 			data: JSON.stringify({active:true, admin: !admin}), | ||||
| 			contentType: "application/json" | ||||
| 		}); | ||||
|  | ||||
| 	});  | ||||
|  | ||||
| 	$(".user-row").on('click', '.btn-group .btn-danger', function(){ | ||||
| 		// gets the unique identifier for the click row, which is | ||||
| 		// the user login id. | ||||
| 		var row = $( this ).closest("[data-id]"); | ||||
| 		var id  = row.data("id"); | ||||
| 		 | ||||
| 		var r = confirm("Are you sure you want to delete "+id+"?"); | ||||
| 		if (r === false) { | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		// makes an API call to delete the user and then, if successful, | ||||
| 		// removes from the list. | ||||
| 		$.ajax({ | ||||
| 			url: "/api/users/"+id, | ||||
| 			type: "DELETE", | ||||
| 			success: function( data ) { | ||||
| 				row.parent().remove(); | ||||
| 			} | ||||
| 		}); | ||||
| 	}); | ||||
| } | ||||
| @@ -1,15 +0,0 @@ | ||||
| Array.prototype.remove = function() { | ||||
| 	var what, a = arguments, L = a.length, ax; | ||||
| 	while (L && this.length) { | ||||
| 		what = a[--L]; | ||||
| 		while ((ax = this.indexOf(what)) !== -1) { | ||||
| 			this.splice(ax, 1); | ||||
| 		} | ||||
| 	} | ||||
| 	return this; | ||||
| }; | ||||
|  | ||||
| function escapeHTML(html) { | ||||
|     return document.createElement('div').appendChild( | ||||
|     	document.createTextNode(html)).parentNode.innerHTML; | ||||
| } | ||||
							
								
								
									
										138
									
								
								static/scripts/vendor/livestamp.js
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,138 +0,0 @@ | ||||
| // Livestamp.js / v2.0.0 / (c) 2015 Matt Bradley / MIT License | ||||
| (function (plugin) { | ||||
|   if (typeof define === 'function' && define.amd) { | ||||
|     // AMD. Register as an anonymous module. | ||||
|     define(['jquery', 'moment'], plugin); | ||||
|   } else { | ||||
|     // Browser globals | ||||
|     plugin(jQuery, moment); | ||||
|   } | ||||
| }(function($, moment) { | ||||
|   var updateInterval = 1e3, | ||||
|       paused = false, | ||||
|       $livestamps = $([]), | ||||
|  | ||||
|   init = function() { | ||||
|     livestampGlobal.resume(); | ||||
|   }, | ||||
|  | ||||
|   prep = function($el, timestamp) { | ||||
|     var oldData = $el.data('livestampdata'); | ||||
|     if (typeof timestamp == 'number') | ||||
|       timestamp *= 1e3; | ||||
|  | ||||
|     $el.removeAttr('data-livestamp') | ||||
|       .removeData('livestamp'); | ||||
|  | ||||
|     timestamp = moment(timestamp); | ||||
|     if (moment.isMoment(timestamp) && !isNaN(+timestamp)) { | ||||
|       var newData = $.extend({ }, { 'original': $el.contents() }, oldData); | ||||
|       newData.moment = moment(timestamp); | ||||
|  | ||||
|       $el.data('livestampdata', newData).empty(); | ||||
|       $livestamps.push($el[0]); | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   run = function() { | ||||
|     if (paused) return; | ||||
|     livestampGlobal.update(); | ||||
|     setTimeout(run, updateInterval); | ||||
|   }, | ||||
|  | ||||
|   livestampGlobal = { | ||||
|     update: function() { | ||||
|       $('[data-livestamp]').each(function() { | ||||
|         var $this = $(this); | ||||
|         prep($this, $this.data('livestamp')); | ||||
|       }); | ||||
|  | ||||
|       var toRemove = []; | ||||
|       $livestamps.each(function() { | ||||
|         var $this = $(this), | ||||
|             data = $this.data('livestampdata'); | ||||
|  | ||||
|         if (data === undefined) | ||||
|           toRemove.push(this); | ||||
|         else if (moment.isMoment(data.moment)) { | ||||
|           var from = $this.html(), | ||||
|               to = data.moment.fromNow(); | ||||
|  | ||||
|           if (from != to) { | ||||
|             var e = $.Event('change.livestamp'); | ||||
|             $this.trigger(e, [from, to]); | ||||
|             if (!e.isDefaultPrevented()) | ||||
|               $this.html(to); | ||||
|           } | ||||
|         } | ||||
|       }); | ||||
|  | ||||
|       $livestamps = $livestamps.not(toRemove); | ||||
|       delete $livestamps.prevObject | ||||
|     }, | ||||
|  | ||||
|     pause: function() { | ||||
|       paused = true; | ||||
|     }, | ||||
|  | ||||
|     resume: function() { | ||||
|       paused = false; | ||||
|       run(); | ||||
|     }, | ||||
|  | ||||
|     interval: function(interval) { | ||||
|       if (interval === undefined) | ||||
|         return updateInterval; | ||||
|       updateInterval = interval; | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   livestampLocal = { | ||||
|     add: function($el, timestamp) { | ||||
|       if (typeof timestamp == 'number') | ||||
|         timestamp *= 1e3; | ||||
|       timestamp = moment(timestamp); | ||||
|  | ||||
|       if (moment.isMoment(timestamp) && !isNaN(+timestamp)) { | ||||
|         $el.each(function() { | ||||
|           prep($(this), timestamp); | ||||
|         }); | ||||
|         livestampGlobal.update(); | ||||
|       } | ||||
|  | ||||
|       return $el; | ||||
|     }, | ||||
|  | ||||
|     destroy: function($el) { | ||||
|       $livestamps = $livestamps.not($el); | ||||
|       $el.each(function() { | ||||
|         var $this = $(this), | ||||
|             data = $this.data('livestampdata'); | ||||
|  | ||||
|         if (data === undefined) | ||||
|           return $el; | ||||
|  | ||||
|         $this | ||||
|           .html(data.original ? data.original : '') | ||||
|           .removeData('livestampdata'); | ||||
|       }); | ||||
|  | ||||
|       return $el; | ||||
|     }, | ||||
|  | ||||
|     isLivestamp: function($el) { | ||||
|       return $el.data('livestampdata') !== undefined; | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   $.livestamp = livestampGlobal; | ||||
|   $(init); | ||||
|   $.fn.livestamp = function(method, options) { | ||||
|     if (!livestampLocal[method]) { | ||||
|       options = method; | ||||
|       method = 'add'; | ||||
|     } | ||||
|  | ||||
|     return livestampLocal[method](this, options); | ||||
|   }; | ||||
| })); | ||||
							
								
								
									
										666
									
								
								static/scripts/vendor/plates.js
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,666 +0,0 @@ | ||||
| var Plates = (typeof module !== 'undefined' && 'id' in module && typeof exports !== 'undefined') ? exports : {}; | ||||
|  | ||||
| !function(exports, env, undefined) { | ||||
|   "use strict"; | ||||
|  | ||||
|   // | ||||
|   // Cache variables to increase lookup speed. | ||||
|   // | ||||
|   var _toString = Object.prototype.toString; | ||||
|  | ||||
|   // | ||||
|   // Polyfill the Array#indexOf method for cross browser compatibility. | ||||
|   // | ||||
|   [].indexOf || (Array.prototype.indexOf = function indexOf(a, b ,c){ | ||||
|     for ( | ||||
|       c = this.length , b = (c+ ~~b) % c; | ||||
|       b < c && (!(b in this) || this[b] !==a ); | ||||
|       b++ | ||||
|     ); | ||||
|  | ||||
|     return b^c ? b : -1; | ||||
|   }); | ||||
|  | ||||
|   // | ||||
|   // Polyfill Array.isArray for cross browser compatibility. | ||||
|   // | ||||
|   Array.isArray || (Array.isArray = function isArray(a) { | ||||
|     return _toString.call(a) === '[object Array]'; | ||||
|   }); | ||||
|  | ||||
|   // | ||||
|   // ### function fetch(data, mapping, value, key) | ||||
|   // #### @data {Object} the data that we need to fetch a value from | ||||
|   // #### @mapping {Object} The iterated mapping step | ||||
|   // #### @tagbody {String} the tagbody we operated against | ||||
|   // #### @key {String} optional key if the mapping doesn't have a dataKey | ||||
|   // Fetches the correct piece of data | ||||
|   // | ||||
|   function fetch(data, mapping, value, tagbody, key) { | ||||
|     key = mapping.dataKey || key; | ||||
|  | ||||
|     // | ||||
|     // Check if we have data manipulation or filtering function. | ||||
|     // | ||||
|     if (mapping.dataKey && typeof mapping.dataKey === 'function') { | ||||
|       return mapping.dataKey(data, value || '', tagbody || '', key); | ||||
|     } | ||||
|  | ||||
|     // | ||||
|     // See if we are using dot notation style | ||||
|     // | ||||
|     if (!~key.indexOf('.')) return data[key]; | ||||
|  | ||||
|     var result = key | ||||
|       , structure = data; | ||||
|  | ||||
|     for (var paths = key.split('.'), i = 0, length = paths.length; i < length && structure; i++) { | ||||
|       result = structure[+paths[i] || paths[i]]; | ||||
|       structure = result; | ||||
|     } | ||||
|  | ||||
|     return result !== undefined ? result : data[key]; | ||||
|   } | ||||
|  | ||||
|   // | ||||
|   // compileMappings | ||||
|   // | ||||
|   // sort the mappings so that mappings for the same attribute and value go consecutive | ||||
|   // and inside those, those that change attributes appear first. | ||||
|   // | ||||
|   function compileMappings(oldMappings) { | ||||
|     var mappings = oldMappings.slice(0); | ||||
|  | ||||
|     mappings.sort(function(map1, map2) { | ||||
|       if (!map1.attribute) return 1; | ||||
|       if (!map2.attribute) return -1; | ||||
|  | ||||
|       if (map1.attribute !== map2.attribute) { | ||||
|         return map1.attribute < map2.attribute ? -1 : 1; | ||||
|       } | ||||
|       if (map1.value !== map2.value) { | ||||
|         return map1.value < map2.value ? -1 : 1; | ||||
|       } | ||||
|       if (! ('replace' in map1) && ! ('replace' in map2)) { | ||||
|         throw new Error('Conflicting mappings for attribute ' + map1.attribute + ' and value ' + map1.value); | ||||
|       } | ||||
|       if (map1.replace) { | ||||
|         return 1; | ||||
|       } | ||||
|       return -1; | ||||
|     }); | ||||
|  | ||||
|     return mappings; | ||||
|   } | ||||
|  | ||||
| // | ||||
| // Matches a closing tag to a open tag | ||||
| // | ||||
| function matchClosing(input, tagname, html) { | ||||
|   var closeTag = '</' + tagname + '>', | ||||
|       openTag  = new RegExp('< *' + tagname + '( *|>)', 'g'), | ||||
|       closeCount = 0, | ||||
|       openCount = -1, | ||||
|       from, to, chunk | ||||
|       ; | ||||
|  | ||||
|   from = html.search(input); | ||||
|   to = from; | ||||
|  | ||||
|   while(to > -1 && closeCount !== openCount) { | ||||
|     to = html.indexOf(closeTag, to); | ||||
|     if (to > -1) { | ||||
|       to += tagname.length + 3; | ||||
|       closeCount ++; | ||||
|       chunk = html.slice(from, to); | ||||
|       openCount = chunk.match(openTag).length; | ||||
|     } | ||||
|   } | ||||
|   if (to === -1) { | ||||
|     throw new Error('Unmatched tag ' + tagname + ' in ' + html) | ||||
|   } | ||||
|  | ||||
|   return chunk; | ||||
| } | ||||
|  | ||||
|   var Merge = function Merge() {}; | ||||
|   Merge.prototype = { | ||||
|     nest: [], | ||||
|  | ||||
|     tag: new RegExp([ | ||||
|       '<', | ||||
|       '(/?)', // 2 - is closing | ||||
|       '([-:\\w]+)', // 3 - name | ||||
|       '((?:[-\\w]+(?:', '=', | ||||
|       '(?:\\w+|["|\'](?:.*)["|\']))?)*)', // 4 - attributes | ||||
|       '(/?)', // 5 - is self-closing | ||||
|       '>' | ||||
|     ].join('\\s*')), | ||||
|  | ||||
|     // | ||||
|     // HTML attribute parser. | ||||
|     // | ||||
|     attr: /([\-\w]*)\s*=\s*(?:(["\'])([\-\.\w\s\/:;&#]*)\2)/gi, | ||||
|  | ||||
|     // | ||||
|     // In HTML5 it's allowed to have to use self closing tags without closing | ||||
|     // separators. So we need to detect these elements based on the tag name. | ||||
|     // | ||||
|     selfClosing: /^(area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/, | ||||
|  | ||||
|     // | ||||
|     // ### function hasClass(str, className) | ||||
|     // #### @str {String} the class attribute | ||||
|     // #### @className {String} the className that the classAttribute should contain | ||||
|     // | ||||
|     // Helper function for detecting if a class attribute contains the className | ||||
|     // | ||||
|     hasClass: function hasClass(str, className) { | ||||
|       return ~str.split(' ').indexOf(className); | ||||
|     }, | ||||
|  | ||||
|     // | ||||
|     // ### function iterate(html, value, components, tagname, key) | ||||
|     // #### @html {String} peice of HTML | ||||
|     // #### @value {Mixed} iterateable object with data | ||||
|     // #### @components {Array} result of the this.tag regexp execution | ||||
|     // #### @tagname {String} the name of the tag that we iterate on | ||||
|     // #### @key {String} the key of the data that we need to extract from the value | ||||
|     // #### @map {Object} attribute mappings | ||||
|     // | ||||
|     // Iterate over over the supplied HTML. | ||||
|     // | ||||
|     iterate: function iterate(html, value, components, tagname, key, map) { | ||||
|       var output  = '', | ||||
|           segment = matchClosing(components.input, tagname, html), | ||||
|           data = {}; | ||||
|  | ||||
|       // Is it an array? | ||||
|       if (Array.isArray(value)) { | ||||
|         // Yes: set the output to the result of iterating through the array | ||||
|         for (var i = 0, l = value.length; i < l; i++) { | ||||
|           // If there is a key, then we have a simple object and | ||||
|           // must construct a simple object to use as the data | ||||
|           if (key) { | ||||
|             data[key] = value[i]; | ||||
|           } else { | ||||
|             data = value[i]; | ||||
|           } | ||||
|  | ||||
|           output += this.bind(segment, data, map); | ||||
|         } | ||||
|  | ||||
|         return output; | ||||
|       } else if (typeof value === 'object') { | ||||
|         // We need to refine the selection now that we know we're dealing with a | ||||
|         // nested object | ||||
|         segment = segment.slice(components.input.length, -(tagname.length + 3)); | ||||
|         return output += this.bind(segment, value, map); | ||||
|       } | ||||
|  | ||||
|       return value; | ||||
|     }, | ||||
|  | ||||
|     // | ||||
|     // ### function bind(html, data, map) | ||||
|     // #### @html {String} the template that we need to modify | ||||
|     // #### @data {Object} data for the template | ||||
|     // #### @map {Mapper} instructions for the data placement in the template | ||||
|     // Process the actual template | ||||
|     // | ||||
|     bind: function bind(html, data, map) { | ||||
|       if (Array.isArray(data)) { | ||||
|         var output = ''; | ||||
|  | ||||
|         for (var i = 0, l = data.length; i<l; i++) { | ||||
|           output += this.bind(html, data[i], map); | ||||
|         } | ||||
|  | ||||
|         return output; | ||||
|       } | ||||
|  | ||||
|       html = (html || '').toString(); | ||||
|       data = data || {}; | ||||
|  | ||||
|       var that = this; | ||||
|  | ||||
|       var openers = 0, | ||||
|           remove = 0, | ||||
|           components, | ||||
|           attributes, | ||||
|           mappings = map && compileMappings(map.mappings), | ||||
|           intag = false, | ||||
|           tagname = '', | ||||
|           isClosing = false, | ||||
|           isSelfClosing = false, | ||||
|           selfClosing = false, | ||||
|           matchmode = false, | ||||
|           createAttribute = map && map.conf && map.conf.create, | ||||
|           closing, | ||||
|           tagbody; | ||||
|  | ||||
|       var c, | ||||
|           buffer = '', | ||||
|           left; | ||||
|  | ||||
|       for (var i = 0, l = html.length; i < l; i++) { | ||||
|         c = html.charAt(i); | ||||
|  | ||||
|         // | ||||
|         // Figure out which part of the HTML we are currently processing. And if | ||||
|         // we have queued up enough HTML to process it's data. | ||||
|         // | ||||
|         if (c === '!' && intag && !matchmode) { | ||||
|           intag = false; | ||||
|           buffer += html.slice(left, i + 1); | ||||
|         } else if (c === '<' && !intag) { | ||||
|           closing = true; | ||||
|           intag = true; | ||||
|           left = i; | ||||
|         } else if (c === '>' && intag) { | ||||
|           intag = false; | ||||
|           tagbody = html.slice(left, i + 1); | ||||
|           components = this.tag.exec(tagbody); | ||||
|  | ||||
|           if(!components) { | ||||
|             intag = true; | ||||
|             continue; | ||||
|           } | ||||
|  | ||||
|           isClosing = components[1]; | ||||
|           tagname = components[2]; | ||||
|           attributes = components[3]; | ||||
|           selfClosing = components[4]; | ||||
|           isSelfClosing = this.selfClosing.test(tagname); | ||||
|  | ||||
|           if (matchmode) { | ||||
|             // | ||||
|             // and its a closing. | ||||
|             // | ||||
|             if (!!isClosing) { | ||||
|               if (openers <= 0) { | ||||
|                 matchmode = false; | ||||
|               } else { | ||||
|                 --openers; | ||||
|               } | ||||
|             } else if (!isSelfClosing) { | ||||
|               // | ||||
|               // and its not a self-closing tag | ||||
|               // | ||||
|               ++openers; | ||||
|             } | ||||
|           } | ||||
|  | ||||
|           if (!isClosing && !matchmode) { | ||||
|             // | ||||
|             // if there is a match in progress and | ||||
|             // | ||||
|             if (mappings && mappings.length > 0) { | ||||
|               for (var ii = mappings.length - 1; ii >= 0; ii--) { | ||||
|                 var setAttribute = false | ||||
|                   , mapping = mappings[ii] | ||||
|                   , shouldSetAttribute = mapping.re && attributes.match(mapping.re); | ||||
|  | ||||
|                 // | ||||
|                 // check if we are targetting a element only or attributes | ||||
|                 // | ||||
|                 if ('tag' in mapping && !this.attr.test(tagbody) && mapping.tag === tagname) { | ||||
|                   tagbody = tagbody + fetch(data, mapping, '', tagbody); | ||||
|                   continue; | ||||
|                 } | ||||
|  | ||||
|                 tagbody = tagbody.replace(this.attr, function(str, key, q, value, a) { | ||||
|                   var newdata; | ||||
|  | ||||
|                   if (shouldSetAttribute && mapping.replace !== key || remove) { | ||||
|                     return str; | ||||
|                   } else if (shouldSetAttribute || typeof mapping.replacePartial1 !== 'undefined') { | ||||
|                     setAttribute = true; | ||||
|  | ||||
|                     // | ||||
|                     // determine if we should use the replace argument or some value from the data object. | ||||
|                     // | ||||
|                     if (typeof mapping.replacePartial2 !== 'undefined') { | ||||
|                       newdata = value.replace(mapping.replacePartial1, mapping.replacePartial2); | ||||
|                     } else if (typeof mapping.replacePartial1 !== 'undefined' && mapping.dataKey) { | ||||
|                       newdata = value.replace(mapping.replacePartial1, fetch(data, mapping, value, tagbody, key)); | ||||
|                     } else { | ||||
|                       newdata = fetch(data, mapping, value, tagbody, key); | ||||
|                     } | ||||
|  | ||||
|                     return key + '="' + (newdata || '') + '"'; | ||||
|                   } else if (!mapping.replace && mapping.attribute === key) { | ||||
|                     if ( | ||||
|                       mapping.value === value || | ||||
|                       that.hasClass(value, mapping.value || | ||||
|                       mappings.conf.where === key) || | ||||
|                       (_toString.call(mapping.value) === '[object RegExp]' && | ||||
|                         mapping.value.exec(value) !== null) | ||||
|                     ) { | ||||
|                       if (mapping.remove) { | ||||
|                         // | ||||
|                         // only increase the remove counter if it's not a self | ||||
|                         // closing element. As matchmode is suffectient to | ||||
|                         // remove tose | ||||
|                         // | ||||
|                         if (!isSelfClosing) remove++; | ||||
|                         matchmode = true; | ||||
|                       } else if (mapping.plates) { | ||||
|                         var partial = that.bind( | ||||
|                             mapping.plates | ||||
|                           , typeof mapping.data === 'string' ? fetch(data, { dataKey: mapping.data }) : mapping.data || data | ||||
|                           , mapping.mapper | ||||
|                         ); | ||||
|  | ||||
|                         buffer += tagbody + that.iterate(html, partial, components, tagname, undefined, map); | ||||
|                         matchmode = true; | ||||
|                       } else { | ||||
|                         var v = newdata = fetch(data, mapping, value, tagbody, key); | ||||
|                         newdata = tagbody + newdata; | ||||
|  | ||||
|                         if (Array.isArray(v)) { | ||||
|                           newdata = that.iterate(html, v, components, tagname, value, map); | ||||
|                           // If the item is an array, then we need to tell | ||||
|                           // Plates that we're dealing with nests | ||||
|                           that.nest.push(tagname); | ||||
|                         } else if (typeof v === 'object') { | ||||
|                           newdata = tagbody + that.iterate(html, v, components, tagname, value, map); | ||||
|                         } | ||||
|  | ||||
|                         buffer += newdata || ''; | ||||
|                         matchmode = true; | ||||
|                       } | ||||
|                     } | ||||
|                   } | ||||
|  | ||||
|                   return str; | ||||
|                 }); | ||||
|  | ||||
|                 // | ||||
|                 // Do we need to create the attributes if they don't exist. | ||||
|                 // | ||||
|                 if (createAttribute && shouldSetAttribute && !setAttribute) { | ||||
|                   var spliced = selfClosing ? 2 : 1 | ||||
|                     , close = selfClosing ? '/>': '>' | ||||
|                     , left = tagbody.substr(0, tagbody.length - spliced); | ||||
|  | ||||
|                   if (left[left.length - 1] === ' ') { | ||||
|                     left = left.substr(0, left.length - 1); | ||||
|  | ||||
|                     if (selfClosing) { | ||||
|                       close = ' ' + close; | ||||
|                     } | ||||
|                   } | ||||
|  | ||||
|                   tagbody = [ | ||||
|                     left, | ||||
|                     ' ', | ||||
|                     mapping.replace, | ||||
|                     '="', | ||||
|                     fetch(data, mapping), | ||||
|                     '"', | ||||
|                     close | ||||
|                   ].join(''); | ||||
|                 } | ||||
|               } | ||||
|             } else { | ||||
|               // | ||||
|               // if there is no map, we are just looking to match | ||||
|               // the specified id to a data key in the data object. | ||||
|               // | ||||
|               tagbody.replace(this.attr, function (attr, key, q, value, idx) { | ||||
|                 if (key === map && map.conf.where || 'id' && data[value]) { | ||||
|                   var v = data[value], | ||||
|                       nest = Array.isArray(v), | ||||
|                       output = nest || typeof v === 'object' | ||||
|                         ? that.iterate(html.substr(left), v, components, tagname, value, map) | ||||
|                         : v; | ||||
|  | ||||
|                   // If the item is an array, then we need to tell | ||||
|                   // Plates that we're dealing with nests | ||||
|                   if (nest) { that.nest.push(tagname); } | ||||
|  | ||||
|                   buffer += nest ? output : tagbody + output; | ||||
|                   matchmode = true; | ||||
|                 } | ||||
|               }); | ||||
|             } | ||||
|           } | ||||
|  | ||||
|           // | ||||
|           // if there is currently no match in progress | ||||
|           // just write the tagbody to the buffer. | ||||
|           // | ||||
|           if (!matchmode && that.nest.length === 0) { | ||||
|             if (!remove) buffer += tagbody; | ||||
|  | ||||
|             if (remove && !!isClosing) --remove; | ||||
|           } else if (!matchmode && that.nest.length) { | ||||
|               this.nest.pop(); | ||||
|           } | ||||
|         } else if (!intag && !matchmode) { | ||||
|           // | ||||
|           // currently not inside a tag and there is no | ||||
|           // match in progress, we can write the char to | ||||
|           // the buffer. | ||||
|           // | ||||
|           if (!remove) buffer += c; | ||||
|         } | ||||
|       } | ||||
|       return buffer; | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   // | ||||
|   // ### function Mapper(conf) | ||||
|   // #### @conf {Object} configuration object | ||||
|   // Constructor function for the Mapper instance that is responsible for | ||||
|   // providing the mapping for the data structure | ||||
|   // | ||||
|   function Mapper(conf) { | ||||
|     if (!(this instanceof Mapper)) { return new Mapper(conf); } | ||||
|  | ||||
|     this.mappings = []; | ||||
|     this.conf = conf || {}; | ||||
|   } | ||||
|  | ||||
|   // | ||||
|   // ### function last(newitem) | ||||
|   // #### @newitem {Boolean} do we need to add a new item to the mapping | ||||
|   // Helper function for adding new attribute maps to a Mapper instance | ||||
|   // | ||||
|   function last(newitem) { | ||||
|     if (newitem) { | ||||
|       this.mappings.push({}); | ||||
|     } | ||||
|  | ||||
|     var m = this.mappings[this.mappings.length - 1]; | ||||
|  | ||||
|     if (m && m.attribute && m.value && m.dataKey && m.replace) { | ||||
|       m.re = new RegExp(m.attribute + '=([\'"]?)' + m.value + '\\1'); | ||||
|     } else if (m) { | ||||
|       delete m.re; | ||||
|     } | ||||
|  | ||||
|     return m; | ||||
|   } | ||||
|  | ||||
|   // | ||||
|   // Create the actual chainable methods: where('class').is('foo').insert('bla') | ||||
|   // | ||||
|   Mapper.prototype = { | ||||
|     // | ||||
|     // ### function replace(val1, val2) | ||||
|     // #### @val1 {String|RegExp} The part of the attribute that needs to be replaced | ||||
|     // #### @val2 {String} The value it should be replaced with | ||||
|     // | ||||
|     replace: function replace(val1, val2) { | ||||
|       var l = last.call(this); | ||||
|       l.replacePartial1 = val1; | ||||
|       l.replacePartial2 = val2; | ||||
|       return this; | ||||
|     }, | ||||
|  | ||||
|     // | ||||
|     // ### function use(val) | ||||
|     // #### @val {String} A string that represents a key. | ||||
|     // Data will be inserted into the attribute that was specified in the | ||||
|     // `where` clause. | ||||
|     // | ||||
|     use: function use(val) { | ||||
|       last.call(this).dataKey = val; | ||||
|       return last.call(this) && this; | ||||
|     }, | ||||
|  | ||||
|     // | ||||
|     // ### function where(val) | ||||
|     // #### @val {String} an attribute that may be found in a tag | ||||
|     // This method will initiate a clause. Once a clause has been established | ||||
|     // other member methods will be chained to each other in any order. | ||||
|     // | ||||
|     where: function where(val) { | ||||
|       last.call(this, true).attribute = val; | ||||
|       return last.call(this) && this; | ||||
|     }, | ||||
|  | ||||
|     // | ||||
|     // ### function class(val) | ||||
|     // #### @val {String} a value that may be found in the `class` attribute of a tag | ||||
|     // the method name should be wrapped in quotes or it will throw errors in IE. | ||||
|     // | ||||
|     'class': function className(val) { | ||||
|       return this.where('class').is(val); | ||||
|     }, | ||||
|  | ||||
|     // | ||||
|     // ### function tag(val) | ||||
|     // #### @val {String} the name of the tag should be found | ||||
|     // | ||||
|     tag: function tag(val) { | ||||
|       last.call(this, true).tag = val; | ||||
|       return this; | ||||
|     }, | ||||
|  | ||||
|     // | ||||
|     // ### function is(val) | ||||
|     // #### @val {string} The value of the attribute that was specified in the | ||||
|     // `where` clause. | ||||
|     // | ||||
|     is: function is(val) { | ||||
|       last.call(this).value = val; | ||||
|       return last.call(this) && this; | ||||
|     }, | ||||
|  | ||||
|     // | ||||
|     // ### function has(val) | ||||
|     // #### @val {String|RegExp} The value of the attribute that was specified | ||||
|     // in the `where` clause. | ||||
|     // | ||||
|     has: function has(val) { | ||||
|       last.call(this).value = val; | ||||
|       this.replace(val); | ||||
|       return last.call(this) && this; | ||||
|     }, | ||||
|  | ||||
|     // | ||||
|     // ### function insert(val) | ||||
|     // #### @val {String} A string that represents a key. Data will be inserted | ||||
|     // in to the attribute that was specified in the `where` clause. | ||||
|     // | ||||
|     insert: function insert(val) { | ||||
|       var l = last.call(this); | ||||
|       l.replace = l.attribute; | ||||
|       l.dataKey = val; | ||||
|       return last.call(this) && this; | ||||
|     }, | ||||
|  | ||||
|     // | ||||
|     // ### function as(val) | ||||
|     // #### @val {String} A string that represents an attribute in the tag. | ||||
|     // If there is no attribute by that name name found, one may be created | ||||
|     // depending on the options that where passed in the `Plates.Map` | ||||
|     // constructor. | ||||
|     // | ||||
|     as: function as(val) { | ||||
|       last.call(this).replace = val; | ||||
|       return last.call(this) && this; | ||||
|     }, | ||||
|  | ||||
|     // | ||||
|     // ### function remove() | ||||
|     // This will remove the element that was specified in the `where` clause | ||||
|     // from the template. | ||||
|     // | ||||
|     remove: function remove() { | ||||
|       last.call(this).remove = true; | ||||
|       return last.call(this, true); | ||||
|     }, | ||||
|  | ||||
|     // | ||||
|     // ### function append(plates, data, map) | ||||
|     // #### @plates {String} Template or path/id of the template | ||||
|     // #### @data {Object|String} data for the appended template | ||||
|     // #### @map {Plates.Map} mapping for the data | ||||
|     // | ||||
|     append: function append(plates, data, map) { | ||||
|       var l = last.call(this); | ||||
|  | ||||
|       if (data instanceof Mapper) { | ||||
|         map = data; | ||||
|         data = undefined; | ||||
|       } | ||||
|  | ||||
|       // If the supplied plates template doesn't contain any HTML it's most | ||||
|       // likely that we need to import it. To improve performance we will cache | ||||
|       // the result of the file system. | ||||
|       if (!/<[^<]+?>/.test(plates) && !exports.cache[plates]) { | ||||
|         // figure out if we are running in Node.js or a browser | ||||
|         if ('document' in env && 'getElementById' in env.document) { | ||||
|           exports.cache[plates] = document.getElementById(plates).innerHTML; | ||||
|         } else { | ||||
|           exports.cache[plates] = require('fs').readFileSync( | ||||
|             require('path').join(process.cwd(), plates), | ||||
|             'utf8' | ||||
|           ); | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       l.plates = exports.cache[plates] || plates; | ||||
|       l.data = data; | ||||
|       l.mapper = map; | ||||
|  | ||||
|       return last.call(this, true); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   // | ||||
|   // Provide helpful aliases that well help with increased compatibility as not | ||||
|   // all browsers allow the Mapper#class prototype (IE). | ||||
|   // | ||||
|   Mapper.prototype.className = Mapper.prototype['class']; | ||||
|  | ||||
|   // | ||||
|   // Aliases of different methods. | ||||
|   // | ||||
|   Mapper.prototype.partial = Mapper.prototype.append; | ||||
|   Mapper.prototype.to = Mapper.prototype.use; | ||||
|  | ||||
|   // | ||||
|   // Expose a simple cache object so people can clear the cached partials if | ||||
|   // they want to. | ||||
|   // | ||||
|   exports.cache = {}; | ||||
|  | ||||
|   // | ||||
|   // Expose the Plates#bind interface. | ||||
|   // | ||||
|   exports.bind = function bind(html, data, map) { | ||||
|     var merge = new Merge(); | ||||
|     return merge.bind(html, data, map); | ||||
|   }; | ||||
|  | ||||
|   // | ||||
|   // Expose the Mapper. | ||||
|   // | ||||
|   exports.Map = Mapper; | ||||
| }(Plates, this); | ||||
| @@ -1,27 +0,0 @@ | ||||
| package static | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/elazarl/go-bindata-assetfs" | ||||
| ) | ||||
|  | ||||
| //go:generate go run ../contrib/generate-js.go -dir scripts/ -o scripts_gen/drone.min.js | ||||
|  | ||||
| //go:generate sassc --style compact styles/style.sass styles_gen/style.css | ||||
| //go:generate go-bindata-assetfs -ignore "\\.go" -pkg static -o static_gen.go ./... | ||||
|  | ||||
| func FileSystem() http.FileSystem { | ||||
| 	fs := &assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: ""} | ||||
| 	return &binaryFileSystem{ | ||||
| 		fs, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type binaryFileSystem struct { | ||||
| 	fs http.FileSystem | ||||
| } | ||||
|  | ||||
| func (b *binaryFileSystem) Open(name string) (http.File, error) { | ||||
| 	return b.fs.Open(name[1:]) | ||||
| } | ||||
| @@ -1,2 +0,0 @@ | ||||
|  | ||||
|  | ||||
| @@ -1,56 +0,0 @@ | ||||
| .navbar | ||||
| 	background-color: #FFF; | ||||
| 	border-radius: 0px; | ||||
| 	height: 53px; | ||||
| 	z-index: 2; | ||||
| 	margin-top: 10px; | ||||
|  | ||||
| .navbar-brand | ||||
| 	background-image: url(/static/images/logo_dark.svg); | ||||
| 	width: 30px; | ||||
| 	height: 30px; | ||||
| 	margin-top: 3px; | ||||
| 	background-size: 30px; | ||||
| 	background-repeat: no-repeat; | ||||
| 	background-position: center center; | ||||
|  | ||||
| .navbar img | ||||
| 	width: 32px; | ||||
| 	height: 32px; | ||||
| 	border-radius: 50%; | ||||
|  | ||||
| .navbar .dropdown | ||||
| 	display: inline-block; | ||||
|  | ||||
| 	button | ||||
| 		background: none; | ||||
| 		border: none; | ||||
| 		color: #2b303b; | ||||
| 		min-wdith: 24px; | ||||
| 		max-width: 24px; | ||||
|  | ||||
| 		.material-icons | ||||
| 			line-height: 32px; | ||||
| 			min-wdith: 24px; | ||||
| 			max-width: 24px; | ||||
|  | ||||
| .navbar-nav.navbar-right | ||||
| 	display: inline-block; | ||||
| 	float: right; | ||||
| 	li | ||||
| 		display: inline-block; | ||||
| 		vertical-align: middle; | ||||
|  | ||||
| .navbar-form | ||||
| 	margin-right: 20px; | ||||
| 	width: 350px; | ||||
| 	.twitter-typeahead | ||||
| 		width: 100%; | ||||
|  | ||||
| 	.form-control | ||||
| 		color: #747C84; | ||||
| 		border: none; | ||||
| 		background-color: #eff1f5; | ||||
| 		border-radius: 0px; | ||||
| 		padding: 0.3rem 0.75rem; | ||||
| 		width: 100%; | ||||
| @@ -1,95 +0,0 @@ | ||||
| input[type="range"]:focus ~ .slider-label | ||||
| 	display: inline-block; | ||||
|  | ||||
| input[type=range] | ||||
| 	-webkit-appearance: none; | ||||
| 	margin: 6px 0; | ||||
| 	width: 200px; | ||||
|  | ||||
| input[type=range]:focus | ||||
| 	outline: none; | ||||
|  | ||||
| input[type=range]::-webkit-slider-runnable-track | ||||
| 	width: 100%; | ||||
| 	height: 8px; | ||||
| 	cursor: pointer; | ||||
| 	animate: 0.2s; | ||||
| 	box-shadow: none; | ||||
| 	background: rgba(0, 150, 136, 0.5); | ||||
| 	background: rgba(102, 187, 106, 0.5); | ||||
| 	border-radius: 5px; | ||||
| 	border: none; | ||||
|  | ||||
| input[type=range]::-webkit-slider-thumb | ||||
| 	box-shadow: none; | ||||
| 	border: none; | ||||
| 	height: 26px; | ||||
| 	width: 26px; | ||||
| 	border-radius: 50px; | ||||
| 	background: #009688; | ||||
| 	background: #66bb6a; | ||||
| 	cursor: pointer; | ||||
| 	-webkit-appearance: none; | ||||
| 	margin-top: -10px; | ||||
|  | ||||
| input[type=range]:focus::-webkit-slider-runnable-track | ||||
| 	background: rgba(0, 150, 136, 0.5); | ||||
| 	background: rgba(102, 187, 106, 0.5); | ||||
|  | ||||
| input[type=range]::-moz-range-track | ||||
| 	width: 100%; | ||||
| 	height: 8px; | ||||
| 	cursor: pointer; | ||||
| 	animate: 0.2s; | ||||
| 	box-shadow: none; | ||||
| 	background: rgba(0, 150, 136, 0.5); | ||||
| 	background: rgba(102, 187, 106, 0.5); | ||||
| 	border-radius: 5px; | ||||
| 	border: none; | ||||
|  | ||||
| input[type=range]::-moz-range-thumb | ||||
| 	box-shadow: none; | ||||
| 	border: none; | ||||
| 	height: 26px; | ||||
| 	width: 26px; | ||||
| 	border-radius: 50px; | ||||
| 	background: #009688; | ||||
| 	background: #66bb6a; | ||||
| 	cursor: pointer; | ||||
|  | ||||
| input[type=range]::-ms-track | ||||
| 	width: 100%; | ||||
| 	height: 8.4px; | ||||
| 	cursor: pointer; | ||||
| 	animate: 0.2s; | ||||
| 	background: transparent; | ||||
| 	border-color: transparent; | ||||
| 	border-width: 16px 0; | ||||
| 	color: transparent; | ||||
|  | ||||
| input[type=range]::-ms-fill-lower | ||||
| 	background: #2a6495; | ||||
| 	border: 0.2px solid #010101; | ||||
| 	border-radius: 2.6px; | ||||
| 	box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; | ||||
|  | ||||
| input[type=range]::-ms-fill-upper | ||||
| 	background: #3071a9; | ||||
| 	border: 0.2px solid #010101; | ||||
| 	border-radius: 2.6px; | ||||
| 	box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; | ||||
|  | ||||
| input[type=range]::-ms-thumb | ||||
| 	box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; | ||||
| 	border: 1px solid #000000; | ||||
| 	height: 36px; | ||||
| 	width: 16px; | ||||
| 	border-radius: 3px; | ||||
| 	background: #ffffff; | ||||
| 	cursor: pointer; | ||||
|  | ||||
| input[type=range]:focus::-ms-fill-lower | ||||
| 	background: #3071a9; | ||||
|  | ||||
| input[type=range]:focus::-ms-fill-upper | ||||
| 	background: #367ebd; | ||||
| @@ -1,46 +0,0 @@ | ||||
| .success, | ||||
| .failure, | ||||
| .killed, | ||||
| .error, | ||||
| .running, | ||||
| .pending | ||||
| 	padding: 0px 15px; | ||||
| 	color: #FFF; | ||||
| 	width: 100px; | ||||
| 	text-align: center; | ||||
| 	border-radius: 2px; | ||||
| 	text-transform: uppercase; | ||||
| 	font-size: 11px; | ||||
| 	line-height: 22px; | ||||
| 	display: inline-block; | ||||
| 	margin-right: 10px; | ||||
|  | ||||
| .error, | ||||
| .killed, | ||||
| .failure | ||||
| 	background: #bf616a; | ||||
|  | ||||
| .success | ||||
| 	background: #a3be8c; | ||||
|  | ||||
| .pending, | ||||
| .running | ||||
| 	background: #ebcb8b; | ||||
| 	animation: horizontal 2s ease infinite; | ||||
|  | ||||
|  | ||||
| @keyframes horizontal | ||||
| 	0% | ||||
| 		transform: translate(0,0) | ||||
| 	6% | ||||
| 		transform: translate(5px,0) | ||||
| 	12% | ||||
| 		transform: translate(0,0) | ||||
| 	18% | ||||
| 		transform: translate(5px,0) | ||||
| 	24% | ||||
| 		transform: translate(0,0) | ||||
| 	30% | ||||
| 		transform: translate(5px,0) | ||||
| 	36%,100% | ||||
| 		transform: translate(0,0) | ||||
| @@ -1,95 +0,0 @@ | ||||
| .subnav | ||||
| 	background: #FFF; | ||||
| 	border-bottom: 1px solid #EEE; | ||||
| 	height: 75px; | ||||
| 	min-height: 75px; | ||||
| 	max-height: 75px; | ||||
| 	margin-bottom: 40px; | ||||
| 	position: relative; | ||||
|  | ||||
| 	ol | ||||
| 		float: left; | ||||
| 		margin: 0px; | ||||
| 		margin-left: 20px; | ||||
| 		padding: 0px; | ||||
|  | ||||
| 		li | ||||
| 			line-height: 75px; | ||||
| 			display: inline-block; | ||||
| 			vertical-align: middle; | ||||
| 			font-size: 21px; | ||||
|  | ||||
| 			.btn | ||||
| 				background: transparent | ||||
| 				outline: none; | ||||
| 			.btn:focus | ||||
| 				outline: none; | ||||
|  | ||||
| 			.btn-info | ||||
| 				cursor: pointer; | ||||
| 				width: auto; | ||||
| 				text-transform: uppercase; | ||||
| 				padding: 0px 10px; | ||||
| 				border-radius: 2px; | ||||
| 				font-size: 11px; | ||||
| 				line-height: 30px; | ||||
| 				height: auto; | ||||
| 				margin-left: 10px; | ||||
| 				border-color: #95AEC7; | ||||
| 				color: #95AEC7; | ||||
|  | ||||
| 		li.separator i | ||||
| 			vertical-align: middle | ||||
|  | ||||
| 		li a | ||||
| 			color: #2B303B; | ||||
| 			line-height: 75px; | ||||
| 			text-decoration: none; | ||||
| 			&:hover | ||||
| 				text-decoration: none; | ||||
|  | ||||
|  | ||||
| 	.nav-tabs | ||||
| 		position: absolute; | ||||
| 		bottom: -1px; | ||||
| 		right: 30px; | ||||
| 		border-bottom: 1px solid #eee; | ||||
|  | ||||
| 	.nav-tabs .nav-link | ||||
| 		border: none; | ||||
| 		color: #C7CCD1; | ||||
| 		font-size: 13px; | ||||
| 		padding: 10px 20px; | ||||
| 		text-transform: uppercase; | ||||
|  | ||||
| 	.nav-tabs .nav-link.active | ||||
| 		border: 1px solid #eee; | ||||
| 		border-bottom: 1px solid #fff; | ||||
| 		color: #2b303b; | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| //	.nav-tabs | ||||
| //		position:absolute; | ||||
| //		bottom:-1px; | ||||
| //		right:30px; | ||||
| //		border-bottom:1px solid #eee; | ||||
| // | ||||
| //	.nav-tabs .nav-link | ||||
| //		border:none; | ||||
| //		color: #C7CCD1; | ||||
| //		font-size:13px; | ||||
| //		padding:10px 0px; | ||||
| //		margin-left:10px; | ||||
| //		margin-right:10px; | ||||
| //		text-transform:uppercase; | ||||
| // | ||||
| //	.nav-tabs .nav-item:last-child .nav-link | ||||
| //		margin-right:5px; | ||||
| // | ||||
| //	.nav-tabs .nav-link.active | ||||
| //		border: none; | ||||
| //		border-bottom:2px solid #2b303b; | ||||
| //		color: #2b303b; | ||||
| @@ -1,43 +0,0 @@ | ||||
| // http://codepen.io/batazor/pen/KwKryj | ||||
|  | ||||
| .switch | ||||
| 	display: inline-block; | ||||
| 	position: relative; | ||||
| 	width: 40px; | ||||
| 	height: 8px; | ||||
| 	border-radius: 10.416666666666668px; | ||||
| 	background: #E0E0E0; | ||||
| 	-webkit-transition: background 0.28s cubic-bezier(0.4, 0, 0.2, 1); | ||||
| 	transition: background 0.28s cubic-bezier(0.4, 0, 0.2, 1); | ||||
| 	vertical-align: middle; | ||||
| 	cursor: pointer; | ||||
|  | ||||
| .switch::before | ||||
| 	content: ''; | ||||
| 	position: absolute; | ||||
| 	top: -8.604166666666667px; | ||||
| 	left: -2.604166666666667px; | ||||
| 	width: 26.04166666666667px; | ||||
| 	height: 26.04166666666667px; | ||||
| 	background: #bdbdbd; | ||||
| 	border-radius: 50%; | ||||
| 	-webkit-transition: left 0.28s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1); | ||||
| 	transition: left 0.28s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1); | ||||
|  | ||||
| switch:active::before | ||||
| 	box-shadow: 0 2px 10.416666666666668px rgba(0, 0, 0, 0.28), 0 0 0 25px rgba(0, 0, 0, 0.1); | ||||
|  | ||||
| switch:active::before | ||||
| 	box-shadow: 0 2px 10.416666666666668px rgba(0, 0, 0, 0.28), 0 0 0 25px rgba(0, 0, 0, 0.1); | ||||
|  | ||||
| input:checked + .switch | ||||
| 	background: rgba(0, 150, 136, 0.5); | ||||
| 	background: rgba(102, 187, 106, 0.5); | ||||
|  | ||||
| input:checked + .switch::before | ||||
| 	left: 20.562499999999996px; | ||||
| 	background: #009688; | ||||
| 	background: #66bb6a; | ||||
|  | ||||
| input:checked + .switch:active::before | ||||
| 	box-shadow: 0 2px 10.416666666666668px rgba(0, 0, 0, 0.28), 0 0 0 25px rgba(0, 150, 136, 0.2); | ||||
| @@ -1,86 +0,0 @@ | ||||
| .timeline | ||||
| 	padding-left: 50px; | ||||
| 	position: relative; | ||||
| 	margin-top: 10px; | ||||
| 	margin-bottom: 40px; | ||||
|  | ||||
| 	.card | ||||
| 		display: flex; | ||||
| 		border: none; | ||||
| 		border-radius: 0px; | ||||
| 		border-top: 1px solid #eceeef; | ||||
| 		text-decoration: none; | ||||
| 		color: #2b303b; | ||||
| 		.card-header | ||||
| 			background: #FFF; | ||||
| 			border: none; | ||||
| 			padding: 0px; | ||||
| 			width: 50px; | ||||
| 			min-width: 50px; | ||||
| 			max-width: 50px; | ||||
| 			padding-top: 30px; | ||||
| 		.card-block | ||||
| 			flex: 1 1 auto; | ||||
| 			padding: 30px 12px 12px 12px; | ||||
| 			p | ||||
| 				color: #ADB3BA; | ||||
| 				margin-top: 2px; | ||||
| 				font-size: 0px; | ||||
|  | ||||
| 				em | ||||
| 					text-decoration: none; | ||||
| 					color: #747C84; | ||||
| 					font-style: normal; | ||||
| 					font-size: 0.9rem; | ||||
| 				span | ||||
| 					margin: 0px 5px; | ||||
| 					font-size: 0.9rem; | ||||
|  | ||||
| 			h3 | ||||
| 				display: inline-block; | ||||
| 				line-height: 22px; | ||||
| 				font-size: 18px; | ||||
|  | ||||
| 	.card:nth-child(2) | ||||
| 		border-top: 0px; | ||||
|  | ||||
| .timeline:before | ||||
| 	position: absolute; | ||||
| 	top: 0; | ||||
| 	bottom: 0; | ||||
| 	left: 14px; | ||||
| 	z-index: -1; | ||||
| 	display: block; | ||||
| 	width: 1px; | ||||
| 	content: ""; | ||||
| 	background-color: #DFE2E5; | ||||
|  | ||||
| .group | ||||
| 	padding-bottom: 16px; | ||||
|  | ||||
| .group-title | ||||
| 	font-size: 13px; | ||||
| 	text-transform: uppercase; | ||||
| 	color: #ADB3BA; | ||||
| 	margin-bottom: 30px; | ||||
| 	margin-top: 30px; | ||||
| 	position: relative; | ||||
| 	line-height: 20px; | ||||
| 			 | ||||
| .group-title:before | ||||
| 	content: '\f01f'; | ||||
| 	font-family: "octicons"; | ||||
| 	position: absolute; | ||||
| 	left: -48px; | ||||
| 	font-size: 20px; | ||||
| 	background: #FFF; | ||||
| 	width: 24px; | ||||
| 	vertical-align: middle; | ||||
| 	text-align: center; | ||||
| 	color: #ADB3BA; | ||||
| 				 | ||||
| .group:first-child .group-title | ||||
| 	margin-top: 0px; | ||||
| 				 | ||||
| .group:last-child | ||||
| 	padding-bottom: 0px; | ||||
| @@ -1,195 +0,0 @@ | ||||
|  | ||||
| // this is the build output section of the page that displays | ||||
| // the ansi terminal output. | ||||
|  | ||||
| #output | ||||
| 	background: #0d1926; | ||||
| 	color: #d9e6f2; | ||||
| 	margin-right: 15px; | ||||
| 	font-size: 13px; | ||||
| 	color: #eff1f5; | ||||
| 	border-radius: 2px; | ||||
| 	background: #2b303b; | ||||
| 	white-space: pre-wrap; | ||||
| 	word-wrap: break-word; | ||||
| 	box-sizing: border-box; | ||||
| 	padding: 35px 40px; | ||||
| 	font-family: "Roboto Mono"; | ||||
| 	min-height: calc(100vh - 205px); | ||||
|  | ||||
| #output > pre | ||||
| 	font-size: 13px; | ||||
| 	color: #eff1f5; | ||||
| 	border-radius: 2px; | ||||
| 	background: #2b303b; | ||||
| 	white-space: pre-wrap; | ||||
| 	word-wrap: break-word; | ||||
| 	box-sizing: border-box; | ||||
| 	padding: 0px; | ||||
| 	font-family: "Roboto Mono"; | ||||
| 	margin: 0px; | ||||
| 	margin-top: 25px; | ||||
|  | ||||
| #output > pre:first-child | ||||
| 	margin-top: 0px; | ||||
|  | ||||
| #output > pre:before | ||||
| 	content: attr(data-title); | ||||
| 	display: block; | ||||
| 	padding: 5px; | ||||
| 	margin-bottom: 10px; | ||||
| 	border-bottom: 1px solid rgba(255,255,255,0.3); | ||||
| 	padding-left: 0px; | ||||
|  | ||||
| #follow | ||||
| 	position: absolute; | ||||
|  | ||||
| .build-summary | ||||
| 	padding-left: 20px; | ||||
| 	p | ||||
| 		color: #ADB3BA; | ||||
| 		margin-top: 4px; | ||||
| 		font-size: 0.9rem; | ||||
| 	em | ||||
| 		color: #747C84; | ||||
| 		text-decoration: none; | ||||
| 		font-style: normal; | ||||
|  | ||||
| //	.success, | ||||
| //	.failure, | ||||
| //	.killed, | ||||
| //	.error, | ||||
| //	.running, | ||||
| //	.pending | ||||
| //		font-size: 15px; | ||||
| //		padding: 2px 28px; | ||||
| //		width: auto; | ||||
| .job-summary | ||||
| 	padding-left: 20px; | ||||
| 	font-size: 0.9rem; | ||||
| 	dt | ||||
| 		color: #ADB3BA; | ||||
| 		font-weight: normal; | ||||
| 	dd | ||||
| 		color: #747C84; | ||||
| 		text-decoration: none; | ||||
| 		font-style: normal; | ||||
|  | ||||
| .build-summary > div | ||||
| 	h3 | ||||
| 		display: inline; | ||||
| 		line-height: 28px; | ||||
| 		font-size: 18px; | ||||
| 		a.material-icons | ||||
| 			vertical-align: middle; | ||||
| 			text-decoration: none; | ||||
| 			color: #ADB3BA; | ||||
| 			font-size: 20px; | ||||
| 			padding-left: 5px; | ||||
|  | ||||
| .job-list | ||||
| 	padding-left: 20px; | ||||
| 	margin-bottom: 20px; | ||||
|  | ||||
| .job-list a | ||||
| 	h3 | ||||
| 		margin-bottom: 10px; | ||||
| 		color: #2b303b; | ||||
| 		font-size: 14px; | ||||
| 		margin-top: 14px; | ||||
| 		.env | ||||
| 			font-family: "Roboto Mono" | ||||
| 	text-decoration: none; | ||||
| 	color: #747C84; | ||||
| 	font-size: 15px; | ||||
| 	display: block; | ||||
| 	border-top: 1px solid #eee; | ||||
| 	padding-top: 20px; | ||||
| 	padding-bottom: 20px; | ||||
| 	position: relative; | ||||
| 	&:not(.active) > div:nth-child(2) > div | ||||
| 		display: none | ||||
|  | ||||
| //	&:not(.active) > div:nth-child(2) > h3 | ||||
| //		color:#ADB3BA; | ||||
| //	&:not(.active) > div:nth-child(1):after | ||||
| //		content: "\2026"; | ||||
| //		display:block; | ||||
| //		padding:0px 10px; | ||||
| //		vertical-align:middle; | ||||
| //		border:1px solid #eee; | ||||
| //		background:#fff; | ||||
| //		position:absolute; | ||||
| //		right:0px; | ||||
| //		top:0px; | ||||
| //		height: 20px; | ||||
| //		line-height: 10px; | ||||
|  | ||||
| .job-list a:last-child | ||||
| 	border-bottom: 1px solid #eee; | ||||
|  | ||||
| .job-list a > div:first-child | ||||
| 	margin-bottom: 10px; | ||||
| 	position: relative; | ||||
|  | ||||
| //.job-list a.active:after | ||||
| //	content: ""; | ||||
| //	border-left: 2px solid #eee; | ||||
| //	position: absolute; | ||||
| //	top: 0px; | ||||
| //	left: -35px; | ||||
| //	bottom: 0px; | ||||
| //	width: 10px; | ||||
|  | ||||
| .build-btn-group | ||||
| 	margin-left: 20px; | ||||
| 	.btn | ||||
| 		background: #FFF; | ||||
| 		outline: none; | ||||
| 		cursor: pointer; | ||||
| 		width: auto; | ||||
| 		text-transform: uppercase; | ||||
| 		padding: 0px 10px; | ||||
| 		border-radius: 2px; | ||||
| 		font-size: 11px; | ||||
| 		line-height: 30px; | ||||
| 		height: auto; | ||||
| 		margin-right: 10px; | ||||
|  | ||||
| 		.btn-danger | ||||
| 			border: 1px solid #bf616a; | ||||
| 			color: #bf616a; | ||||
|  | ||||
| 		&.btn-info | ||||
| 			border-color: #95AEC7; | ||||
| 			color: #95AEC7; | ||||
|  | ||||
| .tail | ||||
| 	position: fixed; | ||||
| 	bottom: 50px; | ||||
| 	right: 80px; | ||||
| 	width: 38px; | ||||
| 	height: 38px; | ||||
| 	background: rgba(255,255,255,0.2); | ||||
| 	border-radius: 50%; | ||||
| 	box-shadow: 1px 2px 2px rgba(0,0,0,0.2); | ||||
| 	cursor: pointer; | ||||
| 	bottom: 15px; | ||||
| 	right: 60px; | ||||
| 	border: none; | ||||
| 	outline: none; | ||||
| 	display: none; | ||||
|  | ||||
| .tail i | ||||
| 	color: rgba(255,255,255,0.5); | ||||
| 	line-height: 38px; | ||||
| 	display: inline-block; | ||||
|  | ||||
| @supports (position:sticky) | ||||
| 	.sticky | ||||
| 		position: sticky; | ||||
| 		top: 20px; | ||||
|  | ||||
| @supports not (position:sticky) | ||||
| 	.sticky | ||||
| 		top: 0px; | ||||
| @@ -1,331 +0,0 @@ | ||||
|  | ||||
| .toc | ||||
| 	list-style-type: none; | ||||
| 	padding: 0px; | ||||
| 	margin: 0px; | ||||
| 	padding-bottom: 40px; | ||||
|  | ||||
| 	h2 | ||||
| 		font-size: 21px; | ||||
| 		font-weight: normal; | ||||
| 		margin-bottom: 20px; | ||||
| 		color: #2b303b; | ||||
|  | ||||
| 	ul | ||||
| 		list-style-type: none; | ||||
| 		padding: 0px; | ||||
| 		margin: 0px; | ||||
| 		margin-bottom: 40px; | ||||
| 		border-bottom: 1px solid #EEE; | ||||
| 		padding-bottom: 40px; | ||||
| 		li | ||||
| 			a | ||||
| 				color: #2b303b; | ||||
| 				text-decoration: none; | ||||
| 			a:hover | ||||
| 				text-decoration: underline; | ||||
|  | ||||
| 	[data-method]:before | ||||
| 		content: attr(data-method); | ||||
| 		padding: 0px 10px; | ||||
| 		line-height: 18px; | ||||
| 		min-width: 70px; | ||||
| 		font-size: 11px; | ||||
| 		text-transform: uppercase; | ||||
| 		display: inline-block; | ||||
| 		text-align: center; | ||||
| 		color: #FFF; | ||||
| 		border-radius: 2px; | ||||
| 		margin-right: 20px; | ||||
|  | ||||
| 	[data-method="GET"]:before | ||||
| 		background-color: #1ABC9C; | ||||
| 	[data-method="PUT"]:before | ||||
| 		background-color: #9B59B6; | ||||
| 	[data-method="POST"]:before | ||||
| 		background-color: #3498DB; | ||||
| 	[data-method="PATCH"]:before | ||||
| 		background-color: #E67E22; | ||||
| 	[data-method="DELETE"]:before | ||||
| 		background-color: #E74C3C; | ||||
|  | ||||
|  | ||||
|  | ||||
| 	[data-method]:before | ||||
| 		background: #FFF; | ||||
| 		border: 1px solid #FFF; | ||||
| 	[data-method="GET"]:before | ||||
| 		color: #1ABC9C; | ||||
| 		border-color: #1ABC9C; | ||||
| 	[data-method="PUT"]:before | ||||
| 		color: #9B59B6; | ||||
| 		border-color: #9B59B6; | ||||
| 	[data-method="POST"]:before | ||||
| 		color: #3498DB; | ||||
| 		border-color: #3498DB; | ||||
| 	[data-method="PATCH"]:before | ||||
| 		color: #E67E22; | ||||
| 		border-color: #E67E22; | ||||
| 	[data-method="DELETE"]:before | ||||
| 		color: #E74C3C; | ||||
| 		border-color: #E74C3C; | ||||
|  | ||||
| .operation | ||||
|  | ||||
| 	[data-method]:before | ||||
| 		content: attr(data-method); | ||||
| 		padding: 0px 10px; | ||||
| 		line-height: 18px; | ||||
| 		min-width: 70px; | ||||
| 		font-size: 11px; | ||||
| 		text-transform: uppercase; | ||||
| 		display: inline-block; | ||||
| 		text-align: center; | ||||
| 		color: #FFF; | ||||
| 		border-radius: 2px; | ||||
| 		margin-right: 20px; | ||||
| 		background: #FFF; | ||||
| 		border: 1px solid #FFF; | ||||
| 		line-height: 20px; | ||||
| 		vertical-align: top; | ||||
|  | ||||
|  | ||||
| 	[data-method]:before | ||||
| 		background: #FFF; | ||||
| 		border: 1px solid #FFF; | ||||
| 	[data-method="GET"]:before | ||||
| 		color: #1ABC9C; | ||||
| 		border-color: #1ABC9C; | ||||
| 	[data-method="PUT"]:before | ||||
| 		color: #9B59B6; | ||||
| 		border-color: #9B59B6; | ||||
| 	[data-method="POST"]:before | ||||
| 		color: #3498DB; | ||||
| 		border-color: #3498DB; | ||||
| 	[data-method="PATCH"]:before | ||||
| 		color: #E67E22; | ||||
| 		border-color: #E67E22; | ||||
| 	[data-method="DELETE"]:before | ||||
| 		color: #E74C3C; | ||||
| 		border-color: #E74C3C; | ||||
|  | ||||
| .docs | ||||
| 	margin-top: 40px; | ||||
| 	padding: 0px 50px; | ||||
| 	padding-right: 40px; | ||||
|  | ||||
| .docs-api | ||||
| 	pre | ||||
| 		margin-right: 15px; | ||||
| 		font-size: 13px; | ||||
| 		color: #eff1f5; | ||||
| 		color: #2b303b; | ||||
| 		border-radius: 2px; | ||||
| 		background: #2b303b; | ||||
| 		background: #ECF0F1; | ||||
| 		white-space: pre-wrap; | ||||
| 		word-wrap: break-word; | ||||
| 		box-sizing: border-box; | ||||
| 		padding: 25px 30px; | ||||
| 		font-family: "Roboto Mono"; | ||||
|  | ||||
| 		margin-right: 0px; | ||||
| 		padding-left: 40px; | ||||
| 		background: #eff1f5; | ||||
| 		color: #2b303b; | ||||
| 		white-space: pre-wrap; | ||||
| 		word-wrap: break-word; | ||||
| 		box-sizing: border-box; | ||||
| 		padding: 25px 30px; | ||||
| 		font-family: "Roboto Mono"; | ||||
| 		border-radius: 2px; | ||||
| 		font-size: 13px; | ||||
|  | ||||
| .operation | ||||
| 	min-height: 100vh; | ||||
| 	padding: 20px 0px; | ||||
| 	display: flex | ||||
| 	&> aside, | ||||
| 	&> div | ||||
| 		min-width: 50%; | ||||
| 		max-width: 50%; | ||||
| 		width: 0%; | ||||
| 		padding-right: 40px; | ||||
| 	h2 | ||||
| 		color: #2b303b; | ||||
| 		font-size: 21px; | ||||
| 	h3 | ||||
| 		font-size: 16px; | ||||
| 		margin-bottom: 20px; | ||||
| 		margin-top: 40px; | ||||
| 	aside | ||||
| 		background: rgba(43, 48, 59, 0.95); | ||||
| 		box-sizing: border-box; | ||||
| 		padding: 20px 0px 10px 0px; | ||||
| 		border-radius: 2px; | ||||
| 		h4 | ||||
| 			color: #d0d4d7; | ||||
| 			font-size: 15px; | ||||
| 			padding: 20px; | ||||
| 			padding-left: 40px; | ||||
| 		pre | ||||
| 			background: #2b303b; | ||||
| 			color: #d0d4d7; | ||||
| 			margin-right: 0px; | ||||
| 			padding-left: 40px; | ||||
|  | ||||
| 	.params | ||||
| 		padding: 0px; | ||||
| 		margin: 0px; | ||||
| 		list-style-type: none; | ||||
| 		border-top: 1px solid #f0f4f7 | ||||
| 		li | ||||
| 			padding: 15px 10px; | ||||
| 			border-bottom: 1px solid #f0f4f7; | ||||
| 			font-size: 15px | ||||
| 			p | ||||
| 				line-height: 20px; | ||||
| 				margin: 0 0 0 170px; | ||||
|  | ||||
| 		li:after | ||||
| 			visibility: hidden; | ||||
| 			display: block; | ||||
| 			font-size: 0; | ||||
| 			content: " "; | ||||
| 			clear: both; | ||||
| 			height: 0; | ||||
|  | ||||
| 		h4 | ||||
| 			float: left; | ||||
| 			line-height: 20px; | ||||
| 			text-align: right; | ||||
| 			padding-right: 2 0px; | ||||
| 			width: 150px; | ||||
| 			font-weight: bold; | ||||
| 			font-size: 15px; | ||||
|  | ||||
| 		small | ||||
| 			display: block; | ||||
| 			text-transform: uppercase; | ||||
| 			color: #E67E22; | ||||
| 			font-size: 11px; | ||||
| 			font-weight: normal; | ||||
| 			margin-top: 5px; | ||||
|  | ||||
|  | ||||
| .operation | ||||
| 	aside | ||||
| 		background: rgba(239, 241, 245, 0.49) | ||||
| 		h4 | ||||
| 			color: #2b303b | ||||
| 		pre | ||||
| 			background: #eff1f5 | ||||
| 			color: #2b303b | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| .docs-usage | ||||
| 	.row | ||||
| 		display: flex; | ||||
| 		.content-nav | ||||
| 			max-width: 250px; | ||||
| 			min-width: 250px; | ||||
| 			width: 250px; | ||||
| 			ul | ||||
| 				padding: 0px; | ||||
| 				margin: 0px; | ||||
| 				list-style-type: none; | ||||
| 				li | ||||
| 					position: relative; | ||||
| 					color: rgba(0,0,0,0.5); | ||||
| 					text-transform: uppercase; | ||||
| 					font-size: 13px; | ||||
| 				li a | ||||
| 					color: rgba(43, 48, 59, 0.8); | ||||
| 					line-height: 30px; | ||||
| 					display: block; | ||||
| 					font-size: 16px; | ||||
| 					text-transform: none; | ||||
| 				li.active a | ||||
| 					color: #2b303b; | ||||
| 					font-size: 16px; | ||||
| 					text-transform: none; | ||||
| 				li.active:before | ||||
| 					content: ""; | ||||
| 					border-left: 4px solid #2b303b; | ||||
| 					position: absolute; | ||||
| 					top: 0px; | ||||
| 					bottom: 0px; | ||||
| 					left: -20px; | ||||
| 				ul | ||||
| 					margin-bottom: 25px; | ||||
| 					margin-top: 5px; | ||||
| 		.content-main | ||||
| 			max-width: 900px; | ||||
| 			margin: 0px auto; | ||||
| 			margin-bottom: 40px; | ||||
| 			font-size: 14px; | ||||
|  | ||||
| 			p | ||||
| 				line-height: 20px; | ||||
| 				margin: 20px 0px; | ||||
|  | ||||
| 			blockquote | ||||
| 				color: #31708f; | ||||
| 				background-color: #d9edf7; | ||||
| 				border-color: #bcdff1; | ||||
| 				padding: 30px; | ||||
| 				margin: 15px 0px; | ||||
| 				border-radius: 2px; | ||||
|  | ||||
| 			blockquote p:first-child | ||||
| 				margin-top: 0px; | ||||
|  | ||||
| 			blockquote p:last-child | ||||
| 				margin-bottom: 0px; | ||||
|  | ||||
| 			strong | ||||
| 				font-weight: bold; | ||||
|  | ||||
| 			h1 | ||||
| 				margin: 40px 0px 20px 0px; | ||||
| 				font-size: 22px; | ||||
|  | ||||
| 			h1:first-child | ||||
| 			    margin-top: 0px; | ||||
|  | ||||
| 			h2 | ||||
| 				margin: 40px 0px 30px 0px; | ||||
| 				font-size: 20px; | ||||
| 				border-top: 1px solid #EEE; | ||||
| 				padding-top: 30px; | ||||
| 			h3 | ||||
| 			    margin: 40px 0px 20px 0px; | ||||
| 			    font-size: 16px; | ||||
|  | ||||
| 			ul code, ol code, p code | ||||
| 				background: #eff1f5; | ||||
| 				color: #2b303b; | ||||
| 				padding: 0px 7px; | ||||
| 				font-family: "Roboto Mono"; | ||||
| 				font-size: 13px; | ||||
| 				white-space: nowrap; | ||||
| 			pre | ||||
| 				margin-right: 0px; | ||||
| 				padding-left: 40px; | ||||
| 				background: #eff1f5; | ||||
| 				color: #2b303b; | ||||
| 				white-space: pre-wrap; | ||||
| 				word-wrap: break-word; | ||||
| 				box-sizing: border-box; | ||||
| 				padding: 25px 30px; | ||||
| 				font-family: "Roboto Mono"; | ||||
| 				border-radius: 2px; | ||||
| 				font-size: 13px; | ||||
| 			pre > code | ||||
| 				background: #eff1f5; | ||||
| 				color: #2b303b; | ||||
| 				font-family: "Roboto Mono"; | ||||
| 				font-size: 13px; | ||||
|  | ||||
| @@ -1,57 +0,0 @@ | ||||
| .repo-row | ||||
| 	.col-sm-4 | ||||
| 		width: 100%; | ||||
| 	.col-sm-4:last-child .card | ||||
| 		border-bottom: none | ||||
| 	.card | ||||
| 		border: none; | ||||
| 		border-bottom: 1px solid #EEE; | ||||
| 		border-radius: 0px; | ||||
| 		position: relative | ||||
| 		display: flex; | ||||
| 		text-decoration: none; | ||||
| 	.card-block | ||||
| 		h3 | ||||
| 			font-size: 1.1rem; | ||||
| 			color: #2b303b; | ||||
| 		.card-text | ||||
| 			color: rgba(0,0,0,0.5); | ||||
| 			font-size: 0.95rem; | ||||
| 			margin: 0px; | ||||
|  | ||||
| 		.btn-group | ||||
| 			position: absolute; | ||||
| 			top: 15px; | ||||
| 			right: 0px; | ||||
|  | ||||
| 			.btn-danger | ||||
| 				color: #bf616a; | ||||
| 				background: #FFF; | ||||
| 				border: 1px solid #bf616a; | ||||
| 				outline: none; | ||||
| 				cursor: pointer; | ||||
| 				width: auto; | ||||
| 				text-transform: uppercase; | ||||
| 				padding: 0px 10px; | ||||
| 				border-radius: 2px; | ||||
| 				font-size: 11px; | ||||
| 				line-height: 30px; | ||||
| 				height: auto; | ||||
| 				margin-left: 10px; | ||||
|  | ||||
| 	.card-header | ||||
| 		background: #FFF; | ||||
| 		border-bottom: none; | ||||
| 		padding-right: 0px; | ||||
| 		padding-left: 0px; | ||||
| 		width: 45px; | ||||
|  | ||||
| .repo-search | ||||
| 	color: #747C84; | ||||
| 	border: none; | ||||
| 	background-color: #eff1f5; | ||||
| 	border-radius: 0px; | ||||
| 	padding: 9px 15px; | ||||
| 	width: 100%; | ||||
| 	margin-bottom: 45px; | ||||
| 	border-radius: 2px; | ||||
| @@ -1,120 +0,0 @@ | ||||
|  | ||||
| body.login | ||||
| 	display: -webkit-box; | ||||
| 	display: -webkit-flex; | ||||
| 	display: -ms-flexbox; | ||||
| 	display: flex; | ||||
| 	-webkit-box-align: center; | ||||
| 	-webkit-align-items: center; | ||||
| 	-ms-flex-align: center; | ||||
| 	align-items: center; | ||||
| 	-webkit-box-pack: center; | ||||
| 	-webkit-justify-content: center; | ||||
| 	-ms-flex-pack: center; | ||||
| 	justify-content: center; | ||||
| 	width: 100%; | ||||
| 	height: 100%; | ||||
| 	overflow: hidden; | ||||
|  | ||||
| 	div | ||||
| 		position: relative; | ||||
| 		width: 220px; | ||||
| 		height: 30px; | ||||
|  | ||||
|  | ||||
|  | ||||
| 		div.logo | ||||
| 			background-image: url(/static/images/logo_dark.svg); | ||||
| 			background-size: 35px; | ||||
| 			background-repeat: no-repeat; | ||||
| 			background-position: center center; | ||||
| 			position: absolute; | ||||
| 			top: 0px; | ||||
| 			left: 0px; | ||||
| 			height: 30px; | ||||
| 			width: 35px; | ||||
|  | ||||
| 			animation-name:             fadein; | ||||
| 			animation-duration:         1.5s; | ||||
| 			animation-timing-function:  ease-in; | ||||
|  | ||||
| 		input[type="submit"], | ||||
| 		a | ||||
| 			background: #2b303b; | ||||
| 			color: #FFF; | ||||
| 			text-decoration: none; | ||||
| 			position: absolute; | ||||
| 			top: 0px; | ||||
| 			right: 0px; | ||||
| 			text-transform: uppercase; | ||||
| 			font-size: 13px; | ||||
| 			min-width: 150px; | ||||
| 			text-align: center; | ||||
| 			padding: 5px; | ||||
| 			border: none; | ||||
| 			animation-name:             fadein; | ||||
| 			animation-duration:         1.5s; | ||||
| 			animation-timing-function:  ease-in; | ||||
|  | ||||
| 	div.alert | ||||
| 		position: fixed; | ||||
| 		top: 0px; | ||||
| 		left: 0px; | ||||
| 		right: 0px; | ||||
| 		line-height: 25px; | ||||
| 		padding: 20px; | ||||
| 		width: 100%; | ||||
| 		border: none; | ||||
| 		text-align: center; | ||||
| 		vertical-align: middle; | ||||
| 		height: auto; | ||||
| 		border-radius: 0px; | ||||
|  | ||||
| body.login.login-form | ||||
| 	&> div | ||||
| 		width: 300px; | ||||
| 		height: 100px; | ||||
| 		display: flex; | ||||
| 		div.logo | ||||
| 			width: 65px; | ||||
| 			position: relative; | ||||
| 			background-position: left center; | ||||
| 			animation-name: none; | ||||
| 		form | ||||
| 			flex: 1 1 auto; | ||||
|  | ||||
| 		input[type="text"], | ||||
| 		input[type="password"] | ||||
| 			display: block; | ||||
| 			width: 100%; | ||||
| 			padding: 5px 10px; | ||||
| 			background-color: #eff1f5; | ||||
| 			border: none; | ||||
| 			color: #747C84; | ||||
|  | ||||
| 		input[type="password"] | ||||
| 			margin-top: 1px; | ||||
|  | ||||
| 		input[type="submit"] | ||||
| 			position: relative; | ||||
| 			width: 100%; | ||||
| 			margin-top: 20px; | ||||
| 			animation-name: none; | ||||
|  | ||||
| @keyframes flyin | ||||
| 	0% | ||||
| 		left: -3000px; | ||||
| 	100% | ||||
| 		left: 0px; | ||||
|  | ||||
| @keyframes flyin_right | ||||
| 	0% | ||||
| 		right: -3000px; | ||||
| 	100% | ||||
| 		right: 0px; | ||||
|  | ||||
| @keyframes fadein | ||||
| 	0% | ||||
| 		opacity: 0; | ||||
| 	100% | ||||
| 		opacity: 1; | ||||
| @@ -1,101 +0,0 @@ | ||||
|  | ||||
| .container.repo_config, | ||||
| .container.repo_secrets | ||||
| 	max-width: 800px !important; | ||||
| 	.row | ||||
| 		border-bottom: 1px solid #f0f4f7; | ||||
|  | ||||
| 	.row:last-child | ||||
| 		border: none; | ||||
| 		margin-bottom: 30px; | ||||
| 		.col-md-12 | ||||
| 			padding: 0px; | ||||
| 	.row:nth-last-child(2) | ||||
| 		border: none; | ||||
|  | ||||
| 	.col-md-3 | ||||
| 		padding: 30px; | ||||
| 		padding-left: 0px; | ||||
| 		max-width: 200px; | ||||
| 		font-size: 15px; | ||||
| 		color: #2b303b; | ||||
| 	.col-md-9 | ||||
| 		padding: 30px; | ||||
| 		color: #65737e; | ||||
| 		font-size: 15px; | ||||
|  | ||||
| 	.btn.btn-info | ||||
| 		border-color: #95AEC7; | ||||
| 		color: #95AEC7; | ||||
| 		background: #FFF; | ||||
| 		outline: none; | ||||
| 		cursor: pointer; | ||||
| 		width: auto; | ||||
| 		text-transform: uppercase; | ||||
| 		padding: 0px 10px; | ||||
| 		border-radius: 2px; | ||||
| 		font-size: 11px; | ||||
| 		line-height: 30px; | ||||
| 		height: auto; | ||||
| 		margin-left: 10px; | ||||
|  | ||||
| 	pre | ||||
| 		white-space: pre-wrap; | ||||
| 		word-wrap: break-word; | ||||
| 		box-sizing: border-box; | ||||
| 		font-family: "Roboto Mono"; | ||||
| 		color: #2b303b; | ||||
|  | ||||
| 	input[type="range"] + span | ||||
| 		margin-left: 15px; | ||||
|  | ||||
| 	.alert.alert-danger | ||||
| 		background: #FFF; | ||||
| 		border: 1px solid #bf616a; | ||||
| 		border-radius: 2px; | ||||
| 		margin-top: 40px; | ||||
| 		color: #bf616a; | ||||
| 		.btn.btn-danger | ||||
| 			margin-right: 15px; | ||||
| 			border: 1px solid #bf616a; | ||||
| 			color: #bf616a; | ||||
| 			background: #FFF; | ||||
| 			border-radius: 2px; | ||||
| 			font-size: 13px; | ||||
| 			text-transform: uppercase; | ||||
|  | ||||
|  | ||||
| .container.repo_secrets | ||||
| 	textarea | ||||
| 		border: none; | ||||
| 		background: #eff1f5; | ||||
| 		width: 100%; | ||||
| 		border-radius: 3px; | ||||
| 		margin-bottom: 10px; | ||||
| 		height: 150px; | ||||
| 		padding: 10px 15px; | ||||
| 	.btn.btn-info | ||||
| 		margin-left: 0px; | ||||
| 	.result | ||||
| 		margin-top: 20px; | ||||
| 		white-space: pre; | ||||
|  | ||||
|  | ||||
| .container.repo_activate | ||||
| 	max-width: 800px !important; | ||||
|  | ||||
| 	.alert.alert-info | ||||
| 		color: #3498DB; | ||||
| 		background: rgba(52, 152, 219, 0.12); | ||||
| 		border-radius: 2px; | ||||
| 		border: none; | ||||
| 		margin-top: 20px; | ||||
| 		padding: 30px; | ||||
| 	.btn.btn-info | ||||
| 		color: #3498DB; | ||||
| 		background: #3498DB; | ||||
| 		border: 1px solid #3498DB; | ||||
| 		background: #FFF; | ||||
| 		border-radius: 2px; | ||||
| 		text-transform: uppercase; | ||||
| 		font-size: 13px; | ||||
| @@ -1,44 +0,0 @@ | ||||
|  | ||||
| .container.profile | ||||
| 	max-width: 800px !important; | ||||
| 	margin-bottom: 40px; | ||||
|  | ||||
| 	.row | ||||
| 		border-bottom: 1px solid #f0f4f7; | ||||
|  | ||||
| 	.row:last-child | ||||
| 		border-bottom: none; | ||||
|  | ||||
| 	.col-md-3 | ||||
| 		padding: 30px; | ||||
| 		padding-left: 0px; | ||||
| 		max-width: 200px; | ||||
| 		font-size: 15px; | ||||
| 		color: #2b303b; | ||||
| 		 | ||||
| 	.col-md-9 | ||||
| 		padding: 30px; | ||||
| 		color: #65737e; | ||||
| 		font-size: 15px; | ||||
|  | ||||
| 	.btn.btn-info | ||||
| 		border-color: #95AEC7; | ||||
| 		color: #95AEC7; | ||||
| 		background: #FFF; | ||||
| 		outline: none; | ||||
| 		cursor: pointer; | ||||
| 		width: auto; | ||||
| 		text-transform: uppercase; | ||||
| 		padding: 0px 10px; | ||||
| 		border-radius: 2px; | ||||
| 		font-size: 11px; | ||||
| 		line-height: 30px; | ||||
| 		height: auto; | ||||
| 		margin-left: 10px; | ||||
|  | ||||
| 	pre | ||||
| 		white-space: pre-wrap; | ||||
| 		word-wrap: break-word; | ||||
| 		box-sizing: border-box; | ||||
| 		font-family: "Roboto Mono"; | ||||
| 		color: #2b303b; | ||||
| @@ -1,71 +0,0 @@ | ||||
| .user-row | ||||
| 	.col-sm-4 | ||||
| 		width: 100%; | ||||
| 	.col-sm-4:last-child .card | ||||
| 		border-bottom: none | ||||
| 	.card | ||||
| 		border: none; | ||||
| 		border-bottom: 1px solid #EEE; | ||||
| 		border-radius: 0px; | ||||
| 		position: relative | ||||
| 		display: flex; | ||||
| 	.card-block | ||||
| 		h3 | ||||
| 			font-size: 1.1rem; | ||||
|  | ||||
| 		.card-text | ||||
| 			color: rgba(0,0,0,0.5); | ||||
| 			font-size: 0.95rem; | ||||
| 			margin: 0px; | ||||
|  | ||||
| 		.btn-group | ||||
| 			position: absolute; | ||||
| 			top: 15px; | ||||
| 			right: 0px; | ||||
|  | ||||
| 			.btn | ||||
| 				background: #FFF; | ||||
| 				outline: none; | ||||
| 				cursor: pointer; | ||||
| 				width: auto; | ||||
| 				text-transform: uppercase; | ||||
| 				padding: 0px 10px; | ||||
| 				border-radius: 2px; | ||||
| 				font-size: 11px; | ||||
| 				line-height: 30px; | ||||
| 				height: auto; | ||||
| 				margin-left: 10px; | ||||
|  | ||||
| 			.btn-danger | ||||
| 				border: 1px solid #bf616a; | ||||
| 				color: #bf616a; | ||||
|  | ||||
| 			.btn-info | ||||
| 				border-color: #95AEC7; | ||||
| 				color: #95AEC7; | ||||
|  | ||||
| 	.card-header | ||||
| 		background: #FFF; | ||||
| 		border-bottom: none; | ||||
| 		padding-right: 0px; | ||||
| 		padding-left: 0px; | ||||
| 		width: 45px; | ||||
|  | ||||
|  | ||||
| .card-header | ||||
| 	img | ||||
| 		vertical-align: middle; | ||||
| 		width: 32px; | ||||
| 		height: 32px; | ||||
| 		border-radius: 3px; | ||||
|  | ||||
| [data-admin="true"] h3:after | ||||
| 	content: "admin"; | ||||
| 	text-transform: uppercase; | ||||
| 	color: #fff; | ||||
| 	display: inline-block; | ||||
| 	font-size: 12px; | ||||
| 	margin-left: 15px; | ||||
| 	background: #a3be8c; | ||||
| 	border-radius: 3px; | ||||
| 	padding: 2px 5px; | ||||
| @@ -1,56 +0,0 @@ | ||||
|  | ||||
|  | ||||
| .tt-open | ||||
| 	position: absolute; | ||||
| 	top: 34px; | ||||
| 	left: 0px; | ||||
| 	z-index: 100; | ||||
| 	display: none; | ||||
| 	background: #FFF; | ||||
| 	min-width: 100%; | ||||
| 	border: 1px solid #eee; | ||||
| 	border-radius: 0px; | ||||
|  | ||||
| .tt-selectable:hover, | ||||
| .tt-cursor | ||||
| 	background: #eff1f5; | ||||
|  | ||||
| .tt-selectable | ||||
| 	padding: 1rem 0.75rem; | ||||
| 	cursor: pointer; | ||||
| 	display: flex; | ||||
|  | ||||
| 	div:first-child | ||||
| 		width: 75px; | ||||
| 		max-width: 75px; | ||||
| 		min-width: 75px; | ||||
|  | ||||
| 		img | ||||
| 			width: 32px; | ||||
| 			max-width: 32px; | ||||
| 			min-width: 32px; | ||||
| 			border-radius: 50%; | ||||
| 			text-align: left; | ||||
| 			margin-left: 1rem; | ||||
| 	div:last-child | ||||
| 		flex: 1 1 auto; | ||||
| 		line-height: 32px; | ||||
|  | ||||
| .no-matches-message | ||||
| 	padding: 20px; | ||||
| 	color: rgba(0,0,0,0.5); | ||||
| 	font-style: italic; | ||||
|  | ||||
| .not-indexed-message | ||||
| 	padding: 20px; | ||||
| 	padding-bottom: 0px; | ||||
| 	color: rgba(0,0,0,0.5); | ||||
| 	font-style: italic; | ||||
| 	em | ||||
| 		color: #2b303b; | ||||
| 	p:last-child | ||||
| 		margin-top: 20px; | ||||
| 		font-size: 15px; | ||||
| 		color: rgba(0, 0, 0, 0.4); | ||||
| 		a | ||||
| 			color: rgba(0, 0, 0, 0.8); | ||||
| @@ -1,108 +0,0 @@ | ||||
|  | ||||
|  | ||||
| @import modules/badges.sass | ||||
| @import modules/navbar.sass | ||||
| @import modules/subnav.sass | ||||
| @import modules/switch.sass | ||||
| @import modules/range.sass | ||||
| @import modules/timeline.sass | ||||
| @import modules/status.sass | ||||
|  | ||||
|  | ||||
| @import pages/build.sass | ||||
| @import pages/users.sass | ||||
| @import pages/user.sass | ||||
| @import pages/repo.sass | ||||
| @import pages/login.sass | ||||
| @import pages/feed.sass | ||||
| @import pages/docs.sass | ||||
|  | ||||
| @import header | ||||
| @import search | ||||
|  | ||||
|  | ||||
| .hidden | ||||
| 	display: none; | ||||
|  | ||||
| // page navigation | ||||
|  | ||||
| :focus | ||||
| 	outline: none; | ||||
|  | ||||
| ::-moz-focus-inner | ||||
| 	border: 0; | ||||
|  | ||||
| .container | ||||
| 	max-width: 980px !important; | ||||
|  | ||||
|  | ||||
| .modal-content | ||||
| 	padding: 20px; | ||||
| 	.form-control | ||||
| 		border-radius: 2px; | ||||
| 	.btn | ||||
| 		margin-top: 20px; | ||||
|  | ||||
| // node section | ||||
|  | ||||
| .node-row | ||||
| 	.col-sm-4 | ||||
| 		width: 100%; | ||||
| 	.col-sm-4:last-child .card | ||||
| 		border-bottom: none | ||||
| 	.card | ||||
| 		border: none; | ||||
| 		border-bottom: 1px solid #EEE; | ||||
| 		border-radius: 0px; | ||||
| 		position: relative | ||||
| 		display: flex; | ||||
| 	.card-block | ||||
| 		h3 | ||||
| 			font-size: 1.1rem; | ||||
| 		.card-text | ||||
| 			color: rgba(0,0,0,0.5); | ||||
| 			font-size: 0.95rem; | ||||
| 			margin: 0px; | ||||
|  | ||||
| 		.btn-group | ||||
| 			position: absolute; | ||||
| 			top: 15px; | ||||
| 			right: 0px; | ||||
|  | ||||
| 			.btn-danger | ||||
| 				color: #bf616a; | ||||
| 				background: #FFF; | ||||
| 				border: 1px solid #bf616a; | ||||
| 				outline: none; | ||||
| 				cursor: pointer; | ||||
| 				width: auto; | ||||
| 				text-transform: uppercase; | ||||
| 				padding: 0px 10px; | ||||
| 				border-radius: 2px; | ||||
| 				font-size: 11px; | ||||
| 				line-height: 30px; | ||||
| 				height: auto; | ||||
| 				margin-left: 10px; | ||||
|  | ||||
| 	.card-header | ||||
| 		background: #FFF; | ||||
| 		border-bottom: none; | ||||
| 		padding-right: 0px; | ||||
| 		padding-left: 0px; | ||||
| 		width: 45px; | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| i.linux_amd64 | ||||
| 	width: 32px; | ||||
| 	height: 32px; | ||||
| 	display: inline-block; | ||||
| 	background: url(/static/images/ubuntu.svg); | ||||
| 	background-size: 32px; | ||||
| 	background-repeat: no-repeat; | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -1,492 +0,0 @@ | ||||
| .navbar { background-color: #FFF; border-radius: 0px; height: 53px; z-index: 2; margin-top: 10px; } | ||||
|  | ||||
| .navbar-brand { background-image: url(/static/images/logo_dark.svg); width: 30px; height: 30px; margin-top: 3px; background-size: 30px; background-repeat: no-repeat; background-position: center center; } | ||||
|  | ||||
| .navbar img { width: 32px; height: 32px; border-radius: 50%; } | ||||
|  | ||||
| .navbar .dropdown { display: inline-block; } | ||||
|  | ||||
| .navbar .dropdown button { background: none; border: none; color: #2b303b; min-wdith: 24px; max-width: 24px; } | ||||
|  | ||||
| .navbar .dropdown button .material-icons { line-height: 32px; min-wdith: 24px; max-width: 24px; } | ||||
|  | ||||
| .navbar-nav.navbar-right { display: inline-block; float: right; } | ||||
|  | ||||
| .navbar-nav.navbar-right li { display: inline-block; vertical-align: middle; } | ||||
|  | ||||
| .navbar-form { margin-right: 20px; width: 350px; } | ||||
|  | ||||
| .navbar-form .twitter-typeahead { width: 100%; } | ||||
|  | ||||
| .navbar-form .form-control { color: #747C84; border: none; background-color: #eff1f5; border-radius: 0px; padding: 0.3rem 0.75rem; width: 100%; } | ||||
|  | ||||
| .subnav { background: #FFF; border-bottom: 1px solid #EEE; height: 75px; min-height: 75px; max-height: 75px; margin-bottom: 40px; position: relative; } | ||||
|  | ||||
| .subnav ol { float: left; margin: 0px; margin-left: 20px; padding: 0px; } | ||||
|  | ||||
| .subnav ol li { line-height: 75px; display: inline-block; vertical-align: middle; font-size: 21px; } | ||||
|  | ||||
| .subnav ol li .btn { background: transparent; outline: none; } | ||||
|  | ||||
| .subnav ol li .btn:focus { outline: none; } | ||||
|  | ||||
| .subnav ol li .btn-info { cursor: pointer; width: auto; text-transform: uppercase; padding: 0px 10px; border-radius: 2px; font-size: 11px; line-height: 30px; height: auto; margin-left: 10px; border-color: #95AEC7; color: #95AEC7; } | ||||
|  | ||||
| .subnav ol li.separator i { vertical-align: middle; } | ||||
|  | ||||
| .subnav ol li a { color: #2B303B; line-height: 75px; text-decoration: none; } | ||||
|  | ||||
| .subnav ol li a:hover { text-decoration: none; } | ||||
|  | ||||
| .subnav .nav-tabs { position: absolute; bottom: -1px; right: 30px; border-bottom: 1px solid #eee; } | ||||
|  | ||||
| .subnav .nav-tabs .nav-link { border: none; color: #C7CCD1; font-size: 13px; padding: 10px 20px; text-transform: uppercase; } | ||||
|  | ||||
| .subnav .nav-tabs .nav-link.active { border: 1px solid #eee; border-bottom: 1px solid #fff; color: #2b303b; } | ||||
|  | ||||
| .switch { display: inline-block; position: relative; width: 40px; height: 8px; border-radius: 10.416666666666668px; background: #E0E0E0; -webkit-transition: background 0.28s cubic-bezier(0.4, 0, 0.2, 1); transition: background 0.28s cubic-bezier(0.4, 0, 0.2, 1); vertical-align: middle; cursor: pointer; } | ||||
|  | ||||
| .switch::before { content: ''; position: absolute; top: -8.604166666666667px; left: -2.604166666666667px; width: 26.04166666666667px; height: 26.04166666666667px; background: #bdbdbd; border-radius: 50%; -webkit-transition: left 0.28s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1); transition: left 0.28s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1); } | ||||
|  | ||||
| switch:active::before { box-shadow: 0 2px 10.41667px rgba(0, 0, 0, 0.28), 0 0 0 25px rgba(0, 0, 0, 0.1); } | ||||
|  | ||||
| switch:active::before { box-shadow: 0 2px 10.41667px rgba(0, 0, 0, 0.28), 0 0 0 25px rgba(0, 0, 0, 0.1); } | ||||
|  | ||||
| input:checked + .switch { background: rgba(0, 150, 136, 0.5); background: rgba(102, 187, 106, 0.5); } | ||||
|  | ||||
| input:checked + .switch::before { left: 20.562499999999996px; background: #009688; background: #66bb6a; } | ||||
|  | ||||
| input:checked + .switch:active::before { box-shadow: 0 2px 10.41667px rgba(0, 0, 0, 0.28), 0 0 0 25px rgba(0, 150, 136, 0.2); } | ||||
|  | ||||
| input[type="range"]:focus ~ .slider-label { display: inline-block; } | ||||
|  | ||||
| input[type=range] { -webkit-appearance: none; margin: 6px 0; width: 200px; } | ||||
|  | ||||
| input[type=range]:focus { outline: none; } | ||||
|  | ||||
| input[type=range]::-webkit-slider-runnable-track { width: 100%; height: 8px; cursor: pointer; animate: 0.2s; box-shadow: none; background: rgba(0, 150, 136, 0.5); background: rgba(102, 187, 106, 0.5); border-radius: 5px; border: none; } | ||||
|  | ||||
| input[type=range]::-webkit-slider-thumb { box-shadow: none; border: none; height: 26px; width: 26px; border-radius: 50px; background: #009688; background: #66bb6a; cursor: pointer; -webkit-appearance: none; margin-top: -10px; } | ||||
|  | ||||
| input[type=range]:focus::-webkit-slider-runnable-track { background: rgba(0, 150, 136, 0.5); background: rgba(102, 187, 106, 0.5); } | ||||
|  | ||||
| input[type=range]::-moz-range-track { width: 100%; height: 8px; cursor: pointer; animate: 0.2s; box-shadow: none; background: rgba(0, 150, 136, 0.5); background: rgba(102, 187, 106, 0.5); border-radius: 5px; border: none; } | ||||
|  | ||||
| input[type=range]::-moz-range-thumb { box-shadow: none; border: none; height: 26px; width: 26px; border-radius: 50px; background: #009688; background: #66bb6a; cursor: pointer; } | ||||
|  | ||||
| input[type=range]::-ms-track { width: 100%; height: 8.4px; cursor: pointer; animate: 0.2s; background: transparent; border-color: transparent; border-width: 16px 0; color: transparent; } | ||||
|  | ||||
| input[type=range]::-ms-fill-lower { background: #2a6495; border: 0.2px solid #010101; border-radius: 2.6px; box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; } | ||||
|  | ||||
| input[type=range]::-ms-fill-upper { background: #3071a9; border: 0.2px solid #010101; border-radius: 2.6px; box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; } | ||||
|  | ||||
| input[type=range]::-ms-thumb { box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; border: 1px solid #000000; height: 36px; width: 16px; border-radius: 3px; background: #ffffff; cursor: pointer; } | ||||
|  | ||||
| input[type=range]:focus::-ms-fill-lower { background: #3071a9; } | ||||
|  | ||||
| input[type=range]:focus::-ms-fill-upper { background: #367ebd; } | ||||
|  | ||||
| .timeline { padding-left: 50px; position: relative; margin-top: 10px; margin-bottom: 40px; } | ||||
|  | ||||
| .timeline .card { display: flex; border: none; border-radius: 0px; border-top: 1px solid #eceeef; text-decoration: none; color: #2b303b; } | ||||
|  | ||||
| .timeline .card .card-header { background: #FFF; border: none; padding: 0px; width: 50px; min-width: 50px; max-width: 50px; padding-top: 30px; } | ||||
|  | ||||
| .timeline .card .card-block { flex: 1 1 auto; padding: 30px 12px 12px 12px; } | ||||
|  | ||||
| .timeline .card .card-block p { color: #ADB3BA; margin-top: 2px; font-size: 0px; } | ||||
|  | ||||
| .timeline .card .card-block p em { text-decoration: none; color: #747C84; font-style: normal; font-size: 0.9rem; } | ||||
|  | ||||
| .timeline .card .card-block p span { margin: 0px 5px; font-size: 0.9rem; } | ||||
|  | ||||
| .timeline .card .card-block h3 { display: inline-block; line-height: 22px; font-size: 18px; } | ||||
|  | ||||
| .timeline .card:nth-child(2) { border-top: 0px; } | ||||
|  | ||||
| .timeline:before { position: absolute; top: 0; bottom: 0; left: 14px; z-index: -1; display: block; width: 1px; content: ""; background-color: #DFE2E5; } | ||||
|  | ||||
| .group { padding-bottom: 16px; } | ||||
|  | ||||
| .group-title { font-size: 13px; text-transform: uppercase; color: #ADB3BA; margin-bottom: 30px; margin-top: 30px; position: relative; line-height: 20px; } | ||||
|  | ||||
| .group-title:before { content: '\f01f'; font-family: "octicons"; position: absolute; left: -48px; font-size: 20px; background: #FFF; width: 24px; vertical-align: middle; text-align: center; color: #ADB3BA; } | ||||
|  | ||||
| .group:first-child .group-title { margin-top: 0px; } | ||||
|  | ||||
| .group:last-child { padding-bottom: 0px; } | ||||
|  | ||||
| .success, .failure, .killed, .error, .running, .pending { padding: 0px 15px; color: #FFF; width: 100px; text-align: center; border-radius: 2px; text-transform: uppercase; font-size: 11px; line-height: 22px; display: inline-block; margin-right: 10px; } | ||||
|  | ||||
| .error, .killed, .failure { background: #bf616a; } | ||||
|  | ||||
| .success { background: #a3be8c; } | ||||
|  | ||||
| .pending, .running { background: #ebcb8b; animation: horizontal 2s ease infinite; } | ||||
|  | ||||
| @keyframes horizontal { 0% { transform: translate(0, 0); } | ||||
|   6% { transform: translate(5px, 0); } | ||||
|   12% { transform: translate(0, 0); } | ||||
|   18% { transform: translate(5px, 0); } | ||||
|   24% { transform: translate(0, 0); } | ||||
|   30% { transform: translate(5px, 0); } | ||||
|   36%, 100% { transform: translate(0, 0); } } | ||||
|  | ||||
| #output { background: #0d1926; color: #d9e6f2; margin-right: 15px; font-size: 13px; color: #eff1f5; border-radius: 2px; background: #2b303b; white-space: pre-wrap; word-wrap: break-word; box-sizing: border-box; padding: 35px 40px; font-family: "Roboto Mono"; min-height: calc(100vh - 205px); } | ||||
|  | ||||
| #output > pre { font-size: 13px; color: #eff1f5; border-radius: 2px; background: #2b303b; white-space: pre-wrap; word-wrap: break-word; box-sizing: border-box; padding: 0px; font-family: "Roboto Mono"; margin: 0px; margin-top: 25px; } | ||||
|  | ||||
| #output > pre:first-child { margin-top: 0px; } | ||||
|  | ||||
| #output > pre:before { content: attr(data-title); display: block; padding: 5px; margin-bottom: 10px; border-bottom: 1px solid rgba(255, 255, 255, 0.3); padding-left: 0px; } | ||||
|  | ||||
| #follow { position: absolute; } | ||||
|  | ||||
| .build-summary { padding-left: 20px; } | ||||
|  | ||||
| .build-summary p { color: #ADB3BA; margin-top: 4px; font-size: 0.9rem; } | ||||
|  | ||||
| .build-summary em { color: #747C84; text-decoration: none; font-style: normal; } | ||||
|  | ||||
| .job-summary { padding-left: 20px; font-size: 0.9rem; } | ||||
|  | ||||
| .job-summary dt { color: #ADB3BA; font-weight: normal; } | ||||
|  | ||||
| .job-summary dd { color: #747C84; text-decoration: none; font-style: normal; } | ||||
|  | ||||
| .build-summary > div h3 { display: inline; line-height: 28px; font-size: 18px; } | ||||
|  | ||||
| .build-summary > div h3 a.material-icons { vertical-align: middle; text-decoration: none; color: #ADB3BA; font-size: 20px; padding-left: 5px; } | ||||
|  | ||||
| .job-list { padding-left: 20px; margin-bottom: 20px; } | ||||
|  | ||||
| .job-list a { text-decoration: none; color: #747C84; font-size: 15px; display: block; border-top: 1px solid #eee; padding-top: 20px; padding-bottom: 20px; position: relative; } | ||||
|  | ||||
| .job-list a h3 { margin-bottom: 10px; color: #2b303b; font-size: 14px; margin-top: 14px; } | ||||
|  | ||||
| .job-list a h3 .env { font-family: "Roboto Mono"; } | ||||
|  | ||||
| .job-list a:not(.active) > div:nth-child(2) > div { display: none; } | ||||
|  | ||||
| .job-list a:last-child { border-bottom: 1px solid #eee; } | ||||
|  | ||||
| .job-list a > div:first-child { margin-bottom: 10px; position: relative; } | ||||
|  | ||||
| .build-btn-group { margin-left: 20px; } | ||||
|  | ||||
| .build-btn-group .btn { background: #FFF; outline: none; cursor: pointer; width: auto; text-transform: uppercase; padding: 0px 10px; border-radius: 2px; font-size: 11px; line-height: 30px; height: auto; margin-right: 10px; } | ||||
|  | ||||
| .build-btn-group .btn .btn-danger { border: 1px solid #bf616a; color: #bf616a; } | ||||
|  | ||||
| .build-btn-group .btn.btn-info { border-color: #95AEC7; color: #95AEC7; } | ||||
|  | ||||
| .tail { position: fixed; bottom: 50px; right: 80px; width: 38px; height: 38px; background: rgba(255, 255, 255, 0.2); border-radius: 50%; box-shadow: 1px 2px 2px rgba(0, 0, 0, 0.2); cursor: pointer; bottom: 15px; right: 60px; border: none; outline: none; display: none; } | ||||
|  | ||||
| .tail i { color: rgba(255, 255, 255, 0.5); line-height: 38px; display: inline-block; } | ||||
|  | ||||
| @supports (position: sticky) { .sticky { position: sticky; top: 20px; } } | ||||
|  | ||||
| @supports not (position: sticky) { .sticky { top: 0px; } } | ||||
|  | ||||
| .user-row .col-sm-4 { width: 100%; } | ||||
|  | ||||
| .user-row .col-sm-4:last-child .card { border-bottom: none; } | ||||
|  | ||||
| .user-row .card { border: none; border-bottom: 1px solid #EEE; border-radius: 0px; position: relative; display: flex; } | ||||
|  | ||||
| .user-row .card-block h3 { font-size: 1.1rem; } | ||||
|  | ||||
| .user-row .card-block .card-text { color: rgba(0, 0, 0, 0.5); font-size: 0.95rem; margin: 0px; } | ||||
|  | ||||
| .user-row .card-block .btn-group { position: absolute; top: 15px; right: 0px; } | ||||
|  | ||||
| .user-row .card-block .btn-group .btn { background: #FFF; outline: none; cursor: pointer; width: auto; text-transform: uppercase; padding: 0px 10px; border-radius: 2px; font-size: 11px; line-height: 30px; height: auto; margin-left: 10px; } | ||||
|  | ||||
| .user-row .card-block .btn-group .btn-danger { border: 1px solid #bf616a; color: #bf616a; } | ||||
|  | ||||
| .user-row .card-block .btn-group .btn-info { border-color: #95AEC7; color: #95AEC7; } | ||||
|  | ||||
| .user-row .card-header { background: #FFF; border-bottom: none; padding-right: 0px; padding-left: 0px; width: 45px; } | ||||
|  | ||||
| .card-header img { vertical-align: middle; width: 32px; height: 32px; border-radius: 3px; } | ||||
|  | ||||
| [data-admin="true"] h3:after { content: "admin"; text-transform: uppercase; color: #fff; display: inline-block; font-size: 12px; margin-left: 15px; background: #a3be8c; border-radius: 3px; padding: 2px 5px; } | ||||
|  | ||||
| .container.profile { max-width: 800px !important; margin-bottom: 40px; } | ||||
|  | ||||
| .container.profile .row { border-bottom: 1px solid #f0f4f7; } | ||||
|  | ||||
| .container.profile .row:last-child { border-bottom: none; } | ||||
|  | ||||
| .container.profile .col-md-3 { padding: 30px; padding-left: 0px; max-width: 200px; font-size: 15px; color: #2b303b; } | ||||
|  | ||||
| .container.profile .col-md-9 { padding: 30px; color: #65737e; font-size: 15px; } | ||||
|  | ||||
| .container.profile .btn.btn-info { border-color: #95AEC7; color: #95AEC7; background: #FFF; outline: none; cursor: pointer; width: auto; text-transform: uppercase; padding: 0px 10px; border-radius: 2px; font-size: 11px; line-height: 30px; height: auto; margin-left: 10px; } | ||||
|  | ||||
| .container.profile pre { white-space: pre-wrap; word-wrap: break-word; box-sizing: border-box; font-family: "Roboto Mono"; color: #2b303b; } | ||||
|  | ||||
| .container.repo_config, .container.repo_secrets { max-width: 800px !important; } | ||||
|  | ||||
| .container.repo_config .row, .container.repo_secrets .row { border-bottom: 1px solid #f0f4f7; } | ||||
|  | ||||
| .container.repo_config .row:last-child, .container.repo_secrets .row:last-child { border: none; margin-bottom: 30px; } | ||||
|  | ||||
| .container.repo_config .row:last-child .col-md-12, .container.repo_secrets .row:last-child .col-md-12 { padding: 0px; } | ||||
|  | ||||
| .container.repo_config .row:nth-last-child(2), .container.repo_secrets .row:nth-last-child(2) { border: none; } | ||||
|  | ||||
| .container.repo_config .col-md-3, .container.repo_secrets .col-md-3 { padding: 30px; padding-left: 0px; max-width: 200px; font-size: 15px; color: #2b303b; } | ||||
|  | ||||
| .container.repo_config .col-md-9, .container.repo_secrets .col-md-9 { padding: 30px; color: #65737e; font-size: 15px; } | ||||
|  | ||||
| .container.repo_config .btn.btn-info, .container.repo_secrets .btn.btn-info { border-color: #95AEC7; color: #95AEC7; background: #FFF; outline: none; cursor: pointer; width: auto; text-transform: uppercase; padding: 0px 10px; border-radius: 2px; font-size: 11px; line-height: 30px; height: auto; margin-left: 10px; } | ||||
|  | ||||
| .container.repo_config pre, .container.repo_secrets pre { white-space: pre-wrap; word-wrap: break-word; box-sizing: border-box; font-family: "Roboto Mono"; color: #2b303b; } | ||||
|  | ||||
| .container.repo_config input[type="range"] + span, .container.repo_secrets input[type="range"] + span { margin-left: 15px; } | ||||
|  | ||||
| .container.repo_config .alert.alert-danger, .container.repo_secrets .alert.alert-danger { background: #FFF; border: 1px solid #bf616a; border-radius: 2px; margin-top: 40px; color: #bf616a; } | ||||
|  | ||||
| .container.repo_config .alert.alert-danger .btn.btn-danger, .container.repo_secrets .alert.alert-danger .btn.btn-danger { margin-right: 15px; border: 1px solid #bf616a; color: #bf616a; background: #FFF; border-radius: 2px; font-size: 13px; text-transform: uppercase; } | ||||
|  | ||||
| .container.repo_secrets textarea { border: none; background: #eff1f5; width: 100%; border-radius: 3px; margin-bottom: 10px; height: 150px; padding: 10px 15px; } | ||||
|  | ||||
| .container.repo_secrets .btn.btn-info { margin-left: 0px; } | ||||
|  | ||||
| .container.repo_secrets .result { margin-top: 20px; white-space: pre; } | ||||
|  | ||||
| .container.repo_activate { max-width: 800px !important; } | ||||
|  | ||||
| .container.repo_activate .alert.alert-info { color: #3498DB; background: rgba(52, 152, 219, 0.12); border-radius: 2px; border: none; margin-top: 20px; padding: 30px; } | ||||
|  | ||||
| .container.repo_activate .btn.btn-info { color: #3498DB; background: #3498DB; border: 1px solid #3498DB; background: #FFF; border-radius: 2px; text-transform: uppercase; font-size: 13px; } | ||||
|  | ||||
| body.login { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; -webkit-box-pack: center; -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; width: 100%; height: 100%; overflow: hidden; } | ||||
|  | ||||
| body.login div { position: relative; width: 220px; height: 30px; } | ||||
|  | ||||
| body.login div div.logo { background-image: url(/static/images/logo_dark.svg); background-size: 35px; background-repeat: no-repeat; background-position: center center; position: absolute; top: 0px; left: 0px; height: 30px; width: 35px; animation-name: fadein; animation-duration: 1.5s; animation-timing-function: ease-in; } | ||||
|  | ||||
| body.login div input[type="submit"], body.login div a { background: #2b303b; color: #FFF; text-decoration: none; position: absolute; top: 0px; right: 0px; text-transform: uppercase; font-size: 13px; min-width: 150px; text-align: center; padding: 5px; border: none; animation-name: fadein; animation-duration: 1.5s; animation-timing-function: ease-in; } | ||||
|  | ||||
| body.login div.alert { position: fixed; top: 0px; left: 0px; right: 0px; line-height: 25px; padding: 20px; width: 100%; border: none; text-align: center; vertical-align: middle; height: auto; border-radius: 0px; } | ||||
|  | ||||
| body.login.login-form > div { width: 300px; height: 100px; display: flex; } | ||||
|  | ||||
| body.login.login-form > div div.logo { width: 65px; position: relative; background-position: left center; animation-name: none; } | ||||
|  | ||||
| body.login.login-form > div form { flex: 1 1 auto; } | ||||
|  | ||||
| body.login.login-form > div input[type="text"], body.login.login-form > div input[type="password"] { display: block; width: 100%; padding: 5px 10px; background-color: #eff1f5; border: none; color: #747C84; } | ||||
|  | ||||
| body.login.login-form > div input[type="password"] { margin-top: 1px; } | ||||
|  | ||||
| body.login.login-form > div input[type="submit"] { position: relative; width: 100%; margin-top: 20px; animation-name: none; } | ||||
|  | ||||
| @keyframes flyin { 0% { left: -3000px; } | ||||
|   100% { left: 0px; } } | ||||
|  | ||||
| @keyframes flyin_right { 0% { right: -3000px; } | ||||
|   100% { right: 0px; } } | ||||
|  | ||||
| @keyframes fadein { 0% { opacity: 0; } | ||||
|   100% { opacity: 1; } } | ||||
|  | ||||
| .repo-row .col-sm-4 { width: 100%; } | ||||
|  | ||||
| .repo-row .col-sm-4:last-child .card { border-bottom: none; } | ||||
|  | ||||
| .repo-row .card { border: none; border-bottom: 1px solid #EEE; border-radius: 0px; position: relative; display: flex; text-decoration: none; } | ||||
|  | ||||
| .repo-row .card-block h3 { font-size: 1.1rem; color: #2b303b; } | ||||
|  | ||||
| .repo-row .card-block .card-text { color: rgba(0, 0, 0, 0.5); font-size: 0.95rem; margin: 0px; } | ||||
|  | ||||
| .repo-row .card-block .btn-group { position: absolute; top: 15px; right: 0px; } | ||||
|  | ||||
| .repo-row .card-block .btn-group .btn-danger { color: #bf616a; background: #FFF; border: 1px solid #bf616a; outline: none; cursor: pointer; width: auto; text-transform: uppercase; padding: 0px 10px; border-radius: 2px; font-size: 11px; line-height: 30px; height: auto; margin-left: 10px; } | ||||
|  | ||||
| .repo-row .card-header { background: #FFF; border-bottom: none; padding-right: 0px; padding-left: 0px; width: 45px; } | ||||
|  | ||||
| .repo-search { color: #747C84; border: none; background-color: #eff1f5; border-radius: 0px; padding: 9px 15px; width: 100%; margin-bottom: 45px; border-radius: 2px; } | ||||
|  | ||||
| .toc { list-style-type: none; padding: 0px; margin: 0px; padding-bottom: 40px; } | ||||
|  | ||||
| .toc h2 { font-size: 21px; font-weight: normal; margin-bottom: 20px; color: #2b303b; } | ||||
|  | ||||
| .toc ul { list-style-type: none; padding: 0px; margin: 0px; margin-bottom: 40px; border-bottom: 1px solid #EEE; padding-bottom: 40px; } | ||||
|  | ||||
| .toc ul li a { color: #2b303b; text-decoration: none; } | ||||
|  | ||||
| .toc ul li a:hover { text-decoration: underline; } | ||||
|  | ||||
| .toc [data-method]:before { content: attr(data-method); padding: 0px 10px; line-height: 18px; min-width: 70px; font-size: 11px; text-transform: uppercase; display: inline-block; text-align: center; color: #FFF; border-radius: 2px; margin-right: 20px; } | ||||
|  | ||||
| .toc [data-method="GET"]:before { background-color: #1ABC9C; } | ||||
|  | ||||
| .toc [data-method="PUT"]:before { background-color: #9B59B6; } | ||||
|  | ||||
| .toc [data-method="POST"]:before { background-color: #3498DB; } | ||||
|  | ||||
| .toc [data-method="PATCH"]:before { background-color: #E67E22; } | ||||
|  | ||||
| .toc [data-method="DELETE"]:before { background-color: #E74C3C; } | ||||
|  | ||||
| .toc [data-method]:before { background: #FFF; border: 1px solid #FFF; } | ||||
|  | ||||
| .toc [data-method="GET"]:before { color: #1ABC9C; border-color: #1ABC9C; } | ||||
|  | ||||
| .toc [data-method="PUT"]:before { color: #9B59B6; border-color: #9B59B6; } | ||||
|  | ||||
| .toc [data-method="POST"]:before { color: #3498DB; border-color: #3498DB; } | ||||
|  | ||||
| .toc [data-method="PATCH"]:before { color: #E67E22; border-color: #E67E22; } | ||||
|  | ||||
| .toc [data-method="DELETE"]:before { color: #E74C3C; border-color: #E74C3C; } | ||||
|  | ||||
| .operation [data-method]:before { content: attr(data-method); padding: 0px 10px; line-height: 18px; min-width: 70px; font-size: 11px; text-transform: uppercase; display: inline-block; text-align: center; color: #FFF; border-radius: 2px; margin-right: 20px; background: #FFF; border: 1px solid #FFF; line-height: 20px; vertical-align: top; } | ||||
|  | ||||
| .operation [data-method]:before { background: #FFF; border: 1px solid #FFF; } | ||||
|  | ||||
| .operation [data-method="GET"]:before { color: #1ABC9C; border-color: #1ABC9C; } | ||||
|  | ||||
| .operation [data-method="PUT"]:before { color: #9B59B6; border-color: #9B59B6; } | ||||
|  | ||||
| .operation [data-method="POST"]:before { color: #3498DB; border-color: #3498DB; } | ||||
|  | ||||
| .operation [data-method="PATCH"]:before { color: #E67E22; border-color: #E67E22; } | ||||
|  | ||||
| .operation [data-method="DELETE"]:before { color: #E74C3C; border-color: #E74C3C; } | ||||
|  | ||||
| .docs { margin-top: 40px; padding: 0px 50px; padding-right: 40px; } | ||||
|  | ||||
| .docs-api pre { margin-right: 15px; font-size: 13px; color: #eff1f5; color: #2b303b; border-radius: 2px; background: #2b303b; background: #ECF0F1; white-space: pre-wrap; word-wrap: break-word; box-sizing: border-box; padding: 25px 30px; font-family: "Roboto Mono"; margin-right: 0px; padding-left: 40px; background: #eff1f5; color: #2b303b; white-space: pre-wrap; word-wrap: break-word; box-sizing: border-box; padding: 25px 30px; font-family: "Roboto Mono"; border-radius: 2px; font-size: 13px; } | ||||
|  | ||||
| .operation { min-height: 100vh; padding: 20px 0px; display: flex; } | ||||
|  | ||||
| .operation > aside, .operation > div { min-width: 50%; max-width: 50%; width: 0%; padding-right: 40px; } | ||||
|  | ||||
| .operation h2 { color: #2b303b; font-size: 21px; } | ||||
|  | ||||
| .operation h3 { font-size: 16px; margin-bottom: 20px; margin-top: 40px; } | ||||
|  | ||||
| .operation aside { background: rgba(43, 48, 59, 0.95); box-sizing: border-box; padding: 20px 0px 10px 0px; border-radius: 2px; } | ||||
|  | ||||
| .operation aside h4 { color: #d0d4d7; font-size: 15px; padding: 20px; padding-left: 40px; } | ||||
|  | ||||
| .operation aside pre { background: #2b303b; color: #d0d4d7; margin-right: 0px; padding-left: 40px; } | ||||
|  | ||||
| .operation .params { padding: 0px; margin: 0px; list-style-type: none; border-top: 1px solid #f0f4f7; } | ||||
|  | ||||
| .operation .params li { padding: 15px 10px; border-bottom: 1px solid #f0f4f7; font-size: 15px; } | ||||
|  | ||||
| .operation .params li p { line-height: 20px; margin: 0 0 0 170px; } | ||||
|  | ||||
| .operation .params li:after { visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; } | ||||
|  | ||||
| .operation .params h4 { float: left; line-height: 20px; text-align: right; padding-right: 2 0px; width: 150px; font-weight: bold; font-size: 15px; } | ||||
|  | ||||
| .operation .params small { display: block; text-transform: uppercase; color: #E67E22; font-size: 11px; font-weight: normal; margin-top: 5px; } | ||||
|  | ||||
| .operation aside { background: rgba(239, 241, 245, 0.49); } | ||||
|  | ||||
| .operation aside h4 { color: #2b303b; } | ||||
|  | ||||
| .operation aside pre { background: #eff1f5; color: #2b303b; } | ||||
|  | ||||
| .docs-usage .row { display: flex; } | ||||
|  | ||||
| .docs-usage .row .content-nav { max-width: 250px; min-width: 250px; width: 250px; } | ||||
|  | ||||
| .docs-usage .row .content-nav ul { padding: 0px; margin: 0px; list-style-type: none; } | ||||
|  | ||||
| .docs-usage .row .content-nav ul li { position: relative; color: rgba(0, 0, 0, 0.5); text-transform: uppercase; font-size: 13px; } | ||||
|  | ||||
| .docs-usage .row .content-nav ul li a { color: rgba(43, 48, 59, 0.8); line-height: 30px; display: block; font-size: 16px; text-transform: none; } | ||||
|  | ||||
| .docs-usage .row .content-nav ul li.active a { color: #2b303b; font-size: 16px; text-transform: none; } | ||||
|  | ||||
| .docs-usage .row .content-nav ul li.active:before { content: ""; border-left: 4px solid #2b303b; position: absolute; top: 0px; bottom: 0px; left: -20px; } | ||||
|  | ||||
| .docs-usage .row .content-nav ul ul { margin-bottom: 25px; margin-top: 5px; } | ||||
|  | ||||
| .docs-usage .row .content-main { max-width: 900px; margin: 0px auto; margin-bottom: 40px; font-size: 14px; } | ||||
|  | ||||
| .docs-usage .row .content-main p { line-height: 20px; margin: 20px 0px; } | ||||
|  | ||||
| .docs-usage .row .content-main blockquote { color: #31708f; background-color: #d9edf7; border-color: #bcdff1; padding: 30px; margin: 15px 0px; border-radius: 2px; } | ||||
|  | ||||
| .docs-usage .row .content-main blockquote p:first-child { margin-top: 0px; } | ||||
|  | ||||
| .docs-usage .row .content-main blockquote p:last-child { margin-bottom: 0px; } | ||||
|  | ||||
| .docs-usage .row .content-main strong { font-weight: bold; } | ||||
|  | ||||
| .docs-usage .row .content-main h1 { margin: 40px 0px 20px 0px; font-size: 22px; } | ||||
|  | ||||
| .docs-usage .row .content-main h1:first-child { margin-top: 0px; } | ||||
|  | ||||
| .docs-usage .row .content-main h2 { margin: 40px 0px 30px 0px; font-size: 20px; border-top: 1px solid #EEE; padding-top: 30px; } | ||||
|  | ||||
| .docs-usage .row .content-main h3 { margin: 40px 0px 20px 0px; font-size: 16px; } | ||||
|  | ||||
| .docs-usage .row .content-main ul code, .docs-usage .row .content-main ol code, .docs-usage .row .content-main p code { background: #eff1f5; color: #2b303b; padding: 0px 7px; font-family: "Roboto Mono"; font-size: 13px; white-space: nowrap; } | ||||
|  | ||||
| .docs-usage .row .content-main pre { margin-right: 0px; padding-left: 40px; background: #eff1f5; color: #2b303b; white-space: pre-wrap; word-wrap: break-word; box-sizing: border-box; padding: 25px 30px; font-family: "Roboto Mono"; border-radius: 2px; font-size: 13px; } | ||||
|  | ||||
| .docs-usage .row .content-main pre > code { background: #eff1f5; color: #2b303b; font-family: "Roboto Mono"; font-size: 13px; } | ||||
|  | ||||
| .tt-open { position: absolute; top: 34px; left: 0px; z-index: 100; display: none; background: #FFF; min-width: 100%; border: 1px solid #eee; border-radius: 0px; } | ||||
|  | ||||
| .tt-selectable:hover, .tt-cursor { background: #eff1f5; } | ||||
|  | ||||
| .tt-selectable { padding: 1rem 0.75rem; cursor: pointer; display: flex; } | ||||
|  | ||||
| .tt-selectable div:first-child { width: 75px; max-width: 75px; min-width: 75px; } | ||||
|  | ||||
| .tt-selectable div:first-child img { width: 32px; max-width: 32px; min-width: 32px; border-radius: 50%; text-align: left; margin-left: 1rem; } | ||||
|  | ||||
| .tt-selectable div:last-child { flex: 1 1 auto; line-height: 32px; } | ||||
|  | ||||
| .no-matches-message { padding: 20px; color: rgba(0, 0, 0, 0.5); font-style: italic; } | ||||
|  | ||||
| .not-indexed-message { padding: 20px; padding-bottom: 0px; color: rgba(0, 0, 0, 0.5); font-style: italic; } | ||||
|  | ||||
| .not-indexed-message em { color: #2b303b; } | ||||
|  | ||||
| .not-indexed-message p:last-child { margin-top: 20px; font-size: 15px; color: rgba(0, 0, 0, 0.4); } | ||||
|  | ||||
| .not-indexed-message p:last-child a { color: rgba(0, 0, 0, 0.8); } | ||||
|  | ||||
| .hidden { display: none; } | ||||
|  | ||||
| :focus { outline: none; } | ||||
|  | ||||
| ::-moz-focus-inner { border: 0; } | ||||
|  | ||||
| .container { max-width: 980px !important; } | ||||
|  | ||||
| .modal-content { padding: 20px; } | ||||
|  | ||||
| .modal-content .form-control { border-radius: 2px; } | ||||
|  | ||||
| .modal-content .btn { margin-top: 20px; } | ||||
|  | ||||
| .node-row .col-sm-4 { width: 100%; } | ||||
|  | ||||
| .node-row .col-sm-4:last-child .card { border-bottom: none; } | ||||
|  | ||||
| .node-row .card { border: none; border-bottom: 1px solid #EEE; border-radius: 0px; position: relative; display: flex; } | ||||
|  | ||||
| .node-row .card-block h3 { font-size: 1.1rem; } | ||||
|  | ||||
| .node-row .card-block .card-text { color: rgba(0, 0, 0, 0.5); font-size: 0.95rem; margin: 0px; } | ||||
|  | ||||
| .node-row .card-block .btn-group { position: absolute; top: 15px; right: 0px; } | ||||
|  | ||||
| .node-row .card-block .btn-group .btn-danger { color: #bf616a; background: #FFF; border: 1px solid #bf616a; outline: none; cursor: pointer; width: auto; text-transform: uppercase; padding: 0px 10px; border-radius: 2px; font-size: 11px; line-height: 30px; height: auto; margin-left: 10px; } | ||||
|  | ||||
| .node-row .card-header { background: #FFF; border-bottom: none; padding-right: 0px; padding-left: 0px; width: 45px; } | ||||
|  | ||||
| i.linux_amd64 { width: 32px; height: 32px; display: inline-block; background: url(/static/images/ubuntu.svg); background-size: 32px; background-repeat: no-repeat; } | ||||
| @@ -1,8 +0,0 @@ | ||||
| extends base | ||||
|  | ||||
| block append head | ||||
|     title Bad Request | ||||
|  | ||||
| block content | ||||
|     div.container | ||||
|         div.alert.alert-danger Bad Request | ||||
| @@ -1,8 +0,0 @@ | ||||
| extends base | ||||
|  | ||||
| block append head | ||||
|     title Not Authorized | ||||
|  | ||||
| block content | ||||
|     div.container | ||||
|         div.alert.alert-danger Not Authorized | ||||
| @@ -1,8 +0,0 @@ | ||||
| extends base | ||||
|  | ||||
| block append head | ||||
|     title Forbidden | ||||
|  | ||||
| block content | ||||
|     div.container | ||||
|         div.alert.alert-danger Access Forbidden. | ||||
| @@ -1,8 +0,0 @@ | ||||
| extends base | ||||
|  | ||||
| block append head | ||||
|     title Not Found | ||||
|  | ||||
| block content | ||||
|     div.container | ||||
|         div.alert.alert-danger Not Found | ||||
| @@ -1,8 +0,0 @@ | ||||
| extends base | ||||
|  | ||||
| block append head | ||||
|     title Internal Server Error | ||||
|  | ||||
| block content | ||||
|     div.container | ||||
|         div.alert.alert-danger Oops. Something went wrong. | ||||
| @@ -1,71 +0,0 @@ | ||||
| extends base | ||||
|  | ||||
| block append head | ||||
|     title Dashboard | ||||
|  | ||||
| block menu | ||||
|     li | ||||
|         a[href="/"] Home | ||||
|     li | ||||
|         a[href="/settings/profile"] Profile | ||||
|     if User.Admin | ||||
|         li | ||||
|             a[href="/settings/people"] People | ||||
|         li | ||||
|             a[href="/settings/nodes"] Nodes | ||||
|  | ||||
|  | ||||
| block content | ||||
|     h1 Feed | ||||
|     hr | ||||
|     table | ||||
|         tbody[data-bind="foreach: feed"] | ||||
|             tr | ||||
|                 td | ||||
|                     a[data-bind="text: full_name, attr: {href: full_name() + '/' + number() }"] | ||||
|                 td[data-bind="text: $data.full_name"] | ||||
|                 td[data-bind="text: $data.owner"] | ||||
|                 td[data-bind="text: $data.name"] | ||||
|                 td[data-bind="text: $data.private"] | ||||
|                 td[data-bind="text: $data.trusted"] | ||||
|                 td[data-bind="text: $data.avatar_url"] | ||||
|                 td[data-bind="text: $data.clone_url"] | ||||
|                 td[data-bind="text: $data.number"] | ||||
|                 td[data-bind="text: $data.commit"] | ||||
|                 td[data-bind="text: $data.branch"] | ||||
|                 td[data-bind="text: $data.author"] | ||||
|                 td[data-bind="text: $data.status"] | ||||
|  | ||||
| block append scripts | ||||
|     script | ||||
|         var feed = #{json(Feed)}; | ||||
|  | ||||
|         function Activity(data) { | ||||
|             this.full_name  = ko.observable(data.full_name); | ||||
|             this.owner      = ko.observable(data.owner); | ||||
|             this.name       = ko.observable(data.name); | ||||
|             this.private    = ko.observable(data.private); | ||||
|             this.trusted    = ko.observable(data.trusted); | ||||
|             this.avatar_url = ko.observable(data.avatar); | ||||
|             this.clone_url  = ko.observable(data.clone_url); | ||||
|             this.link_url   = ko.observable(data.link_url); | ||||
|             this.number      = ko.observable(data.number); | ||||
|             this.commit      = ko.observable(data.commit); | ||||
|             this.started_at  = ko.observable(data.started_at); | ||||
|             this.finished_at = ko.observable(data.finished_at); | ||||
|             this.exit_code   = ko.observable(data.exit_code); | ||||
|             this.status      = ko.observable(data.status); | ||||
|             this.environment = ko.observable(data.environment); | ||||
|         } | ||||
|  | ||||
|         function FeedViewModel() { | ||||
|             var self = this; | ||||
|  | ||||
|             var mapped = $.map(feed, function(activity) { | ||||
|                 return new Activity(activity) | ||||
|             }); | ||||
|  | ||||
|             self.feed = ko.observableArray(mapped); | ||||
|         } | ||||
|  | ||||
|         ko.applyBindings(new FeedViewModel()); | ||||
| @@ -1,97 +0,0 @@ | ||||
| extends base | ||||
|  | ||||
| block append head | ||||
|     title Users | ||||
|  | ||||
| block content | ||||
|     div.container | ||||
|         h1 Users | ||||
|         hr | ||||
|         form | ||||
|             input[type="text"][placeholder="ie octocat"][data-bind="value: newUserLogin"] | ||||
|             button[type="button"][data-bind="click: addUser"] add user | ||||
|         hr | ||||
|         if len(Users) <= 1 | ||||
|             div.alert.alert-info | ||||
|                 | You should add users | ||||
|         table | ||||
|             tbody[data-bind="foreachInit: users"] | ||||
|                 tr[data-template] | ||||
|                     td[data-bind="text: $data.login"] | ||||
|                     td[data-bind="text: $data.email"] | ||||
|                     td[data-bind="text: $data.avatar_url"] | ||||
|                     td[data-bind="text: $data.active"] | ||||
|                     td[data-bind="text: $data.admin"] | ||||
|                     td | ||||
|                         button.delete[data-bind="click: $parent.removeUser"] delete | ||||
|                     td | ||||
|                         button.delete[data-bind="click: $parent.toggleAdmin"] toggle | ||||
|                 each $user in Users | ||||
|                     tr[data-init] | ||||
|                         td[data-bind="init, text: login"] #{$user.Login} | ||||
|                         td[data-bind="init, text: email"] #{$user.Email} | ||||
|                         td[data-bind="init, text: avatar_url"] #{$user.Avatar} | ||||
|                         td[data-bind="init, text: active"] #{$user.Active} | ||||
|                         td[data-bind="init, text: admin"]  #{$user.Admin} | ||||
|                         td | ||||
|                             button.delete[data-bind="init, click: $parent.removeUser"] delete | ||||
|                         td | ||||
|                             button.delete[data-bind="init, click: $parent.toggleAdmin"] toggle | ||||
|  | ||||
| block append scripts | ||||
|     script | ||||
|         function User(data) { | ||||
|             this.login = ko.observable(data.login); | ||||
|             this.email = ko.observable(data.email); | ||||
|             this.avatar_url = ko.observable(data.avatar_url); | ||||
|             this.active = ko.observable(data.active); | ||||
|             this.admin = ko.observable(data.admin); | ||||
|         } | ||||
|  | ||||
|         function UserViewModel() { | ||||
|             var self = this; | ||||
|  | ||||
|             self.users = ko.observableArray(); | ||||
|             self.newUserLogin = ko.observable(); | ||||
|              | ||||
|             self.removeUser = function(user) { | ||||
|                 $.ajax({ | ||||
|                     url: "/api/users/"+user.login(), | ||||
|                     type: "DELETE", | ||||
|                     success: function( data ) { | ||||
|                         self.users.remove(user); | ||||
|                         self.users.sort(self.sort); | ||||
|                     } | ||||
|                 }); | ||||
|             }; | ||||
|  | ||||
|             self.addUser = function() { | ||||
|                 $.ajax({ | ||||
|                     url: "/api/users", | ||||
|                     type: "POST", | ||||
|                     data: ko.toJSON({ "login": self.newUserLogin() }), | ||||
|                     contentType: "application/json", | ||||
|                     success: function( data ) { | ||||
|                         self.newUserLogin(""); | ||||
|                         self.users.push(new User(data)); | ||||
|                         self.users.sort(self.sort); | ||||
|                     } | ||||
|                 }); | ||||
|             }; | ||||
|  | ||||
|             self.toggleAdmin = function(user) { | ||||
|                 user.admin(!user.admin()); | ||||
|                 $.ajax({ | ||||
|                     url: "/api/users/"+user.login(), | ||||
|                     type: "PATCH", | ||||
|                     data: ko.toJSON(user), | ||||
|                     contentType: "application/json" | ||||
|                 }); | ||||
|             }; | ||||
|  | ||||
|             self.sort = function(a, b) { | ||||
|                 return a.login().toLowerCase() > b.login().toLowerCase() ? 1 : -1;   | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         ko.applyBindings(new UserViewModel()); | ||||
| @@ -1,54 +0,0 @@ | ||||
| doctype 5 | ||||
| html | ||||
|     head | ||||
|         block head | ||||
|         meta[charset="utf-8"] | ||||
|         meta[name="viewport"][content="width=device-width, initial-scale=1"] | ||||
|         meta[http-equiv="x-ua-compatible"][content="ie=edge"] | ||||
|         link[rel="icon"][type="image/x-icon"][href="/static/images/favicon.ico"] | ||||
|  | ||||
|         link[rel="stylesheet"][href="//fonts.googleapis.com/icon?family=Material+Icons"] | ||||
|         link[rel="stylesheet"][href="//fonts.googleapis.com/css?family=Roboto+Mono"] | ||||
|         link[rel="stylesheet"][href="//fonts.googleapis.com/css?family=Roboto"] | ||||
|         link[rel="stylesheet"][href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.min.css"] | ||||
|         link[rel="stylesheet"][href="//cdnjs.cloudflare.com/ajax/libs/octicons/3.1.0/octicons.min.css"] | ||||
|         link[rel="stylesheet"][href="/static/styles_gen/style.css"] | ||||
|  | ||||
|         if Csrf | ||||
|             meta[name="_csrf"][content=Csrf] | ||||
|  | ||||
|     body | ||||
|         nav.navbar | ||||
|             div.container-fluid | ||||
|                 a.navbar-brand[href="/"] | ||||
|                 if User | ||||
|                     ul.nav.navbar-nav.navbar-right | ||||
|                         li | ||||
|                             form.navbar-form | ||||
|                                 input.form-control.typeahead[type="text"][placeholder="Search..."] | ||||
|                         li | ||||
|                             img[src=User.Avatar] | ||||
|                         li | ||||
|                             div.dropdown | ||||
|                                 button[type="button"][data-toggle="dropdown"] | ||||
|                                     i.material-icons expand_more | ||||
|                                 div.dropdown-menu.dropdown-menu-right | ||||
|                                     a.dropdown-item[href="/settings/profile"] Profile | ||||
|                                     a.dropdown-item[href="/logout"] Logout | ||||
|  | ||||
|  | ||||
|         div.subnav | ||||
|             div.container-fluid | ||||
|                 block header | ||||
|  | ||||
|         div#content | ||||
|             block content | ||||
|  | ||||
|         block scripts | ||||
|             script[type="text/javascript"][src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"] | ||||
|             script[type="text/javascript"][src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.6/moment.min.js"] | ||||
|             script[type="text/javascript"][src="//cdnjs.cloudflare.com/ajax/libs/jquery-searcher/0.2.0/jquery.searcher.min.js"] | ||||
|             script[type="text/javascript"][src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/js/bootstrap.min.js"] | ||||
|             script[type="text/javascript"][src="//cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.11.1/typeahead.bundle.min.js"] | ||||
|             script[text="text/javascript"][src="//cdnjs.cloudflare.com/ajax/libs/stickyfill/1.1.2/stickyfill.js"] | ||||
|             script[type="text/javascript"][src="/static/scripts_gen/drone.min.js"] | ||||
| @@ -1,99 +0,0 @@ | ||||
| extends base | ||||
|  | ||||
| block append head | ||||
|     title #{Repo.Name} · #{Build.Number} | ||||
|  | ||||
|  | ||||
| block header | ||||
|     ol | ||||
|         li | ||||
|             a[href="/"+Repo.FullName] | ||||
|                 | #{Repo.Owner} / #{Repo.Name} | ||||
|         li.separator | ||||
|             i.material-icons chevron_right | ||||
|         li #{Build.Number} | ||||
|  | ||||
|  | ||||
| block content | ||||
|     div.container-fluid | ||||
|         div.row | ||||
|             div.col-md-4.sticky | ||||
|                 div.build-summary | ||||
|                     div | ||||
|                         div[class=Build.Status][style="display:none"] #{Build.Status} | ||||
|                         h3 | ||||
|                             | #{Build.Message} | ||||
|                             a.material-icons[href=Build.Link][target="_blank"] link | ||||
|                     p | ||||
|                         em #{Build.Author} | ||||
|                         if Build.Event != "deployment" | ||||
|                             span authored | ||||
|                         else | ||||
|                             span deployed | ||||
|                         em[data-livestamp=Build.Created] | ||||
|                         span to | ||||
|                         if Build.Event != "deployment" | ||||
|                             em #{Build.Branch} | ||||
|                         else | ||||
|                             em #{Build.Deploy} | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|                 div.job-list | ||||
|                     $curr = Job | ||||
|                     $build = Build | ||||
|                     $repo = Repo | ||||
|                     each $job in Jobs | ||||
|                         a[href="/" + $repo.FullName +"/"+ $build.Number+"/"+$job.Number][data-job=$job.Number] | ||||
|                             .active ? $curr.Number == $job.Number | ||||
|                             div | ||||
|                                 div.status[class=$job.Status] #{$job.Status} | ||||
|                             div | ||||
|                                 if len($job.Environment) != 0 | ||||
|                                     h3 | ||||
|                                         each $key, $val in $job.Environment | ||||
|                                             div.env #{$key}=#{$val} | ||||
|                                 div[class="msg-pending"] | ||||
|                                     .hidden ? $job.Status != "pending" | ||||
|                                     | pending assignment to a worker | ||||
|                                 div[class="msg-running"] | ||||
|                                     .hidden ? $job.Status != "running" | ||||
|                                     | started | ||||
|                                     span[data-livestamp=$job.Started] | ||||
|                                 div[class="msg-finished"] | ||||
|                                     .hidden ? $job.Finished == 0 | ||||
|                                     | finished | ||||
|                                     span[data-livestamp=$job.Finished] | ||||
|                                 div[class="msg-exited"] | ||||
|                                     .hidden ? $job.Finished == 0 | ||||
|                                     | with exit code | ||||
|                                     span #{$job.ExitCode} | ||||
|  | ||||
|                 div.build-btn-group | ||||
|                     button.btn.btn-info.hidden#restart restart | ||||
|                     button.btn.btn-info.hidden#cancel cancel | ||||
|  | ||||
|             div.col-md-8 | ||||
|                 if Build.Signed | ||||
|                     if Build.Verified | ||||
|                         noscript | ||||
|                     else | ||||
|                         div.alert.alert-warning | ||||
|                             | Your .drone.yml.sig file did not match your .drone.yml | ||||
|                 if Job.Error != "" | ||||
|                     div.alert.alert-danger #{Job.Error} | ||||
|                 else | ||||
|                     div#output | ||||
|                     button.tail#tail | ||||
|                         i.material-icons expand_more | ||||
|  | ||||
| block append scripts | ||||
|     script | ||||
|         $('.sticky').Stickyfill(); | ||||
|         var repo = #{json(Repo.FullName)}; | ||||
|         var build = #{json(Build.Number)}; | ||||
|         var job = #{json(Job.Number)}; | ||||
|         var status = #{json(Job.Status)}; | ||||
|  | ||||
|         var view = new JobViewModel(repo, build, job, status); | ||||
| @@ -1,40 +0,0 @@ | ||||
| extends base | ||||
|  | ||||
| block append head | ||||
| 	title #{Site.Name} · Drone | ||||
|  | ||||
| block header | ||||
| 	ol | ||||
| 		li Documentation | ||||
| 	ul.nav.nav-tabs | ||||
| 		li.nav-item | ||||
| 			a[class="nav-link"][href="../setup/"] | ||||
| 				.active ? Site.Name == "Install" | ||||
| 				| Install | ||||
| 		li.nav-item | ||||
| 			a[class="nav-link"][href="../build/"] | ||||
| 				.active ? Site.Name == "Builds" | ||||
| 				| Builds | ||||
| 		li.nav-item | ||||
| 			a[class="nav-link"][href="../plugin/"] | ||||
| 				.active ? Site.Name == "Plugins" | ||||
| 				| Plugins | ||||
| 		li.nav-item | ||||
| 			a[class="nav-link"][href="../cli/"] | ||||
| 				.active ? Site.Name == "CLI" | ||||
| 				| CLI | ||||
| 		li.nav-item | ||||
| 			a.nav-link[href="../api/"] API Reference | ||||
|  | ||||
| block content | ||||
| 	div.container-fluid.docs.docs-usage | ||||
| 		div.row | ||||
| 			div.content-nav | ||||
| 				ul | ||||
| 					#{Site.Nav.HTML} | ||||
| 			div.content-main | ||||
| 				#{Page.HTML} | ||||
|  | ||||
| block scripts | ||||
| 	script[type="text/javascript"][src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"] | ||||
| 	script[type="text/javascript"][src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/js/bootstrap.min.js"] | ||||
| @@ -1,46 +0,0 @@ | ||||
| extends base | ||||
|  | ||||
| block append head | ||||
|     title Active Repositories | ||||
|  | ||||
| block header | ||||
|     ol | ||||
|         li Active Repositories | ||||
|  | ||||
|     ul.nav.nav-tabs | ||||
|         li.nav-item | ||||
|             a.nav-link.active[href="/"] Active Repositories | ||||
|         li.nav-item | ||||
|             a.nav-link[href="/repos"] Available Repositories | ||||
|  | ||||
| block content | ||||
|     div.container | ||||
|         if len(Repos) == 0 | ||||
|             div.alert.alert-info | ||||
|                 | Your repository list is empty. | ||||
|  | ||||
|         else | ||||
|             div.row.repo-row | ||||
|                 input.repo-search[type="search"][placeholder="Filter..."] | ||||
|                 div.repo-list | ||||
|                     each $repo in Repos | ||||
|                         div.col-sm-4 | ||||
|                             a.card[href="/"+$repo.FullName] | ||||
|                                 div.card-header | ||||
|                                     if $repo.Avatar != "" | ||||
|                                         img.avatar[src=$repo.Avatar] | ||||
|                                     else | ||||
|                                         img.avatar[src="/static/images/dummy.png"] | ||||
|                                 div.card-block | ||||
|                                     h3.login #{$repo.Name} | ||||
|                                     div.full_name.hidden #{$repo.FullName} | ||||
| block append scripts | ||||
|     if len(Repos) != 0 | ||||
|         script | ||||
|             $(window).load(function(){ | ||||
|                 $(".repo-list").searcher({ | ||||
|                     itemSelector: ".col-sm-4", | ||||
|                     textSelector:  ".full_name", | ||||
|                     inputSelector: ".repo-search" | ||||
|                 }); | ||||
|             }); | ||||
| @@ -1,35 +0,0 @@ | ||||
| doctype 5 | ||||
| html | ||||
|     head | ||||
|         title Login | ||||
|         meta[charset="utf-8"] | ||||
|         meta[name="viewport"][content="width=device-width, initial-scale=1"] | ||||
|         meta[http-equiv="x-ua-compatible"][content="ie=edge"] | ||||
|         link[rel="icon"][type="image/x-icon"][href="/static/images/favicon.ico"] | ||||
|  | ||||
|         link[rel="stylesheet"][href="//fonts.googleapis.com/icon?family=Material+Icons"] | ||||
|         link[rel="stylesheet"][href="//fonts.googleapis.com/css?family=Roboto+Mono"] | ||||
|         link[rel="stylesheet"][href="//fonts.googleapis.com/css?family=Roboto"] | ||||
|         link[rel="stylesheet"][href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.min.css"] | ||||
|         link[rel="stylesheet"][href="/static/styles_gen/style.css"] | ||||
|         style | ||||
|             html { height: 100%; overflow: hidden; } | ||||
|  | ||||
|     body.login | ||||
|         div | ||||
|             div.logo | ||||
|             a[href="/authorize"] Login | ||||
|  | ||||
|         if Error == "oauth_error" | ||||
|             div.alert.alert-danger | ||||
|                 | We failed to authorize your account. Please contact your | ||||
|                 | system administrator to check the logs and see what went wrong. | ||||
|  | ||||
|         else if Error == "access_denied" | ||||
|             div.alert.alert-danger | ||||
|                 | Unable to login. Registration is closed. | ||||
|  | ||||
|         else if Error == "internal_error" | ||||
|             div.alert.alert-danger | ||||
|                 | We encountered an unexpected error. Please contact your | ||||
|                 | system administrator to check the logs and see what went wrong. | ||||
| @@ -1,24 +0,0 @@ | ||||
| doctype 5 | ||||
| html | ||||
|     head | ||||
|         title Login | ||||
|         meta[charset="utf-8"] | ||||
|         meta[name="viewport"][content="width=device-width, initial-scale=1"] | ||||
|         meta[http-equiv="x-ua-compatible"][content="ie=edge"] | ||||
|         link[rel="icon"][type="image/x-icon"][href="/static/images/favicon.ico"] | ||||
|  | ||||
|         link[rel="stylesheet"][href="//fonts.googleapis.com/icon?family=Material+Icons"] | ||||
|         link[rel="stylesheet"][href="//fonts.googleapis.com/css?family=Roboto+Mono"] | ||||
|         link[rel="stylesheet"][href="//fonts.googleapis.com/css?family=Roboto"] | ||||
|         link[rel="stylesheet"][href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.min.css"] | ||||
|         link[rel="stylesheet"][href="/static/styles_gen/style.css"] | ||||
|         style | ||||
|             html { height: 100%; overflow: hidden; } | ||||
|  | ||||
|     body.login.login-form | ||||
|         div | ||||
|             div.logo | ||||
|             form[action="/authorize"][method="post"] | ||||
|                 input[type="text"][placeholder="Username"][name="username"] | ||||
|                 input[type="password"][placeholder="Password"][name="password"] | ||||
|                 input[type="submit"][value="Login"] | ||||
| @@ -1,52 +0,0 @@ | ||||
| extends base | ||||
|  | ||||
| block append head | ||||
|     title Docker Nodes | ||||
|  | ||||
|  | ||||
| block header | ||||
|     ol | ||||
|         li Docker Nodes | ||||
|         li | ||||
|             button.btn.btn-info[data-toggle="modal"][data-target=".modal-node"] | ||||
|                 | add node   | ||||
|  | ||||
|  | ||||
| block content | ||||
|     div.container | ||||
|         div.alert.alert-danger.hidden | ||||
|         div.row.node-row | ||||
|             each $node in Nodes | ||||
|                 div.col-sm-4 | ||||
|                     div.card[data-id=$node.ID] | ||||
|                         div.card-header | ||||
|                             i[class="linux_amd64"] | ||||
|                         div.card-block | ||||
|                             h3.addr #{$node.Addr} | ||||
|                             p.arch.card-text #{$node.Arch} | ||||
|                             div.btn-group | ||||
|                                 button.btn.btn-danger Delete | ||||
|  | ||||
|         div.modal.modal-node[role="dialog"] | ||||
|             div.modal-dialog | ||||
|                 div.modal-content | ||||
|                     form | ||||
|                         fieldset.form-group | ||||
|                             label[for="addr"] Address | ||||
|                             input.form-control[type="text"][placeholder="unix:///var/run/docker.sock"]#addr | ||||
|                         fieldset.form-group | ||||
|                             label[for="key"] Key | ||||
|                             textarea.form-control#key | ||||
|                         fieldset.form-group | ||||
|                             label[for="cert"] Cert | ||||
|                             textarea.form-control#cert | ||||
|                         fieldset.form-group | ||||
|                             label[for="ca"] CA | ||||
|                             textarea.form-control#ca | ||||
|                         button.btn.btn-info[type="button"] add node | ||||
|  | ||||
|  | ||||
|  | ||||
| block append scripts | ||||
|     script | ||||
|         var view = new NodeViewModel(); | ||||
| @@ -1,59 +0,0 @@ | ||||
| extends base | ||||
|  | ||||
| block append head | ||||
|     title #{Repo.Name} | ||||
|  | ||||
| block header | ||||
|     ol | ||||
|         li #{Repo.Owner} / #{Repo.Name} | ||||
|  | ||||
|     ul.nav.nav-tabs | ||||
|         li.nav-item | ||||
|             a.nav-link.active[href="/"+Repo.FullName] Builds | ||||
|         li.nav-item | ||||
|             a.nav-link[href="/"+Repo.FullName+"/settings/badges"] Badges | ||||
|         li.nav-item | ||||
|             a.nav-link[href="/"+Repo.FullName+"/settings/encrypt"] Secrets | ||||
|         li.nav-item | ||||
|             a.nav-link[href="/"+Repo.FullName+"/settings"] Settings | ||||
|  | ||||
| block content | ||||
|     div.container | ||||
|         if len(Builds) == 0 | ||||
|             div.alert.alert-info | ||||
|                 | You have no builds | ||||
|  | ||||
|         div.timeline | ||||
|             $repo = Repo | ||||
|             each $group in Groups | ||||
|                 div.group | ||||
|                     div.group-title | ||||
|                         | commits on #{$group.Date} | ||||
|                     each $build in $group.Builds | ||||
|                         a.card[href=$repo.Name+"/"+$build.Number][data-build=$build.Number] | ||||
|                             div.card-header | ||||
|                                 if $build.Avatar != "" | ||||
|                                     img[src=$build.Avatar] | ||||
|                                 else | ||||
|                                     img[src="/static/images/dummy.png"] | ||||
|                             div.card-block | ||||
|                                 div | ||||
|                                     div.status[class=$build.Status] #{$build.Status} | ||||
|                                     h3 #{$build.Message} | ||||
|                                 p.card-text | ||||
|                                     em #{$build.Author} | ||||
|                                     if $build.Event != "deployment" | ||||
|                                         span authored | ||||
|                                     else | ||||
|                                         span deployed | ||||
|                                     em[data-livestamp=$build.Created] | ||||
|                                     span to | ||||
|                                     if $build.Event != "deployment" | ||||
|                                         em #{$build.Branch} | ||||
|                                     else | ||||
|                                         em #{$build.Deploy} | ||||
|  | ||||
|  | ||||
| block append scripts | ||||
|     script | ||||
|         new RepoViewModel("#{Repo.FullName}"); | ||||
| @@ -1,33 +0,0 @@ | ||||
| extends base | ||||
|  | ||||
| block append head | ||||
|     title #{Repo.Name} | ||||
|  | ||||
| block header | ||||
|     ol | ||||
|         li #{Repo.Owner} / #{Repo.Name} | ||||
|  | ||||
| block content | ||||
|     div.container.repo_activate | ||||
|         div.alert.alert-danger.hidden | ||||
|         div.alert.alert-info | ||||
|         	| This repository is not yet activated. | ||||
|         button.btn.btn-info#activateRepo Activate Now | ||||
|  | ||||
|  | ||||
| block append scripts | ||||
|     script | ||||
|         $( "#activateRepo" ).click(function() { | ||||
|             $( "#activateRepo" ).hide(); | ||||
|             $.ajax({ | ||||
|                 type: "POST", | ||||
|                 url: "/api/repos/#{Repo.FullName}", | ||||
|                 success: function() { | ||||
|                     window.location.href="/#{Repo.FullName}" | ||||
|                 }, | ||||
|                 error: function(err) { | ||||
|                 $(".alert-info").hide(); | ||||
|                 $(".alert-danger").text("Unable to activate this repository. You must have administrative access to this repository. Also, please check the third-party application access policy to ensure Drone is approved. "+ err.responseText).show(); | ||||
|                 } | ||||
|             }); | ||||
|         }); | ||||
| @@ -1,37 +0,0 @@ | ||||
| extends base | ||||
|  | ||||
| block append head | ||||
|     title #{Repo.Name} | ||||
|  | ||||
| block header | ||||
|     ol | ||||
|         li #{Repo.Owner} / #{Repo.Name} | ||||
|  | ||||
|     ul.nav.nav-tabs | ||||
|         li.nav-item | ||||
|             a.nav-link[href="/"+Repo.FullName] Builds | ||||
|         li.nav-item | ||||
|             a.nav-link.active[href="/"+Repo.FullName+"/settings/badges"] Badges | ||||
|         li.nav-item | ||||
|             a.nav-link[href="/"+Repo.FullName+"/settings/encrypt"] Secrets | ||||
|         li.nav-item | ||||
|             a.nav-link[href="/"+Repo.FullName+"/settings"] Settings | ||||
|  | ||||
| block content | ||||
|     div.container.repo_config | ||||
|         div.row | ||||
|             div.col-md-3 Markdown | ||||
|             div.col-md-9 | ||||
|                 pre | ||||
|                     | [](#{Link}/#{Repo.FullName}) | ||||
|         div.row | ||||
|             div.col-md-3 Markup | ||||
|             div.col-md-9 | ||||
|                 pre | ||||
|                     | <a href="#{Link}/#{Repo.FullName}"><img src="#{Link}/api/badges/#{Repo.FullName}/status.svg" /></a> | ||||
|         div.row | ||||
|             div.col-md-3 CC Menu | ||||
|             div.col-md-9 | ||||
|                 pre | ||||
|                     | #{Link}/api/badges/#{Repo.FullName}/cc.xml | ||||
|  | ||||
| @@ -1,77 +0,0 @@ | ||||
| extends base | ||||
|  | ||||
| block append head | ||||
|     title #{Repo.Name} | ||||
|  | ||||
| block header | ||||
|     ol | ||||
|         li #{Repo.Owner} / #{Repo.Name} | ||||
|  | ||||
|     ul.nav.nav-tabs | ||||
|         li.nav-item | ||||
|             a.nav-link[href="/"+Repo.FullName] Builds | ||||
|         li.nav-item | ||||
|             a.nav-link[href="/"+Repo.FullName+"/settings/badges"] Badges | ||||
|         li.nav-item | ||||
|             a.nav-link[href="/"+Repo.FullName+"/settings/encrypt"] Secrets | ||||
|         li.nav-item | ||||
|             a.nav-link.active[href="/"+Repo.FullName+"/settings"] Settings | ||||
|  | ||||
| block content | ||||
|     div.container.repo_config | ||||
|         div.row | ||||
|             div.col-md-3 Push Hooks | ||||
|             div.col-md-9 | ||||
|                 if Repo.AllowPush | ||||
|                     input#push[type="checkbox"][hidden="hidden"][checked] | ||||
|                 else | ||||
|                     input#push[type="checkbox"][hidden="hidden"] | ||||
|                 label.switch[for="push"] | ||||
|         div.row | ||||
|             div.col-md-3 Pull Request Hooks | ||||
|             div.col-md-9 | ||||
|                 if Repo.AllowPull | ||||
|                     input#pull[type="checkbox"][hidden="hidden"][checked] | ||||
|                 else | ||||
|                     input#pull[type="checkbox"][hidden="hidden"] | ||||
|                 label.switch[for="pull"] | ||||
|         div.row | ||||
|             div.col-md-3 Tag Hooks | ||||
|             div.col-md-9 | ||||
|                 if Repo.AllowTag | ||||
|                     input#tag[type="checkbox"][hidden="hidden"][checked] | ||||
|                 else | ||||
|                     input#tag[type="checkbox"][hidden="hidden"] | ||||
|                 label.switch[for="tag"] | ||||
|         div.row | ||||
|             div.col-md-3 Deploy Hook | ||||
|             div.col-md-9 | ||||
|                 if Repo.AllowDeploy | ||||
|                     input#deploy[type="checkbox"][hidden="hidden"][checked] | ||||
|                 else | ||||
|                     input#deploy[type="checkbox"][hidden="hidden"] | ||||
|                 label.switch[for="deploy"] | ||||
|         div.row | ||||
|             div.col-md-3 Timeout in Minutes | ||||
|             div.col-md-9 | ||||
|                 input[type="range"][min="0"][max="900"][value=Repo.Timeout] | ||||
|                 span.timeout-label | ||||
|                     | #{Repo.Timeout} minutes | ||||
|         div.row | ||||
|             div.col-md-3 Trusted | ||||
|             div.col-md-9 | ||||
|                 if Repo.IsTrusted | ||||
|                     input#trusted[type="checkbox"][hidden="hidden"][checked] | ||||
|                 else | ||||
|                     input#trusted[type="checkbox"][hidden="hidden"] | ||||
|                 label.switch[for="trusted"] | ||||
|         div.row | ||||
|             div.col-md-12 | ||||
|                 div.alert.alert-danger | ||||
|                     button.btn.btn-danger Delete | ||||
|                     span Permanently deletes the build history. This action cannot be undone. | ||||
|  | ||||
|  | ||||
| block append scripts | ||||
|     script | ||||
|         var view = new RepoConfigViewModel(#{Repo.FullName}); | ||||
| @@ -1,45 +0,0 @@ | ||||
| extends base | ||||
|  | ||||
| block append head | ||||
|     title #{Repo.Name} | ||||
|  | ||||
| block header | ||||
|     ol | ||||
|         li #{Repo.Owner} / #{Repo.Name} | ||||
|  | ||||
|     ul.nav.nav-tabs | ||||
|         li.nav-item | ||||
|             a.nav-link[href="/"+Repo.FullName] Builds | ||||
|         li.nav-item | ||||
|             a.nav-link[href="/"+Repo.FullName+"/settings/badges"] Badges | ||||
|         li.nav-item | ||||
|             a.nav-link.active[href="/"+Repo.FullName+"/settings/encrypt"] Secrets | ||||
|         li.nav-item | ||||
|             a.nav-link[href="/"+Repo.FullName+"/settings"] Settings | ||||
|  | ||||
| block content | ||||
|     div.container.repo_secrets | ||||
|         div | ||||
|             textarea.secrets[spellcheck="false"] | ||||
|             div.alert.alert-danger.hidden | ||||
|             button.btn.btn-info#encryptButton Generate | ||||
|         div | ||||
|             pre.result | ||||
|  | ||||
| block append scripts | ||||
|     script | ||||
|         $("#encryptButton").click(function(){ | ||||
|             $( ".alert-danger" ).hide(); | ||||
|             $.ajax({ | ||||
|                 url: "/api/repos/#{Repo.FullName}/encrypt", | ||||
|                 type: "POST", | ||||
|                 contentType: "text/plain", | ||||
|                 data: $(".secrets").val(), | ||||
|                 success: function( data ) { | ||||
|                     $( ".result" ).text( data ); | ||||
|                 }, | ||||
|                 error: function( data ) { | ||||
|                     $( ".alert-danger" ).text(data.responseText).show(); | ||||
|                 } | ||||
|             }); | ||||
|         }); | ||||
| @@ -1,47 +0,0 @@ | ||||
| extends base | ||||
|  | ||||
| block append head | ||||
|     title Available Repositories | ||||
|  | ||||
| block header | ||||
|     ol | ||||
|         li Available Repositories | ||||
|  | ||||
|     ul.nav.nav-tabs | ||||
|         li.nav-item | ||||
|             a.nav-link[href="/"] Active Repositories | ||||
|         li.nav-item | ||||
|             a.nav-link.active[href="/repos"] Available Repositories | ||||
|  | ||||
| block content | ||||
|     div.container | ||||
|         if len(Repos) == 0 | ||||
|             div.alert.alert-info | ||||
|                 | Your repository list is empty. | ||||
|  | ||||
|         else | ||||
|             div.row.repo-row | ||||
|                 input.repo-search[type="search"][placeholder="Filter..."] | ||||
|                 div.repo-list | ||||
|                     each $repo in Repos | ||||
|                         div.col-sm-4 | ||||
|                             a.card[href="/"+$repo.FullName] | ||||
|                                 div.card-header | ||||
|                                     if $repo.Avatar != "" | ||||
|                                         img.avatar[src=$repo.Avatar] | ||||
|                                     else | ||||
|                                         img.avatar[src="/static/images/dummy.png"] | ||||
|                                 div.card-block | ||||
|                                     h3.login #{$repo.Name} | ||||
|                                     div.full_name.hidden #{$repo.FullName} | ||||
|  | ||||
| block append scripts | ||||
|     if len(Repos) != 0 | ||||
|         script | ||||
|             $(window).load(function(){ | ||||
|                 $(".repo-list").searcher({ | ||||
|                     itemSelector: ".col-sm-4", | ||||
|                     textSelector:  ".full_name", | ||||
|                     inputSelector: ".repo-search" | ||||
|                 }); | ||||
|             }); | ||||
| @@ -1,76 +0,0 @@ | ||||
| extends base | ||||
|  | ||||
| block append head | ||||
| 	title API · Drone | ||||
|  | ||||
| block header | ||||
| 	ol | ||||
| 		li Documentation | ||||
| 	ul.nav.nav-tabs | ||||
| 		li.nav-item | ||||
| 			a.nav-link[href="../setup/"] Install | ||||
| 		li.nav-item | ||||
| 			a.nav-link[href="../build/"] Builds | ||||
| 		li.nav-item | ||||
| 			a.nav-link[href="../plugin/"] Plugins | ||||
| 		li.nav-item | ||||
| 			a.nav-link[href="../cli/"] CLI | ||||
| 		li.nav-item | ||||
| 			a.nav-link.active[href="#"] API Reference | ||||
|  | ||||
| block content | ||||
| 	div.container-fluid.docs.docs-api | ||||
| 		a[name="top"] | ||||
| 		div.row | ||||
| 			ul.toc | ||||
| 				each $tag in Swagger.Tags | ||||
| 					li | ||||
| 						h2 #{$tag.Name} | ||||
| 						ul | ||||
| 							each $op in $tag.Ops | ||||
| 								li | ||||
| 									a[href="#"+$op.ID][data-method=$op.Method] #{$op.Summary} | ||||
| 		div.row | ||||
| 			each $tag in Swagger.Tags | ||||
| 				each $op in $tag.Ops | ||||
| 					a[name=$op.ID] | ||||
| 					div.operation | ||||
| 						div | ||||
| 							h2[data-method=$op.Method] #{$op.Summary} | ||||
| 							p #{$op.Desc} | ||||
|  | ||||
| 							h3 Request Parameters | ||||
| 							ul.params | ||||
| 								each $param in $op.Params | ||||
| 									li | ||||
| 										h4 | ||||
| 											| #{$param.Name} | ||||
| 											small Required | ||||
| 										p #{$param.Desc} | ||||
|  | ||||
| 							h3 Response Messages | ||||
| 							ul.params | ||||
| 								each $result in $op.Results | ||||
| 									li | ||||
| 										h4 | ||||
| 											| #{$result.Status} | ||||
| 										p #{$result.Desc} | ||||
| 						aside | ||||
| 							h4 Endpoint | ||||
| 							pre #{$op.Method} /api#{$op.Path} | ||||
| 							each $param in $op.Params | ||||
| 								if $param.Example | ||||
| 									h4 Example Request | ||||
| 									if $param.IsObject | ||||
| 										pre #{$param.Example} | ||||
| 							each $res in $op.Results | ||||
| 								if $res.Example | ||||
| 									h4 Example Response | ||||
| 									if $res.IsArray | ||||
| 										pre [#{$res.Example}] | ||||
| 									else if $res.IsObject | ||||
| 										pre #{$res.Example} | ||||
|  | ||||
| block scripts | ||||
| 	script[type="text/javascript"][src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"] | ||||
| 	script[type="text/javascript"][src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/js/bootstrap.min.js"] | ||||
| @@ -1,33 +0,0 @@ | ||||
| extends base | ||||
|  | ||||
| block append head | ||||
|     title Profile | ||||
|  | ||||
| block header | ||||
|     ol | ||||
|         li Account Profile | ||||
|  | ||||
|  | ||||
| block content | ||||
|     div.container.profile | ||||
|         div.row | ||||
|             div.col-md-3 Login | ||||
|             div.col-md-9 #{User.Login} | ||||
|         div.row | ||||
|             div.col-md-3 Email | ||||
|             div.col-md-9 #{User.Email} | ||||
|  | ||||
|         div.row | ||||
|             div.col-md-3 Token | ||||
|             div.col-md-9 | ||||
|                 button.btn.btn-info#showToken show token | ||||
|                 pre.result  | ||||
|  | ||||
| block append scripts | ||||
|     script | ||||
|         $( "#showToken" ).click(function() { | ||||
|             $( "#showToken" ).hide(); | ||||
|             $.post( "/api/user/token", function( data ) { | ||||
|                  $( ".result" ).text( data ); | ||||
|             }); | ||||
|         }); | ||||
| @@ -1,40 +0,0 @@ | ||||
| extends base | ||||
|  | ||||
| block append head | ||||
|     title Users | ||||
|  | ||||
| block header | ||||
|     ol | ||||
|         li User Management | ||||
|         li | ||||
|             button.btn.btn-info[data-toggle="modal"][data-target=".modal-user"] | ||||
|                 | add user | ||||
|  | ||||
| block content | ||||
|     div.container | ||||
|         div.alert.alert-danger.hidden | ||||
|         div.row.user-row | ||||
|             each $user in Users | ||||
|                 div.col-sm-4 | ||||
|                     div.card[data-id=$user.Login][data-admin=$user.Admin] | ||||
|                         div.card-header | ||||
|                             img.avatar[src=$user.Avatar] | ||||
|                         div.card-block | ||||
|                             h3.login #{$user.Login} | ||||
|                             p.email.card-text #{$user.Email} | ||||
|                             div.btn-group | ||||
|                                 button.btn.btn-info Toggle Admin | ||||
|                                 button.btn.btn-danger Delete | ||||
|  | ||||
|  | ||||
|         div.modal.modal-user[role="dialog"] | ||||
|             div.modal-dialog | ||||
|                 div.modal-content | ||||
|                     form | ||||
|                         label[for="login"] Username | ||||
|                         input.form-control[type="text"][placeholder="ie octocat"]#login | ||||
|                         button.btn.btn-info[type="button"] add user | ||||
|  | ||||
| block append scripts | ||||
|     script | ||||
|         var view = new UserViewModel(); | ||||
| @@ -1,59 +0,0 @@ | ||||
| package template | ||||
|  | ||||
| //go:generate sh -c "amberc amber/400.amber            > amber_gen/400.html" | ||||
| //go:generate sh -c "amberc amber/401.amber            > amber_gen/401.html" | ||||
| //go:generate sh -c "amberc amber/403.amber            > amber_gen/403.html" | ||||
| //go:generate sh -c "amberc amber/404.amber            > amber_gen/404.html" | ||||
| //go:generate sh -c "amberc amber/500.amber            > amber_gen/500.html" | ||||
| //go:generate sh -c "amberc amber/build.amber          > amber_gen/build.html" | ||||
| //go:generate sh -c "amberc amber/login.amber          > amber_gen/login.html" | ||||
| //go:generate sh -c "amberc amber/login_form.amber     > amber_gen/login_form.html" | ||||
| //go:generate sh -c "amberc amber/repos.amber          > amber_gen/repos.html" | ||||
| //go:generate sh -c "amberc amber/repo.amber           > amber_gen/repo.html" | ||||
| //go:generate sh -c "amberc amber/repo_badge.amber     > amber_gen/repo_badge.html" | ||||
| //go:generate sh -c "amberc amber/repo_activate.amber  > amber_gen/repo_activate.html" | ||||
| //go:generate sh -c "amberc amber/repo_config.amber    > amber_gen/repo_config.html" | ||||
| //go:generate sh -c "amberc amber/repo_secret.amber    > amber_gen/repo_secret.html" | ||||
| //go:generate sh -c "amberc amber/users.amber          > amber_gen/users.html" | ||||
| //go:generate sh -c "amberc amber/user.amber           > amber_gen/user.html" | ||||
| //go:generate sh -c "amberc amber/nodes.amber          > amber_gen/nodes.html" | ||||
| //go:generate sh -c "amberc amber/index.amber          > amber_gen/index.html" | ||||
|  | ||||
| //go:generate go-bindata -pkg template -o template_gen.go amber_gen/ | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"html/template" | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	"github.com/eknkc/amber" | ||||
| ) | ||||
|  | ||||
| func Load() *template.Template { | ||||
| 	amber.FuncMap["json"] = marshal | ||||
|  | ||||
| 	dir, _ := AssetDir("amber_gen") | ||||
| 	tmpl := template.New("_") | ||||
| 	tmpl.Funcs(amber.FuncMap) | ||||
|  | ||||
| 	for _, name := range dir { | ||||
| 		if filepath.Ext(name) != ".html" { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		path := filepath.Join("amber_gen", name) | ||||
| 		src := MustAsset(path) | ||||
| 		tmpl = template.Must( | ||||
| 			tmpl.New(name).Parse(string(src)), | ||||
| 		) | ||||
| 	} | ||||
|  | ||||
| 	return tmpl | ||||
| } | ||||
|  | ||||
| // marshal is a helper function to render data as JSON | ||||
| // inside the template. | ||||
| func marshal(v interface{}) template.JS { | ||||
| 	a, _ := json.Marshal(v) | ||||
| 	return template.JS(a) | ||||
| } | ||||