From dd9d2560560a474926f10da0ac5b992bb32ef204 Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Wed, 1 Apr 2015 22:37:08 -0400 Subject: [PATCH 001/238] refactor to minimal shipyard support Signed-off-by: Evan Hazlett --- auth.go => auth/auth.go | 2 +- auth_test.go => auth/auth_test.go | 2 +- cli/.docker/autocomplete | 14 - cli/.docker/bashrc | 2 - cli/.gitignore | 1 - cli/Dockerfile | 8 - cli/Godeps/Godeps.json | 43 -- cli/Godeps/Readme | 5 - cli/Makefile | 25 - cli/accounts.go | 110 --- cli/cli.go | 71 -- cli/config.go | 14 - cli/containers.go | 71 -- cli/destroy.go | 43 -- cli/engine.go | 233 ------- cli/events.go | 49 -- cli/extensions.go | 139 ---- cli/info.go | 47 -- cli/login.go | 107 --- cli/logs.go | 61 -- cli/readme.md | 6 - cli/restart.go | 43 -- cli/run.go | 157 ----- cli/scale.go | 47 -- cli/servicekeys.go | 90 --- cli/stop.go | 43 -- cli/utils.go | 131 ---- cli/utils_test.go | 33 - cli/webhookkeys.go | 86 --- controller/Dockerfile | 4 +- controller/commands/server.go | 474 +++++++++++++ controller/fig.yml | 17 - controller/main.go | 901 +++---------------------- controller/manager/handler.go | 32 +- controller/manager/manager.go | 663 ++---------------- controller/middleware/access/access.go | 4 +- event.go | 13 +- fig.yml | 15 +- utils/utils.go | 95 +++ version.go | 2 +- 40 files changed, 756 insertions(+), 3147 deletions(-) rename auth.go => auth/auth.go (98%) rename auth_test.go => auth/auth_test.go (97%) delete mode 100644 cli/.docker/autocomplete delete mode 100644 cli/.docker/bashrc delete mode 100644 cli/.gitignore delete mode 100644 cli/Dockerfile delete mode 100644 cli/Godeps/Godeps.json delete mode 100644 cli/Godeps/Readme delete mode 100644 cli/Makefile delete mode 100644 cli/accounts.go delete mode 100644 cli/cli.go delete mode 100644 cli/config.go delete mode 100644 cli/containers.go delete mode 100644 cli/destroy.go delete mode 100644 cli/engine.go delete mode 100644 cli/events.go delete mode 100644 cli/extensions.go delete mode 100644 cli/info.go delete mode 100644 cli/login.go delete mode 100644 cli/logs.go delete mode 100644 cli/readme.md delete mode 100644 cli/restart.go delete mode 100644 cli/run.go delete mode 100644 cli/scale.go delete mode 100644 cli/servicekeys.go delete mode 100644 cli/stop.go delete mode 100644 cli/utils.go delete mode 100644 cli/utils_test.go delete mode 100644 cli/webhookkeys.go create mode 100644 controller/commands/server.go delete mode 100644 controller/fig.yml create mode 100644 utils/utils.go diff --git a/auth.go b/auth/auth.go similarity index 98% rename from auth.go rename to auth/auth.go index 199b3cf94..156356246 100644 --- a/auth.go +++ b/auth/auth.go @@ -1,4 +1,4 @@ -package shipyard +package auth import ( "errors" diff --git a/auth_test.go b/auth/auth_test.go similarity index 97% rename from auth_test.go rename to auth/auth_test.go index 237eb423d..91e9694f2 100644 --- a/auth_test.go +++ b/auth/auth_test.go @@ -1,4 +1,4 @@ -package shipyard +package auth import ( "testing" diff --git a/cli/.docker/autocomplete b/cli/.docker/autocomplete deleted file mode 100644 index 1732b5798..000000000 --- a/cli/.docker/autocomplete +++ /dev/null @@ -1,14 +0,0 @@ -#! /bin/bash - -_cli_bash_autocomplete() { - local cur prev opts base - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" - opts=$( ${COMP_WORDS[@]:0:COMP_CWORD} --generate-bash-completion ) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - -} - -complete -F _cli_bash_autocomplete $PROG diff --git a/cli/.docker/bashrc b/cli/.docker/bashrc deleted file mode 100644 index 09f3dd09d..000000000 --- a/cli/.docker/bashrc +++ /dev/null @@ -1,2 +0,0 @@ -export PS1='\e[0;34mshipyard cli${NAME:+ ($NAME)}> \e[0;m' -source $HOME/.autocomplete diff --git a/cli/.gitignore b/cli/.gitignore deleted file mode 100644 index b3cb8d53a..000000000 --- a/cli/.gitignore +++ /dev/null @@ -1 +0,0 @@ -Godeps/_workspace diff --git a/cli/Dockerfile b/cli/Dockerfile deleted file mode 100644 index ee5678c8c..000000000 --- a/cli/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM debian:jessie -RUN apt-get update && apt-get install -y ca-certificates less -ADD cli /bin/shipyard -ENV PROG shipyard -ADD .docker/autocomplete /root/.autocomplete -ADD .docker/bashrc /root/.bashrc -RUN /bin/shipyard --generate-bash-completion > /etc/bash_completion.d/shipyard -CMD ["/bin/bash"] diff --git a/cli/Godeps/Godeps.json b/cli/Godeps/Godeps.json deleted file mode 100644 index 00d17cc42..000000000 --- a/cli/Godeps/Godeps.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "ImportPath": "github.com/shipyard/shipyard/cli", - "GoVersion": "go1.3.3", - "Deps": [ - { - "ImportPath": "code.google.com/p/go.crypto/bcrypt", - "Comment": "null-233", - "Rev": "8fec09c61d5d66f460d227fd1df3473d7e015bc6" - }, - { - "ImportPath": "code.google.com/p/go.crypto/ssh/terminal", - "Comment": "null-233", - "Rev": "8fec09c61d5d66f460d227fd1df3473d7e015bc6" - }, - { - "ImportPath": "github.com/Sirupsen/logrus", - "Comment": "v0.6.0-5-gf92b795", - "Rev": "f92b7950b372b1db80bd3527e4d40e42555fe6c2" - }, - { - "ImportPath": "github.com/citadel/citadel", - "Comment": "beta4-75-gace93dd", - "Rev": "ace93dd4591186c7d3281b45e37da7da2c7a2138" - }, - { - "ImportPath": "github.com/codegangsta/cli", - "Comment": "1.2.0-26-gf7ebb76", - "Rev": "f7ebb761e83e21225d1d8954fde853bf8edd46c4" - }, - { - "ImportPath": "github.com/howeyc/gopass", - "Rev": "438f04ab2449de187e96e9b2dbaead5dde5f78ab" - }, - { - "ImportPath": "github.com/samalba/dockerclient", - "Rev": "8ee44c0342d57b7daece793afb6c2bdd13220e8f" - }, - { - "ImportPath": "golang.org/x/crypto/blowfish", - "Rev": "1fbbd62cfec66bd39d91e97749579579d4d3037e" - } - ] -} diff --git a/cli/Godeps/Readme b/cli/Godeps/Readme deleted file mode 100644 index 4cdaa53d5..000000000 --- a/cli/Godeps/Readme +++ /dev/null @@ -1,5 +0,0 @@ -This directory tree is generated automatically by godep. - -Please do not edit. - -See https://github.com/tools/godep for more information. diff --git a/cli/Makefile b/cli/Makefile deleted file mode 100644 index 3739c8f45..000000000 --- a/cli/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -TAG=${TAG:-latest} - -all: deps build - -deps: - @godep restore - -clean: - @rm -rf Godeps/_workspace controller - -build: - @godep go build . - -image: build - @docker build -t shipyard/shipyard-cli:$(TAG) . - -release: image - @docker push shipyard/shipyard-cli:$(TAG) - -test: clean - @godep restore - @export GOPATH=$GOPATH:$(pwd)/Godeps/_workspace - @godep go test -v ./... - -.PHONY: all deps build clean image test release diff --git a/cli/accounts.go b/cli/accounts.go deleted file mode 100644 index f5d0909a9..000000000 --- a/cli/accounts.go +++ /dev/null @@ -1,110 +0,0 @@ -package main - -import ( - "fmt" - "os" - "text/tabwriter" - - "github.com/codegangsta/cli" - "github.com/shipyard/shipyard" - "github.com/shipyard/shipyard/client" -) - -var accountsCommand = cli.Command{ - Name: "accounts", - Usage: "show accounts", - Action: accountsAction, -} - -func accountsAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - accounts, err := m.Accounts() - if err != nil { - logger.Fatalf("error getting accounts: %s", err) - } - if len(accounts) == 0 { - return - } - w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) - fmt.Fprintln(w, "Username\tRole") - for _, u := range accounts { - fmt.Fprintf(w, "%s\t%s\n", u.Username, u.Role.Name) - } - w.Flush() -} - -var addAccountCommand = cli.Command{ - Name: "add-account", - Usage: "add account", - Action: addAccountAction, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "username, u", - Usage: "account username", - }, - cli.StringFlag{ - Name: "password, p", - Usage: "account password", - }, - cli.StringFlag{ - Name: "role, r", - Usage: "account role (admin, user)", - }, - }, -} - -func addAccountAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - user := c.String("username") - pass := c.String("password") - if user == "" || pass == "" { - logger.Fatalf("you must specify a username and password") - } - role, err := m.Role(c.String("role")) - if err != nil { - logger.Fatal(err) - } - account := &shipyard.Account{ - Username: user, - Password: pass, - Role: role, - } - if err := m.AddAccount(account); err != nil { - logger.Fatalf("error adding account: %s", err) - } -} - -var deleteAccountCommand = cli.Command{ - Name: "delete-account", - Usage: "delete account", - Description: "delete-account []", - Action: deleteAccountAction, -} - -func deleteAccountAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - accounts := c.Args() - if len(accounts) == 0 { - return - } - for _, acct := range accounts { - account := &shipyard.Account{ - Username: acct, - } - if err := m.DeleteAccount(account); err != nil { - logger.Fatalf("error deleting account: %s", err) - } - } -} diff --git a/cli/cli.go b/cli/cli.go deleted file mode 100644 index 1bc6676cb..000000000 --- a/cli/cli.go +++ /dev/null @@ -1,71 +0,0 @@ -package main - -import ( - "os" - - "github.com/Sirupsen/logrus" - "github.com/codegangsta/cli" - "github.com/shipyard/shipyard" -) - -var ( - shipyardHost string - logger = logrus.New() -) - -func main() { - cfg, err := loadConfig(nil) - if err != nil { - if err != ErrConfigDoesNotExist { - logger.Fatal(err) - } - } - if cfg != nil { - sUrl := os.Getenv("SHIPYARD_URL") - if sUrl == "" { - cfg.Url = sUrl - } - } - app := cli.NewApp() - app.Name = "shipyard" - app.Usage = "manage a shipyard cluster" - app.Version = shipyard.VERSION - app.EnableBashCompletion = true - app.Flags = []cli.Flag{ - cli.BoolFlag{ - Name: "allow-insecure", - Usage: "allow insecure certificates if using TLS", - }, - } - app.Commands = []cli.Command{ - loginCommand, - changePasswordCommand, - accountsCommand, - addAccountCommand, - deleteAccountCommand, - containersCommand, - containerInspectCommand, - runCommand, - stopCommand, - restartCommand, - scaleCommand, - logsCommand, - destroyCommand, - engineListCommand, - engineAddCommand, - engineRemoveCommand, - engineInspectCommand, - serviceKeysListCommand, - serviceKeyCreateCommand, - serviceKeyRemoveCommand, - extensionsCommand, - addExtensionCommand, - removeExtensionCommand, - webhookKeysListCommand, - webhookKeyCreateCommand, - webhookKeyRemoveCommand, - infoCommand, - eventsCommand, - } - app.Run(os.Args) -} diff --git a/cli/config.go b/cli/config.go deleted file mode 100644 index 2fea3e134..000000000 --- a/cli/config.go +++ /dev/null @@ -1,14 +0,0 @@ -package main - -import ( - "errors" -) - -const ( - CONFIG_PATH = ".shipyardrc" // this is joined to the user's home dir -) - -var ( - ErrConfigDoesNotExist = errors.New("config does not exist; try logging in") - ErrInvalidConfig = errors.New("invalid config") -) diff --git a/cli/containers.go b/cli/containers.go deleted file mode 100644 index ba389d196..000000000 --- a/cli/containers.go +++ /dev/null @@ -1,71 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "os" - "strings" - "text/tabwriter" - - "github.com/codegangsta/cli" - "github.com/shipyard/shipyard/client" -) - -var containersCommand = cli.Command{ - Name: "containers", - Usage: "list containers", - Action: containersAction, -} - -func containersAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - containers, err := m.Containers() - if err != nil { - logger.Fatalf("error getting containers: %s", err) - } - if len(containers) == 0 { - return - } - w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) - fmt.Fprintln(w, "ID\tImage\tName\tHost\tState\tPorts") - for _, c := range containers { - portDefs := []string{} - for _, port := range c.Ports { - p := fmt.Sprintf("%s/:%s:%d:%d", port.Proto, port.HostIp, port.Port, port.ContainerPort) - portDefs = append(portDefs, p) - } - ports := strings.Join(portDefs, ", ") - name := c.Name[1:] - fmt.Fprintf(w, fmt.Sprintf("%s\t%s\t%s\t%s\t%v\t%s\n", c.ID[:12], c.Image.Name, name, c.Engine.ID, c.State, ports)) - } - w.Flush() -} - -var containerInspectCommand = cli.Command{ - Name: "inspect", - Usage: "inspect container", - Action: containerInspectAction, -} - -func containerInspectAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - args := c.Args() - if len(args) == 0 { - logger.Fatalf("you must specify a container id") - } - containerId := args[0] - container, err := m.GetContainer(containerId) - if err != nil { - logger.Fatalf("error getting container info: %s", err) - } - b, err := json.MarshalIndent(container, "", " ") - fmt.Println(string(b)) -} diff --git a/cli/destroy.go b/cli/destroy.go deleted file mode 100644 index e9527993c..000000000 --- a/cli/destroy.go +++ /dev/null @@ -1,43 +0,0 @@ -package main - -import ( - "fmt" - "strings" - - "github.com/codegangsta/cli" - "github.com/shipyard/shipyard/client" -) - -var destroyCommand = cli.Command{ - Name: "destroy", - Usage: "destroy a container", - Description: "destroy []", - Action: destroyAction, -} - -func destroyAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - containers, err := m.Containers() - if err != nil { - logger.Fatalf("error getting container info: %s", err) - } - ids := c.Args() - if len(ids) == 0 { - logger.Fatalf("you must specify at least one id") - } - for _, cnt := range containers { - // this can probably be more efficient - for _, i := range ids { - if strings.HasPrefix(cnt.ID, i) { - if err := m.Destroy(cnt); err != nil { - logger.Fatalf("error destroying container: %s\n", err) - } - fmt.Printf("destroyed %s\n", cnt.ID[:12]) - } - } - } -} diff --git a/cli/engine.go b/cli/engine.go deleted file mode 100644 index 9e6d35cd1..000000000 --- a/cli/engine.go +++ /dev/null @@ -1,233 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "strconv" - "strings" - "text/tabwriter" - - "github.com/citadel/citadel" - "github.com/codegangsta/cli" - "github.com/shipyard/shipyard" - "github.com/shipyard/shipyard/client" -) - -var engineListCommand = cli.Command{ - Name: "engines", - Usage: "list engines", - Action: engineListAction, -} - -func engineListAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - engines, err := m.Engines() - if err != nil { - logger.Fatalf("error getting engines: %s", err) - return - } - if len(engines) == 0 { - return - } - w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) - fmt.Fprintln(w, "ID\tName\tCpus\tMemory\tHost\tLabels\tHealth\tResponse Time (ms)\tDocker Version") - for _, e := range engines { - labels := strings.Join(e.Engine.Labels, ",") - responseTime := responseTimeToString(e.Health.ResponseTime) - fmt.Fprintf(w, "%s\t%s\t%.2f\t%.2f\t%s\t%s\t%s\t%s\t%s\n", e.ID, e.Engine.ID, e.Engine.Cpus, e.Engine.Memory, e.Engine.Addr, labels, e.Health.Status, responseTime, e.DockerVersion) - } - w.Flush() -} - -func responseTimeToString(responseTime int64) (rt string) { - if responseTime == 0 { - rt = "-" - } else { - timeInMillis := float64(responseTime) / 1000000. - rt = strconv.FormatFloat(timeInMillis, 'f', 1, 64) - } - return -} - -var engineAddCommand = cli.Command{ - Name: "add-engine", - Usage: "add shipyard engine", - Action: engineAddAction, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "id", - Value: "", - Usage: "engine id", - }, - cli.StringFlag{ - Name: "addr", - Value: "http://127.0.0.1:2375", - Usage: "engine address", - }, - cli.StringFlag{ - Name: "cpus", - Value: "1.0", - Usage: "engine cpus", - }, - cli.StringFlag{ - Name: "memory", - Value: "1024", - Usage: "engine memory", - }, - cli.StringSliceFlag{ - Name: "label", - Value: &cli.StringSlice{}, - Usage: "engine labels", - }, - cli.StringFlag{ - Name: "ssl-cert", - Value: "", - Usage: "path to ssl certificate", - }, - cli.StringFlag{ - Name: "ssl-key", - Value: "", - Usage: "path to ssl key", - }, - cli.StringFlag{ - Name: "ca-cert", - Value: "", - Usage: "path to ca certificate", - }, - }, -} - -func engineAddAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - id := c.String("id") - addr := c.String("addr") - if id == "" || addr == "" { - logger.Fatalf("you must specify an id and address") - } - engine := &citadel.Engine{ - ID: id, - Addr: addr, - Cpus: c.Float64("cpus"), - Memory: c.Float64("memory"), - Labels: c.StringSlice("label"), - } - sslCertPath := c.String("ssl-cert") - sslKeyPath := c.String("ssl-key") - caCertPath := c.String("ca-cert") - var ( - sslCertData = []byte{} - sslKeyData = []byte{} - caCertData = []byte{} - sslErr error - ) - if sslCertPath != "" && sslKeyPath != "" && caCertPath != "" { - sslCert, err := os.Open(sslCertPath) - if err != nil { - logger.Fatalf("unable to open ssl certificate: %s", err) - } - sslKey, err := os.Open(sslKeyPath) - if err != nil { - logger.Fatalf("unable to open ssl key: %s", err) - } - caCert, err := os.Open(caCertPath) - if err != nil { - logger.Fatalf("unable to open ca certificate: %s", err) - } - if _, err := sslCert.Stat(); err != nil { - logger.Fatalf("ssl cert is not accessible: %s", err) - } - if _, err := sslKey.Stat(); err != nil { - logger.Fatalf("ssl key is not accessible: %s", err) - } - if _, err := caCert.Stat(); err != nil { - logger.Fatalf("ca cert is not accessible: %s", err) - } - sslCertData, sslErr = ioutil.ReadAll(sslCert) - if sslErr != nil { - logger.Fatalf("unable to read ssl certificate: %s", sslErr) - } - sslKeyData, sslErr = ioutil.ReadAll(sslKey) - if sslErr != nil { - logger.Fatalf("unable to read ssl key: %s", sslErr) - } - caCertData, sslErr = ioutil.ReadAll(caCert) - if sslErr != nil { - logger.Fatalf("unable to read ca certificate: %s", sslErr) - } - } - shipyardEngine := &shipyard.Engine{ - SSLCertificate: string(sslCertData), - SSLKey: string(sslKeyData), - CACertificate: string(caCertData), - Engine: engine, - } - if err := m.AddEngine(shipyardEngine); err != nil { - logger.Fatalf("error adding engine: %s", err) - } -} - -var engineRemoveCommand = cli.Command{ - Name: "remove-engine", - Usage: "removes an engine", - Description: "remove-engine []", - Action: engineRemoveAction, -} - -func engineRemoveAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - engines, err := m.Engines() - if err != nil { - logger.Fatalf("error removing engine: %s\n", err) - } - removeEngines := c.Args() - for _, eng := range engines { - // this can probably be more efficient - for _, i := range removeEngines { - if eng.ID == i { - if err := m.RemoveEngine(eng); err != nil { - logger.Fatalf("error removing engine: %s", err) - } - fmt.Printf("removed %s\n", eng.Engine.ID) - } - } - } -} - -var engineInspectCommand = cli.Command{ - Name: "inspect-engine", - Usage: "inspect an engine", - Description: "inspect-engine ", - Action: engineInspectAction, -} - -func engineInspectAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - if len(c.Args()) == 0 { - logger.Fatal("you must specify an id") - } - id := c.Args()[0] - eng, err := m.GetEngine(id) - if err != nil { - logger.Fatalf("error inspecting engine: %s", err) - } - b, err := json.MarshalIndent(eng, "", " ") - fmt.Println(string(b)) -} diff --git a/cli/events.go b/cli/events.go deleted file mode 100644 index 5753cf677..000000000 --- a/cli/events.go +++ /dev/null @@ -1,49 +0,0 @@ -package main - -import ( - "fmt" - "os" - "strings" - "text/tabwriter" - "time" - - "github.com/codegangsta/cli" - "github.com/shipyard/shipyard/client" -) - -var eventsCommand = cli.Command{ - Name: "events", - Usage: "show cluster events", - Action: eventsAction, -} - -func eventsAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - events, err := m.Events() - if err != nil { - logger.Fatalf("error getting events: %s", err) - } - if len(events) == 0 { - return - } - w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) - fmt.Fprintln(w, "Time\tMessage\tEngine\tType\tTags") - for _, e := range events { - tags := strings.Join(e.Tags, ",") - message := e.Message - engine := "" - if e.Container.ID != "" { - cntId := e.Container.ID[:12] - message = fmt.Sprintf("container:%s %s", cntId, e.Message) - } - if e.Engine.ID != "" { - engine = e.Engine.ID - } - fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", e.Time.Format(time.RubyDate), message, engine, e.Type, tags) - } - w.Flush() -} diff --git a/cli/extensions.go b/cli/extensions.go deleted file mode 100644 index 47d0e172f..000000000 --- a/cli/extensions.go +++ /dev/null @@ -1,139 +0,0 @@ -package main - -import ( - "bufio" - "encoding/json" - "fmt" - "net/http" - "os" - "text/tabwriter" - - "github.com/codegangsta/cli" - "github.com/shipyard/shipyard" - "github.com/shipyard/shipyard/client" -) - -var extensionsCommand = cli.Command{ - Name: "extensions", - Usage: "show extensions", - Action: extensionsAction, -} - -func extensionsAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - exts, err := m.Extensions() - if err != nil { - logger.Fatalf("error getting extensions: %s", err) - } - if len(exts) == 0 { - return - } - w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) - fmt.Fprintln(w, "ID\tName\tVersion\tAuthor\tImage\tUrl\tDescription") - for _, e := range exts { - fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", e.ID, e.Name, e.Version, e.Author, e.Image, e.Url, e.Description) - } - w.Flush() -} - -var addExtensionCommand = cli.Command{ - Name: "add-extension", - Usage: "add extension", - Action: addExtensionAction, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "url", - Usage: "extension config url", - }, - cli.StringSliceFlag{ - Name: "env", - Usage: "environment variables (key=value pairs)", - Value: &cli.StringSlice{}, - }, - cli.StringSliceFlag{ - Name: "arg", - Usage: "arguments", - Value: &cli.StringSlice{}, - }, - }, -} - -func addExtensionAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - extUrl := c.String("url") - if extUrl == "" { - logger.Fatalf("you must specify an extension config url") - } - env := parseEnvironmentVariables(c.StringSlice("env")) - args := c.StringSlice("arg") - resp, err := http.Get(extUrl) - if err != nil { - logger.Fatalf("unable to get extension config: %s", err) - } - var ext *shipyard.Extension - if err := json.NewDecoder(resp.Body).Decode(&ext); err != nil { - logger.Fatalf("error parsing extension config: %s", err, err) - } - fmt.Printf("configuring %s (%s for more info)\n", ext.Name, ext.Url) - // check for configuration - for _, pe := range ext.Config.PromptEnvironment { - fmt.Printf("enter value for container environment variable %s: ", pe) - b := bufio.NewReader(os.Stdin) - r, _, err := b.ReadLine() - if err != nil { - logger.Fatalf("unable to parse input: %s", err) - } - env[pe] = string(r) - } - for _, pa := range ext.Config.PromptArgs { - fmt.Printf("enter value for container argument %s: ", pa) - b := bufio.NewReader(os.Stdin) - r, _, err := b.ReadLine() - if err != nil { - logger.Fatalf("unable to parse input: %s", err) - } - arg := string(r) - if pa != "" { - arg = fmt.Sprintf("%s=%s", pa, r) - } - args = append(args, arg) - } - ext.Config.Environment = env - ext.Config.Args = args - if err := m.AddExtension(ext); err != nil { - logger.Fatalf("error adding extension: %s", err) - } - fmt.Printf("added extension name=%s version=%s\n", ext.Name, ext.Version) -} - -var removeExtensionCommand = cli.Command{ - Name: "remove-extension", - Usage: "remove an extension", - Description: "remove-extension [id]", - Action: removeExtensionAction, -} - -func removeExtensionAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - extIds := c.Args() - if len(extIds) == 0 { - return - } - for _, id := range extIds { - if err := m.RemoveExtension(id); err != nil { - logger.Fatalf("error removing extension: %s", err) - } - } -} diff --git a/cli/info.go b/cli/info.go deleted file mode 100644 index 661ccbadd..000000000 --- a/cli/info.go +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - "fmt" - "os" - "text/tabwriter" - - "github.com/codegangsta/cli" - "github.com/shipyard/shipyard/client" -) - -var infoCommand = cli.Command{ - Name: "info", - Usage: "show cluster info", - Action: infoAction, -} - -func infoAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - info, err := m.Info() - if err != nil { - logger.Fatalf("error getting cluster info: %s", err) - } - cpuPercentage := 0.0 - memPercentage := 0.0 - if info.ReservedCpus > 0.0 && info.Cpus > 0.0 { - cpuPercentage = (info.ReservedCpus / info.Cpus) * 100 - } - if info.ReservedMemory > 0.0 && info.Memory > 0.0 { - memPercentage = (info.ReservedMemory / info.Memory) * 100 - } - - w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) - fmt.Fprintf(w, "Controller Version: %s\n", info.Version) - fmt.Fprintf(w, "Cpus: %.2f\n", info.Cpus) - fmt.Fprintf(w, "Memory: %.2f MB\n", info.Memory) - fmt.Fprintf(w, "Containers: %d\n", info.ContainerCount) - fmt.Fprintf(w, "Images: %d\n", info.ImageCount) - fmt.Fprintf(w, "Engines: %d\n", info.EngineCount) - fmt.Fprintf(w, "Reserved Cpus: %.2f%% (%.2f)\n", cpuPercentage, info.ReservedCpus) - fmt.Fprintf(w, "Reserved Memory: %.2f%% (%.2f MB)\n", memPercentage, info.ReservedMemory) - w.Flush() -} diff --git a/cli/login.go b/cli/login.go deleted file mode 100644 index 11157d55f..000000000 --- a/cli/login.go +++ /dev/null @@ -1,107 +0,0 @@ -package main - -import ( - "bufio" - "encoding/json" - "fmt" - "os" - "os/user" - "path/filepath" - "strings" - - "github.com/codegangsta/cli" - "github.com/howeyc/gopass" - "github.com/shipyard/shipyard/client" -) - -var loginCommand = cli.Command{ - Name: "login", - Usage: "login to a shipyard cluster", - Action: loginAction, -} - -func saveConfig(cfg *client.ShipyardConfig) error { - usr, err := user.Current() - if err != nil { - return err - } - path := filepath.Join(usr.HomeDir, CONFIG_PATH) - f, err := os.OpenFile(path, os.O_WRONLY, 0600) - if err != nil { - if os.IsNotExist(err) { - fc, fErr := os.Create(path) - if fErr != nil { - return err - } - fc.Chmod(0600) - f = fc - } else { - return err - } - } - defer f.Close() - if err := json.NewEncoder(f).Encode(cfg); err != nil { - return err - } - return nil -} - -func loginAction(c *cli.Context) { - reader := bufio.NewReader(os.Stdin) - fmt.Printf("URL: ") - ur, err := reader.ReadString('\n') - if err != nil { - logger.Fatal(err) - } - fmt.Printf("Username: ") - u, err := reader.ReadString('\n') - if err != nil { - logger.Fatal(err) - } - fmt.Printf("Password: ") - p := gopass.GetPasswd() - sUrl := strings.TrimSpace(string(ur[:])) - username := strings.TrimSpace(string(u[:])) - pass := strings.TrimSpace(string(p[:])) - - cfg := &client.ShipyardConfig{ - Url: sUrl, - Username: username, - AllowInsecure: c.GlobalBool("allow-insecure"), - } - m := client.NewManager(cfg) - token, err := m.Login(username, pass) - if err != nil { - logger.Fatal(err) - } - cfg.Token = token.Token - if err := saveConfig(cfg); err != nil { - logger.Fatal(err) - } -} - -var changePasswordCommand = cli.Command{ - Name: "change-password", - Usage: "update your password", - Action: changePasswordAction, -} - -func changePasswordAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - fmt.Printf("Password: ") - p1 := gopass.GetPasswd() - fmt.Printf("Confirm: ") - p2 := gopass.GetPasswd() - pass := strings.TrimSpace(string(p1[:])) - pass_confirm := strings.TrimSpace(string(p2[:])) - if pass != pass_confirm { - logger.Fatal("passwords do not match") - } - if err := m.ChangePassword(pass); err != nil { - logger.Fatal(err) - } -} diff --git a/cli/logs.go b/cli/logs.go deleted file mode 100644 index 7b0ea5e3a..000000000 --- a/cli/logs.go +++ /dev/null @@ -1,61 +0,0 @@ -package main - -import ( - "bytes" - "io" - "os" - - "github.com/codegangsta/cli" - "github.com/shipyard/shipyard/client" -) - -var logsCommand = cli.Command{ - Name: "logs", - Usage: "show container logs", - Description: "logs [--stdout] [--stderr]", - Action: logsAction, - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "stdout", - Usage: "show stdout", - }, - cli.BoolFlag{ - Name: "stderr", - Usage: "show stderr", - }, - }, -} - -func logsAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - - m := client.NewManager(cfg) - ids := c.Args() - if len(ids) == 0 { - logger.Fatal("you must specify an id") - } - id := ids[0] - - container, err := m.Container(id) - stdout := c.Bool("stdout") - stderr := c.Bool("stderr") - - // if output not specified, use both - if stdout == false && stderr == false { - stdout = true - stderr = true - } - - data, err := m.Logs(container, stdout, stderr) - if err != nil { - logger.Fatalf("error reading logs: %s", err) - } - - buf := new(bytes.Buffer) - buf.ReadFrom(data) - - io.Copy(os.Stdout, buf) -} diff --git a/cli/readme.md b/cli/readme.md deleted file mode 100644 index 9a343cc0a..000000000 --- a/cli/readme.md +++ /dev/null @@ -1,6 +0,0 @@ -# Shipyard CLI -Command line interface to manage a Shipyard cluster - -# Usage - -* `docker run -it --rm shipyard/shipyard-cli -h` diff --git a/cli/restart.go b/cli/restart.go deleted file mode 100644 index ac9eae16f..000000000 --- a/cli/restart.go +++ /dev/null @@ -1,43 +0,0 @@ -package main - -import ( - "fmt" - "strings" - - "github.com/codegangsta/cli" - "github.com/shipyard/shipyard/client" -) - -var restartCommand = cli.Command{ - Name: "restart", - Usage: "restart a container", - Description: "restart []", - Action: restartAction, -} - -func restartAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - containers, err := m.Containers() - if err != nil { - logger.Fatalf("error getting container info: %s", err) - } - ids := c.Args() - if len(ids) == 0 { - logger.Fatalf("you must specify at least one id") - } - for _, cnt := range containers { - // this can probably be more efficient - for _, i := range ids { - if strings.HasPrefix(cnt.ID, i) { - if err := m.Restart(cnt); err != nil { - logger.Fatalf("error restarting container: %s\n", err) - } - fmt.Printf("restarted %s\n", cnt.ID[:12]) - } - } - } -} diff --git a/cli/run.go b/cli/run.go deleted file mode 100644 index 385fb1034..000000000 --- a/cli/run.go +++ /dev/null @@ -1,157 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/citadel/citadel" - "github.com/codegangsta/cli" - "github.com/shipyard/shipyard/client" -) - -var runCommand = cli.Command{ - Name: "run", - Usage: "run a container", - Action: runAction, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "name", - Usage: "image name", - }, - cli.StringFlag{ - Name: "container-name", - Usage: "container name", - }, - cli.StringFlag{ - Name: "cpus", - Value: "0.1", - Usage: "cpu shares", - }, - cli.StringFlag{ - Name: "cpuset", - Value: "", - Usage: "cpuset to run on", - }, - cli.StringFlag{ - Name: "memory", - Value: "256", - Usage: "memory (in MB)", - }, - cli.StringFlag{ - Name: "type", - Value: "service", - Usage: "type (service, batch, etc.)", - }, - cli.StringFlag{ - Name: "hostname", - Value: "", - Usage: "container hostname", - }, - cli.StringFlag{ - Name: "domain", - Value: "", - Usage: "container domain name", - }, - cli.StringFlag{ - Name: "network", - Value: "bridge", - Usage: "container network mode", - }, - cli.StringSliceFlag{ - Name: "env", - Usage: "environment variables (key=value pairs)", - Value: &cli.StringSlice{}, - }, - cli.StringSliceFlag{ - Name: "link", - Usage: "container link (container:name pair)", - Value: &cli.StringSlice{}, - }, - cli.StringSliceFlag{ - Name: "arg", - Usage: "run arguments", - Value: &cli.StringSlice{}, - }, - cli.StringSliceFlag{ - Name: "vol", - Usage: "volume (/host/path:/container/path or /container/path)", - Value: &cli.StringSlice{}, - }, - cli.StringSliceFlag{ - Name: "label", - Usage: "labels", - Value: &cli.StringSlice{}, - }, - cli.StringSliceFlag{ - Name: "port", - Usage: "expose container ports. usage: --port /:: i.e. --port tcp/::8080 --port tcp/:80:8080, tcp/10.1.2.3:80:8080", - Value: &cli.StringSlice{}, - }, - cli.BoolFlag{ - Name: "publish", - Usage: "publish all exposed ports", - }, - cli.BoolFlag{ - Name: "pull", - Usage: "pull the image from the repository", - }, - cli.IntFlag{ - Name: "count", - Usage: "number of instances", - Value: 1, - }, - cli.StringFlag{ - Name: "restart", - Value: "no", - Usage: "restart policy for container (on-failure, always, on-failure:5, etc.)", - }, - }, -} - -func runAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - if c.String("name") == "" { - logger.Fatal("you must specify an image name") - } - vols := c.StringSlice("vol") - env := parseEnvironmentVariables(c.StringSlice("env")) - ports := parsePorts(c.StringSlice("port")) - links := parseContainerLinks(c.StringSlice("link")) - policy, maxRetries, err := parseRestartPolicy(c.String("restart")) - if err != nil { - logger.Fatalf("error parsing restart policy: %s", err) - } - rp := citadel.RestartPolicy{ - Name: policy, - MaximumRetryCount: maxRetries, - } - image := &citadel.Image{ - Name: c.String("name"), - ContainerName: c.String("container-name"), - Cpus: c.Float64("cpus"), - Cpuset: c.String("cpuset"), - Memory: c.Float64("memory"), - Hostname: c.String("hostname"), - Domainname: c.String("domain"), - NetworkMode: c.String("network"), - Labels: c.StringSlice("label"), - Args: c.StringSlice("arg"), - Environment: env, - Links: links, - Publish: c.Bool("publish"), - Volumes: vols, - BindPorts: ports, - RestartPolicy: rp, - Type: c.String("type"), - } - containers, err := m.Run(image, c.Int("count"), c.Bool("pull")) - if err != nil { - logger.Fatalf("error running container: %s\n", err) - } - for _, c := range containers { - fmt.Printf("started %s on %s\n", c.ID[:12], c.Engine.ID) - } -} diff --git a/cli/scale.go b/cli/scale.go deleted file mode 100644 index 12837975c..000000000 --- a/cli/scale.go +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/codegangsta/cli" - "github.com/shipyard/shipyard/client" -) - -var scaleCommand = cli.Command{ - Name: "scale", - Usage: "scale a container", - Action: scaleAction, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "id", - Value: "", - Usage: "container id", - }, - cli.StringFlag{ - Name: "count", - Value: "", - Usage: "total number of instances for container", - }, - }, -} - -func scaleAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - containerId := c.String("id") - count := c.Int("count") - container, err := m.Container(containerId) - if err != nil { - logger.Fatalf("error getting container info: %s", err) - } - if containerId == "" { - logger.Fatalf("you must specify a container id") - } - if err := m.Scale(container, count); err != nil { - logger.Fatalf("error scaling container: %s\n", err) - } - fmt.Printf("scaled %s to %d\n", container.ID[:12], count) -} diff --git a/cli/servicekeys.go b/cli/servicekeys.go deleted file mode 100644 index 25fd3228e..000000000 --- a/cli/servicekeys.go +++ /dev/null @@ -1,90 +0,0 @@ -package main - -import ( - "fmt" - "os" - "text/tabwriter" - - "github.com/codegangsta/cli" - "github.com/shipyard/shipyard" - "github.com/shipyard/shipyard/client" -) - -var serviceKeysListCommand = cli.Command{ - Name: "service-keys", - Usage: "list service keys", - Action: serviceKeysListAction, -} - -func serviceKeysListAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - keys, err := m.ServiceKeys() - if err != nil { - logger.Fatalf("error getting service keys: %s", err) - return - } - if len(keys) == 0 { - return - } - w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) - fmt.Fprintln(w, "Key\tDescription") - for _, k := range keys { - fmt.Fprintf(w, "%s\t%s\n", k.Key, k.Description) - } - w.Flush() -} - -var serviceKeyCreateCommand = cli.Command{ - Name: "add-service-key", - Usage: "adds a service key", - Action: serviceKeyCreateAction, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "description, d", - Value: "", - Usage: "service key description", - }, - }, -} - -func serviceKeyCreateAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - key, err := m.NewServiceKey(c.String("description")) - if err != nil { - logger.Fatalf("error generating service key: %s\n", err) - } - fmt.Printf("created key: %s\n", key.Key) -} - -var serviceKeyRemoveCommand = cli.Command{ - Name: "remove-service-key", - Usage: "removes a service key", - Description: "remove-service-key []", - Action: serviceKeyRemoveAction, -} - -func serviceKeyRemoveAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - removeKeys := c.Args() - for _, key := range removeKeys { - k := &shipyard.ServiceKey{ - Key: key, - } - if err := m.RemoveServiceKey(k); err != nil { - logger.Fatalf("error removing service key: %s", err) - } - fmt.Printf("removed %s\n", key) - } -} diff --git a/cli/stop.go b/cli/stop.go deleted file mode 100644 index 8c0b4c0ee..000000000 --- a/cli/stop.go +++ /dev/null @@ -1,43 +0,0 @@ -package main - -import ( - "fmt" - "strings" - - "github.com/codegangsta/cli" - "github.com/shipyard/shipyard/client" -) - -var stopCommand = cli.Command{ - Name: "stop", - Usage: "stop a container", - Description: "stop []", - Action: stopAction, -} - -func stopAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - containers, err := m.Containers() - if err != nil { - logger.Fatalf("error getting container info: %s", err) - } - ids := c.Args() - if len(ids) == 0 { - logger.Fatalf("you must specify at least one id") - } - for _, cnt := range containers { - // this can probably be more efficient - for _, i := range ids { - if strings.HasPrefix(cnt.ID, i) { - if err := m.Stop(cnt); err != nil { - logger.Fatalf("error stopping container: %s\n", err) - } - fmt.Printf("stopped %s\n", cnt.ID[:12]) - } - } - } -} diff --git a/cli/utils.go b/cli/utils.go deleted file mode 100644 index e81942d44..000000000 --- a/cli/utils.go +++ /dev/null @@ -1,131 +0,0 @@ -package main - -import ( - "encoding/json" - "os" - "os/user" - "path/filepath" - "strconv" - "strings" - - "github.com/citadel/citadel" - "github.com/codegangsta/cli" - "github.com/shipyard/shipyard/client" -) - -func parseEnvironmentVariables(pairs []string) map[string]string { - env := make(map[string]string) - for _, p := range pairs { - parts := strings.Split(p, "=") - if len(parts) != 2 { - logger.Error("environment variables must be in key=value pairs") - return nil - } - k := parts[0] - v := parts[1] - env[k] = v - } - return env -} - -func parseRestartPolicy(policy string) (string, int64, error) { - retry := 0 - if strings.Index(policy, ":") > -1 { - parts := strings.Split(policy, ":") - r, err := strconv.Atoi(parts[1]) - if err != nil { - return "", 0, err - } - retry = r - policy = parts[0] - } - return policy, int64(retry), nil -} - -func parseContainerLinks(pairs []string) map[string]string { - links := make(map[string]string) - for _, p := range pairs { - parts := strings.Split(p, ":") - if len(parts) != 2 { - logger.Error("container links must be in container:name pairs") - return nil - } - k := parts[0] - v := parts[1] - links[k] = v - } - return links -} - -func parsePorts(pairs []string) []*citadel.Port { - ports := []*citadel.Port{} - for _, p := range pairs { - parts := strings.Split(p, "/") - if len(parts) != 2 { - logger.Error("port definitions must be in /: pairs") - return nil - } - proto := parts[0] - portDef := parts[1] - // parse ports - portParts := strings.Split(portDef, ":") - if len(portParts) != 3 { - logger.Error("port definitions must be in /:: pairs") - return nil - } - hostIp := portParts[0] - hostPortDef := portParts[1] - containerPortDef := portParts[2] - hostPort := 0 - containerPort := 0 - if hostPortDef != "" { - i, err := strconv.Atoi(hostPortDef) - if err != nil { - logger.Error("unable to parse port: %s", err) - return nil - } - hostPort = i - } - if containerPortDef != "" { - i, err := strconv.Atoi(containerPortDef) - if err != nil { - logger.Error("unable to parse port: %s", err) - return nil - } - containerPort = i - } - port := &citadel.Port{ - HostIp: hostIp, - Proto: proto, - Port: hostPort, - ContainerPort: containerPort, - } - ports = append(ports, port) - } - return ports -} - -func loadConfig(c *cli.Context) (*client.ShipyardConfig, error) { - usr, err := user.Current() - if err != nil { - return nil, err - } - path := filepath.Join(usr.HomeDir, CONFIG_PATH) - f, err := os.OpenFile(path, os.O_RDONLY, 0600) - if err != nil { - if os.IsNotExist(err) { - return nil, ErrConfigDoesNotExist - } else { - return nil, err - } - } - defer f.Close() - var cfg *client.ShipyardConfig - if err := json.NewDecoder(f).Decode(&cfg); err != nil { - return nil, ErrInvalidConfig - } - if c != nil && c.GlobalBool("allow-insecure") { - cfg.AllowInsecure = true - } - return cfg, nil -} diff --git a/cli/utils_test.go b/cli/utils_test.go deleted file mode 100644 index 45e78e5e9..000000000 --- a/cli/utils_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package main - -import ( - "testing" -) - -func TestParseRestartPolicy(t *testing.T) { - p := "on-failure" - policy, retry, err := parseRestartPolicy(p) - if err != nil { - t.Error(err) - } - if policy != p { - t.Errorf("expected policy %s; received %s", p, policy) - } - if retry != 0 { - t.Errorf("expected 0 retries; received %s", retry) - } -} - -func TestParseRestartPolicyWithMaximum(t *testing.T) { - p := "on-failure:5" - policy, retry, err := parseRestartPolicy(p) - if err != nil { - t.Error(err) - } - if policy != "on-failure" { - t.Errorf("expected policy %s; received %s", p, policy) - } - if retry != 5 { - t.Errorf("expected 5 retries; received %s", retry) - } -} diff --git a/cli/webhookkeys.go b/cli/webhookkeys.go deleted file mode 100644 index 8075a993e..000000000 --- a/cli/webhookkeys.go +++ /dev/null @@ -1,86 +0,0 @@ -package main - -import ( - "fmt" - "os" - "text/tabwriter" - - "github.com/codegangsta/cli" - "github.com/shipyard/shipyard/client" -) - -var webhookKeysListCommand = cli.Command{ - Name: "webhook-keys", - Usage: "list webhook keys", - Action: webhookKeysListAction, -} - -func webhookKeysListAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - keys, err := m.WebhookKeys() - if err != nil { - logger.Fatalf("error getting webhook keys: %s", err) - return - } - if len(keys) == 0 { - return - } - w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) - fmt.Fprintln(w, "Image\tKey") - for _, k := range keys { - fmt.Fprintf(w, "%s\t%s\n", k.Image, k.Key) - } - w.Flush() -} - -var webhookKeyCreateCommand = cli.Command{ - Name: "add-webhook-key", - Usage: "adds a webhook key", - Action: webhookKeyCreateAction, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "image, i", - Value: "", - Usage: "webhook key docker image", - }, - }, -} - -func webhookKeyCreateAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - key, err := m.NewWebhookKey(c.String("image")) - if err != nil { - logger.Fatalf("error generating webhook key: %s\n", err) - } - fmt.Printf("created key: %s\n", key.Key) -} - -var webhookKeyRemoveCommand = cli.Command{ - Name: "remove-webhook-key", - Usage: "removes a webhook key", - Description: "remove-webhook-key []", - Action: webhookKeyRemoveAction, -} - -func webhookKeyRemoveAction(c *cli.Context) { - cfg, err := loadConfig(c) - if err != nil { - logger.Fatal(err) - } - m := client.NewManager(cfg) - removeKeys := c.Args() - for _, key := range removeKeys { - if err := m.RemoveWebhookKey(key); err != nil { - logger.Fatalf("error removing webhook key: %s", err) - } - fmt.Printf("removed %s\n", key) - } -} diff --git a/controller/Dockerfile b/controller/Dockerfile index 7069e0b3b..d2c19f305 100644 --- a/controller/Dockerfile +++ b/controller/Dockerfile @@ -1,5 +1,5 @@ FROM scratch ADD static /static -ADD controller /controller +ADD shipyard /bin/shipyard EXPOSE 8080 -ENTRYPOINT ["/controller"] +ENTRYPOINT ["/bin/shipyard"] diff --git a/controller/commands/server.go b/controller/commands/server.go new file mode 100644 index 000000000..e1ad5ee20 --- /dev/null +++ b/controller/commands/server.go @@ -0,0 +1,474 @@ +package commands + +import ( + "encoding/json" + "net/http" + "strconv" + "strings" + + log "github.com/Sirupsen/logrus" + "github.com/codegangsta/cli" + "github.com/codegangsta/negroni" + "github.com/gorilla/context" + "github.com/gorilla/mux" + "github.com/shipyard/shipyard" + "github.com/shipyard/shipyard/auth" + "github.com/shipyard/shipyard/controller/manager" + "github.com/shipyard/shipyard/controller/middleware/access" + mAuth "github.com/shipyard/shipyard/controller/middleware/auth" + "github.com/shipyard/shipyard/dockerhub" + "github.com/shipyard/shipyard/utils" +) + +var ( + listenAddr string + rethinkdbAddr string + rethinkdbDatabase string + rethinkdbAuthKey string + disableUsageInfo bool + showVersion bool + controllerManager *manager.Manager +) + +type ( + Credentials struct { + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + } +) + +func addServiceKey(w http.ResponseWriter, r *http.Request) { + var k *auth.ServiceKey + if err := json.NewDecoder(r.Body).Decode(&k); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + key, err := controllerManager.NewServiceKey(k.Description) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + log.Infof("created service key key=%s description=%s", key.Key, key.Description) + if err := json.NewEncoder(w).Encode(key); err != nil { + log.Error(err) + } +} + +func serviceKeys(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", "application/json") + + keys, err := controllerManager.ServiceKeys() + if err != nil { + log.Error(err) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if err := json.NewEncoder(w).Encode(keys); err != nil { + log.Error(err) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } +} + +func removeServiceKey(w http.ResponseWriter, r *http.Request) { + var key *auth.ServiceKey + if err := json.NewDecoder(r.Body).Decode(&key); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err := controllerManager.RemoveServiceKey(key.Key); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + log.Infof("removed service key %s", key.Key) + w.WriteHeader(http.StatusNoContent) +} + +func events(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", "application/json") + + limit := -1 + l := r.FormValue("limit") + if l != "" { + lt, err := strconv.Atoi(l) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + limit = lt + } + events, err := controllerManager.Events(limit) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err := json.NewEncoder(w).Encode(events); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func purgeEvents(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", "application/json") + + if err := controllerManager.PurgeEvents(); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + log.Info("cluster events purged") + w.WriteHeader(http.StatusNoContent) +} + +func accounts(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", "application/json") + + accounts, err := controllerManager.Accounts() + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err := json.NewEncoder(w).Encode(accounts); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func addAccount(w http.ResponseWriter, r *http.Request) { + var account *auth.Account + if err := json.NewDecoder(r.Body).Decode(&account); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + if err := controllerManager.SaveAccount(account); err != nil { + log.Errorf("error saving account: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + log.Infof("saved account %s", account.Username) + w.WriteHeader(http.StatusNoContent) +} + +func deleteAccount(w http.ResponseWriter, r *http.Request) { + var acct *auth.Account + if err := json.NewDecoder(r.Body).Decode(&acct); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + account, err := controllerManager.Account(acct.Username) + if err != nil { + log.Errorf("error deleting account: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err := controllerManager.DeleteAccount(account); err != nil { + log.Errorf("error deleting account: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + log.Infof("deleted account %s (%s)", account.Username, account.ID) + w.WriteHeader(http.StatusNoContent) +} + +func roles(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", "application/json") + + roles, err := controllerManager.Roles() + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err := json.NewEncoder(w).Encode(roles); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func role(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", "application/json") + + vars := mux.Vars(r) + name := vars["name"] + role, err := controllerManager.Role(name) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err := json.NewEncoder(w).Encode(role); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func addRole(w http.ResponseWriter, r *http.Request) { + var role *auth.Role + if err := json.NewDecoder(r.Body).Decode(&role); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + if err := controllerManager.SaveRole(role); err != nil { + log.Errorf("error saving role: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + log.Infof("saved role %s", role.Name) + w.WriteHeader(http.StatusNoContent) +} + +func deleteRole(w http.ResponseWriter, r *http.Request) { + var role *auth.Role + if err := json.NewDecoder(r.Body).Decode(&role); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err := controllerManager.DeleteRole(role); err != nil { + log.Errorf("error deleting role: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func webhookKeys(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", "application/json") + + keys, err := controllerManager.WebhookKeys() + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err := json.NewEncoder(w).Encode(keys); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func webhookKey(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", "application/json") + + vars := mux.Vars(r) + id := vars["id"] + key, err := controllerManager.WebhookKey(id) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err := json.NewEncoder(w).Encode(key); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func addWebhookKey(w http.ResponseWriter, r *http.Request) { + var k *dockerhub.WebhookKey + if err := json.NewDecoder(r.Body).Decode(&k); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + key, err := controllerManager.NewWebhookKey(k.Image) + if err != nil { + log.Errorf("error generating webhook key: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + log.Infof("saved webhook key image=%s", key.Image) + if err := json.NewEncoder(w).Encode(key); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func deleteWebhookKey(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + id := vars["id"] + if err := controllerManager.DeleteWebhookKey(id); err != nil { + log.Errorf("error deleting webhook key: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + log.Infof("removed webhook key id=%s", id) + w.WriteHeader(http.StatusNoContent) +} + +func login(w http.ResponseWriter, r *http.Request) { + var creds *Credentials + if err := json.NewDecoder(r.Body).Decode(&creds); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if !controllerManager.Authenticate(creds.Username, creds.Password) { + log.Errorf("invalid login for %s from %s", creds.Username, r.RemoteAddr) + http.Error(w, "invalid username/password", http.StatusForbidden) + return + } + // return token + token, err := controllerManager.NewAuthToken(creds.Username, r.UserAgent()) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err := json.NewEncoder(w).Encode(token); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func changePassword(w http.ResponseWriter, r *http.Request) { + session, _ := controllerManager.Store().Get(r, controllerManager.StoreKey) + var creds *Credentials + if err := json.NewDecoder(r.Body).Decode(&creds); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + username := session.Values["username"].(string) + if username == "" { + http.Error(w, "unauthorized", http.StatusInternalServerError) + return + } + if err := controllerManager.ChangePassword(username, creds.Password); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func hubWebhook(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + id := vars["id"] + key, err := controllerManager.WebhookKey(id) + if err != nil { + log.Errorf("invalid webook key: id=%s from %s", id, r.RemoteAddr) + http.Error(w, err.Error(), http.StatusNotFound) + return + } + var webhook *dockerhub.Webhook + if err := json.NewDecoder(r.Body).Decode(&webhook); err != nil { + log.Errorf("error parsing webhook: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if strings.Index(webhook.Repository.RepoName, key.Image) == -1 { + log.Errorf("webhook key image does not match: repo=%s image=%s", webhook.Repository.RepoName, key.Image) + http.Error(w, "not found", http.StatusNotFound) + return + } + log.Infof("received webhook notification for %s", webhook.Repository.RepoName) + //TODO: redeploy containers +} + +func CmdServer(c *cli.Context) { + rethinkdbAddr := c.String("rethinkdb-addr") + rethinkdbDatabase := c.String("rethinkdb-database") + rethinkdbAuthKey := c.String("rethinkdb-auth-key") + disableUsageInfo := c.Bool("disable-usage-info") + listenAddr := c.String("listen") + + var ( + mErr error + globalMux = http.NewServeMux() + ) + + log.Infof("shipyard version %s", shipyard.Version) + + dockerUrl := c.GlobalString("docker") + tlsCaCert := c.GlobalString("tls-ca-cert") + tlsCert := c.GlobalString("tls-cert") + tlsKey := c.GlobalString("tls-key") + allowInsecure := c.GlobalBool("allow-insecure") + client, err := utils.GetClient(dockerUrl, tlsCaCert, tlsCert, tlsKey, allowInsecure) + if err != nil { + log.Fatal(err) + } + + controllerManager, mErr = manager.NewManager(rethinkdbAddr, rethinkdbDatabase, rethinkdbAuthKey, client, disableUsageInfo) + if mErr != nil { + log.Fatal(mErr) + } + + apiRouter := mux.NewRouter() + apiRouter.HandleFunc("/api/accounts", accounts).Methods("GET") + apiRouter.HandleFunc("/api/accounts", addAccount).Methods("POST") + apiRouter.HandleFunc("/api/accounts", deleteAccount).Methods("DELETE") + apiRouter.HandleFunc("/api/roles", roles).Methods("GET") + apiRouter.HandleFunc("/api/roles/{name}", role).Methods("GET") + apiRouter.HandleFunc("/api/roles", addRole).Methods("POST") + apiRouter.HandleFunc("/api/roles", deleteRole).Methods("DELETE") + apiRouter.HandleFunc("/api/events", events).Methods("GET") + apiRouter.HandleFunc("/api/events", purgeEvents).Methods("DELETE") + apiRouter.HandleFunc("/api/servicekeys", serviceKeys).Methods("GET") + apiRouter.HandleFunc("/api/servicekeys", addServiceKey).Methods("POST") + apiRouter.HandleFunc("/api/servicekeys", removeServiceKey).Methods("DELETE") + apiRouter.HandleFunc("/api/webhookkeys", webhookKeys).Methods("GET") + apiRouter.HandleFunc("/api/webhookkeys/{id}", webhookKey).Methods("GET") + apiRouter.HandleFunc("/api/webhookkeys", addWebhookKey).Methods("POST") + apiRouter.HandleFunc("/api/webhookkeys/{id}", deleteWebhookKey).Methods("DELETE") + + // global handler + globalMux.Handle("/", http.FileServer(http.Dir("static"))) + + // api router; protected by auth + apiAuthRouter := negroni.New() + apiAuthRequired := mAuth.NewAuthRequired(controllerManager) + apiAccessRequired := access.NewAccessRequired(controllerManager) + apiAuthRouter.Use(negroni.HandlerFunc(apiAuthRequired.HandlerFuncWithNext)) + apiAuthRouter.Use(negroni.HandlerFunc(apiAccessRequired.HandlerFuncWithNext)) + apiAuthRouter.UseHandler(apiRouter) + globalMux.Handle("/api/", apiAuthRouter) + + // account router ; protected by auth + accountRouter := mux.NewRouter() + accountRouter.HandleFunc("/account/changepassword", changePassword).Methods("POST") + accountAuthRouter := negroni.New() + accountAuthRequired := mAuth.NewAuthRequired(controllerManager) + accountAuthRouter.Use(negroni.HandlerFunc(accountAuthRequired.HandlerFuncWithNext)) + accountAuthRouter.UseHandler(accountRouter) + globalMux.Handle("/account/", accountAuthRouter) + + // login handler; public + loginRouter := mux.NewRouter() + loginRouter.HandleFunc("/auth/login", login).Methods("POST") + globalMux.Handle("/auth/", loginRouter) + + // hub handler; public + hubRouter := mux.NewRouter() + hubRouter.HandleFunc("/hub/webhook/{id}", hubWebhook).Methods("POST") + globalMux.Handle("/hub/", hubRouter) + + // check for admin user + if _, err := controllerManager.Account("admin"); err == manager.ErrAccountDoesNotExist { + // create roles + r := &auth.Role{ + Name: "admin", + } + ru := &auth.Role{ + Name: "user", + } + if err := controllerManager.SaveRole(r); err != nil { + log.Fatal(err) + } + if err := controllerManager.SaveRole(ru); err != nil { + log.Fatal(err) + } + role, err := controllerManager.Role(r.Name) + if err != nil { + log.Fatal(err) + } + acct := &auth.Account{ + Username: "admin", + Password: "shipyard", + Role: role, + } + if err := controllerManager.SaveAccount(acct); err != nil { + log.Fatal(err) + } + log.Infof("created admin user: username: admin password: shipyard") + } + + log.Infof("controller listening on %s", listenAddr) + + if err := http.ListenAndServe(listenAddr, context.ClearHandler(globalMux)); err != nil { + log.Fatal(err) + } +} diff --git a/controller/fig.yml b/controller/fig.yml deleted file mode 100644 index c6c11f263..000000000 --- a/controller/fig.yml +++ /dev/null @@ -1,17 +0,0 @@ -rethinkdb: - image: shipyard/rethinkdb - ports: - - "8080" - - "28015" - - "29015" - -controller: - build: . - links: - - rethinkdb - volumes: - - .:/app - working_dir: /app - command: go get -d ./... && go build . && ./controller - expose: - - "8080" diff --git a/controller/main.go b/controller/main.go index efbc09d11..38070174f 100644 --- a/controller/main.go +++ b/controller/main.go @@ -1,836 +1,97 @@ package main import ( - "encoding/json" - "flag" - "fmt" - "net/http" "os" - "strconv" - "strings" - "github.com/Sirupsen/logrus" - "github.com/citadel/citadel" - "github.com/codegangsta/negroni" - "github.com/docker/docker/pkg/stdcopy" - "github.com/gorilla/context" - "github.com/gorilla/mux" + log "github.com/Sirupsen/logrus" + "github.com/codegangsta/cli" "github.com/shipyard/shipyard" - "github.com/shipyard/shipyard/controller/manager" - "github.com/shipyard/shipyard/controller/middleware/access" - "github.com/shipyard/shipyard/controller/middleware/auth" - "github.com/shipyard/shipyard/dockerhub" -) - -var ( - listenAddr string - rethinkdbAddr string - rethinkdbDatabase string - rethinkdbAuthKey string - disableUsageInfo bool - showVersion bool - controllerManager *manager.Manager - logger = logrus.New() + "github.com/shipyard/shipyard/controller/commands" ) const ( STORE_KEY = "shipyard" - VERSION = shipyard.VERSION -) - -type ( - Credentials struct { - Username string `json:"username,omitempty"` - Password string `json:"password,omitempty"` - } ) -func init() { - flag.StringVar(&listenAddr, "listen", ":8080", "listen address") - flag.StringVar(&rethinkdbAddr, "rethinkdb-addr", "127.0.0.1:28015", "rethinkdb address") - flag.StringVar(&rethinkdbDatabase, "rethinkdb-database", "shipyard", "rethinkdb database") - flag.StringVar(&rethinkdbAuthKey, "rethinkdb-auth-key", "", "rethinkdb auth key") - flag.BoolVar(&disableUsageInfo, "disable-usage-info", false, "disable anonymous usage info") - flag.BoolVar(&showVersion, "version", false, "show version and exit") -} - -func destroy(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - id := vars["id"] - container, err := controllerManager.Container(id) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if container == nil { - http.Error(w, "container not found", http.StatusNotFound) - return - } - - if err := controllerManager.Destroy(container); err != nil { - logger.Errorf("error destroying %s: %s", container.ID, err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - logger.Infof("destroyed container %s (%s)", container.ID, container.Image.Name) - - w.WriteHeader(http.StatusNoContent) -} - -func run(w http.ResponseWriter, r *http.Request) { - r.ParseForm() - p := r.FormValue("pull") - c := r.FormValue("count") - count := 1 - pull := false - if p != "" { - pv, err := strconv.ParseBool(p) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - pull = pv - } - if c != "" { - cc, err := strconv.Atoi(c) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - count = cc - } - var image *citadel.Image - if err := json.NewDecoder(r.Body).Decode(&image); err != nil { - logger.Warnf("error decoding image: %s", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - launched, err := controllerManager.Run(image, count, pull) - if err != nil { - logger.Warnf("error running container: %s", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("content-type", "application/json") - w.WriteHeader(http.StatusCreated) - - if err := json.NewEncoder(w).Encode(launched); err != nil { - logger.Error(err) - } -} - -func stopContainer(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - id := vars["id"] - container, err := controllerManager.Container(id) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if container == nil { - http.Error(w, "container not found", http.StatusNotFound) - return - } - - if err := controllerManager.ClusterManager().Stop(container); err != nil { - logger.Errorf("error stopping %s: %s", container.ID, err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - logger.Infof("stopped container %s (%s)", container.ID, container.Image.Name) - - w.WriteHeader(http.StatusNoContent) -} - -func containerLogs(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - id := vars["id"] - container, err := controllerManager.Container(id) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if container == nil { - http.Error(w, "container not found", http.StatusNotFound) - return - } - - data, err := controllerManager.ClusterManager().Logs(container, true, true) - if err != nil { - logger.Errorf("error getting logs for %s: %s", container.ID, err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - stdcopy.StdCopy(w, w, data) -} - -func restartContainer(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - id := vars["id"] - container, err := controllerManager.Container(id) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if container == nil { - http.Error(w, "container not found", http.StatusNotFound) - return - } - - if err := controllerManager.ClusterManager().Restart(container, 10); err != nil { - logger.Errorf("error restarting %s: %s", container.ID, err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - logger.Infof("restarted container %s (%s)", container.ID, container.Image.Name) - - w.WriteHeader(http.StatusNoContent) -} - -func scaleContainer(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - id := vars["id"] - r.ParseForm() - sCount := r.FormValue("count") - if sCount == "" { - http.Error(w, "you must specify a count", http.StatusBadRequest) - return - } - count, err := strconv.Atoi(sCount) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - container, err := controllerManager.Container(id) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if container == nil { - http.Error(w, "container not found", http.StatusNotFound) - return - } - - if err := controllerManager.Scale(container, count); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - logger.Infof("scaled container %s (%s) to %d", container.ID, container.Image.Name, count) - - w.WriteHeader(http.StatusNoContent) -} - -func engines(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - engines := controllerManager.Engines() - if err := json.NewEncoder(w).Encode(engines); err != nil { - logger.Error(err) - http.Error(w, err.Error(), http.StatusBadRequest) - return - } -} - -func inspectEngine(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - vars := mux.Vars(r) - id := vars["id"] - engine := controllerManager.Engine(id) - if err := json.NewEncoder(w).Encode(engine); err != nil { - logger.Error(err) - } -} - -func containers(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - containers := controllerManager.ClusterManager().ListContainers(true, false, "") - if err := json.NewEncoder(w).Encode(containers); err != nil { - logger.Error(err) - } -} - -func inspectContainer(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - vars := mux.Vars(r) - id := vars["id"] - container, err := controllerManager.Container(id) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if container == nil { - http.Error(w, "container not found", http.StatusNotFound) - return - } - if err := json.NewEncoder(w).Encode(container); err != nil { - logger.Error(err) - } -} - -func addEngine(w http.ResponseWriter, r *http.Request) { - var engine *shipyard.Engine - if err := json.NewDecoder(r.Body).Decode(&engine); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - health := &shipyard.Health{ - Status: "pending", - ResponseTime: 0, - } - engine.Health = health - if err := controllerManager.AddEngine(engine); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - logger.Infof("added engine id=%s addr=%s cpus=%f memory=%f", engine.Engine.ID, engine.Engine.Addr, engine.Engine.Cpus, engine.Engine.Memory) - w.WriteHeader(http.StatusCreated) -} - -func removeEngine(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - id := vars["id"] - engine := controllerManager.Engine(id) - if err := controllerManager.RemoveEngine(engine.ID); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - logger.Infof("removed engine id=%s", engine.Engine.ID) - w.WriteHeader(http.StatusNoContent) -} - -func clusterInfo(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - info := controllerManager.ClusterInfo() - if err := json.NewEncoder(w).Encode(info); err != nil { - logger.Error(err) - } -} - -func addServiceKey(w http.ResponseWriter, r *http.Request) { - var k *shipyard.ServiceKey - if err := json.NewDecoder(r.Body).Decode(&k); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - key, err := controllerManager.NewServiceKey(k.Description) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - logger.Infof("created service key key=%s description=%s", key.Key, key.Description) - if err := json.NewEncoder(w).Encode(key); err != nil { - logger.Error(err) - } -} - -func serviceKeys(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - keys, err := controllerManager.ServiceKeys() - if err != nil { - logger.Error(err) - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - if err := json.NewEncoder(w).Encode(keys); err != nil { - logger.Error(err) - http.Error(w, err.Error(), http.StatusBadRequest) - return - } -} - -func removeServiceKey(w http.ResponseWriter, r *http.Request) { - var key *shipyard.ServiceKey - if err := json.NewDecoder(r.Body).Decode(&key); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := controllerManager.RemoveServiceKey(key.Key); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - logger.Infof("removed service key %s", key.Key) - w.WriteHeader(http.StatusNoContent) -} - -func events(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - limit := -1 - l := r.FormValue("limit") - if l != "" { - lt, err := strconv.Atoi(l) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - limit = lt - } - events, err := controllerManager.Events(limit) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := json.NewEncoder(w).Encode(events); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func purgeEvents(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - if err := controllerManager.PurgeEvents(); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - logger.Info("cluster events purged") - w.WriteHeader(http.StatusNoContent) -} - -func accounts(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - accounts, err := controllerManager.Accounts() - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := json.NewEncoder(w).Encode(accounts); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func addAccount(w http.ResponseWriter, r *http.Request) { - var account *shipyard.Account - if err := json.NewDecoder(r.Body).Decode(&account); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - if err := controllerManager.SaveAccount(account); err != nil { - logger.Errorf("error saving account: %s", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - logger.Infof("saved account %s", account.Username) - w.WriteHeader(http.StatusNoContent) -} - -func deleteAccount(w http.ResponseWriter, r *http.Request) { - var acct *shipyard.Account - if err := json.NewDecoder(r.Body).Decode(&acct); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - account, err := controllerManager.Account(acct.Username) - if err != nil { - logger.Errorf("error deleting account: %s", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := controllerManager.DeleteAccount(account); err != nil { - logger.Errorf("error deleting account: %s", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - logger.Infof("deleted account %s (%s)", account.Username, account.ID) - w.WriteHeader(http.StatusNoContent) -} - -func roles(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - roles, err := controllerManager.Roles() - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := json.NewEncoder(w).Encode(roles); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func role(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - vars := mux.Vars(r) - name := vars["name"] - role, err := controllerManager.Role(name) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := json.NewEncoder(w).Encode(role); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func addRole(w http.ResponseWriter, r *http.Request) { - var role *shipyard.Role - if err := json.NewDecoder(r.Body).Decode(&role); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - if err := controllerManager.SaveRole(role); err != nil { - logger.Errorf("error saving role: %s", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - logger.Infof("saved role %s", role.Name) - w.WriteHeader(http.StatusNoContent) -} - -func deleteRole(w http.ResponseWriter, r *http.Request) { - var role *shipyard.Role - if err := json.NewDecoder(r.Body).Decode(&role); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := controllerManager.DeleteRole(role); err != nil { - logger.Errorf("error deleting role: %s", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func extensions(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - exts, err := controllerManager.Extensions() - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := json.NewEncoder(w).Encode(exts); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func extension(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - vars := mux.Vars(r) - id := vars["id"] - ext, err := controllerManager.Extension(id) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := json.NewEncoder(w).Encode(ext); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func addExtension(w http.ResponseWriter, r *http.Request) { - var ext *shipyard.Extension - if err := json.NewDecoder(r.Body).Decode(&ext); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - if err := controllerManager.SaveExtension(ext); err != nil { - logger.Errorf("error saving extension: %s", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - logger.Infof("saved extension name=%s version=%s author=%s", ext.Name, ext.Version, ext.Author) - w.WriteHeader(http.StatusNoContent) -} - -func deleteExtension(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - id := vars["id"] - if err := controllerManager.DeleteExtension(id); err != nil { - logger.Errorf("error deleting extension: %s", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - logger.Infof("removed extension %s", id) - w.WriteHeader(http.StatusNoContent) -} - -func webhookKeys(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - keys, err := controllerManager.WebhookKeys() - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := json.NewEncoder(w).Encode(keys); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func webhookKey(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - vars := mux.Vars(r) - id := vars["id"] - key, err := controllerManager.WebhookKey(id) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := json.NewEncoder(w).Encode(key); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func addWebhookKey(w http.ResponseWriter, r *http.Request) { - var k *dockerhub.WebhookKey - if err := json.NewDecoder(r.Body).Decode(&k); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - key, err := controllerManager.NewWebhookKey(k.Image) - if err != nil { - logger.Errorf("error generating webhook key: %s", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - logger.Infof("saved webhook key image=%s", key.Image) - if err := json.NewEncoder(w).Encode(key); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func deleteWebhookKey(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - id := vars["id"] - if err := controllerManager.DeleteWebhookKey(id); err != nil { - logger.Errorf("error deleting webhook key: %s", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - logger.Infof("removed webhook key id=%s", id) - w.WriteHeader(http.StatusNoContent) -} - -func login(w http.ResponseWriter, r *http.Request) { - var creds *Credentials - if err := json.NewDecoder(r.Body).Decode(&creds); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if !controllerManager.Authenticate(creds.Username, creds.Password) { - logger.Errorf("invalid login for %s from %s", creds.Username, r.RemoteAddr) - http.Error(w, "invalid username/password", http.StatusForbidden) - return - } - // return token - token, err := controllerManager.NewAuthToken(creds.Username, r.UserAgent()) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := json.NewEncoder(w).Encode(token); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func changePassword(w http.ResponseWriter, r *http.Request) { - session, _ := controllerManager.Store().Get(r, controllerManager.StoreKey) - var creds *Credentials - if err := json.NewDecoder(r.Body).Decode(&creds); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - username := session.Values["username"].(string) - if username == "" { - http.Error(w, "unauthorized", http.StatusInternalServerError) - return - } - if err := controllerManager.ChangePassword(username, creds.Password); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func hubWebhook(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - id := vars["id"] - key, err := controllerManager.WebhookKey(id) - if err != nil { - logger.Errorf("invalid webook key: id=%s from %s", id, r.RemoteAddr) - http.Error(w, err.Error(), http.StatusNotFound) - return - } - var webhook *dockerhub.Webhook - if err := json.NewDecoder(r.Body).Decode(&webhook); err != nil { - logger.Errorf("error parsing webhook: %s", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if strings.Index(webhook.Repository.RepoName, key.Image) == -1 { - logger.Errorf("webhook key image does not match: repo=%s image=%s", webhook.Repository.RepoName, key.Image) - http.Error(w, "not found", http.StatusNotFound) - return - } - logger.Infof("received webhook notification for %s", webhook.Repository.RepoName) - if err := controllerManager.RedeployContainers(webhook.Repository.RepoName); err != nil { - logger.Errorf("error redeploying containers: %s", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - func main() { - rHost := os.Getenv("RETHINKDB_PORT_28015_TCP_ADDR") - rPort := os.Getenv("RETHINKDB_PORT_28015_TCP_PORT") - rDb := os.Getenv("RETHINKDB_DATABASE") - rAuthKey := os.Getenv("RETHINKDB_AUTH_KEY") - if rHost != "" && rPort != "" { - rethinkdbAddr = fmt.Sprintf("%s:%s", rHost, rPort) - } - if rDb != "" { - rethinkdbDatabase = rDb - } - if rAuthKey != "" { - rethinkdbAuthKey = rAuthKey - } - flag.Parse() - if showVersion { - fmt.Println(VERSION) - os.Exit(0) - } - var ( - mErr error - globalMux = http.NewServeMux() - ) - - logger.Infof("shipyard version %s", VERSION) - - controllerManager, mErr = manager.NewManager(rethinkdbAddr, rethinkdbDatabase, rethinkdbAuthKey, VERSION, disableUsageInfo) - if mErr != nil { - logger.Fatal(mErr) - } - - apiRouter := mux.NewRouter() - apiRouter.HandleFunc("/api/accounts", accounts).Methods("GET") - apiRouter.HandleFunc("/api/accounts", addAccount).Methods("POST") - apiRouter.HandleFunc("/api/accounts", deleteAccount).Methods("DELETE") - apiRouter.HandleFunc("/api/roles", roles).Methods("GET") - apiRouter.HandleFunc("/api/roles/{name}", role).Methods("GET") - apiRouter.HandleFunc("/api/roles", addRole).Methods("POST") - apiRouter.HandleFunc("/api/roles", deleteRole).Methods("DELETE") - apiRouter.HandleFunc("/api/cluster/info", clusterInfo).Methods("GET") - apiRouter.HandleFunc("/api/containers", containers).Methods("GET") - apiRouter.HandleFunc("/api/containers", run).Methods("POST") - apiRouter.HandleFunc("/api/containers/{id}", inspectContainer).Methods("GET") - apiRouter.HandleFunc("/api/containers/{id}", destroy).Methods("DELETE") - apiRouter.HandleFunc("/api/containers/{id}/stop", stopContainer).Methods("GET") - apiRouter.HandleFunc("/api/containers/{id}/restart", restartContainer).Methods("GET") - apiRouter.HandleFunc("/api/containers/{id}/scale", scaleContainer).Methods("GET") - apiRouter.HandleFunc("/api/containers/{id}/logs", containerLogs).Methods("GET") - apiRouter.HandleFunc("/api/events", events).Methods("GET") - apiRouter.HandleFunc("/api/events", purgeEvents).Methods("DELETE") - apiRouter.HandleFunc("/api/engines", engines).Methods("GET") - apiRouter.HandleFunc("/api/engines", addEngine).Methods("POST") - apiRouter.HandleFunc("/api/engines/{id}", inspectEngine).Methods("GET") - apiRouter.HandleFunc("/api/engines/{id}", removeEngine).Methods("DELETE") - apiRouter.HandleFunc("/api/extensions", extensions).Methods("GET") - apiRouter.HandleFunc("/api/extensions/{id}", extension).Methods("GET") - apiRouter.HandleFunc("/api/extensions", addExtension).Methods("POST") - apiRouter.HandleFunc("/api/extensions/{id}", deleteExtension).Methods("DELETE") - apiRouter.HandleFunc("/api/servicekeys", serviceKeys).Methods("GET") - apiRouter.HandleFunc("/api/servicekeys", addServiceKey).Methods("POST") - apiRouter.HandleFunc("/api/servicekeys", removeServiceKey).Methods("DELETE") - apiRouter.HandleFunc("/api/webhookkeys", webhookKeys).Methods("GET") - apiRouter.HandleFunc("/api/webhookkeys/{id}", webhookKey).Methods("GET") - apiRouter.HandleFunc("/api/webhookkeys", addWebhookKey).Methods("POST") - apiRouter.HandleFunc("/api/webhookkeys/{id}", deleteWebhookKey).Methods("DELETE") - - // global handler - globalMux.Handle("/", http.FileServer(http.Dir("static"))) - - // api router; protected by auth - apiAuthRouter := negroni.New() - apiAuthRequired := auth.NewAuthRequired(controllerManager) - apiAccessRequired := access.NewAccessRequired(controllerManager) - apiAuthRouter.Use(negroni.HandlerFunc(apiAuthRequired.HandlerFuncWithNext)) - apiAuthRouter.Use(negroni.HandlerFunc(apiAccessRequired.HandlerFuncWithNext)) - apiAuthRouter.UseHandler(apiRouter) - globalMux.Handle("/api/", apiAuthRouter) - - // account router ; protected by auth - accountRouter := mux.NewRouter() - accountRouter.HandleFunc("/account/changepassword", changePassword).Methods("POST") - accountAuthRouter := negroni.New() - accountAuthRequired := auth.NewAuthRequired(controllerManager) - accountAuthRouter.Use(negroni.HandlerFunc(accountAuthRequired.HandlerFuncWithNext)) - accountAuthRouter.UseHandler(accountRouter) - globalMux.Handle("/account/", accountAuthRouter) - - // login handler; public - loginRouter := mux.NewRouter() - loginRouter.HandleFunc("/auth/login", login).Methods("POST") - globalMux.Handle("/auth/", loginRouter) - - // hub handler; public - hubRouter := mux.NewRouter() - hubRouter.HandleFunc("/hub/webhook/{id}", hubWebhook).Methods("POST") - globalMux.Handle("/hub/", hubRouter) - - // check for admin user - if _, err := controllerManager.Account("admin"); err == manager.ErrAccountDoesNotExist { - // create roles - r := &shipyard.Role{ - Name: "admin", - } - ru := &shipyard.Role{ - Name: "user", - } - if err := controllerManager.SaveRole(r); err != nil { - logger.Fatal(err) + app := cli.NewApp() + app.Name = "shipyard" + app.Usage = "composable docker management" + app.Version = shipyard.Version + app.Author = "" + app.Email = "" + app.Before = func(c *cli.Context) error { + if c.GlobalBool("debug") { + log.SetLevel(log.DebugLevel) } - if err := controllerManager.SaveRole(ru); err != nil { - logger.Fatal(err) - } - role, err := controllerManager.Role(r.Name) - if err != nil { - logger.Fatal(err) - } - acct := &shipyard.Account{ - Username: "admin", - Password: "shipyard", - Role: role, - } - if err := controllerManager.SaveAccount(acct); err != nil { - logger.Fatal(err) - } - logger.Infof("created admin user: username: admin password: shipyard") - } - - logger.Infof("controller listening on %s", listenAddr) - - if err := http.ListenAndServe(listenAddr, context.ClearHandler(globalMux)); err != nil { - logger.Fatal(err) + return nil + } + app.Commands = []cli.Command{ + { + Name: "server", + Usage: "run shipyard controller", + Action: commands.CmdServer, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "listen, l", + Usage: "listen address", + Value: ":8080", + }, + cli.StringFlag{ + Name: "rethinkdb-addr", + Usage: "RethinkDB address", + Value: "127.0.0.1:28015", + }, + cli.StringFlag{ + Name: "rethinkdb-auth-key", + Usage: "RethinkDB auth key", + Value: "", + }, + cli.StringFlag{ + Name: "rethinkdb-database", + Usage: "RethinkDB database name", + Value: "shipyard", + }, + cli.BoolFlag{ + Name: "disable-usage-info", + Usage: "disable anonymous usage reporting", + }, + cli.StringFlag{ + Name: "docker, d", + Value: "unix:///var/run/docker.sock", + Usage: "docker swarm addr", + EnvVar: "DOCKER_HOST", + }, + cli.StringFlag{ + Name: "tls-ca-cert", + Value: "", + Usage: "tls ca certificate", + }, + cli.StringFlag{ + Name: "tls-cert", + Value: "", + Usage: "tls certificate", + }, + cli.StringFlag{ + Name: "tls-key", + Value: "", + Usage: "tls key", + }, + cli.BoolFlag{ + Name: "allow-insecure", + Usage: "enable insecure tls communication", + }, + }, + }, + } + app.Flags = []cli.Flag{ + cli.BoolFlag{ + Name: "debug, D", + Usage: "enable debug", + }, + } + + if err := app.Run(os.Args); err != nil { + log.Fatal(err) } } diff --git a/controller/manager/handler.go b/controller/manager/handler.go index a0c5bd2af..23e7ac923 100644 --- a/controller/manager/handler.go +++ b/controller/manager/handler.go @@ -2,10 +2,11 @@ package manager import ( "fmt" - "time" - "github.com/citadel/citadel" + log "github.com/Sirupsen/logrus" + "github.com/samalba/dockerclient" "github.com/shipyard/shipyard" + "github.com/shipyard/shipyard/utils" ) type ( @@ -14,21 +15,30 @@ type ( } ) -func (h *EventHandler) Handle(e *citadel.Event) error { - logger.Infof("event: date=%s type=%s image=%s container=%s", e.Time.Format(time.RubyDate), e.Type, e.Container.Image.Name, e.Container.ID[:12]) +func (h *EventHandler) Handle(e *dockerclient.Event) error { + log.Infof("event: date=%d status=%s container=%s", e.Time, e.Status, e.Id[:12]) h.logDockerEvent(e) return nil } -func (h *EventHandler) logDockerEvent(e *citadel.Event) error { +func (h *EventHandler) logDockerEvent(e *dockerclient.Event) error { + info, err := h.Manager.Container(e.Id) + if err != nil { + return err + } + + ts, err := utils.FromUnixTimestamp(e.Time) + if err != nil { + return err + } + evt := &shipyard.Event{ - Type: e.Type, + Type: e.Status, Message: fmt.Sprintf("action=%s container=%s", - e.Type, e.Container.ID[:12]), - Time: e.Time, - Container: e.Container, - Engine: e.Engine, - Tags: []string{"docker"}, + e.Status, e.Id[:12]), + Time: *ts, + ContainerInfo: info, + Tags: []string{"docker"}, } if err := h.Manager.SaveEvent(evt); err != nil { return err diff --git a/controller/manager/manager.go b/controller/manager/manager.go index 8e8a14daf..6a6749ade 100644 --- a/controller/manager/manager.go +++ b/controller/manager/manager.go @@ -2,24 +2,20 @@ package manager import ( "bytes" - "crypto/tls" "encoding/json" "errors" "fmt" - "io" "net" "net/http" "strings" - "sync" "time" - "github.com/Sirupsen/logrus" - "github.com/citadel/citadel" - "github.com/citadel/citadel/cluster" - "github.com/citadel/citadel/scheduler" + log "github.com/Sirupsen/logrus" r "github.com/dancannon/gorethink" "github.com/gorilla/sessions" + "github.com/samalba/dockerclient" "github.com/shipyard/shipyard" + "github.com/shipyard/shipyard/auth" "github.com/shipyard/shipyard/dockerhub" ) @@ -45,27 +41,24 @@ var ( ErrInvalidAuthToken = errors.New("invalid auth token") ErrExtensionDoesNotExist = errors.New("extension does not exist") ErrWebhookKeyDoesNotExist = errors.New("webhook key does not exist") - logger = logrus.New() store = sessions.NewCookieStore([]byte(storeKey)) ) type ( Manager struct { + StoreKey string address string database string authKey string session *r.Session - clusterManager *cluster.Cluster - engines []*shipyard.Engine - authenticator *shipyard.Authenticator + authenticator *auth.Authenticator store *sessions.CookieStore - StoreKey string - version string + client *dockerclient.DockerClient disableUsageInfo bool } ) -func NewManager(addr string, database string, authKey string, version string, disableUsageInfo bool) (*Manager, error) { +func NewManager(addr string, database string, authKey string, client *dockerclient.DockerClient, disableUsageInfo bool) (*Manager, error) { session, err := r.Connect(r.ConnectOpts{ Address: addr, Database: database, @@ -76,17 +69,17 @@ func NewManager(addr string, database string, authKey string, version string, di if err != nil { return nil, err } - logger.Info("checking database") + log.Info("checking database") r.DbCreate(database).Run(session) m := &Manager{ address: addr, database: database, authKey: authKey, session: session, - authenticator: &shipyard.Authenticator{}, + authenticator: &auth.Authenticator{}, store: store, + client: client, StoreKey: storeKey, - version: version, disableUsageInfo: disableUsageInfo, } m.initdb() @@ -94,10 +87,6 @@ func NewManager(addr string, database string, authKey string, version string, di return m, nil } -func (m *Manager) ClusterManager() *cluster.Cluster { - return m.clusterManager -} - func (m *Manager) Store() *sessions.CookieStore { return m.store } @@ -109,71 +98,16 @@ func (m *Manager) initdb() { _, err := r.Table(tbl).Run(m.session) if err != nil { if _, err := r.Db(m.database).TableCreate(tbl).Run(m.session); err != nil { - logger.Fatalf("error creating table: %s", err) + log.Fatalf("error creating table: %s", err) } } } } -func (m *Manager) init() []*shipyard.Engine { - engines := []*shipyard.Engine{} - res, err := r.Table(tblNameConfig).Run(m.session) - if err != nil { - logger.Fatalf("error getting configuration: %s", err) - } - if err := res.All(&engines); err != nil { - logger.Fatalf("error loading configuration: %s", err) - } - m.engines = engines - var engs []*citadel.Engine - for _, d := range engines { - tlsConfig := &tls.Config{} - if d.CACertificate != "" && d.SSLCertificate != "" && d.SSLKey != "" { - caCert := []byte(d.CACertificate) - sslCert := []byte(d.SSLCertificate) - sslKey := []byte(d.SSLKey) - c, err := getTLSConfig(caCert, sslCert, sslKey) - if err != nil { - logger.Errorf("error getting tls config: %s", err) - } - tlsConfig = c - } - if err := setEngineClient(d.Engine, tlsConfig); err != nil { - logger.Errorf("error setting tls config for engine: %s", err) - } - engs = append(engs, d.Engine) - logger.Infof("loaded engine id=%s addr=%s", d.Engine.ID, d.Engine.Addr) - } - clusterManager, err := cluster.New(scheduler.NewResourceManager(), engs...) - if err != nil { - logger.Fatal(err) - } - if err := clusterManager.Events(&EventHandler{Manager: m}); err != nil { - logger.Fatalf("unable to register event handler: %s", err) - } - var ( - labelScheduler = &scheduler.LabelScheduler{} - uniqueScheduler = &scheduler.UniqueScheduler{} - hostScheduler = &scheduler.HostScheduler{} - - multiScheduler = scheduler.NewMultiScheduler( - labelScheduler, - uniqueScheduler, - ) - ) - // TODO: refactor to be configurable - clusterManager.RegisterScheduler("service", labelScheduler) - clusterManager.RegisterScheduler("unique", uniqueScheduler) - clusterManager.RegisterScheduler("multi", multiScheduler) - clusterManager.RegisterScheduler("host", hostScheduler) - m.clusterManager = clusterManager - // start extension health check - go m.extensionHealthCheck() - // start engine check - go m.engineCheck() +func (m *Manager) init() error { // anonymous usage info go m.usageReport() - return engines + return nil } func (m *Manager) usageReport() { @@ -202,256 +136,25 @@ func (m *Manager) uploadUsage() { } } } - info := m.clusterManager.ClusterInfo() usage := &shipyard.Usage{ - ID: id, - Version: m.version, - NumOfEngines: info.EngineCount, - NumOfImages: info.ImageCount, - NumOfContainers: info.ContainerCount, - TotalCpus: info.Cpus, - TotalMemory: info.Memory, + ID: id, + Version: shipyard.Version, } b, err := json.Marshal(usage) if err != nil { - logger.Warnf("error serializing usage info: %s", err) + log.Warnf("error serializing usage info: %s", err) } buf := bytes.NewBuffer(b) if _, err := http.Post(fmt.Sprintf("%s/update", trackerHost), "application/json", buf); err != nil { - logger.Warnf("error sending usage info: %s", err) - } -} - -func (m *Manager) extensionHealthCheck() { - t := time.NewTicker(time.Second * 1).C - for { - select { - case <-t: - exts, err := m.Extensions() - if err != nil { - logger.Warnf("error running extension health check: %s", err) - return - } - for _, ext := range exts { - var once sync.Once - once.Do(func() { m.checkExtensionHealth(ext) }) - } - } - } -} - -func (m *Manager) checkExtensionHealth(ext *shipyard.Extension) error { - containers := m.Containers(true) - engs := m.Engines() - engines := []*citadel.Engine{} - for _, eng := range engs { - engines = append(engines, eng.Engine) - } - extEngines := []*citadel.Engine{} - for _, c := range containers { - if val, ok := c.Image.Environment["_SHIPYARD_EXTENSION"]; ok { - // check if the same parent extension - if val == ext.ID { - extEngines = append(extEngines, c.Engine) - } - } - } - if len(extEngines) == 0 || ext.Config.DeployPerEngine && len(extEngines) < len(engines) { - logger.Infof("recovering extension %s", ext.Name) - // extension is missing a container; deploy - if err := m.RegisterExtension(ext); err != nil { - logger.Warnf("error recovering extension: %s", err) - } - } - return nil -} - -func (m *Manager) engineCheck() { - t := time.NewTicker(time.Second * 10).C - for { - select { - case <-t: - engs := m.Engines() - for _, eng := range engs { - health := &shipyard.Health{} - start_time := time.Now() - stat, err := eng.Ping() - if err != nil { - logger.Warnf("unable to ping engine: %s", err) - } - if stat != 200 { - health.Status = EngineHealthDown - } else { - health.Status = EngineHealthUp - health.ResponseTime = int64(time.Since(start_time) / time.Nanosecond) - } - eng.Health = health - // get version - version, err := eng.Engine.Version() - if err != nil { - logger.Warnf("unable to detect docker version: %s", err) - } - ver := "" - if version != nil { - ver = version.Version - } - eng.DockerVersion = ver - m.SaveEngine(eng) - } - } - } -} - -func (m *Manager) Engines() []*shipyard.Engine { - return m.engines -} - -func (m *Manager) Engine(id string) *shipyard.Engine { - for _, e := range m.engines { - if e.ID == id { - return e - } - } - return nil -} - -func (m *Manager) AddEngine(engine *shipyard.Engine) error { - stat, err := engine.Ping() - if err != nil { - return err - } - if stat != 200 { - err := fmt.Errorf("Received status code '%d' when contacting %s", stat, engine.Engine.Addr) - return err - } - if _, err := r.Table(tblNameConfig).Insert(engine).RunWrite(m.session); err != nil { - return err - } - m.init() - evt := &shipyard.Event{ - Type: "add-engine", - Message: fmt.Sprintf("addr=%s", engine.Engine.Addr), - Time: time.Now(), - Engine: engine.Engine, - Tags: []string{"cluster"}, - } - if err := m.SaveEvent(evt); err != nil { - return err - } - return nil -} - -func (m *Manager) SaveEngine(engine *shipyard.Engine) error { - if _, err := r.Table(tblNameConfig).Replace(engine).RunWrite(m.session); err != nil { - return err - } - return nil -} - -func (m *Manager) RemoveEngine(id string) error { - var engine *shipyard.Engine - res, err := r.Table(tblNameConfig).Filter(map[string]string{"id": id}).Run(m.session) - if err != nil { - return err - } - if err := res.One(&engine); err != nil { - if err == r.ErrEmptyResult { - return nil - } - return err - } - evt := &shipyard.Event{ - Type: "remove-engine", - Message: fmt.Sprintf("addr=%s", engine.Engine.Addr), - Time: time.Now(), - Engine: engine.Engine, - Tags: []string{"cluster"}, - } - if err := m.SaveEvent(evt); err != nil { - return err - } - if _, err := r.Table(tblNameConfig).Get(id).Delete().RunWrite(m.session); err != nil { - return err - } - m.init() - return nil -} - -func (m *Manager) Container(id string) (*citadel.Container, error) { - containers := m.clusterManager.ListContainers(true, false, "") - for _, cnt := range containers { - if strings.HasPrefix(cnt.ID, id) { - return cnt, nil - } - } - return nil, nil -} - -func (m *Manager) Logs(container *citadel.Container, stdout bool, stderr bool) (io.ReadCloser, error) { - data, err := m.clusterManager.Logs(container, stdout, stderr) - if err != nil { - return nil, err + log.Warnf("error sending usage info: %s", err) } - return data, nil } -func (m *Manager) Containers(all bool) []*citadel.Container { - return m.clusterManager.ListContainers(all, false, "") +func (m *Manager) Container(id string) (*dockerclient.ContainerInfo, error) { + return m.client.InspectContainer(id) } -func (m *Manager) ContainersByImage(name string, all bool) ([]*citadel.Container, error) { - allContainers := m.Containers(all) - imageContainers := []*citadel.Container{} - for _, c := range allContainers { - if strings.Index(c.Image.Name, name) > -1 { - imageContainers = append(imageContainers, c) - } - } - return imageContainers, nil -} - -func (m *Manager) IdenticalContainers(container *citadel.Container, all bool) ([]*citadel.Container, error) { - containers := []*citadel.Container{} - imageContainers, err := m.ContainersByImage(container.Image.Name, all) - if err != nil { - return nil, err - } - for _, c := range imageContainers { - args := len(c.Image.Args) - origArgs := len(container.Image.Args) - if c.Image.Memory == container.Image.Memory && args == origArgs && c.Image.Type == container.Image.Type { - containers = append(containers, c) - } - } - return containers, nil -} - -func (m *Manager) ClusterInfo() *shipyard.ClusterInfo { - info := m.clusterManager.ClusterInfo() - clusterInfo := &shipyard.ClusterInfo{ - Cpus: info.Cpus, - Memory: info.Memory, - ContainerCount: info.ContainerCount, - EngineCount: info.EngineCount, - ImageCount: info.ImageCount, - ReservedCpus: info.ReservedCpus, - ReservedMemory: info.ReservedMemory, - Version: m.version, - } - return clusterInfo -} - -func (m *Manager) Destroy(container *citadel.Container) error { - if err := m.ClusterManager().Kill(container, 9); err != nil { - return err - } - if err := m.ClusterManager().Remove(container); err != nil { - return err - } - return nil -} - -func (m *Manager) SaveServiceKey(key *shipyard.ServiceKey) error { +func (m *Manager) SaveServiceKey(key *auth.ServiceKey) error { if _, err := r.Table(tblNameServiceKeys).Insert(key).RunWrite(m.session); err != nil { return err } @@ -518,7 +221,7 @@ func (m *Manager) PurgeEvents() error { return nil } -func (m *Manager) ServiceKey(key string) (*shipyard.ServiceKey, error) { +func (m *Manager) ServiceKey(key string) (*auth.ServiceKey, error) { res, err := r.Table(tblNameServiceKeys).Filter(map[string]string{"key": key}).Run(m.session) if err != nil { return nil, err @@ -527,38 +230,38 @@ func (m *Manager) ServiceKey(key string) (*shipyard.ServiceKey, error) { if res.IsNil() { return nil, ErrServiceKeyDoesNotExist } - var k *shipyard.ServiceKey + var k *auth.ServiceKey if err := res.One(&k); err != nil { return nil, err } return k, nil } -func (m *Manager) ServiceKeys() ([]*shipyard.ServiceKey, error) { +func (m *Manager) ServiceKeys() ([]*auth.ServiceKey, error) { res, err := r.Table(tblNameServiceKeys).Run(m.session) if err != nil { return nil, err } - keys := []*shipyard.ServiceKey{} + keys := []*auth.ServiceKey{} if err := res.All(&keys); err != nil { return nil, err } return keys, nil } -func (m *Manager) Accounts() ([]*shipyard.Account, error) { +func (m *Manager) Accounts() ([]*auth.Account, error) { res, err := r.Table(tblNameAccounts).OrderBy(r.Asc("username")).Run(m.session) if err != nil { return nil, err } - accounts := []*shipyard.Account{} + accounts := []*auth.Account{} if err := res.All(&accounts); err != nil { return nil, err } return accounts, nil } -func (m *Manager) Account(username string) (*shipyard.Account, error) { +func (m *Manager) Account(username string) (*auth.Account, error) { res, err := r.Table(tblNameAccounts).Filter(map[string]string{"username": username}).Run(m.session) if err != nil { return nil, err @@ -567,14 +270,14 @@ func (m *Manager) Account(username string) (*shipyard.Account, error) { if res.IsNil() { return nil, ErrAccountDoesNotExist } - var account *shipyard.Account + var account *auth.Account if err := res.One(&account); err != nil { return nil, err } return account, nil } -func (m *Manager) SaveAccount(account *shipyard.Account) error { +func (m *Manager) SaveAccount(account *auth.Account) error { pass := account.Password hash, err := m.authenticator.Hash(pass) if err != nil { @@ -607,7 +310,7 @@ func (m *Manager) SaveAccount(account *shipyard.Account) error { return nil } -func (m *Manager) DeleteAccount(account *shipyard.Account) error { +func (m *Manager) DeleteAccount(account *auth.Account) error { res, err := r.Table(tblNameAccounts).Filter(map[string]string{"id": account.ID}).Delete().Run(m.session) if err != nil { return err @@ -627,19 +330,19 @@ func (m *Manager) DeleteAccount(account *shipyard.Account) error { return nil } -func (m *Manager) Roles() ([]*shipyard.Role, error) { +func (m *Manager) Roles() ([]*auth.Role, error) { res, err := r.Table(tblNameRoles).OrderBy(r.Asc("name")).Run(m.session) if err != nil { return nil, err } - roles := []*shipyard.Role{} + roles := []*auth.Role{} if err := res.All(&roles); err != nil { return nil, err } return roles, nil } -func (m *Manager) Role(name string) (*shipyard.Role, error) { +func (m *Manager) Role(name string) (*auth.Role, error) { res, err := r.Table(tblNameRoles).Filter(map[string]string{"name": name}).Run(m.session) if err != nil { return nil, err @@ -648,30 +351,33 @@ func (m *Manager) Role(name string) (*shipyard.Role, error) { if res.IsNil() { return nil, ErrRoleDoesNotExist } - var role *shipyard.Role + var role *auth.Role if err := res.One(&role); err != nil { return nil, err } return role, nil } -func (m *Manager) SaveRole(role *shipyard.Role) error { +func (m *Manager) SaveRole(role *auth.Role) error { if _, err := r.Table(tblNameRoles).Insert(role).RunWrite(m.session); err != nil { return err } + evt := &shipyard.Event{ Type: "add-role", Time: time.Now(), Message: fmt.Sprintf("name=%s", role.Name), Tags: []string{"cluster", "security"}, } + if err := m.SaveEvent(evt); err != nil { return err } + return nil } -func (m *Manager) DeleteRole(role *shipyard.Role) error { +func (m *Manager) DeleteRole(role *auth.Role) error { res, err := r.Table(tblNameRoles).Get(role.ID).Delete().Run(m.session) if err != nil { return err @@ -694,13 +400,13 @@ func (m *Manager) DeleteRole(role *shipyard.Role) error { func (m *Manager) Authenticate(username, password string) bool { acct, err := m.Account(username) if err != nil { - logger.Error(err) + log.Error(err) return false } return m.authenticator.Authenticate(password, acct.Password) } -func (m *Manager) NewAuthToken(username string, userAgent string) (*shipyard.AuthToken, error) { +func (m *Manager) NewAuthToken(username string, userAgent string) (*auth.AuthToken, error) { tk, err := m.authenticator.GenerateToken() if err != nil { return nil, err @@ -709,7 +415,7 @@ func (m *Manager) NewAuthToken(username string, userAgent string) (*shipyard.Aut if err != nil { return nil, err } - token := &shipyard.AuthToken{} + token := &auth.AuthToken{} tokens := acct.Tokens found := false for _, t := range tokens { @@ -721,7 +427,7 @@ func (m *Manager) NewAuthToken(username string, userAgent string) (*shipyard.Aut } } if !found { - token = &shipyard.AuthToken{ + token = &auth.AuthToken{ UserAgent: userAgent, Token: tk, } @@ -763,12 +469,12 @@ func (m *Manager) VerifyServiceKey(key string) error { return nil } -func (m *Manager) NewServiceKey(description string) (*shipyard.ServiceKey, error) { +func (m *Manager) NewServiceKey(description string) (*auth.ServiceKey, error) { k, err := m.authenticator.GenerateToken() if err != nil { return nil, err } - key := &shipyard.ServiceKey{ + key := &auth.ServiceKey{ Key: k[24:], Description: description, } @@ -789,198 +495,25 @@ func (m *Manager) ChangePassword(username, password string) error { return nil } -func (m *Manager) Extensions() ([]*shipyard.Extension, error) { - res, err := r.Table(tblNameExtensions).OrderBy(r.Asc("name")).Run(m.session) - if err != nil { - return nil, err - } - exts := []*shipyard.Extension{} - if err := res.All(&exts); err != nil { - return nil, err - } - return exts, nil -} - -func (m *Manager) Extension(id string) (*shipyard.Extension, error) { - res, err := r.Table(tblNameExtensions).Get(id).Run(m.session) +func (m *Manager) WebhookKey(key string) (*dockerhub.WebhookKey, error) { + res, err := r.Table(tblNameWebhookKeys).Filter(map[string]string{"key": key}).Run(m.session) if err != nil { return nil, err } + if res.IsNil() { - return nil, ErrExtensionDoesNotExist - } - var ext *shipyard.Extension - if err := res.One(&ext); err != nil { - return nil, err - } - return ext, nil -} + return nil, ErrWebhookKeyDoesNotExist -func (m *Manager) SaveExtension(ext *shipyard.Extension) error { - res, err := r.Table(tblNameExtensions).Insert(ext).RunWrite(m.session) - if err != nil { - return err - } - evt := &shipyard.Event{ - Type: "add-extension", - Time: time.Now(), - Message: fmt.Sprintf("name=%s version=%s author=%s", ext.Name, ext.Version, ext.Author), - Tags: []string{"cluster"}, } - if err := m.SaveEvent(evt); err != nil { - return err - } - key := res.GeneratedKeys[0] - ext.ID = key - // register - if err := m.RegisterExtension(ext); err != nil { - return err - } - return nil -} -func (m *Manager) RegisterExtension(ext *shipyard.Extension) error { - if ext.Config.Environment == nil { - env := make(map[string]string) - ext.Config.Environment = env - } - ext.Config.Environment["_SHIPYARD_EXTENSION"] = ext.ID - rp := citadel.RestartPolicy{ - Name: "on-failure", - MaximumRetryCount: 10, - } - image := &citadel.Image{ - Name: ext.Image, - ContainerName: ext.Config.ContainerName, - Cpus: ext.Config.Cpus, - Memory: ext.Config.Memory, - Environment: ext.Config.Environment, - Links: ext.Config.Links, - Args: ext.Config.Args, - Volumes: ext.Config.Volumes, - BindPorts: ext.Config.Ports, - Labels: []string{}, - Type: "service", - RestartPolicy: rp, - } - if ext.Config.DeployPerEngine { - engs := m.clusterManager.Engines() - extEngines := []*citadel.Engine{} - containers := m.Containers(true) - for _, c := range containers { - if v, ok := c.Image.Environment["_SHIPYARD_EXTENSION"]; ok { - if v == ext.ID { - extEngines = append(extEngines, c.Engine) - } - } - } - for _, eng := range engs { - // skip if already present - for _, xe := range extEngines { - if xe == eng { - continue - } - } - image.Type = "host" - labels := []string{fmt.Sprintf("host:%s", eng.ID)} - image.Labels = labels - container, err := m.clusterManager.Start(image, true) - if err != nil { - logger.Errorf("error running %s for extension image %s: %s", image.Name, ext.Name, err) - return err - } - logger.Infof("started %s (%s) for extension %s", container.ID[:8], image.Name, ext.Name) - } - } else { - container, err := m.clusterManager.Start(image, true) - if err != nil { - logger.Errorf("error running %s for extension image %s: %s", image.Name, ext.Name, err) - return err - } - logger.Infof("started %s (%s) for extension %s", container.ID[:8], image.Name, ext.Name) - } - logger.Infof("registered extension name=%s version=%s author=%s", ext.Name, ext.Version, ext.Author) - return nil -} + var k *dockerhub.WebhookKey + if err := res.One(&k); err != nil { + return nil, err -func (m *Manager) UnregisterExtension(ext *shipyard.Extension) error { - // remove containers that are linked to extension - containers := m.clusterManager.ListContainers(true, false, "") - for _, c := range containers { - // check if has the extension env var - if val, ok := c.Image.Environment["_SHIPYARD_EXTENSION"]; ok { - // check if the same parent extension - if val == ext.ID { - logger.Infof("terminating extension (%s) container %s", ext.Name, c.ID[:8]) - if err := m.clusterManager.Kill(c, 9); err != nil { - logger.Warnf("error terminating extension (%s) container %s: %s", ext.Name, c.ID[:8], err) - } - if err := m.clusterManager.Remove(c); err != nil { - logger.Warnf("error removing extension (%s) container %s: %s", ext.Name, c.ID[:8], err) - } - } - } } - logger.Infof("un-registered extension name=%s version=%s author=%s", ext.Name, ext.Version, ext.Author) - return nil -} -func (m *Manager) DeleteExtension(id string) error { - ext, err := m.Extension(id) - if err != nil { - return err - } - if err := m.UnregisterExtension(ext); err != nil { - return err - } - res, err := r.Table(tblNameExtensions).Get(id).Delete().Run(m.session) - if err != nil { - return err - } - if res.IsNil() { - return ErrExtensionDoesNotExist - } - return nil -} - -func (m *Manager) RedeployContainers(image string) error { - var img *citadel.Image - containers := m.Containers(false) - deployed := false - for _, c := range containers { - if strings.Index(c.Image.Name, image) > -1 { - img = c.Image - logger.Infof("pulling latest image for %s", image) - if err := c.Engine.Pull(image); err != nil { - return err - } - m.Destroy(c) - // in order to keep fast deploys, we must deploy - // to the same host that the image was running on previously - img.Type = "host" - lbl := fmt.Sprintf("host:%s", c.Engine.ID) - img.Labels = []string{lbl} - nc, err := m.ClusterManager().Start(img, false) - if err != nil { - return err - } - deployed = true - logger.Infof("deployed updated container %s via webhook for %s", nc.ID[:8], image) - } - } - if deployed { - evt := &shipyard.Event{ - Type: "deploy", - Message: fmt.Sprintf("%s deployed", image), - Time: time.Now(), - Tags: []string{"deploy"}, - } - if err := m.SaveEvent(evt); err != nil { - return err - } - } - return nil + return k, nil } func (m *Manager) WebhookKeys() ([]*dockerhub.WebhookKey, error) { @@ -1001,127 +534,61 @@ func (m *Manager) NewWebhookKey(image string) (*dockerhub.WebhookKey, error) { Key: k, Image: image, } + if err := m.SaveWebhookKey(key); err != nil { return nil, err } - return key, nil -} -func (m *Manager) WebhookKey(key string) (*dockerhub.WebhookKey, error) { - res, err := r.Table(tblNameWebhookKeys).Filter(map[string]string{"key": key}).Run(m.session) - if err != nil { - return nil, err - - } - if res.IsNil() { - return nil, ErrWebhookKeyDoesNotExist - } - var k *dockerhub.WebhookKey - if err := res.One(&k); err != nil { - return nil, err - } - return k, nil + return key, nil } func (m *Manager) SaveWebhookKey(key *dockerhub.WebhookKey) error { if _, err := r.Table(tblNameWebhookKeys).Insert(key).RunWrite(m.session); err != nil { return err + } + evt := &shipyard.Event{ Type: "add-webhook-key", Time: time.Now(), Message: fmt.Sprintf("image=%s", key.Image), Tags: []string{"docker", "webhook"}, } + if err := m.SaveEvent(evt); err != nil { return err } + return nil } - func (m *Manager) DeleteWebhookKey(id string) error { key, err := m.WebhookKey(id) if err != nil { return err + } res, err := r.Table(tblNameWebhookKeys).Get(key.ID).Delete().Run(m.session) if err != nil { return err + } + if res.IsNil() { return ErrWebhookKeyDoesNotExist + } + evt := &shipyard.Event{ Type: "delete-webhook-key", Time: time.Now(), Message: fmt.Sprintf("image=%s key=%s", key.Image, key.Key), Tags: []string{"docker", "webhook"}, } + if err := m.SaveEvent(evt); err != nil { return err - } - return nil -} - -func (m *Manager) Run(image *citadel.Image, count int, pull bool) ([]*citadel.Container, error) { - launched := []*citadel.Container{} - var wg sync.WaitGroup - wg.Add(count) - var runErr error - for i := 0; i < count; i++ { - go func(wg *sync.WaitGroup) { - container, err := m.ClusterManager().Start(image, pull) - if err != nil { - runErr = err - } - launched = append(launched, container) - wg.Done() - }(&wg) } - wg.Wait() - return launched, runErr -} -func (m *Manager) Scale(container *citadel.Container, count int) error { - imageContainers, err := m.IdenticalContainers(container, true) - if err != nil { - return err - } - containerCount := len(imageContainers) - // check which way we need to scale - if containerCount > count { // down - numKill := containerCount - count - delContainers := imageContainers[0:numKill] - for _, c := range delContainers { - if err := m.Destroy(c); err != nil { - return err - } - } - } else if containerCount < count { // up - numAdd := count - containerCount - // check for vols or links -- if so, launch on same engine - img := container.Image - if len(img.Volumes) > 0 || len(img.Links) > 0 { - eng := container.Engine - t := fmt.Sprintf("host:%s", eng.ID) - lbls := img.Labels - lbls = append(lbls, t) - img.Type = "host" - img.Labels = lbls - } - // bindports must be updated to remove the hostport as they - // will fail to start - for _, p := range img.BindPorts { - p.Port = 0 - } - // reset hostname - img.Hostname = "" - if _, err := m.Run(img, numAdd, false); err != nil { - return err - } - } else { // none - logger.Info("no need to scale") - } return nil } diff --git a/controller/middleware/access/access.go b/controller/middleware/access/access.go index 96ac544f6..4f2146e90 100644 --- a/controller/middleware/access/access.go +++ b/controller/middleware/access/access.go @@ -6,7 +6,7 @@ import ( "strings" "github.com/Sirupsen/logrus" - "github.com/shipyard/shipyard" + "github.com/shipyard/shipyard/auth" "github.com/shipyard/shipyard/controller/manager" ) @@ -86,7 +86,7 @@ func (a *AccessRequired) handleRequest(w http.ResponseWriter, r *http.Request) e return nil } -func (a *AccessRequired) checkAccess(path string, role *shipyard.Role) bool { +func (a *AccessRequired) checkAccess(path string, role *auth.Role) bool { valid := false for _, v := range a.acl[role.Name] { if v == "*" || strings.HasPrefix(path, v) { diff --git a/event.go b/event.go index e5b2f9ef7..364df24a5 100644 --- a/event.go +++ b/event.go @@ -3,14 +3,13 @@ package shipyard import ( "time" - "github.com/citadel/citadel" + "github.com/samalba/dockerclient" ) type Event struct { - Type string `json:"type,omitempty"` - Container *citadel.Container `json:"container,omitempty"` - Engine *citadel.Engine `json:"engine,omitempty"` - Time time.Time `json:"time,omitempty"` - Message string `json:"message,omitempty"` - Tags []string `json:"tags,omitempty"` + Type string `json:"type,omitempty"` + ContainerInfo *dockerclient.ContainerInfo `json:"container_info,omitempty"` + Time time.Time `json:"time,omitempty"` + Message string `json:"message,omitempty"` + Tags []string `json:"tags,omitempty"` } diff --git a/fig.yml b/fig.yml index ca1c1030a..e51bf61cb 100644 --- a/fig.yml +++ b/fig.yml @@ -1,14 +1,13 @@ -shipyard: - image: shipyard/shipyard - ports: - - "8080:8080" - links: - - rethinkdb:rethinkdb - rethinkdb: image: shipyard/rethinkdb - expose: + ports: - "8080" - "28015" - "29015" +controller: + build: shipyard/shipyard + links: + - rethinkdb + expose: + - "8080" diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 000000000..014a7cc3e --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,95 @@ +package utils + +import ( + "crypto/tls" + "crypto/x509" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "time" + + log "github.com/Sirupsen/logrus" + "github.com/samalba/dockerclient" +) + +func FromUnixTimestamp(timestamp int64) (*time.Time, error) { + i, err := strconv.ParseInt("1405544146", 10, 64) + if err != nil { + return nil, err + } + + t := time.Unix(i, 0) + return &t, nil +} + +func GetTLSConfig(caCert, cert, key []byte, allowInsecure bool) (*tls.Config, error) { + // TLS config + var tlsConfig tls.Config + tlsConfig.InsecureSkipVerify = true + certPool := x509.NewCertPool() + + certPool.AppendCertsFromPEM(caCert) + tlsConfig.RootCAs = certPool + keypair, err := tls.X509KeyPair(cert, key) + if err != nil { + return &tlsConfig, err + } + tlsConfig.Certificates = []tls.Certificate{keypair} + if allowInsecure { + tlsConfig.InsecureSkipVerify = true + } + + return &tlsConfig, nil +} + +func GetClient(dockerUrl, tlsCaCert, tlsCert, tlsKey string, allowInsecure bool) (*dockerclient.DockerClient, error) { + // only load env vars if no args + // check environment for docker client config + envDockerHost := os.Getenv("DOCKER_HOST") + if dockerUrl == "" && envDockerHost != "" { + dockerUrl = envDockerHost + } + + // only load env vars if no args + envDockerCertPath := os.Getenv("DOCKER_CERT_PATH") + envDockerTlsVerify := os.Getenv("DOCKER_TLS_VERIFY") + if tlsCaCert == "" && envDockerCertPath != "" && envDockerTlsVerify != "" { + tlsCaCert = filepath.Join(envDockerCertPath, "ca.pem") + tlsCert = filepath.Join(envDockerCertPath, "cert.pem") + tlsKey = filepath.Join(envDockerCertPath, "key.pem") + } + + // load tlsconfig + var tlsConfig *tls.Config + if tlsCaCert != "" && tlsCert != "" && tlsKey != "" { + log.Debug("using tls for communication with docker") + caCert, err := ioutil.ReadFile(tlsCaCert) + if err != nil { + log.Fatalf("error loading tls ca cert: %s", err) + } + + cert, err := ioutil.ReadFile(tlsCert) + if err != nil { + log.Fatalf("error loading tls cert: %s", err) + } + + key, err := ioutil.ReadFile(tlsKey) + if err != nil { + log.Fatalf("error loading tls key: %s", err) + } + + cfg, err := GetTLSConfig(caCert, cert, key, allowInsecure) + if err != nil { + log.Fatalf("error configuring tls: %s", err) + } + tlsConfig = cfg + } + + client, err := dockerclient.NewDockerClient(dockerUrl, tlsConfig) + if err != nil { + return nil, err + } + + return client, nil +} diff --git a/version.go b/version.go index 770a7e45c..9b00b5df5 100644 --- a/version.go +++ b/version.go @@ -1,3 +1,3 @@ package shipyard -const VERSION = "2.0.10" +const Version = "3.0.0" From 689d0530d8aa4adfa228c1bba6532548263f5dfa Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Wed, 1 Apr 2015 22:46:10 -0400 Subject: [PATCH 002/238] cleanup Signed-off-by: Evan Hazlett --- engine.go | 92 ---------------------------------------------------- extension.go | 29 ----------------- 2 files changed, 121 deletions(-) delete mode 100644 engine.go delete mode 100644 extension.go diff --git a/engine.go b/engine.go deleted file mode 100644 index 373cec68a..000000000 --- a/engine.go +++ /dev/null @@ -1,92 +0,0 @@ -package shipyard - -import ( - "crypto/tls" - "crypto/x509" - "fmt" - "net" - "net/http" - "strings" - "time" - - "github.com/citadel/citadel" -) - -const ( - httpTimeout = time.Duration(1 * time.Second) -) - -type ( - Health struct { - Status string `json:"status,omitempty" gorethink:"status,omitempty"` - ResponseTime int64 `json:"response_time,omitempty" gorethink:"response_time,omitempty"` - } - - Engine struct { - ID string `json:"id,omitempty" gorethink:"id,omitempty"` - SSLCertificate string `json:"ssl_cert,omitempty" gorethink:"ssl_cert,omitempty"` - SSLKey string `json:"ssl_key,omitempty" gorethink:"ssl_key,omitempty"` - CACertificate string `json:"ca_cert,omitempty" gorethink:"ca_cert,omitempty"` - Engine *citadel.Engine `json:"engine,omitempty" gorethink:"engine,omitempty"` - Health *Health `json:"health,omitempty" gorethink:"health,omitempty"` - DockerVersion string `json:"docker_version,omitempty"` - } -) - -func dialTimeout(network, addr string) (net.Conn, error) { - return net.DialTimeout(network, addr, httpTimeout) -} - -func (e *Engine) Certificate() (*tls.Certificate, error) { - - if e.SSLCertificate == "" { - return nil, nil - } - cert, err := tls.X509KeyPair([]byte(e.SSLCertificate), []byte(e.SSLKey)) - return &cert, err -} - -func (e *Engine) Ping() (int, error) { - status := 0 - addr := e.Engine.Addr - tlsConfig := &tls.Config{} - - // check for https - if strings.Index(addr, "https") != -1 { - cert, err := e.Certificate() - if err != nil { - return 0, err - } - - tlsConfig = &tls.Config{ - Certificates: []tls.Certificate{*cert}, - } - - // use custom ca cert if specified - if e.CACertificate != "" { - caCertPool := x509.NewCertPool() - caCertPool.AppendCertsFromPEM([]byte(e.CACertificate)) - tlsConfig.RootCAs = caCertPool - } - } - // allow insecure - tlsConfig.InsecureSkipVerify = true - - transport := http.Transport{ - Dial: dialTimeout, - TLSClientConfig: tlsConfig, - } - - client := http.Client{ - Transport: &transport, - } - uri := fmt.Sprintf("%s/_ping", addr) - resp, err := client.Get(uri) - if err != nil { - return 0, err - } else { - defer resp.Body.Close() - status = resp.StatusCode - } - return status, nil -} diff --git a/extension.go b/extension.go deleted file mode 100644 index d7c8de361..000000000 --- a/extension.go +++ /dev/null @@ -1,29 +0,0 @@ -package shipyard - -import "github.com/citadel/citadel" - -type ( - Extension struct { - ID string `json:"id,omitempty" gorethink:"id,omitempty"` - Name string `json:"name,omitempty" gorethink:"name"` - Image string `json:"image,omitempty" gorethink:"image"` - Author string `json:"author,omitempty" gorethink:"author"` - Description string `json:"description,omitempty" gorethink:"description"` - Version string `json:"version,omitempty" gorethink:"version"` - Url string `json:"url,omitempty" gorethink:"url"` - Config ExtensionConfig `json:"config" gorethink:"config"` - } - ExtensionConfig struct { - ContainerName string `json:"container_name,omitempty" gorethink:"container_name"` - Cpus float64 `json:"cpus,omitempty" gorethink:"cpus"` - Memory float64 `json:"memory,omitempty" gorethink:"memory"` - Environment map[string]string `json:"environment,omitempty" gorethink:"environment"` - Links map[string]string `json:"links,omitempty" gorethink:"links"` - Args []string `json:"args,omitempty" gorethink:"args"` - Volumes []string `json:"volumes,omitempty" gorethink:"volumes"` - Ports []*citadel.Port `json:"ports,omitempty" gorethink:"ports"` - DeployPerEngine bool `json:"deploy_per_engine" gorethink:"deploy_per_engine"` - PromptArgs []string `json:"prompt_args,omitempty" gorethink:"prompt_args"` - PromptEnvironment []string `json:"prompt_env,omitempty" gorethink:"prompt_env"` - } -) From 43ca1c8f2a840da0f54dd7bd813d158fde3893bc Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Wed, 1 Apr 2015 23:47:33 -0400 Subject: [PATCH 003/238] implemented swarm proxy redirect handler Signed-off-by: Evan Hazlett --- controller/commands/server.go | 105 ++++++++++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 6 deletions(-) diff --git a/controller/commands/server.go b/controller/commands/server.go index e1ad5ee20..c2586ed86 100644 --- a/controller/commands/server.go +++ b/controller/commands/server.go @@ -2,7 +2,9 @@ package commands import ( "encoding/json" + "fmt" "net/http" + "net/url" "strconv" "strings" @@ -11,6 +13,8 @@ import ( "github.com/codegangsta/negroni" "github.com/gorilla/context" "github.com/gorilla/mux" + "github.com/mailgun/oxy/forward" + "github.com/mailgun/oxy/testutils" "github.com/shipyard/shipyard" "github.com/shipyard/shipyard/auth" "github.com/shipyard/shipyard/controller/manager" @@ -372,11 +376,12 @@ func CmdServer(c *cli.Context) { log.Infof("shipyard version %s", shipyard.Version) - dockerUrl := c.GlobalString("docker") - tlsCaCert := c.GlobalString("tls-ca-cert") - tlsCert := c.GlobalString("tls-cert") - tlsKey := c.GlobalString("tls-key") - allowInsecure := c.GlobalBool("allow-insecure") + dockerUrl := c.String("docker") + tlsCaCert := c.String("tls-ca-cert") + tlsCert := c.String("tls-cert") + tlsKey := c.String("tls-key") + allowInsecure := c.Bool("allow-insecure") + client, err := utils.GetClient(dockerUrl, tlsCaCert, tlsCert, tlsKey, allowInsecure) if err != nil { log.Fatal(err) @@ -387,6 +392,25 @@ func CmdServer(c *cli.Context) { log.Fatal(mErr) } + // forwarder for swarm + fwd, err := forward.New() + if err != nil { + log.Fatal(err) + } + + u, err := url.Parse(dockerUrl) + if err != nil { + log.Fatal(err) + } + + // TODO: handle TLS + dUrl := fmt.Sprintf("http://%s", u.Host) + + swarmRedirect := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + req.URL = testutils.ParseURI(dUrl) + fwd.ServeHTTP(w, req) + }) + apiRouter := mux.NewRouter() apiRouter.HandleFunc("/api/accounts", accounts).Methods("GET") apiRouter.HandleFunc("/api/accounts", addAccount).Methods("POST") @@ -436,6 +460,66 @@ func CmdServer(c *cli.Context) { hubRouter.HandleFunc("/hub/webhook/{id}", hubWebhook).Methods("POST") globalMux.Handle("/hub/", hubRouter) + // swarm + swarmRouter := mux.NewRouter() + swarmRouter.HandleFunc("/_ping", swarmRedirect) + swarmRouter.HandleFunc("/events", swarmRedirect) + swarmRouter.HandleFunc("/version", swarmRedirect) + swarmRouter.HandleFunc("/images/json", swarmRedirect) + swarmRouter.HandleFunc("/images/viz", swarmRedirect) + swarmRouter.HandleFunc("/images/search", swarmRedirect) + swarmRouter.HandleFunc("/images/get", swarmRedirect) + swarmRouter.HandleFunc("/images/create", swarmRedirect) + swarmRouter.HandleFunc("/images/load", swarmRedirect) + swarmRouter.HandleFunc("/images/{name:.*}", swarmRedirect) + swarmRouter.HandleFunc("/images/{name:.*}/get", swarmRedirect) + swarmRouter.HandleFunc("/images/{name:.*}/history", swarmRedirect) + swarmRouter.HandleFunc("/images/{name:.*}/json", swarmRedirect) + swarmRouter.HandleFunc("/images/{name:.*}/push", swarmRedirect) + swarmRouter.HandleFunc("/images/{name:.*}/tag", swarmRedirect) + swarmRouter.HandleFunc("/containers/ps", swarmRedirect) + swarmRouter.HandleFunc("/containers/json", swarmRedirect) + swarmRouter.HandleFunc("/containers/create", swarmRedirect) + swarmRouter.HandleFunc("/containers/{name:.*}", swarmRedirect) + swarmRouter.HandleFunc("/containers/{name:.*}/export", swarmRedirect) + swarmRouter.HandleFunc("/containers/{name:.*}/kill", swarmRedirect) + swarmRouter.HandleFunc("/containers/{name:.*}/pause", swarmRedirect) + swarmRouter.HandleFunc("/containers/{name:.*}/unpause", swarmRedirect) + swarmRouter.HandleFunc("/containers/{name:.*}/rename", swarmRedirect) + swarmRouter.HandleFunc("/containers/{name:.*}/restart", swarmRedirect) + swarmRouter.HandleFunc("/containers/{name:.*}/start", swarmRedirect) + swarmRouter.HandleFunc("/containers/{name:.*}/stop", swarmRedirect) + swarmRouter.HandleFunc("/containers/{name:.*}/wait", swarmRedirect) + swarmRouter.HandleFunc("/containers/{name:.*}/resize", swarmRedirect) + swarmRouter.HandleFunc("/containers/{name:.*}/attach", swarmRedirect) + swarmRouter.HandleFunc("/containers/{name:.*}/copy", swarmRedirect) + swarmRouter.HandleFunc("/containers/{name:.*}/exec", swarmRedirect) + swarmRouter.HandleFunc("/containers/{name:.*}/changes", swarmRedirect) + swarmRouter.HandleFunc("/containers/{name:.*}/json", swarmRedirect) + swarmRouter.HandleFunc("/containers/{name:.*}/top", swarmRedirect) + swarmRouter.HandleFunc("/containers/{name:.*}/logs", swarmRedirect) + swarmRouter.HandleFunc("/containers/{name:.*}/stats", swarmRedirect) + swarmRouter.HandleFunc("/containers/{name:.*}/attach/ws", swarmRedirect) + swarmRouter.HandleFunc("/exec/{execid:.*}/json", swarmRedirect) + swarmRouter.HandleFunc("/exec/{execid:.*}/start", swarmRedirect) + swarmRouter.HandleFunc("/exec/{execid:.*}/resize", swarmRedirect) + swarmRouter.HandleFunc("/commit", swarmRedirect) + swarmRouter.HandleFunc("/build", swarmRedirect) + swarmAuthRouter := negroni.New() + swarmAuthRequired := mAuth.NewAuthRequired(controllerManager) + swarmAccessRequired := access.NewAccessRequired(controllerManager) + swarmAuthRouter.Use(negroni.HandlerFunc(swarmAuthRequired.HandlerFuncWithNext)) + swarmAuthRouter.Use(negroni.HandlerFunc(swarmAccessRequired.HandlerFuncWithNext)) + swarmAuthRouter.UseHandler(swarmRouter) + globalMux.Handle("/containers/", swarmAuthRouter) + globalMux.Handle("/_ping", swarmAuthRouter) + globalMux.Handle("/commit", swarmAuthRouter) + globalMux.Handle("/build", swarmAuthRouter) + globalMux.Handle("/events", swarmAuthRouter) + globalMux.Handle("/version", swarmAuthRouter) + globalMux.Handle("/images/", swarmAuthRouter) + globalMux.Handle("/exec/", swarmAuthRouter) + // check for admin user if _, err := controllerManager.Account("admin"); err == manager.ErrAccountDoesNotExist { // create roles @@ -468,7 +552,16 @@ func CmdServer(c *cli.Context) { log.Infof("controller listening on %s", listenAddr) - if err := http.ListenAndServe(listenAddr, context.ClearHandler(globalMux)); err != nil { + s := &http.Server{ + Addr: listenAddr, + Handler: context.ClearHandler(globalMux), + } + + if err := s.ListenAndServe(); err != nil { log.Fatal(err) } + + //if err := s.ListenAndServe(context.ClearHandler(globalMux)); err != nil { + // log.Fatal(err) + //} } From 39ebe03da9c269c5cbb6d0e5bc15df22e25f43e7 Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Thu, 2 Apr 2015 02:07:38 -0400 Subject: [PATCH 004/238] swarm proxy support Signed-off-by: Evan Hazlett --- controller/commands/server.go | 158 ++++++++++++++++-------- controller/main.go | 9 ++ controller/manager/manager_test.go | 157 ----------------------- controller/middleware/auth/auth.go | 35 +++++- controller/middleware/auth/auth_test.go | 36 +++++- 5 files changed, 182 insertions(+), 213 deletions(-) diff --git a/controller/commands/server.go b/controller/commands/server.go index c2586ed86..7a04acc9a 100644 --- a/controller/commands/server.go +++ b/controller/commands/server.go @@ -25,12 +25,6 @@ import ( ) var ( - listenAddr string - rethinkdbAddr string - rethinkdbDatabase string - rethinkdbAuthKey string - disableUsageInfo bool - showVersion bool controllerManager *manager.Manager ) @@ -362,12 +356,20 @@ func hubWebhook(w http.ResponseWriter, r *http.Request) { //TODO: redeploy containers } +func writeCorsHeaders(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Access-Control-Allow-Origin", "*") + w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept") + w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS") +} + func CmdServer(c *cli.Context) { rethinkdbAddr := c.String("rethinkdb-addr") rethinkdbDatabase := c.String("rethinkdb-database") rethinkdbAuthKey := c.String("rethinkdb-auth-key") disableUsageInfo := c.Bool("disable-usage-info") listenAddr := c.String("listen") + authWhitelist := c.StringSlice("auth-whitelist-cidr") + enableCors := c.Bool("enable-cors") var ( mErr error @@ -376,6 +378,10 @@ func CmdServer(c *cli.Context) { log.Infof("shipyard version %s", shipyard.Version) + if len(authWhitelist) > 0 { + log.Infof("whitelisting the following subnets: %v", authWhitelist) + } + dockerUrl := c.String("docker") tlsCaCert := c.String("tls-ca-cert") tlsCert := c.String("tls-cert") @@ -392,6 +398,8 @@ func CmdServer(c *cli.Context) { log.Fatal(mErr) } + log.Debugf("connected to docker: url=%s", dockerUrl) + // forwarder for swarm fwd, err := forward.New() if err != nil { @@ -403,8 +411,14 @@ func CmdServer(c *cli.Context) { log.Fatal(err) } - // TODO: handle TLS - dUrl := fmt.Sprintf("http://%s", u.Host) + // TODO: handle TLS -- this isn't working yet + scheme := "http://" + if client.TLSConfig != nil { + scheme = "https://" + } + dUrl := fmt.Sprintf("%s%s", scheme, u.Host) + + log.Debugf("configured docker proxy: %s", dUrl) swarmRedirect := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { req.URL = testutils.ParseURI(dUrl) @@ -434,7 +448,7 @@ func CmdServer(c *cli.Context) { // api router; protected by auth apiAuthRouter := negroni.New() - apiAuthRequired := mAuth.NewAuthRequired(controllerManager) + apiAuthRequired := mAuth.NewAuthRequired(controllerManager, authWhitelist) apiAccessRequired := access.NewAccessRequired(controllerManager) apiAuthRouter.Use(negroni.HandlerFunc(apiAuthRequired.HandlerFuncWithNext)) apiAuthRouter.Use(negroni.HandlerFunc(apiAccessRequired.HandlerFuncWithNext)) @@ -445,7 +459,7 @@ func CmdServer(c *cli.Context) { accountRouter := mux.NewRouter() accountRouter.HandleFunc("/account/changepassword", changePassword).Methods("POST") accountAuthRouter := negroni.New() - accountAuthRequired := mAuth.NewAuthRequired(controllerManager) + accountAuthRequired := mAuth.NewAuthRequired(controllerManager, authWhitelist) accountAuthRouter.Use(negroni.HandlerFunc(accountAuthRequired.HandlerFuncWithNext)) accountAuthRouter.UseHandler(accountRouter) globalMux.Handle("/account/", accountAuthRouter) @@ -462,51 +476,85 @@ func CmdServer(c *cli.Context) { // swarm swarmRouter := mux.NewRouter() - swarmRouter.HandleFunc("/_ping", swarmRedirect) - swarmRouter.HandleFunc("/events", swarmRedirect) - swarmRouter.HandleFunc("/version", swarmRedirect) - swarmRouter.HandleFunc("/images/json", swarmRedirect) - swarmRouter.HandleFunc("/images/viz", swarmRedirect) - swarmRouter.HandleFunc("/images/search", swarmRedirect) - swarmRouter.HandleFunc("/images/get", swarmRedirect) - swarmRouter.HandleFunc("/images/create", swarmRedirect) - swarmRouter.HandleFunc("/images/load", swarmRedirect) - swarmRouter.HandleFunc("/images/{name:.*}", swarmRedirect) - swarmRouter.HandleFunc("/images/{name:.*}/get", swarmRedirect) - swarmRouter.HandleFunc("/images/{name:.*}/history", swarmRedirect) - swarmRouter.HandleFunc("/images/{name:.*}/json", swarmRedirect) - swarmRouter.HandleFunc("/images/{name:.*}/push", swarmRedirect) - swarmRouter.HandleFunc("/images/{name:.*}/tag", swarmRedirect) - swarmRouter.HandleFunc("/containers/ps", swarmRedirect) - swarmRouter.HandleFunc("/containers/json", swarmRedirect) - swarmRouter.HandleFunc("/containers/create", swarmRedirect) - swarmRouter.HandleFunc("/containers/{name:.*}", swarmRedirect) - swarmRouter.HandleFunc("/containers/{name:.*}/export", swarmRedirect) - swarmRouter.HandleFunc("/containers/{name:.*}/kill", swarmRedirect) - swarmRouter.HandleFunc("/containers/{name:.*}/pause", swarmRedirect) - swarmRouter.HandleFunc("/containers/{name:.*}/unpause", swarmRedirect) - swarmRouter.HandleFunc("/containers/{name:.*}/rename", swarmRedirect) - swarmRouter.HandleFunc("/containers/{name:.*}/restart", swarmRedirect) - swarmRouter.HandleFunc("/containers/{name:.*}/start", swarmRedirect) - swarmRouter.HandleFunc("/containers/{name:.*}/stop", swarmRedirect) - swarmRouter.HandleFunc("/containers/{name:.*}/wait", swarmRedirect) - swarmRouter.HandleFunc("/containers/{name:.*}/resize", swarmRedirect) - swarmRouter.HandleFunc("/containers/{name:.*}/attach", swarmRedirect) - swarmRouter.HandleFunc("/containers/{name:.*}/copy", swarmRedirect) - swarmRouter.HandleFunc("/containers/{name:.*}/exec", swarmRedirect) - swarmRouter.HandleFunc("/containers/{name:.*}/changes", swarmRedirect) - swarmRouter.HandleFunc("/containers/{name:.*}/json", swarmRedirect) - swarmRouter.HandleFunc("/containers/{name:.*}/top", swarmRedirect) - swarmRouter.HandleFunc("/containers/{name:.*}/logs", swarmRedirect) - swarmRouter.HandleFunc("/containers/{name:.*}/stats", swarmRedirect) - swarmRouter.HandleFunc("/containers/{name:.*}/attach/ws", swarmRedirect) - swarmRouter.HandleFunc("/exec/{execid:.*}/json", swarmRedirect) - swarmRouter.HandleFunc("/exec/{execid:.*}/start", swarmRedirect) - swarmRouter.HandleFunc("/exec/{execid:.*}/resize", swarmRedirect) - swarmRouter.HandleFunc("/commit", swarmRedirect) - swarmRouter.HandleFunc("/build", swarmRedirect) + // these are pulled from the swarm api code to proxy and allow + // usage with the standard Docker cli + m := map[string]map[string]http.HandlerFunc{ + "GET": { + "/_ping": swarmRedirect, + "/events": swarmRedirect, + "/info": swarmRedirect, + "/version": swarmRedirect, + "/images/json": swarmRedirect, + "/images/viz": swarmRedirect, + "/images/search": swarmRedirect, + "/images/get": swarmRedirect, + "/images/{name:.*}/get": swarmRedirect, + "/images/{name:.*}/history": swarmRedirect, + "/images/{name:.*}/json": swarmRedirect, + "/containers/ps": swarmRedirect, + "/containers/json": swarmRedirect, + "/containers/{name:.*}/export": swarmRedirect, + "/containers/{name:.*}/changes": swarmRedirect, + "/containers/{name:.*}/json": swarmRedirect, + "/containers/{name:.*}/top": swarmRedirect, + "/containers/{name:.*}/logs": swarmRedirect, + "/containers/{name:.*}/stats": swarmRedirect, + "/containers/{name:.*}/attach/ws": swarmRedirect, + "/exec/{execid:.*}/json": swarmRedirect, + }, + "POST": { + "/auth": swarmRedirect, + "/commit": swarmRedirect, + "/build": swarmRedirect, + "/images/create": swarmRedirect, + "/images/load": swarmRedirect, + "/images/{name:.*}/push": swarmRedirect, + "/images/{name:.*}/tag": swarmRedirect, + "/containers/create": swarmRedirect, + "/containers/{name:.*}/kill": swarmRedirect, + "/containers/{name:.*}/pause": swarmRedirect, + "/containers/{name:.*}/unpause": swarmRedirect, + "/containers/{name:.*}/rename": swarmRedirect, + "/containers/{name:.*}/restart": swarmRedirect, + "/containers/{name:.*}/start": swarmRedirect, + "/containers/{name:.*}/stop": swarmRedirect, + "/containers/{name:.*}/wait": swarmRedirect, + "/containers/{name:.*}/resize": swarmRedirect, + "/containers/{name:.*}/attach": swarmRedirect, + "/containers/{name:.*}/copy": swarmRedirect, + "/containers/{name:.*}/exec": swarmRedirect, + "/exec/{execid:.*}/start": swarmRedirect, + "/exec/{execid:.*}/resize": swarmRedirect, + }, + "DELETE": { + "/containers/{name:.*}": swarmRedirect, + "/images/{name:.*}": swarmRedirect, + }, + "OPTIONS": { + "": swarmRedirect, + }, + } + + for method, routes := range m { + for route, fct := range routes { + localRoute := route + localFct := fct + wrap := func(w http.ResponseWriter, r *http.Request) { + if enableCors { + writeCorsHeaders(w, r) + } + localFct(w, r) + } + localMethod := method + + // add the new route + swarmRouter.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(wrap) + swarmRouter.Path(localRoute).Methods(localMethod).HandlerFunc(wrap) + } + } + swarmAuthRouter := negroni.New() - swarmAuthRequired := mAuth.NewAuthRequired(controllerManager) + swarmAuthRequired := mAuth.NewAuthRequired(controllerManager, authWhitelist) swarmAccessRequired := access.NewAccessRequired(controllerManager) swarmAuthRouter.Use(negroni.HandlerFunc(swarmAuthRequired.HandlerFuncWithNext)) swarmAuthRouter.Use(negroni.HandlerFunc(swarmAccessRequired.HandlerFuncWithNext)) @@ -519,6 +567,8 @@ func CmdServer(c *cli.Context) { globalMux.Handle("/version", swarmAuthRouter) globalMux.Handle("/images/", swarmAuthRouter) globalMux.Handle("/exec/", swarmAuthRouter) + globalMux.Handle("/v1.17/", swarmAuthRouter) + globalMux.Handle("/v1.18/", swarmAuthRouter) // check for admin user if _, err := controllerManager.Account("admin"); err == manager.ErrAccountDoesNotExist { diff --git a/controller/main.go b/controller/main.go index 38070174f..fa8dde9c2 100644 --- a/controller/main.go +++ b/controller/main.go @@ -81,6 +81,15 @@ func main() { Name: "allow-insecure", Usage: "enable insecure tls communication", }, + cli.BoolFlag{ + Name: "enable-cors", + Usage: "enable cors with swarm", + }, + cli.StringSliceFlag{ + Name: "auth-whitelist-cidr", + Usage: "whitelist CIDR to bypass auth", + Value: &cli.StringSlice{}, + }, }, }, } diff --git a/controller/manager/manager_test.go b/controller/manager/manager_test.go index 7187618f3..5d04392c7 100644 --- a/controller/manager/manager_test.go +++ b/controller/manager/manager_test.go @@ -1,158 +1 @@ package manager - -import ( - "fmt" - "os" - "testing" - "time" - - "github.com/citadel/citadel" - "github.com/shipyard/shipyard" -) - -func newManager() *Manager { - rHost := os.Getenv("RETHINKDB_TEST_PORT_28015_TCP_ADDR") - rPort := os.Getenv("RETHINKDB_TEST_PORT_28015_TCP_PORT") - rDb := os.Getenv("RETHINKDB_TEST_DATABASE") - rethinkdbAddr := "" - if rHost != "" && rPort != "" { - rethinkdbAddr = fmt.Sprintf("%s:%s", rHost, rPort) - } - dockerHostAddr := os.Getenv("DOCKER_TEST_ADDR") - if dockerHostAddr == "" || rethinkdbAddr == "" { - fmt.Println("env vars needed: RETHINKDB_TEST_PORT_28015_TCP_ADDR, RETHINKDB_TEST_PORT_28015_TCP_PORT, RETHINKDB_TEST_DATABASE, DOCKER_TEST_ADDR") - os.Exit(1) - } - m, err := NewManager(rethinkdbAddr, rDb, "", "test", true) - if err != nil { - fmt.Printf("unable to connect to test db: %s\n", err) - os.Exit(1) - } - health := &shipyard.Health{ - Status: "up", - ResponseTime: 1, - } - eng := &shipyard.Engine{ - ID: "test", - Engine: &citadel.Engine{ - ID: "test", - Addr: dockerHostAddr, - Cpus: 4.0, - Memory: 4096, - Labels: []string{"tests"}, - }, - Health: health, - } - m.AddEngine(eng) - return m -} - -func getTestImage() *citadel.Image { - img := &citadel.Image{ - Name: "busybox", - Cpus: 0.1, - Memory: 8, - Type: "service", - Labels: []string{"tests"}, - } - return img -} - -func TestRun(t *testing.T) { - if os.Getenv("RUN_INTEGRATION_TEST") == "" { - t.Skipf("set RUN_INTEGRATION_TEST env var to run") - } - m := newManager() - img := getTestImage() - cTest, err := m.Run(img, 1, true) - if err != nil { - t.Error(err) - } - if len(cTest) != 1 { - t.Errorf("expected 1 container; received %d", len(cTest)) - } - c := cTest[0] - if c.Image.Name != "busybox" { - t.Errorf("expected image %s; received %s", img.Name, c.Image.Name) - } - // cleanup - for _, c := range cTest { - if err := m.Destroy(c); err != nil { - t.Error(err) - } - } - time.Sleep(2 * time.Second) -} - -func TestScaleUp(t *testing.T) { - if os.Getenv("RUN_INTEGRATION_TEST") == "" { - t.Skipf("set RUN_INTEGRATION_TEST env var to run") - } - m := newManager() - img := getTestImage() - cTest, err := m.Run(img, 1, true) - if err != nil { - t.Error(err) - } - - c := cTest[0] - if err := m.Scale(c, 2); err != nil { - t.Error(err) - } - - containers, err := m.IdenticalContainers(c, true) - if err != nil { - t.Error(err) - } - - if len(containers) != 2 { - t.Errorf("expected 2 containers; received %d", len(containers)) - } - - // cleanup - for _, c := range containers { - if err := m.Destroy(c); err != nil { - t.Error(err) - } - } - time.Sleep(2 * time.Second) -} - -func TestScaleDown(t *testing.T) { - if os.Getenv("RUN_INTEGRATION_TEST") == "" { - t.Skipf("set RUN_INTEGRATION_TEST env var to run") - } - m := newManager() - img := getTestImage() - cTest, err := m.Run(img, 4, true) - if err != nil { - t.Error(err) - } - - if len(cTest) != 4 { - t.Errorf("expected to run 4 containers; received %d", len(cTest)) - } - - c := cTest[0] - - if err := m.Scale(c, 1); err != nil { - t.Error(err) - } - - containers, err := m.IdenticalContainers(c, true) - if err != nil { - t.Error(err) - } - - if len(containers) != 1 { - t.Errorf("expected 1 container; received %d", len(containers)) - } - - // cleanup - for _, c := range containers { - if err := m.Destroy(c); err != nil { - t.Error(err) - } - } - time.Sleep(2 * time.Second) -} diff --git a/controller/middleware/auth/auth.go b/controller/middleware/auth/auth.go index 7a3b9715d..ff7fdf781 100644 --- a/controller/middleware/auth/auth.go +++ b/controller/middleware/auth/auth.go @@ -2,6 +2,7 @@ package auth import ( "fmt" + "net" "net/http" "strings" @@ -20,12 +21,14 @@ func defaultDeniedHostHandler(w http.ResponseWriter, r *http.Request) { type AuthRequired struct { deniedHostHandler http.Handler manager *manager.Manager + whitelistCIDRs []string } -func NewAuthRequired(m *manager.Manager) *AuthRequired { +func NewAuthRequired(m *manager.Manager, whitelistCIDRs []string) *AuthRequired { return &AuthRequired{ deniedHostHandler: http.HandlerFunc(defaultDeniedHostHandler), manager: m, + whitelistCIDRs: whitelistCIDRs, } } @@ -40,7 +43,37 @@ func (a *AuthRequired) Handler(h http.Handler) http.Handler { }) } +func (a *AuthRequired) isWhitelisted(addr string) (bool, error) { + parts := strings.Split(addr, ":") + src := parts[0] + + srcIp := net.ParseIP(src) + + // check each whitelisted ip + for _, c := range a.whitelistCIDRs { + _, ipNet, err := net.ParseCIDR(c) + if err != nil { + return false, err + } + + if ipNet.Contains(srcIp) { + return true, nil + } + } + + return false, nil +} + func (a *AuthRequired) handleRequest(w http.ResponseWriter, r *http.Request) error { + whitelisted, err := a.isWhitelisted(r.RemoteAddr) + if err != nil { + return err + } + + if whitelisted { + return nil + } + valid := false // service key takes priority serviceKey := r.Header.Get("X-Service-Key") diff --git a/controller/middleware/auth/auth_test.go b/controller/middleware/auth/auth_test.go index 8c8d45834..9e6c56315 100644 --- a/controller/middleware/auth/auth_test.go +++ b/controller/middleware/auth/auth_test.go @@ -14,10 +14,44 @@ func TestNoAuthToken(t *testing.T) { res := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/", nil) - a := NewAuthRequired(nil) + a := NewAuthRequired(nil, []string{}) a.Handler(testHandler).ServeHTTP(res, req) if res.Code != http.StatusUnauthorized { t.Fatalf("expected 401; got %s", res.Code) } } + +func TestWhiteListAny(t *testing.T) { + res := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/", nil) + + a := NewAuthRequired(nil, []string{}) + a.Handler(testHandler).ServeHTTP(res, req) + + if res.Code != http.StatusUnauthorized { + t.Fatalf("expected 401; got %d", res.Code) + } + + res = httptest.NewRecorder() + req, _ = http.NewRequest("GET", "/", nil) + + a = NewAuthRequired(nil, []string{"0.0.0.0"}) + a.Handler(testHandler).ServeHTTP(res, req) + + if res.Code != http.StatusOK { + t.Fatalf("expected 200; got %d", res.Code) + } +} + +func TestWhiteListInvalid(t *testing.T) { + res := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/", nil) + + a := NewAuthRequired(nil, []string{"1.2.3.4/32"}) + a.Handler(testHandler).ServeHTTP(res, req) + + if res.Code != http.StatusUnauthorized { + t.Fatalf("expected 401; got %d", res.Code) + } +} From 218b4f8062a66575d66b3ddf3049d9bcc3057fe6 Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Thu, 2 Apr 2015 02:42:53 -0400 Subject: [PATCH 005/238] support tls for reverse swarm proxy Signed-off-by: Evan Hazlett --- controller/commands/server.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/controller/commands/server.go b/controller/commands/server.go index 7a04acc9a..712fa04ca 100644 --- a/controller/commands/server.go +++ b/controller/commands/server.go @@ -411,11 +411,25 @@ func CmdServer(c *cli.Context) { log.Fatal(err) } - // TODO: handle TLS -- this isn't working yet + // setup redirect target to swarm scheme := "http://" + + // check if TLS is enabled and configure if so if client.TLSConfig != nil { scheme = "https://" + // setup custom roundtripper with TLS transport + r := forward.RoundTripper( + &http.Transport{ + TLSClientConfig: client.TLSConfig, + }) + f, err := forward.New(r) + if err != nil { + log.Fatal(err) + } + + fwd = f } + dUrl := fmt.Sprintf("%s%s", scheme, u.Host) log.Debugf("configured docker proxy: %s", dUrl) From b69d7d06904727006cd7f882c193580acd32634f Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Thu, 2 Apr 2015 02:46:02 -0400 Subject: [PATCH 006/238] minor logging update Signed-off-by: Evan Hazlett --- controller/commands/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller/commands/server.go b/controller/commands/server.go index 712fa04ca..ba5491bec 100644 --- a/controller/commands/server.go +++ b/controller/commands/server.go @@ -432,7 +432,7 @@ func CmdServer(c *cli.Context) { dUrl := fmt.Sprintf("%s%s", scheme, u.Host) - log.Debugf("configured docker proxy: %s", dUrl) + log.Debugf("configured docker proxy target: %s", dUrl) swarmRedirect := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { req.URL = testutils.ParseURI(dUrl) From b831f92ded7c82ff6bb90b174391d08bf0e53338 Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Thu, 2 Apr 2015 02:53:07 -0400 Subject: [PATCH 007/238] ui update for containers via swarm Signed-off-by: Evan Hazlett --- .../static/app/containers/containers.html | 18 ++++++------------ .../app/containers/containers.service.js | 4 ++-- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/controller/static/app/containers/containers.html b/controller/static/app/containers/containers.html index 7a5b0fbcc..579d9d7af 100644 --- a/controller/static/app/containers/containers.html +++ b/controller/static/app/containers/containers.html @@ -19,12 +19,9 @@ Status - ID - Name - Image - Engine - CPUs - Memory + ID + Name + Image @@ -33,13 +30,10 @@ - {{c.id|truncate}} + {{c.Id|truncate}} - {{c.name|container_name}} - {{c.image.name}} - {{c.engine.id}} - {{c.image.cpus | default:"∞"}} - {{c.image.memory|formatMemory | default:"∞"}} + {{c.Names}} + {{c.Image}} diff --git a/controller/static/app/containers/containers.service.js b/controller/static/app/containers/containers.service.js index 01995c63e..963e3baa2 100644 --- a/controller/static/app/containers/containers.service.js +++ b/controller/static/app/containers/containers.service.js @@ -4,10 +4,10 @@ angular .module('shipyard.containers') .factory('Containers', function($resource) { - return $resource('/api/containers'); + return $resource('/containers/json'); }) .factory('Container', function($resource) { - return $resource('/api/containers/:id/:action', {id: '@id' }, { + return $resource('/containers/:id/:action', {id: '@id' }, { destroy: { method: 'DELETE' }, 'save': { isArray: true, method: 'POST' }, 'control': { isArray: false, method: 'GET' }, From 6fdf551d47b4e2624bf85048302876e037232ce8 Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Thu, 2 Apr 2015 02:54:17 -0400 Subject: [PATCH 008/238] update godeps Signed-off-by: Evan Hazlett --- controller/Godeps/Godeps.json | 28 +- .../docker/docker/pkg/stdcopy/MAINTAINERS | 1 - .../docker/docker/pkg/stdcopy/stdcopy.go | 172 ------------ .../docker/docker/pkg/stdcopy/stdcopy_test.go | 20 -- .../src/gopkg.in/fatih/pool.v2/.gitignore | 22 -- .../src/gopkg.in/fatih/pool.v2/.travis.yml | 2 - .../src/gopkg.in/fatih/pool.v2/LICENSE | 20 -- .../src/gopkg.in/fatih/pool.v2/README.md | 59 ----- .../src/gopkg.in/fatih/pool.v2/channel.go | 131 ---------- .../gopkg.in/fatih/pool.v2/channel_test.go | 247 ------------------ .../src/gopkg.in/fatih/pool.v2/conn.go | 22 -- .../src/gopkg.in/fatih/pool.v2/conn_test.go | 10 - .../src/gopkg.in/fatih/pool.v2/pool.go | 28 -- 13 files changed, 18 insertions(+), 744 deletions(-) delete mode 100644 controller/Godeps/_workspace/src/github.com/docker/docker/pkg/stdcopy/MAINTAINERS delete mode 100644 controller/Godeps/_workspace/src/github.com/docker/docker/pkg/stdcopy/stdcopy.go delete mode 100644 controller/Godeps/_workspace/src/github.com/docker/docker/pkg/stdcopy/stdcopy_test.go delete mode 100644 controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/.gitignore delete mode 100644 controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/.travis.yml delete mode 100644 controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/LICENSE delete mode 100644 controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/README.md delete mode 100644 controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/channel.go delete mode 100644 controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/channel_test.go delete mode 100644 controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/conn.go delete mode 100644 controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/conn_test.go delete mode 100644 controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/pool.go diff --git a/controller/Godeps/Godeps.json b/controller/Godeps/Godeps.json index 41e615158..a5c6653a5 100644 --- a/controller/Godeps/Godeps.json +++ b/controller/Godeps/Godeps.json @@ -1,6 +1,6 @@ { "ImportPath": "github.com/shipyard/shipyard/controller", - "GoVersion": "go1.4", + "GoVersion": "go1.4.2", "Deps": [ { "ImportPath": "code.google.com/p/go.crypto/bcrypt", @@ -22,6 +22,11 @@ "Comment": "beta4-79-g55418e9", "Rev": "55418e9e8d61b680b751b25048b5b27482cd1543" }, + { + "ImportPath": "github.com/codegangsta/cli", + "Comment": "1.2.0-26-gf7ebb76", + "Rev": "f7ebb761e83e21225d1d8954fde853bf8edd46c4" + }, { "ImportPath": "github.com/codegangsta/negroni", "Comment": "v0.1-34-gae96f1c", @@ -32,11 +37,6 @@ "Comment": "v0.5.x-9-g16b174b", "Rev": "16b174b489ff37d396a9fa202536c08171717883" }, - { - "ImportPath": "github.com/docker/docker/pkg/stdcopy", - "Comment": "v1.3.0-422-g3c5155a", - "Rev": "3c5155ac16bbf4d02d88ad5f2c4bfef7844dad4e" - }, { "ImportPath": "github.com/gorilla/context", "Rev": "14f550f51af52180c2eefed15e5fd18d63c0a64a" @@ -53,6 +53,18 @@ "ImportPath": "github.com/gorilla/sessions", "Rev": "aa5e036e6c44aec69a32eb41097001978b29ad31" }, + { + "ImportPath": "github.com/mailgun/oxy/forward", + "Rev": "3a83e7768506e599e700052e260d443496f4bb94" + }, + { + "ImportPath": "github.com/mailgun/oxy/testutils", + "Rev": "3a83e7768506e599e700052e260d443496f4bb94" + }, + { + "ImportPath": "github.com/mailgun/oxy/utils", + "Rev": "3a83e7768506e599e700052e260d443496f4bb94" + }, { "ImportPath": "github.com/samalba/dockerclient", "Rev": "8ee44c0342d57b7daece793afb6c2bdd13220e8f" @@ -60,10 +72,6 @@ { "ImportPath": "golang.org/x/crypto/blowfish", "Rev": "1fbbd62cfec66bd39d91e97749579579d4d3037e" - }, - { - "ImportPath": "gopkg.in/fatih/pool.v2", - "Rev": "dae43b8a8a190d1f2b5908e2f6dd02481e7d59e9" } ] } diff --git a/controller/Godeps/_workspace/src/github.com/docker/docker/pkg/stdcopy/MAINTAINERS b/controller/Godeps/_workspace/src/github.com/docker/docker/pkg/stdcopy/MAINTAINERS deleted file mode 100644 index 6dde4769d..000000000 --- a/controller/Godeps/_workspace/src/github.com/docker/docker/pkg/stdcopy/MAINTAINERS +++ /dev/null @@ -1 +0,0 @@ -Cristian Staretu (@unclejack) diff --git a/controller/Godeps/_workspace/src/github.com/docker/docker/pkg/stdcopy/stdcopy.go b/controller/Godeps/_workspace/src/github.com/docker/docker/pkg/stdcopy/stdcopy.go deleted file mode 100644 index a61779ce5..000000000 --- a/controller/Godeps/_workspace/src/github.com/docker/docker/pkg/stdcopy/stdcopy.go +++ /dev/null @@ -1,172 +0,0 @@ -package stdcopy - -import ( - "encoding/binary" - "errors" - "io" - - log "github.com/Sirupsen/logrus" -) - -const ( - StdWriterPrefixLen = 8 - StdWriterFdIndex = 0 - StdWriterSizeIndex = 4 -) - -type StdType [StdWriterPrefixLen]byte - -var ( - Stdin StdType = StdType{0: 0} - Stdout StdType = StdType{0: 1} - Stderr StdType = StdType{0: 2} -) - -type StdWriter struct { - io.Writer - prefix StdType - sizeBuf []byte -} - -func (w *StdWriter) Write(buf []byte) (n int, err error) { - var n1, n2 int - if w == nil || w.Writer == nil { - return 0, errors.New("Writer not instanciated") - } - binary.BigEndian.PutUint32(w.prefix[4:], uint32(len(buf))) - n1, err = w.Writer.Write(w.prefix[:]) - if err != nil { - n = n1 - StdWriterPrefixLen - } else { - n2, err = w.Writer.Write(buf) - n = n1 + n2 - StdWriterPrefixLen - } - if n < 0 { - n = 0 - } - return -} - -// NewStdWriter instanciates a new Writer. -// Everything written to it will be encapsulated using a custom format, -// and written to the underlying `w` stream. -// This allows multiple write streams (e.g. stdout and stderr) to be muxed into a single connection. -// `t` indicates the id of the stream to encapsulate. -// It can be utils.Stdin, utils.Stdout, utils.Stderr. -func NewStdWriter(w io.Writer, t StdType) *StdWriter { - if len(t) != StdWriterPrefixLen { - return nil - } - - return &StdWriter{ - Writer: w, - prefix: t, - sizeBuf: make([]byte, 4), - } -} - -var ErrInvalidStdHeader = errors.New("Unrecognized input header") - -// StdCopy is a modified version of io.Copy. -// -// StdCopy will demultiplex `src`, assuming that it contains two streams, -// previously multiplexed together using a StdWriter instance. -// As it reads from `src`, StdCopy will write to `dstout` and `dsterr`. -// -// StdCopy will read until it hits EOF on `src`. It will then return a nil error. -// In other words: if `err` is non nil, it indicates a real underlying error. -// -// `written` will hold the total number of bytes written to `dstout` and `dsterr`. -func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error) { - var ( - buf = make([]byte, 32*1024+StdWriterPrefixLen+1) - bufLen = len(buf) - nr, nw int - er, ew error - out io.Writer - frameSize int - ) - - for { - // Make sure we have at least a full header - for nr < StdWriterPrefixLen { - var nr2 int - nr2, er = src.Read(buf[nr:]) - nr += nr2 - if er == io.EOF { - if nr < StdWriterPrefixLen { - log.Debugf("Corrupted prefix: %v", buf[:nr]) - return written, nil - } - break - } - if er != nil { - log.Debugf("Error reading header: %s", er) - return 0, er - } - } - - // Check the first byte to know where to write - switch buf[StdWriterFdIndex] { - case 0: - fallthrough - case 1: - // Write on stdout - out = dstout - case 2: - // Write on stderr - out = dsterr - default: - log.Debugf("Error selecting output fd: (%d)", buf[StdWriterFdIndex]) - return 0, ErrInvalidStdHeader - } - - // Retrieve the size of the frame - frameSize = int(binary.BigEndian.Uint32(buf[StdWriterSizeIndex : StdWriterSizeIndex+4])) - log.Debugf("framesize: %d", frameSize) - - // Check if the buffer is big enough to read the frame. - // Extend it if necessary. - if frameSize+StdWriterPrefixLen > bufLen { - log.Debugf("Extending buffer cap by %d (was %d)", frameSize+StdWriterPrefixLen-bufLen+1, len(buf)) - buf = append(buf, make([]byte, frameSize+StdWriterPrefixLen-bufLen+1)...) - bufLen = len(buf) - } - - // While the amount of bytes read is less than the size of the frame + header, we keep reading - for nr < frameSize+StdWriterPrefixLen { - var nr2 int - nr2, er = src.Read(buf[nr:]) - nr += nr2 - if er == io.EOF { - if nr < frameSize+StdWriterPrefixLen { - log.Debugf("Corrupted frame: %v", buf[StdWriterPrefixLen:nr]) - return written, nil - } - break - } - if er != nil { - log.Debugf("Error reading frame: %s", er) - return 0, er - } - } - - // Write the retrieved frame (without header) - nw, ew = out.Write(buf[StdWriterPrefixLen : frameSize+StdWriterPrefixLen]) - if ew != nil { - log.Debugf("Error writing frame: %s", ew) - return 0, ew - } - // If the frame has not been fully written: error - if nw != frameSize { - log.Debugf("Error Short Write: (%d on %d)", nw, frameSize) - return 0, io.ErrShortWrite - } - written += int64(nw) - - // Move the rest of the buffer to the beginning - copy(buf, buf[frameSize+StdWriterPrefixLen:]) - // Move the index - nr -= frameSize + StdWriterPrefixLen - } -} diff --git a/controller/Godeps/_workspace/src/github.com/docker/docker/pkg/stdcopy/stdcopy_test.go b/controller/Godeps/_workspace/src/github.com/docker/docker/pkg/stdcopy/stdcopy_test.go deleted file mode 100644 index 14e6ed311..000000000 --- a/controller/Godeps/_workspace/src/github.com/docker/docker/pkg/stdcopy/stdcopy_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package stdcopy - -import ( - "bytes" - "io/ioutil" - "testing" -) - -func BenchmarkWrite(b *testing.B) { - w := NewStdWriter(ioutil.Discard, Stdout) - data := []byte("Test line for testing stdwriter performance\n") - data = bytes.Repeat(data, 100) - b.SetBytes(int64(len(data))) - b.ResetTimer() - for i := 0; i < b.N; i++ { - if _, err := w.Write(data); err != nil { - b.Fatal(err) - } - } -} diff --git a/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/.gitignore b/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/.gitignore deleted file mode 100644 index 00268614f..000000000 --- a/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/.gitignore +++ /dev/null @@ -1,22 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe diff --git a/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/.travis.yml b/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/.travis.yml deleted file mode 100644 index d7acc295a..000000000 --- a/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/.travis.yml +++ /dev/null @@ -1,2 +0,0 @@ -language: go -go: 1.3 diff --git a/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/LICENSE b/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/LICENSE deleted file mode 100644 index 25fdaf639..000000000 --- a/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 Fatih Arslan - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/README.md b/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/README.md deleted file mode 100644 index c50bcda46..000000000 --- a/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# Pool [![GoDoc](https://godoc.org/gopkg.in/fatih/pool.v2?status.svg)](https://godoc.org/gopkg.in/fatih/pool.v2) [![Build Status](https://travis-ci.org/fatih/pool.svg)](https://travis-ci.org/fatih/pool) - - -Pool is a thread safe connection pool for net.Conn interface. It can be used to -manage and reuse connections. - - -## Install and Usage - -Install the package with: - -```bash -go get gopkg.in/fatih/pool.v2 -``` - -Import it with: - -```go -import "gopkg.in/fatih/pool.v2" -``` - -and use `pool` as the package name inside the code. - -## Example - -```go -// create a factory() to be used with channel based pool -factory := func() (net.Conn, error) { return net.Dial("tcp", "127.0.0.1:4000") } - -// create a new channel based pool with an initial capacity of 5 and maximum -// capacity of 30. The factory will create 5 initial connections and put it -// into the pool. -p, err := pool.NewChannelPool(5, 30, factory) - -// now you can get a connection from the pool, if there is no connection -// available it will create a new one via the factory function. -conn, err := p.Get() - -// do something with conn and put it back to the pool by closing the connection -// (this doesn't close the underlying connection instead it's putting it back -// to the pool). -conn.Close() - -// close pool any time you want, this closes all the connections inside a pool -p.Close() - -// currently available connections in the pool -current := p.Len() -``` - - -## Credits - - * [Fatih Arslan](https://github.com/fatih) - * [sougou](https://github.com/sougou) - -## License - -The MIT License (MIT) - see LICENSE for more details diff --git a/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/channel.go b/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/channel.go deleted file mode 100644 index 6218fe152..000000000 --- a/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/channel.go +++ /dev/null @@ -1,131 +0,0 @@ -package pool - -import ( - "errors" - "fmt" - "net" - "sync" -) - -// channelPool implements the Pool interface based on buffered channels. -type channelPool struct { - // storage for our net.Conn connections - mu sync.Mutex - conns chan net.Conn - - // net.Conn generator - factory Factory -} - -// Factory is a function to create new connections. -type Factory func() (net.Conn, error) - -// NewChannelPool returns a new pool based on buffered channels with an initial -// capacity and maximum capacity. Factory is used when initial capacity is -// greater than zero to fill the pool. A zero initialCap doesn't fill the Pool -// until a new Get() is called. During a Get(), If there is no new connection -// available in the pool, a new connection will be created via the Factory() -// method. -func NewChannelPool(initialCap, maxCap int, factory Factory) (Pool, error) { - if initialCap < 0 || maxCap <= 0 || initialCap > maxCap { - return nil, errors.New("invalid capacity settings") - } - - c := &channelPool{ - conns: make(chan net.Conn, maxCap), - factory: factory, - } - - // create initial connections, if something goes wrong, - // just close the pool error out. - for i := 0; i < initialCap; i++ { - conn, err := factory() - if err != nil { - c.Close() - return nil, fmt.Errorf("factory is not able to fill the pool: %s", err) - } - c.conns <- conn - } - - return c, nil -} - -func (c *channelPool) getConns() chan net.Conn { - c.mu.Lock() - conns := c.conns - c.mu.Unlock() - return conns -} - -// Get implements the Pool interfaces Get() method. If there is no new -// connection available in the pool, a new connection will be created via the -// Factory() method. -func (c *channelPool) Get() (net.Conn, error) { - conns := c.getConns() - if conns == nil { - return nil, ErrClosed - } - - // wrap our connections with out custom net.Conn implementation (wrapConn - // method) that puts the connection back to the pool if it's closed. - select { - case conn := <-conns: - if conn == nil { - return nil, ErrClosed - } - - return c.wrapConn(conn), nil - default: - conn, err := c.factory() - if err != nil { - return nil, err - } - - return c.wrapConn(conn), nil - } -} - -// put puts the connection back to the pool. If the pool is full or closed, -// conn is simply closed. A nil conn will be rejected. -func (c *channelPool) put(conn net.Conn) error { - if conn == nil { - return errors.New("connection is nil. rejecting") - } - - c.mu.Lock() - defer c.mu.Unlock() - - if c.conns == nil { - // pool is closed, close passed connection - return conn.Close() - } - - // put the resource back into the pool. If the pool is full, this will - // block and the default case will be executed. - select { - case c.conns <- conn: - return nil - default: - // pool is full, close passed connection - return conn.Close() - } -} - -func (c *channelPool) Close() { - c.mu.Lock() - conns := c.conns - c.conns = nil - c.factory = nil - c.mu.Unlock() - - if conns == nil { - return - } - - close(conns) - for conn := range conns { - conn.Close() - } -} - -func (c *channelPool) Len() int { return len(c.getConns()) } diff --git a/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/channel_test.go b/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/channel_test.go deleted file mode 100644 index 642a45652..000000000 --- a/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/channel_test.go +++ /dev/null @@ -1,247 +0,0 @@ -package pool - -import ( - "log" - "math/rand" - "net" - "sync" - "testing" - "time" -) - -var ( - InitialCap = 5 - MaximumCap = 30 - network = "tcp" - address = "127.0.0.1:7777" - factory = func() (net.Conn, error) { return net.Dial(network, address) } -) - -func init() { - // used for factory function - go simpleTCPServer() - time.Sleep(time.Millisecond * 300) // wait until tcp server has been settled - - rand.Seed(time.Now().UTC().UnixNano()) -} - -func TestNew(t *testing.T) { - _, err := newChannelPool() - if err != nil { - t.Errorf("New error: %s", err) - } -} -func TestPool_Get_Impl(t *testing.T) { - p, _ := newChannelPool() - defer p.Close() - - conn, err := p.Get() - if err != nil { - t.Errorf("Get error: %s", err) - } - - _, ok := conn.(poolConn) - if !ok { - t.Errorf("Conn is not of type poolConn") - } -} - -func TestPool_Get(t *testing.T) { - p, _ := newChannelPool() - defer p.Close() - - _, err := p.Get() - if err != nil { - t.Errorf("Get error: %s", err) - } - - // after one get, current capacity should be lowered by one. - if p.Len() != (InitialCap - 1) { - t.Errorf("Get error. Expecting %d, got %d", - (InitialCap - 1), p.Len()) - } - - // get them all - var wg sync.WaitGroup - for i := 0; i < (InitialCap - 1); i++ { - wg.Add(1) - go func() { - defer wg.Done() - _, err := p.Get() - if err != nil { - t.Errorf("Get error: %s", err) - } - }() - } - wg.Wait() - - if p.Len() != 0 { - t.Errorf("Get error. Expecting %d, got %d", - (InitialCap - 1), p.Len()) - } - - _, err = p.Get() - if err != nil { - t.Errorf("Get error: %s", err) - } -} - -func TestPool_Put(t *testing.T) { - p, err := NewChannelPool(0, 30, factory) - if err != nil { - t.Fatal(err) - } - defer p.Close() - - // get/create from the pool - conns := make([]net.Conn, MaximumCap) - for i := 0; i < MaximumCap; i++ { - conn, _ := p.Get() - conns[i] = conn - } - - // now put them all back - for _, conn := range conns { - conn.Close() - } - - if p.Len() != MaximumCap { - t.Errorf("Put error len. Expecting %d, got %d", - 1, p.Len()) - } - - conn, _ := p.Get() - p.Close() // close pool - - conn.Close() // try to put into a full pool - if p.Len() != 0 { - t.Errorf("Put error. Closed pool shouldn't allow to put connections.") - } -} - -func TestPool_UsedCapacity(t *testing.T) { - p, _ := newChannelPool() - defer p.Close() - - if p.Len() != InitialCap { - t.Errorf("InitialCap error. Expecting %d, got %d", - InitialCap, p.Len()) - } -} - -func TestPool_Close(t *testing.T) { - p, _ := newChannelPool() - - // now close it and test all cases we are expecting. - p.Close() - - c := p.(*channelPool) - - if c.conns != nil { - t.Errorf("Close error, conns channel should be nil") - } - - if c.factory != nil { - t.Errorf("Close error, factory should be nil") - } - - _, err := p.Get() - if err == nil { - t.Errorf("Close error, get conn should return an error") - } - - if p.Len() != 0 { - t.Errorf("Close error used capacity. Expecting 0, got %d", p.Len()) - } -} - -func TestPoolConcurrent(t *testing.T) { - p, _ := newChannelPool() - pipe := make(chan net.Conn, 0) - - go func() { - p.Close() - }() - - for i := 0; i < MaximumCap; i++ { - go func() { - conn, _ := p.Get() - - pipe <- conn - }() - - go func() { - conn := <-pipe - if conn == nil { - return - } - conn.Close() - }() - } -} - -func TestPoolWriteRead(t *testing.T) { - p, _ := NewChannelPool(0, 30, factory) - - conn, _ := p.Get() - - msg := "hello" - _, err := conn.Write([]byte(msg)) - if err != nil { - t.Error(err) - } -} - -func TestPoolConcurrent2(t *testing.T) { - p, _ := NewChannelPool(0, 30, factory) - - var wg sync.WaitGroup - - go func() { - for i := 0; i < 10; i++ { - wg.Add(1) - go func(i int) { - conn, _ := p.Get() - time.Sleep(time.Millisecond * time.Duration(rand.Intn(100))) - conn.Close() - wg.Done() - }(i) - } - }() - - for i := 0; i < 10; i++ { - wg.Add(1) - go func(i int) { - conn, _ := p.Get() - time.Sleep(time.Millisecond * time.Duration(rand.Intn(100))) - conn.Close() - wg.Done() - }(i) - } - - wg.Wait() -} - -func newChannelPool() (Pool, error) { - return NewChannelPool(InitialCap, MaximumCap, factory) -} - -func simpleTCPServer() { - l, err := net.Listen(network, address) - if err != nil { - log.Fatal(err) - } - defer l.Close() - - for { - conn, err := l.Accept() - if err != nil { - log.Fatal(err) - } - - go func() { - buffer := make([]byte, 256) - conn.Read(buffer) - }() - } -} diff --git a/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/conn.go b/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/conn.go deleted file mode 100644 index b87ac7173..000000000 --- a/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/conn.go +++ /dev/null @@ -1,22 +0,0 @@ -package pool - -import "net" - -// poolConn is a wrapper around net.Conn to modify the the behavior of -// net.Conn's Close() method. -type poolConn struct { - net.Conn - c *channelPool -} - -// Close() puts the given connects back to the pool instead of closing it. -func (p poolConn) Close() error { - return p.c.put(p.Conn) -} - -// newConn wraps a standard net.Conn to a poolConn net.Conn. -func (c *channelPool) wrapConn(conn net.Conn) net.Conn { - p := poolConn{c: c} - p.Conn = conn - return p -} diff --git a/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/conn_test.go b/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/conn_test.go deleted file mode 100644 index 6d287c62d..000000000 --- a/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/conn_test.go +++ /dev/null @@ -1,10 +0,0 @@ -package pool - -import ( - "net" - "testing" -) - -func TestConn_Impl(t *testing.T) { - var _ net.Conn = new(poolConn) -} diff --git a/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/pool.go b/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/pool.go deleted file mode 100644 index f88f2acbb..000000000 --- a/controller/Godeps/_workspace/src/gopkg.in/fatih/pool.v2/pool.go +++ /dev/null @@ -1,28 +0,0 @@ -// Package pool implements a pool of net.Conn interfaces to manage and reuse them. -package pool - -import ( - "errors" - "net" -) - -var ( - // ErrClosed is the error resulting if the pool is closed via pool.Close(). - ErrClosed = errors.New("pool is closed") -) - -// Pool interface describes a pool implementation. A pool should have maximum -// capacity. An ideal pool is threadsafe and easy to use. -type Pool interface { - // Get returns a new connection from the pool. Closing the connections puts - // it back to the Pool. Closing it when the pool is destroyed or full will - // be counted as an error. - Get() (net.Conn, error) - - // Close closes the pool and all its connections. After Close() the pool is - // no longer usable. - Close() - - // Len returns the current number of connections of the pool. - Len() int -} From d36f49f9378cefbb374c444a17ccdd158326c4b2 Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Fri, 3 Apr 2015 17:31:33 -0400 Subject: [PATCH 009/238] add api for http api; update middleware; add commit versioning Signed-off-by: Evan Hazlett --- controller/Makefile | 3 +- controller/api/api.go | 601 +++++++++++++++++++++++++ controller/api/api_test.go | 322 +++++++++++++ controller/commands/server.go | 590 +----------------------- controller/main.go | 4 +- controller/manager/handler.go | 2 +- controller/manager/manager.go | 121 +++-- controller/manager/manager_test.go | 1 - controller/middleware/access/access.go | 6 +- controller/middleware/auth/auth.go | 6 +- controller/mock_test/helpers.go | 68 +++ controller/mock_test/manager_mock.go | 142 ++++++ version.go | 3 - version/version.go | 6 + 14 files changed, 1239 insertions(+), 636 deletions(-) create mode 100644 controller/api/api.go create mode 100644 controller/api/api_test.go delete mode 100644 controller/manager/manager_test.go create mode 100644 controller/mock_test/helpers.go create mode 100644 controller/mock_test/manager_mock.go delete mode 100644 version.go create mode 100644 version/version.go diff --git a/controller/Makefile b/controller/Makefile index e66a1c695..b434f1eec 100644 --- a/controller/Makefile +++ b/controller/Makefile @@ -2,6 +2,7 @@ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 TAG=${TAG:-latest} +COMMIT=`git rev-parse --short HEAD` all: deps build @@ -12,7 +13,7 @@ clean: @rm -rf Godeps/_workspace controller build: - @godep go build -a -tags 'netgo' -ldflags '-w -linkmode external -extldflags -static' . + @godep go build -a -tags 'netgo' -ldflags "-w -X github.com/shipyard/shipyard/version.GitCommit $(COMMIT) -linkmode external -extldflags -static" . media: @cd static && bower -s install --allow-root -p | xargs echo diff --git a/controller/api/api.go b/controller/api/api.go new file mode 100644 index 000000000..3e8689f3f --- /dev/null +++ b/controller/api/api.go @@ -0,0 +1,601 @@ +package api + +import ( + "encoding/json" + "fmt" + "net/http" + "strconv" + "strings" + + log "github.com/Sirupsen/logrus" + "github.com/codegangsta/negroni" + "github.com/gorilla/context" + "github.com/gorilla/mux" + "github.com/mailgun/oxy/forward" + "github.com/mailgun/oxy/testutils" + "github.com/shipyard/shipyard/auth" + "github.com/shipyard/shipyard/controller/manager" + "github.com/shipyard/shipyard/controller/middleware/access" + mAuth "github.com/shipyard/shipyard/controller/middleware/auth" + "github.com/shipyard/shipyard/dockerhub" +) + +type ( + Api struct { + listenAddr string + manager manager.Manager + authWhitelistCIDRs []string + enableCors bool + } + + Credentials struct { + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + } +) + +func writeCorsHeaders(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Access-Control-Allow-Origin", "*") + w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept") + w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS") +} + +func NewApi(listenAddr string, manager manager.Manager, authWhitelistCIDRs []string, enableCors bool) (*Api, error) { + return &Api{ + listenAddr: listenAddr, + manager: manager, + authWhitelistCIDRs: authWhitelistCIDRs, + enableCors: enableCors, + }, nil +} + +func (a *Api) addServiceKey(w http.ResponseWriter, r *http.Request) { + var k *auth.ServiceKey + if err := json.NewDecoder(r.Body).Decode(&k); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + key, err := a.manager.NewServiceKey(k.Description) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + log.Infof("created service key key=%s description=%s", key.Key, key.Description) + if err := json.NewEncoder(w).Encode(key); err != nil { + log.Error(err) + } +} + +func (a *Api) serviceKeys(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", "application/json") + + keys, err := a.manager.ServiceKeys() + if err != nil { + log.Error(err) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if err := json.NewEncoder(w).Encode(keys); err != nil { + log.Error(err) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } +} + +func (a *Api) removeServiceKey(w http.ResponseWriter, r *http.Request) { + var key *auth.ServiceKey + if err := json.NewDecoder(r.Body).Decode(&key); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err := a.manager.RemoveServiceKey(key.Key); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + log.Infof("removed service key %s", key.Key) + w.WriteHeader(http.StatusNoContent) +} + +func (a *Api) events(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", "application/json") + + limit := -1 + l := r.FormValue("limit") + if l != "" { + lt, err := strconv.Atoi(l) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + limit = lt + } + events, err := a.manager.Events(limit) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err := json.NewEncoder(w).Encode(events); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func (a *Api) purgeEvents(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", "application/json") + + if err := a.manager.PurgeEvents(); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + log.Info("cluster events purged") + w.WriteHeader(http.StatusNoContent) +} + +func (a *Api) accounts(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", "application/json") + + accounts, err := a.manager.Accounts() + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err := json.NewEncoder(w).Encode(accounts); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func (a *Api) addAccount(w http.ResponseWriter, r *http.Request) { + var account *auth.Account + if err := json.NewDecoder(r.Body).Decode(&account); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + if err := a.manager.SaveAccount(account); err != nil { + log.Errorf("error saving account: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + log.Infof("saved account %s", account.Username) + w.WriteHeader(http.StatusNoContent) +} + +func (a *Api) deleteAccount(w http.ResponseWriter, r *http.Request) { + var acct *auth.Account + if err := json.NewDecoder(r.Body).Decode(&acct); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + account, err := a.manager.Account(acct.Username) + if err != nil { + log.Errorf("error deleting account: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err := a.manager.DeleteAccount(account); err != nil { + log.Errorf("error deleting account: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + log.Infof("deleted account %s (%s)", account.Username, account.ID) + w.WriteHeader(http.StatusNoContent) +} + +func (a *Api) roles(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", "application/json") + + roles, err := a.manager.Roles() + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err := json.NewEncoder(w).Encode(roles); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func (a *Api) role(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", "application/json") + + vars := mux.Vars(r) + name := vars["name"] + role, err := a.manager.Role(name) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err := json.NewEncoder(w).Encode(role); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func (a *Api) addRole(w http.ResponseWriter, r *http.Request) { + var role *auth.Role + if err := json.NewDecoder(r.Body).Decode(&role); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + if err := a.manager.SaveRole(role); err != nil { + log.Errorf("error saving role: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + log.Infof("saved role %s", role.Name) + w.WriteHeader(http.StatusNoContent) +} + +func (a *Api) deleteRole(w http.ResponseWriter, r *http.Request) { + var role *auth.Role + if err := json.NewDecoder(r.Body).Decode(&role); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err := a.manager.DeleteRole(role); err != nil { + log.Errorf("error deleting role: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func (a *Api) webhookKeys(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", "application/json") + + keys, err := a.manager.WebhookKeys() + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err := json.NewEncoder(w).Encode(keys); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func (a *Api) webhookKey(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", "application/json") + + vars := mux.Vars(r) + id := vars["id"] + key, err := a.manager.WebhookKey(id) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err := json.NewEncoder(w).Encode(key); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func (a *Api) addWebhookKey(w http.ResponseWriter, r *http.Request) { + var k *dockerhub.WebhookKey + if err := json.NewDecoder(r.Body).Decode(&k); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + key, err := a.manager.NewWebhookKey(k.Image) + if err != nil { + log.Errorf("error generating webhook key: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + log.Infof("saved webhook key image=%s", key.Image) + if err := json.NewEncoder(w).Encode(key); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func (a *Api) deleteWebhookKey(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + id := vars["id"] + if err := a.manager.DeleteWebhookKey(id); err != nil { + log.Errorf("error deleting webhook key: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + log.Infof("removed webhook key id=%s", id) + w.WriteHeader(http.StatusNoContent) +} + +func (a *Api) login(w http.ResponseWriter, r *http.Request) { + var creds *Credentials + if err := json.NewDecoder(r.Body).Decode(&creds); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if !a.manager.Authenticate(creds.Username, creds.Password) { + log.Errorf("invalid login for %s from %s", creds.Username, r.RemoteAddr) + http.Error(w, "invalid username/password", http.StatusForbidden) + return + } + // return token + token, err := a.manager.NewAuthToken(creds.Username, r.UserAgent()) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err := json.NewEncoder(w).Encode(token); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func (a *Api) changePassword(w http.ResponseWriter, r *http.Request) { + session, _ := a.manager.Store().Get(r, a.manager.StoreKey()) + var creds *Credentials + if err := json.NewDecoder(r.Body).Decode(&creds); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + username := session.Values["username"].(string) + if username == "" { + http.Error(w, "unauthorized", http.StatusInternalServerError) + return + } + if err := a.manager.ChangePassword(username, creds.Password); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func (a *Api) hubWebhook(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + id := vars["id"] + key, err := a.manager.WebhookKey(id) + if err != nil { + log.Errorf("invalid webook key: id=%s from %s", id, r.RemoteAddr) + http.Error(w, err.Error(), http.StatusNotFound) + return + } + var webhook *dockerhub.Webhook + if err := json.NewDecoder(r.Body).Decode(&webhook); err != nil { + log.Errorf("error parsing webhook: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if strings.Index(webhook.Repository.RepoName, key.Image) == -1 { + log.Errorf("webhook key image does not match: repo=%s image=%s", webhook.Repository.RepoName, key.Image) + http.Error(w, "not found", http.StatusNotFound) + return + } + log.Infof("received webhook notification for %s", webhook.Repository.RepoName) + // TODO @ehazlett - redeploy containers +} + +func (a *Api) Run() error { + globalMux := http.NewServeMux() + controllerManager := a.manager + client := a.manager.DockerClient() + + // forwarder for swarm + fwd, err := forward.New() + if err != nil { + return err + } + + u := client.URL + + // setup redirect target to swarm + scheme := "http://" + + // check if TLS is enabled and configure if so + if client.TLSConfig != nil { + scheme = "https://" + // setup custom roundtripper with TLS transport + r := forward.RoundTripper( + &http.Transport{ + TLSClientConfig: client.TLSConfig, + }) + f, err := forward.New(r) + if err != nil { + log.Fatal(err) + } + + fwd = f + } + + dUrl := fmt.Sprintf("%s%s", scheme, u.Host) + + log.Debugf("configured docker proxy target: %s", dUrl) + + swarmRedirect := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + req.URL = testutils.ParseURI(dUrl) + fwd.ServeHTTP(w, req) + }) + + apiRouter := mux.NewRouter() + apiRouter.HandleFunc("/api/accounts", a.accounts).Methods("GET") + apiRouter.HandleFunc("/api/accounts", a.addAccount).Methods("POST") + apiRouter.HandleFunc("/api/accounts", a.deleteAccount).Methods("DELETE") + apiRouter.HandleFunc("/api/roles", a.roles).Methods("GET") + apiRouter.HandleFunc("/api/roles/{name}", a.role).Methods("GET") + apiRouter.HandleFunc("/api/roles", a.addRole).Methods("POST") + apiRouter.HandleFunc("/api/roles", a.deleteRole).Methods("DELETE") + apiRouter.HandleFunc("/api/events", a.events).Methods("GET") + apiRouter.HandleFunc("/api/events", a.purgeEvents).Methods("DELETE") + apiRouter.HandleFunc("/api/servicekeys", a.serviceKeys).Methods("GET") + apiRouter.HandleFunc("/api/servicekeys", a.addServiceKey).Methods("POST") + apiRouter.HandleFunc("/api/servicekeys", a.removeServiceKey).Methods("DELETE") + apiRouter.HandleFunc("/api/webhookkeys", a.webhookKeys).Methods("GET") + apiRouter.HandleFunc("/api/webhookkeys/{id}", a.webhookKey).Methods("GET") + apiRouter.HandleFunc("/api/webhookkeys", a.addWebhookKey).Methods("POST") + apiRouter.HandleFunc("/api/webhookkeys/{id}", a.deleteWebhookKey).Methods("DELETE") + + // global handler + globalMux.Handle("/", http.FileServer(http.Dir("static"))) + + // api router; protected by auth + apiAuthRouter := negroni.New() + apiAuthRequired := mAuth.NewAuthRequired(controllerManager, a.authWhitelistCIDRs) + apiAccessRequired := access.NewAccessRequired(controllerManager) + apiAuthRouter.Use(negroni.HandlerFunc(apiAuthRequired.HandlerFuncWithNext)) + apiAuthRouter.Use(negroni.HandlerFunc(apiAccessRequired.HandlerFuncWithNext)) + apiAuthRouter.UseHandler(apiRouter) + globalMux.Handle("/api/", apiAuthRouter) + + // account router ; protected by auth + accountRouter := mux.NewRouter() + accountRouter.HandleFunc("/account/changepassword", a.changePassword).Methods("POST") + accountAuthRouter := negroni.New() + accountAuthRequired := mAuth.NewAuthRequired(controllerManager, a.authWhitelistCIDRs) + accountAuthRouter.Use(negroni.HandlerFunc(accountAuthRequired.HandlerFuncWithNext)) + accountAuthRouter.UseHandler(accountRouter) + globalMux.Handle("/account/", accountAuthRouter) + + // login handler; public + loginRouter := mux.NewRouter() + loginRouter.HandleFunc("/auth/login", a.login).Methods("POST") + globalMux.Handle("/auth/", loginRouter) + + // hub handler; public + hubRouter := mux.NewRouter() + hubRouter.HandleFunc("/hub/webhook/{id}", a.hubWebhook).Methods("POST") + globalMux.Handle("/hub/", hubRouter) + + // swarm + swarmRouter := mux.NewRouter() + // these are pulled from the swarm api code to proxy and allow + // usage with the standard Docker cli + m := map[string]map[string]http.HandlerFunc{ + "GET": { + "/_ping": swarmRedirect, + "/events": swarmRedirect, + "/info": swarmRedirect, + "/version": swarmRedirect, + "/images/json": swarmRedirect, + "/images/viz": swarmRedirect, + "/images/search": swarmRedirect, + "/images/get": swarmRedirect, + "/images/{name:.*}/get": swarmRedirect, + "/images/{name:.*}/history": swarmRedirect, + "/images/{name:.*}/json": swarmRedirect, + "/containers/ps": swarmRedirect, + "/containers/json": swarmRedirect, + "/containers/{name:.*}/export": swarmRedirect, + "/containers/{name:.*}/changes": swarmRedirect, + "/containers/{name:.*}/json": swarmRedirect, + "/containers/{name:.*}/top": swarmRedirect, + "/containers/{name:.*}/logs": swarmRedirect, + "/containers/{name:.*}/stats": swarmRedirect, + "/containers/{name:.*}/attach/ws": swarmRedirect, + "/exec/{execid:.*}/json": swarmRedirect, + }, + "POST": { + "/auth": swarmRedirect, + "/commit": swarmRedirect, + "/build": swarmRedirect, + "/images/create": swarmRedirect, + "/images/load": swarmRedirect, + "/images/{name:.*}/push": swarmRedirect, + "/images/{name:.*}/tag": swarmRedirect, + "/containers/create": swarmRedirect, + "/containers/{name:.*}/kill": swarmRedirect, + "/containers/{name:.*}/pause": swarmRedirect, + "/containers/{name:.*}/unpause": swarmRedirect, + "/containers/{name:.*}/rename": swarmRedirect, + "/containers/{name:.*}/restart": swarmRedirect, + "/containers/{name:.*}/start": swarmRedirect, + "/containers/{name:.*}/stop": swarmRedirect, + "/containers/{name:.*}/wait": swarmRedirect, + "/containers/{name:.*}/resize": swarmRedirect, + "/containers/{name:.*}/attach": swarmRedirect, + "/containers/{name:.*}/copy": swarmRedirect, + "/containers/{name:.*}/exec": swarmRedirect, + "/exec/{execid:.*}/start": swarmRedirect, + "/exec/{execid:.*}/resize": swarmRedirect, + }, + "DELETE": { + "/containers/{name:.*}": swarmRedirect, + "/images/{name:.*}": swarmRedirect, + }, + "OPTIONS": { + "": swarmRedirect, + }, + } + + for method, routes := range m { + for route, fct := range routes { + localRoute := route + localFct := fct + wrap := func(w http.ResponseWriter, r *http.Request) { + if a.enableCors { + writeCorsHeaders(w, r) + } + localFct(w, r) + } + localMethod := method + + // add the new route + swarmRouter.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(wrap) + swarmRouter.Path(localRoute).Methods(localMethod).HandlerFunc(wrap) + } + } + + swarmAuthRouter := negroni.New() + swarmAuthRequired := mAuth.NewAuthRequired(controllerManager, a.authWhitelistCIDRs) + swarmAccessRequired := access.NewAccessRequired(controllerManager) + swarmAuthRouter.Use(negroni.HandlerFunc(swarmAuthRequired.HandlerFuncWithNext)) + swarmAuthRouter.Use(negroni.HandlerFunc(swarmAccessRequired.HandlerFuncWithNext)) + swarmAuthRouter.UseHandler(swarmRouter) + globalMux.Handle("/containers/", swarmAuthRouter) + globalMux.Handle("/_ping", swarmAuthRouter) + globalMux.Handle("/commit", swarmAuthRouter) + globalMux.Handle("/build", swarmAuthRouter) + globalMux.Handle("/events", swarmAuthRouter) + globalMux.Handle("/version", swarmAuthRouter) + globalMux.Handle("/images/", swarmAuthRouter) + globalMux.Handle("/exec/", swarmAuthRouter) + globalMux.Handle("/v1.17/", swarmAuthRouter) + globalMux.Handle("/v1.18/", swarmAuthRouter) + + // check for admin user + if _, err := controllerManager.Account("admin"); err == manager.ErrAccountDoesNotExist { + // create roles + r := &auth.Role{ + Name: "admin", + } + ru := &auth.Role{ + Name: "user", + } + if err := controllerManager.SaveRole(r); err != nil { + log.Fatal(err) + } + if err := controllerManager.SaveRole(ru); err != nil { + log.Fatal(err) + } + role, err := controllerManager.Role(r.Name) + if err != nil { + log.Fatal(err) + } + acct := &auth.Account{ + Username: "admin", + Password: "shipyard", + Role: role, + } + if err := controllerManager.SaveAccount(acct); err != nil { + log.Fatal(err) + } + log.Infof("created admin user: username: admin password: shipyard") + } + + log.Infof("controller listening on %s", a.listenAddr) + + s := &http.Server{ + Addr: a.listenAddr, + Handler: context.ClearHandler(globalMux), + } + + if err := s.ListenAndServe(); err != nil { + log.Fatal(err) + } + + return http.ListenAndServe(a.listenAddr, context.ClearHandler(globalMux)) +} diff --git a/controller/api/api_test.go b/controller/api/api_test.go new file mode 100644 index 000000000..7a9808d0c --- /dev/null +++ b/controller/api/api_test.go @@ -0,0 +1,322 @@ +package api + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "testing" + + log "github.com/Sirupsen/logrus" + "github.com/shipyard/shipyard" + "github.com/shipyard/shipyard/auth" + "github.com/shipyard/shipyard/controller/mock_test" + "github.com/shipyard/shipyard/dockerhub" + "github.com/stretchr/testify/assert" +) + +func getTestApi() (*Api, error) { + log.SetLevel(log.ErrorLevel) + m := mock_test.MockManager{} + return NewApi("", m, nil, false) +} + +func TestApiGetAccounts(t *testing.T) { + api, err := getTestApi() + if err != nil { + t.Fatal(err) + } + + ts := httptest.NewServer(http.HandlerFunc(api.accounts)) + defer ts.Close() + + res, err := http.Get(ts.URL) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, res.StatusCode, 200, "expected response code 200") + accts := []*auth.Account{} + if err := json.NewDecoder(res.Body).Decode(&accts); err != nil { + t.Fatal(err) + } + + assert.NotEqual(t, len(accts), 0, "expected accounts; received none") + + acct := accts[0] + + assert.Equal(t, acct.ID, mock_test.TestAccount.ID, fmt.Sprintf("expected ID %s; got %s", mock_test.TestAccount.ID, acct.ID)) +} + +func TestApiPostAccounts(t *testing.T) { + api, err := getTestApi() + if err != nil { + t.Fatal(err) + } + + ts := httptest.NewServer(http.HandlerFunc(api.addAccount)) + defer ts.Close() + + data := []byte(`{"username": "testuser", "password": "foo"}`) + + res, err := http.Post(ts.URL, "application/json", bytes.NewBuffer(data)) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, res.StatusCode, 204, "expected response code 204") +} + +func TestApiDeleteAccount(t *testing.T) { + api, err := getTestApi() + if err != nil { + t.Fatal(err) + } + + transport := &http.Transport{} + client := &http.Client{Transport: transport} + + ts := httptest.NewServer(http.HandlerFunc(api.addAccount)) + defer ts.Close() + + data := []byte(`{"username": "testuser", "password": "foo"}`) + + req, err := http.NewRequest("DELETE", ts.URL, bytes.NewBuffer(data)) + if err != nil { + t.Fatal(err) + } + + res, err := client.Do(req) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, res.StatusCode, 204, "expected response code 204") +} + +func TestApiGetRoles(t *testing.T) { + api, err := getTestApi() + if err != nil { + t.Fatal(err) + } + + ts := httptest.NewServer(http.HandlerFunc(api.roles)) + defer ts.Close() + + res, err := http.Get(ts.URL) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, res.StatusCode, 200, "expected response code 200") + roles := []*auth.Role{} + if err := json.NewDecoder(res.Body).Decode(&roles); err != nil { + t.Fatal(err) + } + + assert.NotEqual(t, len(roles), 0, "expected roles; received none") + + role := roles[0] + + assert.Equal(t, role.ID, mock_test.TestRole.ID, fmt.Sprintf("expected ID %s; got %s", mock_test.TestRole.ID, role.ID)) +} + +func TestApiGetRole(t *testing.T) { + api, err := getTestApi() + if err != nil { + t.Fatal(err) + } + + ts := httptest.NewServer(http.HandlerFunc(api.role)) + defer ts.Close() + + res, err := http.Get(ts.URL) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, res.StatusCode, 200, "expected response code 200") + role := &auth.Role{} + if err := json.NewDecoder(res.Body).Decode(&role); err != nil { + t.Fatal(err) + } + + assert.NotEqual(t, role.ID, nil, "expected role; received nil") + + assert.Equal(t, role.ID, mock_test.TestRole.ID, fmt.Sprintf("expected ID %s; got %s", mock_test.TestRole.ID, role.ID)) +} + +func TestApiPostRoles(t *testing.T) { + api, err := getTestApi() + if err != nil { + t.Fatal(err) + } + + ts := httptest.NewServer(http.HandlerFunc(api.addRole)) + defer ts.Close() + + data := []byte(`{"name": "testrole"}`) + + res, err := http.Post(ts.URL, "application/json", bytes.NewBuffer(data)) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, res.StatusCode, 204, "expected response code 204") +} + +func TestApiDeleteRole(t *testing.T) { + api, err := getTestApi() + if err != nil { + t.Fatal(err) + } + + transport := &http.Transport{} + client := &http.Client{Transport: transport} + + ts := httptest.NewServer(http.HandlerFunc(api.addAccount)) + defer ts.Close() + + data := []byte(`{"name": "testrole"}`) + + req, err := http.NewRequest("DELETE", ts.URL, bytes.NewBuffer(data)) + if err != nil { + t.Fatal(err) + } + + res, err := client.Do(req) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, res.StatusCode, 204, "expected response code 204") +} + +func TestApiGetEvents(t *testing.T) { + api, err := getTestApi() + if err != nil { + t.Fatal(err) + } + + ts := httptest.NewServer(http.HandlerFunc(api.events)) + defer ts.Close() + + res, err := http.Get(ts.URL) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, res.StatusCode, 200, "expected response code 200") + events := []*shipyard.Event{} + + if err := json.NewDecoder(res.Body).Decode(&events); err != nil { + t.Fatal(err) + } + + assert.NotEqual(t, len(events), 0, "expected events; received none") +} + +func TestApiPurgeEvents(t *testing.T) { + api, err := getTestApi() + if err != nil { + t.Fatal(err) + } + + transport := &http.Transport{} + client := &http.Client{Transport: transport} + + ts := httptest.NewServer(http.HandlerFunc(api.purgeEvents)) + defer ts.Close() + + req, err := http.NewRequest("DELETE", ts.URL, nil) + if err != nil { + t.Fatal(err) + } + + res, err := client.Do(req) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, res.StatusCode, 204, "expected response code 204") +} + +func TestApiGetSerivceKeys(t *testing.T) { + api, err := getTestApi() + if err != nil { + t.Fatal(err) + } + + ts := httptest.NewServer(http.HandlerFunc(api.serviceKeys)) + defer ts.Close() + + res, err := http.Get(ts.URL) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, res.StatusCode, 200, "expected response code 200") + keys := []*auth.ServiceKey{} + + if err := json.NewDecoder(res.Body).Decode(&keys); err != nil { + t.Fatal(err) + } + + assert.NotEqual(t, len(keys), 0, "expected keys; received none") +} + +func TestApiRemoveServiceKey(t *testing.T) { + api, err := getTestApi() + if err != nil { + t.Fatal(err) + } + + transport := &http.Transport{} + client := &http.Client{Transport: transport} + + ts := httptest.NewServer(http.HandlerFunc(api.removeServiceKey)) + defer ts.Close() + + data := []byte(`{"key": "test-key"}`) + + req, err := http.NewRequest("DELETE", ts.URL, bytes.NewBuffer(data)) + if err != nil { + t.Fatal(err) + } + + res, err := client.Do(req) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, res.StatusCode, 204, "expected response code 204") +} + +func TestApiGetWebhookKeys(t *testing.T) { + api, err := getTestApi() + if err != nil { + t.Fatal(err) + } + + ts := httptest.NewServer(http.HandlerFunc(api.webhookKeys)) + defer ts.Close() + + res, err := http.Get(ts.URL) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, res.StatusCode, 200, "expected response code 200") + keys := []*dockerhub.WebhookKey{} + + if err := json.NewDecoder(res.Body).Decode(&keys); err != nil { + t.Fatal(err) + } + + assert.NotEqual(t, len(keys), 0, "expected keys; received none") + + key := "abcdefg" + + assert.Equal(t, keys[0].Key, key, "expected key %s; received %s", key, keys[0].Key) +} diff --git a/controller/commands/server.go b/controller/commands/server.go index ba5491bec..16229e1f3 100644 --- a/controller/commands/server.go +++ b/controller/commands/server.go @@ -1,367 +1,18 @@ package commands import ( - "encoding/json" - "fmt" - "net/http" - "net/url" - "strconv" - "strings" - log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" - "github.com/codegangsta/negroni" - "github.com/gorilla/context" - "github.com/gorilla/mux" - "github.com/mailgun/oxy/forward" - "github.com/mailgun/oxy/testutils" - "github.com/shipyard/shipyard" - "github.com/shipyard/shipyard/auth" + "github.com/shipyard/shipyard/controller/api" "github.com/shipyard/shipyard/controller/manager" - "github.com/shipyard/shipyard/controller/middleware/access" - mAuth "github.com/shipyard/shipyard/controller/middleware/auth" - "github.com/shipyard/shipyard/dockerhub" "github.com/shipyard/shipyard/utils" + "github.com/shipyard/shipyard/version" ) var ( controllerManager *manager.Manager ) -type ( - Credentials struct { - Username string `json:"username,omitempty"` - Password string `json:"password,omitempty"` - } -) - -func addServiceKey(w http.ResponseWriter, r *http.Request) { - var k *auth.ServiceKey - if err := json.NewDecoder(r.Body).Decode(&k); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - key, err := controllerManager.NewServiceKey(k.Description) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - log.Infof("created service key key=%s description=%s", key.Key, key.Description) - if err := json.NewEncoder(w).Encode(key); err != nil { - log.Error(err) - } -} - -func serviceKeys(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - keys, err := controllerManager.ServiceKeys() - if err != nil { - log.Error(err) - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - if err := json.NewEncoder(w).Encode(keys); err != nil { - log.Error(err) - http.Error(w, err.Error(), http.StatusBadRequest) - return - } -} - -func removeServiceKey(w http.ResponseWriter, r *http.Request) { - var key *auth.ServiceKey - if err := json.NewDecoder(r.Body).Decode(&key); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := controllerManager.RemoveServiceKey(key.Key); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - log.Infof("removed service key %s", key.Key) - w.WriteHeader(http.StatusNoContent) -} - -func events(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - limit := -1 - l := r.FormValue("limit") - if l != "" { - lt, err := strconv.Atoi(l) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - limit = lt - } - events, err := controllerManager.Events(limit) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := json.NewEncoder(w).Encode(events); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func purgeEvents(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - if err := controllerManager.PurgeEvents(); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - log.Info("cluster events purged") - w.WriteHeader(http.StatusNoContent) -} - -func accounts(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - accounts, err := controllerManager.Accounts() - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := json.NewEncoder(w).Encode(accounts); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func addAccount(w http.ResponseWriter, r *http.Request) { - var account *auth.Account - if err := json.NewDecoder(r.Body).Decode(&account); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - if err := controllerManager.SaveAccount(account); err != nil { - log.Errorf("error saving account: %s", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - log.Infof("saved account %s", account.Username) - w.WriteHeader(http.StatusNoContent) -} - -func deleteAccount(w http.ResponseWriter, r *http.Request) { - var acct *auth.Account - if err := json.NewDecoder(r.Body).Decode(&acct); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - account, err := controllerManager.Account(acct.Username) - if err != nil { - log.Errorf("error deleting account: %s", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := controllerManager.DeleteAccount(account); err != nil { - log.Errorf("error deleting account: %s", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - log.Infof("deleted account %s (%s)", account.Username, account.ID) - w.WriteHeader(http.StatusNoContent) -} - -func roles(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - roles, err := controllerManager.Roles() - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := json.NewEncoder(w).Encode(roles); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func role(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - vars := mux.Vars(r) - name := vars["name"] - role, err := controllerManager.Role(name) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := json.NewEncoder(w).Encode(role); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func addRole(w http.ResponseWriter, r *http.Request) { - var role *auth.Role - if err := json.NewDecoder(r.Body).Decode(&role); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - if err := controllerManager.SaveRole(role); err != nil { - log.Errorf("error saving role: %s", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - log.Infof("saved role %s", role.Name) - w.WriteHeader(http.StatusNoContent) -} - -func deleteRole(w http.ResponseWriter, r *http.Request) { - var role *auth.Role - if err := json.NewDecoder(r.Body).Decode(&role); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := controllerManager.DeleteRole(role); err != nil { - log.Errorf("error deleting role: %s", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func webhookKeys(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - keys, err := controllerManager.WebhookKeys() - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := json.NewEncoder(w).Encode(keys); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func webhookKey(w http.ResponseWriter, r *http.Request) { - w.Header().Set("content-type", "application/json") - - vars := mux.Vars(r) - id := vars["id"] - key, err := controllerManager.WebhookKey(id) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := json.NewEncoder(w).Encode(key); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func addWebhookKey(w http.ResponseWriter, r *http.Request) { - var k *dockerhub.WebhookKey - if err := json.NewDecoder(r.Body).Decode(&k); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - key, err := controllerManager.NewWebhookKey(k.Image) - if err != nil { - log.Errorf("error generating webhook key: %s", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - log.Infof("saved webhook key image=%s", key.Image) - if err := json.NewEncoder(w).Encode(key); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func deleteWebhookKey(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - id := vars["id"] - if err := controllerManager.DeleteWebhookKey(id); err != nil { - log.Errorf("error deleting webhook key: %s", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - log.Infof("removed webhook key id=%s", id) - w.WriteHeader(http.StatusNoContent) -} - -func login(w http.ResponseWriter, r *http.Request) { - var creds *Credentials - if err := json.NewDecoder(r.Body).Decode(&creds); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if !controllerManager.Authenticate(creds.Username, creds.Password) { - log.Errorf("invalid login for %s from %s", creds.Username, r.RemoteAddr) - http.Error(w, "invalid username/password", http.StatusForbidden) - return - } - // return token - token, err := controllerManager.NewAuthToken(creds.Username, r.UserAgent()) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if err := json.NewEncoder(w).Encode(token); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func changePassword(w http.ResponseWriter, r *http.Request) { - session, _ := controllerManager.Store().Get(r, controllerManager.StoreKey) - var creds *Credentials - if err := json.NewDecoder(r.Body).Decode(&creds); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - username := session.Values["username"].(string) - if username == "" { - http.Error(w, "unauthorized", http.StatusInternalServerError) - return - } - if err := controllerManager.ChangePassword(username, creds.Password); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func hubWebhook(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - id := vars["id"] - key, err := controllerManager.WebhookKey(id) - if err != nil { - log.Errorf("invalid webook key: id=%s from %s", id, r.RemoteAddr) - http.Error(w, err.Error(), http.StatusNotFound) - return - } - var webhook *dockerhub.Webhook - if err := json.NewDecoder(r.Body).Decode(&webhook); err != nil { - log.Errorf("error parsing webhook: %s", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if strings.Index(webhook.Repository.RepoName, key.Image) == -1 { - log.Errorf("webhook key image does not match: repo=%s image=%s", webhook.Repository.RepoName, key.Image) - http.Error(w, "not found", http.StatusNotFound) - return - } - log.Infof("received webhook notification for %s", webhook.Repository.RepoName) - //TODO: redeploy containers -} - -func writeCorsHeaders(w http.ResponseWriter, r *http.Request) { - w.Header().Add("Access-Control-Allow-Origin", "*") - w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept") - w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS") -} - func CmdServer(c *cli.Context) { rethinkdbAddr := c.String("rethinkdb-addr") rethinkdbDatabase := c.String("rethinkdb-database") @@ -371,12 +22,7 @@ func CmdServer(c *cli.Context) { authWhitelist := c.StringSlice("auth-whitelist-cidr") enableCors := c.Bool("enable-cors") - var ( - mErr error - globalMux = http.NewServeMux() - ) - - log.Infof("shipyard version %s", shipyard.Version) + log.Infof("shipyard version %s", version.Version) if len(authWhitelist) > 0 { log.Infof("whitelisting the following subnets: %v", authWhitelist) @@ -393,239 +39,19 @@ func CmdServer(c *cli.Context) { log.Fatal(err) } - controllerManager, mErr = manager.NewManager(rethinkdbAddr, rethinkdbDatabase, rethinkdbAuthKey, client, disableUsageInfo) - if mErr != nil { - log.Fatal(mErr) - } - - log.Debugf("connected to docker: url=%s", dockerUrl) - - // forwarder for swarm - fwd, err := forward.New() + controllerManager, err := manager.NewManager(rethinkdbAddr, rethinkdbDatabase, rethinkdbAuthKey, client, disableUsageInfo) if err != nil { log.Fatal(err) } - u, err := url.Parse(dockerUrl) + log.Debugf("connected to docker: url=%s", dockerUrl) + + shipyardApi, err := api.NewApi(listenAddr, controllerManager, authWhitelist, enableCors) if err != nil { log.Fatal(err) } - // setup redirect target to swarm - scheme := "http://" - - // check if TLS is enabled and configure if so - if client.TLSConfig != nil { - scheme = "https://" - // setup custom roundtripper with TLS transport - r := forward.RoundTripper( - &http.Transport{ - TLSClientConfig: client.TLSConfig, - }) - f, err := forward.New(r) - if err != nil { - log.Fatal(err) - } - - fwd = f - } - - dUrl := fmt.Sprintf("%s%s", scheme, u.Host) - - log.Debugf("configured docker proxy target: %s", dUrl) - - swarmRedirect := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - req.URL = testutils.ParseURI(dUrl) - fwd.ServeHTTP(w, req) - }) - - apiRouter := mux.NewRouter() - apiRouter.HandleFunc("/api/accounts", accounts).Methods("GET") - apiRouter.HandleFunc("/api/accounts", addAccount).Methods("POST") - apiRouter.HandleFunc("/api/accounts", deleteAccount).Methods("DELETE") - apiRouter.HandleFunc("/api/roles", roles).Methods("GET") - apiRouter.HandleFunc("/api/roles/{name}", role).Methods("GET") - apiRouter.HandleFunc("/api/roles", addRole).Methods("POST") - apiRouter.HandleFunc("/api/roles", deleteRole).Methods("DELETE") - apiRouter.HandleFunc("/api/events", events).Methods("GET") - apiRouter.HandleFunc("/api/events", purgeEvents).Methods("DELETE") - apiRouter.HandleFunc("/api/servicekeys", serviceKeys).Methods("GET") - apiRouter.HandleFunc("/api/servicekeys", addServiceKey).Methods("POST") - apiRouter.HandleFunc("/api/servicekeys", removeServiceKey).Methods("DELETE") - apiRouter.HandleFunc("/api/webhookkeys", webhookKeys).Methods("GET") - apiRouter.HandleFunc("/api/webhookkeys/{id}", webhookKey).Methods("GET") - apiRouter.HandleFunc("/api/webhookkeys", addWebhookKey).Methods("POST") - apiRouter.HandleFunc("/api/webhookkeys/{id}", deleteWebhookKey).Methods("DELETE") - - // global handler - globalMux.Handle("/", http.FileServer(http.Dir("static"))) - - // api router; protected by auth - apiAuthRouter := negroni.New() - apiAuthRequired := mAuth.NewAuthRequired(controllerManager, authWhitelist) - apiAccessRequired := access.NewAccessRequired(controllerManager) - apiAuthRouter.Use(negroni.HandlerFunc(apiAuthRequired.HandlerFuncWithNext)) - apiAuthRouter.Use(negroni.HandlerFunc(apiAccessRequired.HandlerFuncWithNext)) - apiAuthRouter.UseHandler(apiRouter) - globalMux.Handle("/api/", apiAuthRouter) - - // account router ; protected by auth - accountRouter := mux.NewRouter() - accountRouter.HandleFunc("/account/changepassword", changePassword).Methods("POST") - accountAuthRouter := negroni.New() - accountAuthRequired := mAuth.NewAuthRequired(controllerManager, authWhitelist) - accountAuthRouter.Use(negroni.HandlerFunc(accountAuthRequired.HandlerFuncWithNext)) - accountAuthRouter.UseHandler(accountRouter) - globalMux.Handle("/account/", accountAuthRouter) - - // login handler; public - loginRouter := mux.NewRouter() - loginRouter.HandleFunc("/auth/login", login).Methods("POST") - globalMux.Handle("/auth/", loginRouter) - - // hub handler; public - hubRouter := mux.NewRouter() - hubRouter.HandleFunc("/hub/webhook/{id}", hubWebhook).Methods("POST") - globalMux.Handle("/hub/", hubRouter) - - // swarm - swarmRouter := mux.NewRouter() - // these are pulled from the swarm api code to proxy and allow - // usage with the standard Docker cli - m := map[string]map[string]http.HandlerFunc{ - "GET": { - "/_ping": swarmRedirect, - "/events": swarmRedirect, - "/info": swarmRedirect, - "/version": swarmRedirect, - "/images/json": swarmRedirect, - "/images/viz": swarmRedirect, - "/images/search": swarmRedirect, - "/images/get": swarmRedirect, - "/images/{name:.*}/get": swarmRedirect, - "/images/{name:.*}/history": swarmRedirect, - "/images/{name:.*}/json": swarmRedirect, - "/containers/ps": swarmRedirect, - "/containers/json": swarmRedirect, - "/containers/{name:.*}/export": swarmRedirect, - "/containers/{name:.*}/changes": swarmRedirect, - "/containers/{name:.*}/json": swarmRedirect, - "/containers/{name:.*}/top": swarmRedirect, - "/containers/{name:.*}/logs": swarmRedirect, - "/containers/{name:.*}/stats": swarmRedirect, - "/containers/{name:.*}/attach/ws": swarmRedirect, - "/exec/{execid:.*}/json": swarmRedirect, - }, - "POST": { - "/auth": swarmRedirect, - "/commit": swarmRedirect, - "/build": swarmRedirect, - "/images/create": swarmRedirect, - "/images/load": swarmRedirect, - "/images/{name:.*}/push": swarmRedirect, - "/images/{name:.*}/tag": swarmRedirect, - "/containers/create": swarmRedirect, - "/containers/{name:.*}/kill": swarmRedirect, - "/containers/{name:.*}/pause": swarmRedirect, - "/containers/{name:.*}/unpause": swarmRedirect, - "/containers/{name:.*}/rename": swarmRedirect, - "/containers/{name:.*}/restart": swarmRedirect, - "/containers/{name:.*}/start": swarmRedirect, - "/containers/{name:.*}/stop": swarmRedirect, - "/containers/{name:.*}/wait": swarmRedirect, - "/containers/{name:.*}/resize": swarmRedirect, - "/containers/{name:.*}/attach": swarmRedirect, - "/containers/{name:.*}/copy": swarmRedirect, - "/containers/{name:.*}/exec": swarmRedirect, - "/exec/{execid:.*}/start": swarmRedirect, - "/exec/{execid:.*}/resize": swarmRedirect, - }, - "DELETE": { - "/containers/{name:.*}": swarmRedirect, - "/images/{name:.*}": swarmRedirect, - }, - "OPTIONS": { - "": swarmRedirect, - }, - } - - for method, routes := range m { - for route, fct := range routes { - localRoute := route - localFct := fct - wrap := func(w http.ResponseWriter, r *http.Request) { - if enableCors { - writeCorsHeaders(w, r) - } - localFct(w, r) - } - localMethod := method - - // add the new route - swarmRouter.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(wrap) - swarmRouter.Path(localRoute).Methods(localMethod).HandlerFunc(wrap) - } - } - - swarmAuthRouter := negroni.New() - swarmAuthRequired := mAuth.NewAuthRequired(controllerManager, authWhitelist) - swarmAccessRequired := access.NewAccessRequired(controllerManager) - swarmAuthRouter.Use(negroni.HandlerFunc(swarmAuthRequired.HandlerFuncWithNext)) - swarmAuthRouter.Use(negroni.HandlerFunc(swarmAccessRequired.HandlerFuncWithNext)) - swarmAuthRouter.UseHandler(swarmRouter) - globalMux.Handle("/containers/", swarmAuthRouter) - globalMux.Handle("/_ping", swarmAuthRouter) - globalMux.Handle("/commit", swarmAuthRouter) - globalMux.Handle("/build", swarmAuthRouter) - globalMux.Handle("/events", swarmAuthRouter) - globalMux.Handle("/version", swarmAuthRouter) - globalMux.Handle("/images/", swarmAuthRouter) - globalMux.Handle("/exec/", swarmAuthRouter) - globalMux.Handle("/v1.17/", swarmAuthRouter) - globalMux.Handle("/v1.18/", swarmAuthRouter) - - // check for admin user - if _, err := controllerManager.Account("admin"); err == manager.ErrAccountDoesNotExist { - // create roles - r := &auth.Role{ - Name: "admin", - } - ru := &auth.Role{ - Name: "user", - } - if err := controllerManager.SaveRole(r); err != nil { - log.Fatal(err) - } - if err := controllerManager.SaveRole(ru); err != nil { - log.Fatal(err) - } - role, err := controllerManager.Role(r.Name) - if err != nil { - log.Fatal(err) - } - acct := &auth.Account{ - Username: "admin", - Password: "shipyard", - Role: role, - } - if err := controllerManager.SaveAccount(acct); err != nil { - log.Fatal(err) - } - log.Infof("created admin user: username: admin password: shipyard") - } - - log.Infof("controller listening on %s", listenAddr) - - s := &http.Server{ - Addr: listenAddr, - Handler: context.ClearHandler(globalMux), - } - - if err := s.ListenAndServe(); err != nil { + if err := shipyardApi.Run(); err != nil { log.Fatal(err) } - - //if err := s.ListenAndServe(context.ClearHandler(globalMux)); err != nil { - // log.Fatal(err) - //} } diff --git a/controller/main.go b/controller/main.go index fa8dde9c2..304107231 100644 --- a/controller/main.go +++ b/controller/main.go @@ -5,8 +5,8 @@ import ( log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" - "github.com/shipyard/shipyard" "github.com/shipyard/shipyard/controller/commands" + "github.com/shipyard/shipyard/version" ) const ( @@ -17,7 +17,7 @@ func main() { app := cli.NewApp() app.Name = "shipyard" app.Usage = "composable docker management" - app.Version = shipyard.Version + app.Version = version.Version + " (" + version.GitCommit + ")" app.Author = "" app.Email = "" app.Before = func(c *cli.Context) error { diff --git a/controller/manager/handler.go b/controller/manager/handler.go index 23e7ac923..34e5a8737 100644 --- a/controller/manager/handler.go +++ b/controller/manager/handler.go @@ -11,7 +11,7 @@ import ( type ( EventHandler struct { - Manager *Manager + Manager Manager } ) diff --git a/controller/manager/manager.go b/controller/manager/manager.go index 6a6749ade..7a92a037f 100644 --- a/controller/manager/manager.go +++ b/controller/manager/manager.go @@ -17,6 +17,7 @@ import ( "github.com/shipyard/shipyard" "github.com/shipyard/shipyard/auth" "github.com/shipyard/shipyard/dockerhub" + "github.com/shipyard/shipyard/version" ) const ( @@ -45,9 +46,8 @@ var ( ) type ( - Manager struct { - StoreKey string - address string + DefaultManager struct { + storeKey string database string authKey string session *r.Session @@ -56,9 +56,42 @@ type ( client *dockerclient.DockerClient disableUsageInfo bool } + + Manager interface { + Accounts() ([]*auth.Account, error) + Account(username string) (*auth.Account, error) + Authenticate(username, password string) bool + SaveAccount(account *auth.Account) error + DeleteAccount(account *auth.Account) error + Roles() ([]*auth.Role, error) + Role(name string) (*auth.Role, error) + SaveRole(role *auth.Role) error + DeleteRole(role *auth.Role) error + Store() *sessions.CookieStore + StoreKey() string + Container(id string) (*dockerclient.ContainerInfo, error) + SaveServiceKey(key *auth.ServiceKey) error + RemoveServiceKey(key string) error + SaveEvent(event *shipyard.Event) error + Events(limit int) ([]*shipyard.Event, error) + PurgeEvents() error + ServiceKey(key string) (*auth.ServiceKey, error) + ServiceKeys() ([]*auth.ServiceKey, error) + NewAuthToken(username string, userAgent string) (*auth.AuthToken, error) + VerifyAuthToken(username, token string) error + VerifyServiceKey(key string) error + NewServiceKey(description string) (*auth.ServiceKey, error) + ChangePassword(username, password string) error + WebhookKey(key string) (*dockerhub.WebhookKey, error) + WebhookKeys() ([]*dockerhub.WebhookKey, error) + NewWebhookKey(image string) (*dockerhub.WebhookKey, error) + SaveWebhookKey(key *dockerhub.WebhookKey) error + DeleteWebhookKey(id string) error + DockerClient() *dockerclient.DockerClient + } ) -func NewManager(addr string, database string, authKey string, client *dockerclient.DockerClient, disableUsageInfo bool) (*Manager, error) { +func NewManager(addr string, database string, authKey string, client *dockerclient.DockerClient, disableUsageInfo bool) (Manager, error) { session, err := r.Connect(r.ConnectOpts{ Address: addr, Database: database, @@ -71,15 +104,14 @@ func NewManager(addr string, database string, authKey string, client *dockerclie } log.Info("checking database") r.DbCreate(database).Run(session) - m := &Manager{ - address: addr, + m := &DefaultManager{ database: database, authKey: authKey, session: session, authenticator: &auth.Authenticator{}, store: store, client: client, - StoreKey: storeKey, + storeKey: storeKey, disableUsageInfo: disableUsageInfo, } m.initdb() @@ -87,11 +119,19 @@ func NewManager(addr string, database string, authKey string, client *dockerclie return m, nil } -func (m *Manager) Store() *sessions.CookieStore { +func (m DefaultManager) Store() *sessions.CookieStore { return m.store } -func (m *Manager) initdb() { +func (m DefaultManager) DockerClient() *dockerclient.DockerClient { + return m.client +} + +func (m DefaultManager) StoreKey() string { + return m.storeKey +} + +func (m DefaultManager) initdb() { // create tables if needed tables := []string{tblNameConfig, tblNameEvents, tblNameAccounts, tblNameRoles, tblNameServiceKeys, tblNameExtensions, tblNameWebhookKeys} for _, tbl := range tables { @@ -104,13 +144,13 @@ func (m *Manager) initdb() { } } -func (m *Manager) init() error { +func (m DefaultManager) init() error { // anonymous usage info go m.usageReport() return nil } -func (m *Manager) usageReport() { +func (m DefaultManager) usageReport() { if m.disableUsageInfo { return } @@ -124,7 +164,7 @@ func (m *Manager) usageReport() { } } -func (m *Manager) uploadUsage() { +func (m DefaultManager) uploadUsage() { id := "anon" ifaces, err := net.Interfaces() if err == nil { @@ -138,7 +178,7 @@ func (m *Manager) uploadUsage() { } usage := &shipyard.Usage{ ID: id, - Version: shipyard.Version, + Version: version.Version, } b, err := json.Marshal(usage) if err != nil { @@ -150,11 +190,11 @@ func (m *Manager) uploadUsage() { } } -func (m *Manager) Container(id string) (*dockerclient.ContainerInfo, error) { +func (m DefaultManager) Container(id string) (*dockerclient.ContainerInfo, error) { return m.client.InspectContainer(id) } -func (m *Manager) SaveServiceKey(key *auth.ServiceKey) error { +func (m DefaultManager) SaveServiceKey(key *auth.ServiceKey) error { if _, err := r.Table(tblNameServiceKeys).Insert(key).RunWrite(m.session); err != nil { return err } @@ -171,7 +211,7 @@ func (m *Manager) SaveServiceKey(key *auth.ServiceKey) error { return nil } -func (m *Manager) RemoveServiceKey(key string) error { +func (m DefaultManager) RemoveServiceKey(key string) error { k, err := m.ServiceKey(key) if err != nil { return err @@ -191,14 +231,14 @@ func (m *Manager) RemoveServiceKey(key string) error { return nil } -func (m *Manager) SaveEvent(event *shipyard.Event) error { +func (m DefaultManager) SaveEvent(event *shipyard.Event) error { if _, err := r.Table(tblNameEvents).Insert(event).RunWrite(m.session); err != nil { return err } return nil } -func (m *Manager) Events(limit int) ([]*shipyard.Event, error) { +func (m DefaultManager) Events(limit int) ([]*shipyard.Event, error) { t := r.Table(tblNameEvents).OrderBy(r.Desc("Time")) if limit > -1 { t.Limit(limit) @@ -214,14 +254,14 @@ func (m *Manager) Events(limit int) ([]*shipyard.Event, error) { return events, nil } -func (m *Manager) PurgeEvents() error { +func (m DefaultManager) PurgeEvents() error { if _, err := r.Table(tblNameEvents).Delete().RunWrite(m.session); err != nil { return err } return nil } -func (m *Manager) ServiceKey(key string) (*auth.ServiceKey, error) { +func (m DefaultManager) ServiceKey(key string) (*auth.ServiceKey, error) { res, err := r.Table(tblNameServiceKeys).Filter(map[string]string{"key": key}).Run(m.session) if err != nil { return nil, err @@ -237,7 +277,7 @@ func (m *Manager) ServiceKey(key string) (*auth.ServiceKey, error) { return k, nil } -func (m *Manager) ServiceKeys() ([]*auth.ServiceKey, error) { +func (m DefaultManager) ServiceKeys() ([]*auth.ServiceKey, error) { res, err := r.Table(tblNameServiceKeys).Run(m.session) if err != nil { return nil, err @@ -249,7 +289,7 @@ func (m *Manager) ServiceKeys() ([]*auth.ServiceKey, error) { return keys, nil } -func (m *Manager) Accounts() ([]*auth.Account, error) { +func (m DefaultManager) Accounts() ([]*auth.Account, error) { res, err := r.Table(tblNameAccounts).OrderBy(r.Asc("username")).Run(m.session) if err != nil { return nil, err @@ -261,7 +301,7 @@ func (m *Manager) Accounts() ([]*auth.Account, error) { return accounts, nil } -func (m *Manager) Account(username string) (*auth.Account, error) { +func (m DefaultManager) Account(username string) (*auth.Account, error) { res, err := r.Table(tblNameAccounts).Filter(map[string]string{"username": username}).Run(m.session) if err != nil { return nil, err @@ -277,7 +317,7 @@ func (m *Manager) Account(username string) (*auth.Account, error) { return account, nil } -func (m *Manager) SaveAccount(account *auth.Account) error { +func (m DefaultManager) SaveAccount(account *auth.Account) error { pass := account.Password hash, err := m.authenticator.Hash(pass) if err != nil { @@ -310,7 +350,7 @@ func (m *Manager) SaveAccount(account *auth.Account) error { return nil } -func (m *Manager) DeleteAccount(account *auth.Account) error { +func (m DefaultManager) DeleteAccount(account *auth.Account) error { res, err := r.Table(tblNameAccounts).Filter(map[string]string{"id": account.ID}).Delete().Run(m.session) if err != nil { return err @@ -330,7 +370,7 @@ func (m *Manager) DeleteAccount(account *auth.Account) error { return nil } -func (m *Manager) Roles() ([]*auth.Role, error) { +func (m DefaultManager) Roles() ([]*auth.Role, error) { res, err := r.Table(tblNameRoles).OrderBy(r.Asc("name")).Run(m.session) if err != nil { return nil, err @@ -342,7 +382,7 @@ func (m *Manager) Roles() ([]*auth.Role, error) { return roles, nil } -func (m *Manager) Role(name string) (*auth.Role, error) { +func (m DefaultManager) Role(name string) (*auth.Role, error) { res, err := r.Table(tblNameRoles).Filter(map[string]string{"name": name}).Run(m.session) if err != nil { return nil, err @@ -358,7 +398,7 @@ func (m *Manager) Role(name string) (*auth.Role, error) { return role, nil } -func (m *Manager) SaveRole(role *auth.Role) error { +func (m DefaultManager) SaveRole(role *auth.Role) error { if _, err := r.Table(tblNameRoles).Insert(role).RunWrite(m.session); err != nil { return err } @@ -377,7 +417,7 @@ func (m *Manager) SaveRole(role *auth.Role) error { return nil } -func (m *Manager) DeleteRole(role *auth.Role) error { +func (m DefaultManager) DeleteRole(role *auth.Role) error { res, err := r.Table(tblNameRoles).Get(role.ID).Delete().Run(m.session) if err != nil { return err @@ -397,7 +437,7 @@ func (m *Manager) DeleteRole(role *auth.Role) error { return nil } -func (m *Manager) Authenticate(username, password string) bool { +func (m DefaultManager) Authenticate(username, password string) bool { acct, err := m.Account(username) if err != nil { log.Error(err) @@ -406,7 +446,7 @@ func (m *Manager) Authenticate(username, password string) bool { return m.authenticator.Authenticate(password, acct.Password) } -func (m *Manager) NewAuthToken(username string, userAgent string) (*auth.AuthToken, error) { +func (m DefaultManager) NewAuthToken(username string, userAgent string) (*auth.AuthToken, error) { tk, err := m.authenticator.GenerateToken() if err != nil { return nil, err @@ -444,7 +484,7 @@ func (m *Manager) NewAuthToken(username string, userAgent string) (*auth.AuthTok return token, nil } -func (m *Manager) VerifyAuthToken(username, token string) error { +func (m DefaultManager) VerifyAuthToken(username, token string) error { acct, err := m.Account(username) if err != nil { return err @@ -462,14 +502,14 @@ func (m *Manager) VerifyAuthToken(username, token string) error { return nil } -func (m *Manager) VerifyServiceKey(key string) error { +func (m DefaultManager) VerifyServiceKey(key string) error { if _, err := m.ServiceKey(key); err != nil { return err } return nil } -func (m *Manager) NewServiceKey(description string) (*auth.ServiceKey, error) { +func (m DefaultManager) NewServiceKey(description string) (*auth.ServiceKey, error) { k, err := m.authenticator.GenerateToken() if err != nil { return nil, err @@ -484,7 +524,7 @@ func (m *Manager) NewServiceKey(description string) (*auth.ServiceKey, error) { return key, nil } -func (m *Manager) ChangePassword(username, password string) error { +func (m DefaultManager) ChangePassword(username, password string) error { hash, err := m.authenticator.Hash(password) if err != nil { return err @@ -495,7 +535,7 @@ func (m *Manager) ChangePassword(username, password string) error { return nil } -func (m *Manager) WebhookKey(key string) (*dockerhub.WebhookKey, error) { +func (m DefaultManager) WebhookKey(key string) (*dockerhub.WebhookKey, error) { res, err := r.Table(tblNameWebhookKeys).Filter(map[string]string{"key": key}).Run(m.session) if err != nil { return nil, err @@ -516,7 +556,7 @@ func (m *Manager) WebhookKey(key string) (*dockerhub.WebhookKey, error) { return k, nil } -func (m *Manager) WebhookKeys() ([]*dockerhub.WebhookKey, error) { +func (m DefaultManager) WebhookKeys() ([]*dockerhub.WebhookKey, error) { res, err := r.Table(tblNameWebhookKeys).OrderBy(r.Asc("image")).Run(m.session) if err != nil { return nil, err @@ -528,7 +568,7 @@ func (m *Manager) WebhookKeys() ([]*dockerhub.WebhookKey, error) { return keys, nil } -func (m *Manager) NewWebhookKey(image string) (*dockerhub.WebhookKey, error) { +func (m DefaultManager) NewWebhookKey(image string) (*dockerhub.WebhookKey, error) { k := generateId(16) key := &dockerhub.WebhookKey{ Key: k, @@ -542,7 +582,7 @@ func (m *Manager) NewWebhookKey(image string) (*dockerhub.WebhookKey, error) { return key, nil } -func (m *Manager) SaveWebhookKey(key *dockerhub.WebhookKey) error { +func (m DefaultManager) SaveWebhookKey(key *dockerhub.WebhookKey) error { if _, err := r.Table(tblNameWebhookKeys).Insert(key).RunWrite(m.session); err != nil { return err @@ -561,7 +601,8 @@ func (m *Manager) SaveWebhookKey(key *dockerhub.WebhookKey) error { return nil } -func (m *Manager) DeleteWebhookKey(id string) error { + +func (m DefaultManager) DeleteWebhookKey(id string) error { key, err := m.WebhookKey(id) if err != nil { return err diff --git a/controller/manager/manager_test.go b/controller/manager/manager_test.go deleted file mode 100644 index 5d04392c7..000000000 --- a/controller/manager/manager_test.go +++ /dev/null @@ -1 +0,0 @@ -package manager diff --git a/controller/middleware/access/access.go b/controller/middleware/access/access.go index 4f2146e90..2ee0b10d7 100644 --- a/controller/middleware/access/access.go +++ b/controller/middleware/access/access.go @@ -20,7 +20,7 @@ func defaultDeniedHandler(w http.ResponseWriter, r *http.Request) { type AccessRequired struct { deniedHandler http.Handler - manager *manager.Manager + manager manager.Manager acl map[string][]string } @@ -36,7 +36,7 @@ func defaultAccessLevels() map[string][]string { return acl } -func NewAccessRequired(m *manager.Manager) *AccessRequired { +func NewAccessRequired(m manager.Manager) *AccessRequired { acl := defaultAccessLevels() a := &AccessRequired{ deniedHandler: http.HandlerFunc(defaultDeniedHandler), @@ -99,7 +99,7 @@ func (a *AccessRequired) checkAccess(path string, role *auth.Role) bool { func (a *AccessRequired) HandlerFuncWithNext(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { err := a.handleRequest(w, r) - session, _ := a.manager.Store().Get(r, a.manager.StoreKey) + session, _ := a.manager.Store().Get(r, a.manager.StoreKey()) username := session.Values["username"] if err != nil { logger.Warnf("access denied for %s to %s from %s", username, r.URL.Path, r.RemoteAddr) diff --git a/controller/middleware/auth/auth.go b/controller/middleware/auth/auth.go index ff7fdf781..a937098c7 100644 --- a/controller/middleware/auth/auth.go +++ b/controller/middleware/auth/auth.go @@ -20,11 +20,11 @@ func defaultDeniedHostHandler(w http.ResponseWriter, r *http.Request) { type AuthRequired struct { deniedHostHandler http.Handler - manager *manager.Manager + manager manager.Manager whitelistCIDRs []string } -func NewAuthRequired(m *manager.Manager, whitelistCIDRs []string) *AuthRequired { +func NewAuthRequired(m manager.Manager, whitelistCIDRs []string) *AuthRequired { return &AuthRequired{ deniedHostHandler: http.HandlerFunc(defaultDeniedHostHandler), manager: m, @@ -91,7 +91,7 @@ func (a *AuthRequired) handleRequest(w http.ResponseWriter, r *http.Request) err if err := a.manager.VerifyAuthToken(user, token); err == nil { valid = true // set current user - session, _ := a.manager.Store().Get(r, a.manager.StoreKey) + session, _ := a.manager.Store().Get(r, a.manager.StoreKey()) session.Values["username"] = user session.Save(r, w) } diff --git a/controller/mock_test/helpers.go b/controller/mock_test/helpers.go new file mode 100644 index 000000000..54d4e298c --- /dev/null +++ b/controller/mock_test/helpers.go @@ -0,0 +1,68 @@ +package mock_test + +import ( + "time" + + "github.com/samalba/dockerclient" + "github.com/shipyard/shipyard" + "github.com/shipyard/shipyard/auth" + "github.com/shipyard/shipyard/dockerhub" +) + +var ( + TestContainerId = "1234567890abcdefg" + TestContainerName = "test-container" + TestContainerImage = "test-image" + TestContainerInfo = &dockerclient.ContainerInfo{ + Id: TestContainerId, + Created: string(time.Now().UnixNano()), + Name: TestContainerName, + Image: TestContainerImage, + } + TestRole = &auth.Role{ + ID: "0", + Name: "testrole", + } + TestAccount = &auth.Account{ + ID: "0", + Username: "testuser", + Password: "test", + Role: TestRole, + } + TestEvent = &shipyard.Event{ + Type: "test-event", + ContainerInfo: TestContainerInfo, + Message: "test message", + Tags: []string{"test-tag"}, + } + TestServiceKey = &auth.ServiceKey{ + Key: "test-key", + Description: "Test Key", + } + TestWebhookKey = &dockerhub.WebhookKey{ + ID: "1234", + Image: "ehazlett/test", + Key: "abcdefg", + } +) + +func getTestContainerInfo(id string, name string, image string) *dockerclient.ContainerInfo { + return &dockerclient.ContainerInfo{ + Id: id, + Created: string(time.Now().UnixNano()), + Name: name, + Image: image, + } +} + +func getTestContainers() []*dockerclient.ContainerInfo { + return []*dockerclient.ContainerInfo{ + getTestContainerInfo(TestContainerId, TestContainerName, TestContainerImage), + } +} + +func getTestEvents() []*shipyard.Event { + return []*shipyard.Event{ + TestEvent, + } +} diff --git a/controller/mock_test/manager_mock.go b/controller/mock_test/manager_mock.go new file mode 100644 index 000000000..88a1fbd08 --- /dev/null +++ b/controller/mock_test/manager_mock.go @@ -0,0 +1,142 @@ +package mock_test + +import ( + "github.com/gorilla/sessions" + "github.com/samalba/dockerclient" + "github.com/shipyard/shipyard" + "github.com/shipyard/shipyard/auth" + "github.com/shipyard/shipyard/dockerhub" +) + +type MockManager struct{} + +func (m MockManager) Container(id string) (*dockerclient.ContainerInfo, error) { + return getTestContainerInfo(TestContainerId, TestContainerName, TestContainerImage), nil +} + +func (m MockManager) DockerClient() *dockerclient.DockerClient { + return nil +} + +func (m MockManager) SaveServiceKey(key *auth.ServiceKey) error { + return nil +} + +func (m MockManager) RemoveServiceKey(key string) error { + return nil +} + +func (m MockManager) SaveEvent(event *shipyard.Event) error { + return nil +} + +func (m MockManager) Events(limit int) ([]*shipyard.Event, error) { + return getTestEvents(), nil +} + +func (m MockManager) PurgeEvents() error { + return nil +} + +func (m MockManager) ServiceKey(key string) (*auth.ServiceKey, error) { + return TestServiceKey, nil +} + +func (m MockManager) ServiceKeys() ([]*auth.ServiceKey, error) { + return []*auth.ServiceKey{ + TestServiceKey, + }, nil +} + +func (m MockManager) Accounts() ([]*auth.Account, error) { + return []*auth.Account{ + TestAccount, + }, nil +} + +func (m MockManager) Account(username string) (*auth.Account, error) { + return nil, nil +} + +func (m MockManager) SaveAccount(account *auth.Account) error { + return nil +} + +func (m MockManager) DeleteAccount(account *auth.Account) error { + return nil +} + +func (m MockManager) Roles() ([]*auth.Role, error) { + return []*auth.Role{ + TestRole, + }, nil +} + +func (m MockManager) Role(name string) (*auth.Role, error) { + return &auth.Role{ + ID: "0", + Name: name, + }, nil +} + +func (m MockManager) SaveRole(role *auth.Role) error { + return nil +} + +func (m MockManager) DeleteRole(role *auth.Role) error { + return nil +} + +func (m MockManager) Authenticate(username, password string) bool { + return false +} + +func (m MockManager) NewAuthToken(username, userAgent string) (*auth.AuthToken, error) { + return nil, nil +} + +func (m MockManager) VerifyAuthToken(username, token string) error { + return nil +} + +func (m MockManager) VerifyServiceKey(key string) error { + return nil +} + +func (m MockManager) NewServiceKey(description string) (*auth.ServiceKey, error) { + return nil, nil +} + +func (m MockManager) ChangePassword(username, password string) error { + return nil +} + +func (m MockManager) WebhookKeys() ([]*dockerhub.WebhookKey, error) { + return []*dockerhub.WebhookKey{ + TestWebhookKey, + }, nil +} + +func (m MockManager) NewWebhookKey(image string) (*dockerhub.WebhookKey, error) { + return nil, nil +} + +func (m MockManager) WebhookKey(key string) (*dockerhub.WebhookKey, error) { + return nil, nil +} + +func (m MockManager) SaveWebhookKey(key *dockerhub.WebhookKey) error { + return nil +} + +func (m MockManager) DeleteWebhookKey(id string) error { + return nil +} + +func (m MockManager) Store() *sessions.CookieStore { + return nil +} + +func (m MockManager) StoreKey() string { + return "" +} diff --git a/version.go b/version.go deleted file mode 100644 index 9b00b5df5..000000000 --- a/version.go +++ /dev/null @@ -1,3 +0,0 @@ -package shipyard - -const Version = "3.0.0" diff --git a/version/version.go b/version/version.go new file mode 100644 index 000000000..2bfb3c89f --- /dev/null +++ b/version/version.go @@ -0,0 +1,6 @@ +package version + +var ( + Version = "3.0.0" + GitCommit = "HEAD" +) From e7c93fb87eb32221db4fe4ca16cfb897d130a8dc Mon Sep 17 00:00:00 2001 From: Tom Barlow Date: Sat, 4 Apr 2015 17:01:59 +0100 Subject: [PATCH 010/238] Removed shipyard-ui V2 --- controller/static/.gitignore | 1 - controller/static/app/app.module.js | 28 - controller/static/app/app.routes.js | 61 -- .../static/app/containers/config.route.js | 39 - .../app/containers/containers.controller.js | 20 - .../static/app/containers/containers.html | 39 - .../app/containers/containers.module.js | 6 - .../app/containers/containers.service.js | 17 - .../app/containers/deploy.controller.js | 205 ----- controller/static/app/containers/deploy.html | 244 ------ .../app/containers/details.controller.js | 134 --- controller/static/app/containers/details.html | 201 ----- .../static/app/containers/logs.controller.js | 20 - controller/static/app/containers/logs.html | 14 - controller/static/app/controllers.js | 26 - .../static/app/dashboard/config.route.js | 18 - .../app/dashboard/dashboard.controller.js | 56 -- .../static/app/dashboard/dashboard.html | 32 - .../static/app/dashboard/dashboard.module.js | 6 - .../static/app/engines/add.controller.js | 46 -- controller/static/app/engines/add.html | 130 --- controller/static/app/engines/config.route.js | 25 - .../static/app/engines/details.controller.js | 66 -- controller/static/app/engines/details.html | 68 -- .../static/app/engines/engines.controller.js | 23 - controller/static/app/engines/engines.html | 47 -- .../static/app/engines/engines.module.js | 6 - .../static/app/engines/engines.service.js | 17 - controller/static/app/events/config.route.js | 18 - .../static/app/events/events.controller.js | 39 - controller/static/app/events/events.html | 50 -- controller/static/app/events/events.module.js | 6 - .../static/app/events/events.service.js | 13 - controller/static/app/filters.js | 126 --- controller/static/app/layout/error.html | 2 - .../static/app/layout/header.controller.js | 16 - controller/static/app/layout/header.html | 16 - controller/static/app/layout/layout.module.js | 6 - .../static/app/layout/menu.controller.js | 18 - controller/static/app/layout/menu.html | 16 - controller/static/app/services.js | 14 - .../static/app/shared/accordion.directive.js | 16 - .../static/app/shared/authtoken.service.js | 38 - .../static/app/shared/checkbox.directive.js | 77 -- .../static/app/shared/dropdown.directive.js | 18 - .../static/app/shared/envvar.directive.js | 25 - .../static/app/shared/popup.directive.js | 18 - controller/static/app/shared/shared.module.js | 6 - .../static/app/shared/tablesort.service.js | 54 -- controller/static/app/utils.js | 112 --- controller/static/bower.json | 35 - controller/static/css/app.css | 72 -- controller/static/css/nv.d3.css | 769 ------------------ controller/static/css/semantic.min.css | 14 - controller/static/fonts/basic.icons.eot | Bin 40166 -> 0 bytes controller/static/fonts/basic.icons.svg | 450 ---------- controller/static/fonts/basic.icons.ttf | Bin 39924 -> 0 bytes controller/static/fonts/basic.icons.woff | Bin 24676 -> 0 bytes controller/static/fonts/icons.eot | Bin 37405 -> 0 bytes controller/static/fonts/icons.otf | Bin 61896 -> 0 bytes controller/static/fonts/icons.svg | 399 --------- controller/static/fonts/icons.ttf | Bin 79076 -> 0 bytes controller/static/fonts/icons.woff | Bin 43572 -> 0 bytes .../static/images/loader-large-inverted.gif | Bin 10168 -> 0 bytes controller/static/images/loader-large.gif | Bin 8492 -> 0 bytes .../static/images/loader-medium-inverted.gif | Bin 5384 -> 0 bytes controller/static/images/loader-medium.gif | Bin 4472 -> 0 bytes .../static/images/loader-mini-inverted.gif | Bin 2722 -> 0 bytes controller/static/images/loader-mini.gif | Bin 2548 -> 0 bytes .../static/images/loader-small-inverted.gif | Bin 4241 -> 0 bytes controller/static/images/loader-small.gif | Bin 3447 -> 0 bytes controller/static/images/logo.png | Bin 3073 -> 0 bytes controller/static/index.html | 86 -- controller/static/templates/login.html | 58 -- 74 files changed, 4182 deletions(-) delete mode 100644 controller/static/.gitignore delete mode 100644 controller/static/app/app.module.js delete mode 100644 controller/static/app/app.routes.js delete mode 100644 controller/static/app/containers/config.route.js delete mode 100644 controller/static/app/containers/containers.controller.js delete mode 100644 controller/static/app/containers/containers.html delete mode 100644 controller/static/app/containers/containers.module.js delete mode 100644 controller/static/app/containers/containers.service.js delete mode 100644 controller/static/app/containers/deploy.controller.js delete mode 100644 controller/static/app/containers/deploy.html delete mode 100644 controller/static/app/containers/details.controller.js delete mode 100644 controller/static/app/containers/details.html delete mode 100644 controller/static/app/containers/logs.controller.js delete mode 100644 controller/static/app/containers/logs.html delete mode 100644 controller/static/app/controllers.js delete mode 100644 controller/static/app/dashboard/config.route.js delete mode 100644 controller/static/app/dashboard/dashboard.controller.js delete mode 100644 controller/static/app/dashboard/dashboard.html delete mode 100644 controller/static/app/dashboard/dashboard.module.js delete mode 100644 controller/static/app/engines/add.controller.js delete mode 100644 controller/static/app/engines/add.html delete mode 100644 controller/static/app/engines/config.route.js delete mode 100644 controller/static/app/engines/details.controller.js delete mode 100644 controller/static/app/engines/details.html delete mode 100644 controller/static/app/engines/engines.controller.js delete mode 100644 controller/static/app/engines/engines.html delete mode 100644 controller/static/app/engines/engines.module.js delete mode 100644 controller/static/app/engines/engines.service.js delete mode 100644 controller/static/app/events/config.route.js delete mode 100644 controller/static/app/events/events.controller.js delete mode 100644 controller/static/app/events/events.html delete mode 100644 controller/static/app/events/events.module.js delete mode 100644 controller/static/app/events/events.service.js delete mode 100644 controller/static/app/filters.js delete mode 100644 controller/static/app/layout/error.html delete mode 100644 controller/static/app/layout/header.controller.js delete mode 100644 controller/static/app/layout/header.html delete mode 100644 controller/static/app/layout/layout.module.js delete mode 100644 controller/static/app/layout/menu.controller.js delete mode 100644 controller/static/app/layout/menu.html delete mode 100644 controller/static/app/services.js delete mode 100644 controller/static/app/shared/accordion.directive.js delete mode 100644 controller/static/app/shared/authtoken.service.js delete mode 100644 controller/static/app/shared/checkbox.directive.js delete mode 100644 controller/static/app/shared/dropdown.directive.js delete mode 100644 controller/static/app/shared/envvar.directive.js delete mode 100644 controller/static/app/shared/popup.directive.js delete mode 100644 controller/static/app/shared/shared.module.js delete mode 100644 controller/static/app/shared/tablesort.service.js delete mode 100644 controller/static/app/utils.js delete mode 100644 controller/static/bower.json delete mode 100644 controller/static/css/app.css delete mode 100644 controller/static/css/nv.d3.css delete mode 100644 controller/static/css/semantic.min.css delete mode 100644 controller/static/fonts/basic.icons.eot delete mode 100644 controller/static/fonts/basic.icons.svg delete mode 100644 controller/static/fonts/basic.icons.ttf delete mode 100644 controller/static/fonts/basic.icons.woff delete mode 100644 controller/static/fonts/icons.eot delete mode 100644 controller/static/fonts/icons.otf delete mode 100644 controller/static/fonts/icons.svg delete mode 100644 controller/static/fonts/icons.ttf delete mode 100644 controller/static/fonts/icons.woff delete mode 100644 controller/static/images/loader-large-inverted.gif delete mode 100644 controller/static/images/loader-large.gif delete mode 100644 controller/static/images/loader-medium-inverted.gif delete mode 100644 controller/static/images/loader-medium.gif delete mode 100644 controller/static/images/loader-mini-inverted.gif delete mode 100644 controller/static/images/loader-mini.gif delete mode 100644 controller/static/images/loader-small-inverted.gif delete mode 100644 controller/static/images/loader-small.gif delete mode 100644 controller/static/images/logo.png delete mode 100644 controller/static/index.html delete mode 100644 controller/static/templates/login.html diff --git a/controller/static/.gitignore b/controller/static/.gitignore deleted file mode 100644 index 8d4ae2536..000000000 --- a/controller/static/.gitignore +++ /dev/null @@ -1 +0,0 @@ -bower_components diff --git a/controller/static/app/app.module.js b/controller/static/app/app.module.js deleted file mode 100644 index f6cec519d..000000000 --- a/controller/static/app/app.module.js +++ /dev/null @@ -1,28 +0,0 @@ -(function() { - 'use strict'; - - angular.module('shipyard', [ - 'ngRoute', - 'ngCookies', - 'shipyard.layout', - 'shipyard.shared', - 'shipyard.dashboard', - 'shipyard.engines', - 'shipyard.containers', - 'shipyard.events', - 'shipyard.filters', - 'shipyard.services', - 'shipyard.controllers', - 'angular-flash.service', - 'angular-flash.flash-alert-directive', - 'angles', - 'ansiToHtml' - ]); - - Chart.defaults.global.responsive = true; - Chart.defaults.global.animation = false; - Chart.defaults.global.showTooltips = true; - -})(); - - diff --git a/controller/static/app/app.routes.js b/controller/static/app/app.routes.js deleted file mode 100644 index 57f5b14af..000000000 --- a/controller/static/app/app.routes.js +++ /dev/null @@ -1,61 +0,0 @@ -(function() { - 'use strict'; - - angular - .module('shipyard') - .config([ - '$routeProvider', - '$httpProvider', - '$provide', - 'flashProvider', - function ($routeProvider, $httpProvider, $provide, flashProvider) { - $routeProvider.when('/login', { - templateUrl: 'templates/login.html', - controller: 'LoginController' - }); - $routeProvider.when('/logout', { - template: "", - controller: 'LogoutController' - }); - $routeProvider.when('/error', { - templateUrl: "app/layout/error.html", - }); - $routeProvider.otherwise({ - redirectTo: '/dashboard' - }); - $provide.factory('httpInterceptor', function ($q, $window, flash, authtoken) { - return { - request: function (config) { - return config || $q.when(config); - }, - requestError: function (rejection) { - return $q.reject(rejection); - }, - response: function (response) { - return response || $q.when(response); - }, - responseError: function (rejection) { - switch (rejection.status) { - case 401: - authtoken.delete(); - $window.location.href = '/#/login'; - $window.location.reload(); - return $q.reject(rejection); - break; - case 403: - flash.error = 'Invalid username/password'; - break; - } - return $q.reject(rejection); - } - }; - }); - $httpProvider.interceptors.push('httpInterceptor'); - // messages - flashProvider.errorClassnames.push('red'); - flashProvider.warnClassnames.push('yellow'); - flashProvider.infoClassnames.push('blue'); - flashProvider.successClassnames.push('green'); - } - ]); -})(); diff --git a/controller/static/app/containers/config.route.js b/controller/static/app/containers/config.route.js deleted file mode 100644 index 3c4d1cca9..000000000 --- a/controller/static/app/containers/config.route.js +++ /dev/null @@ -1,39 +0,0 @@ -(function() { - 'use strict'; - - angular - .module('shipyard.containers') - .config(containersRoute); - - containersRoute.$inject = ['$routeProvider']; - - function containersRoute($routeProvider) { - $routeProvider.when('/containers', { - templateUrl: 'app/containers/containers.html', - controller: 'ContainersController', - controllerAs: 'vm', - resolve: { - resolveContainers: ['Containers', '$window', function (Containers, $window) { - return Containers.query().$promise.then(null, function(errorData) { - $window.location.href = '/#/error'; - $window.location.reload(); - }); - }] - } - }) - $routeProvider.when('/containers/deploy', { - templateUrl: 'app/containers/deploy.html', - controller: 'DeployController', - }); - $routeProvider.when('/containers/:id', { - templateUrl: 'app/containers/details.html', - controller: 'ContainerDetailsController' - }); - $routeProvider.when('/containers/:id/logs', { - templateUrl: 'app/containers/logs.html', - controller: 'ContainerLogsController' - }); - }; - -})() - diff --git a/controller/static/app/containers/containers.controller.js b/controller/static/app/containers/containers.controller.js deleted file mode 100644 index 8ff723d8b..000000000 --- a/controller/static/app/containers/containers.controller.js +++ /dev/null @@ -1,20 +0,0 @@ -(function() { - 'use strict'; - - angular - .module('shipyard.containers') - .controller('ContainersController', ContainersController); - - ContainersController.$inject = ['$location', 'resolveContainers', 'tablesort']; - - function ContainersController($location, resolveContainers, tablesort) { - var vm = this; - vm.tablesort = tablesort; - vm.containers = resolveContainers; - - vm.go = function(container) { - $location.path("/containers/" + container.id); - } - } - -})() diff --git a/controller/static/app/containers/containers.html b/controller/static/app/containers/containers.html deleted file mode 100644 index 579d9d7af..000000000 --- a/controller/static/app/containers/containers.html +++ /dev/null @@ -1,39 +0,0 @@ - - -
- -
-
- Containers -
-

There are no containers deployed.

-
-
- - - - - - - - - - - - - - - - - - -
StatusIDNameImage
diff --git a/controller/static/app/containers/containers.module.js b/controller/static/app/containers/containers.module.js deleted file mode 100644 index e828bfa72..000000000 --- a/controller/static/app/containers/containers.module.js +++ /dev/null @@ -1,6 +0,0 @@ -(function() { - 'use strict'; - - angular. - module('shipyard.containers', []); -})() diff --git a/controller/static/app/containers/containers.service.js b/controller/static/app/containers/containers.service.js deleted file mode 100644 index 963e3baa2..000000000 --- a/controller/static/app/containers/containers.service.js +++ /dev/null @@ -1,17 +0,0 @@ -(function(){ - 'use strict'; - - angular - .module('shipyard.containers') - .factory('Containers', function($resource) { - return $resource('/containers/json'); - }) - .factory('Container', function($resource) { - return $resource('/containers/:id/:action', {id: '@id' }, { - destroy: { method: 'DELETE' }, - 'save': { isArray: true, method: 'POST' }, - 'control': { isArray: false, method: 'GET' }, - query: { isArray: false } - }); - }); -})() diff --git a/controller/static/app/containers/deploy.controller.js b/controller/static/app/containers/deploy.controller.js deleted file mode 100644 index 10ac3c3ff..000000000 --- a/controller/static/app/containers/deploy.controller.js +++ /dev/null @@ -1,205 +0,0 @@ -(function(){ - 'use strict'; - - angular - .module('shipyard.containers') - .controller('DeployController', DeployController); - - DeployController.$inject = ['$scope', '$location', 'Engines', 'Container']; - - function DeployController($scope, $location, Engines, Container) { - var types = [ - "service", - //"engine", // removed until we get the UI to show engines to select - "unique" - ]; - var networkModes = [ - "bridge", - "none", - "container", - "host" - ] - var restartPolicies = [ - "no", - "on-failure", - "always" - ] - $scope.cpus = 0.1; - $scope.memory = 256; - $scope.maxRestarts = ""; - $scope.environment = ""; - $scope.hostname = ""; - $scope.domain = ""; - $scope.count = 1; - $scope.publish = false; - $scope.privileged = false; - $scope.args = null; - $scope.links = null; - $scope.volumes = null; - $scope.pull = false; - $scope.types = types; - $scope.selectType = function(type) { - $scope.selectedType = type; - $(".ui.dropdown").dropdown('hide'); - }; - $scope.selectedNetworkMode = 'bridge'; - $scope.networkModes = networkModes; - $scope.selectNetworkMode = function(mode) { - $scope.selectedNetworkMode = mode; - if (mode === 'container') { - $scope.showNetworkModeContainer = true; - } else { - $scope.showNetworkModeContainer = false; - } - $(".ui.dropdown").dropdown('hide'); - }; - $scope.selectedRestartPolicy = 'no'; - $scope.restartPolicies = restartPolicies; - $scope.selectRestartPolicy = function(policy) { - $scope.selectedRestartPolicy = policy; - if (policy == 'on-failure') { - $scope.showMaxRestarts = true; - } else { - $scope.showMaxRestarts = false; - } - $(".ui.dropdown").dropdown('hide'); - } - var labels = []; - Engines.query(function(engines){ - angular.forEach(engines, function(e) { - angular.forEach(e.engine.labels, function(l){ - if (labels.indexOf(l) == -1) { - this.push(l); - } - }, labels); - }); - $scope.labels = labels; - }); - $scope.addPortDefinition = addPortDefinition; - $scope.showLoader = function() { - $(".ui.loader").removeClass("disabled"); - $(".ui.active").addClass("dimmer"); - }; - $scope.hideLoader = function() { - $(".ui.loader").addClass("disabled"); - $(".ui.active").removeClass("dimmer"); - }; - $scope.deploy = function() { - $scope.showLoader(); - var valid = $(".ui.form").form('validate form'); - if (!valid) { - $scope.hideLoader(); - return false; - } - var selectedLabels = []; - $(".ui.checkbox.engine-label").children(":checked").each(function(i, sel){ - // HACK: use the label text to set the value - selectedLabels.push($(sel).next().text()); - }); - // format environment - var envParts = $scope.environment.match(/(?:['"].+?['"])|\S+/g); - var environment = {}; - if (envParts != null) { - for (var i=0; i 0) { - restartPolicy.maximum_retry = maxRestarts; - } - var params = { - name: $scope.name, - container_name: $scope.containerName, - cpus: parseFloat($scope.cpus), - memory: parseFloat($scope.memory), - environment: environment, - hostname: $scope.hostname, - domain: $scope.domain, - type: $scope.selectedType, - network_mode: networkMode, - args: args, - links: links, - volumes: volumes, - bind_ports: ports, - labels: selectedLabels, - publish: $scope.publish, - privileged: $scope.privileged, - restart_policy: restartPolicy, - }; - if (valid) { - Container.save({count: $scope.count, pull: $scope.pull}, params).$promise.then(function(c){ - $location.path("/containers"); - }, function(err){ - $scope.hideLoader(); - $scope.error = err.data; - return false; - }); - } - }; - } -})() diff --git a/controller/static/app/containers/deploy.html b/controller/static/app/containers/deploy.html deleted file mode 100644 index 0e96fe025..000000000 --- a/controller/static/app/containers/deploy.html +++ /dev/null @@ -1,244 +0,0 @@ -
-
-
-
Launching... Please wait...
-
-
Error
-

{{error}}

-
-
-
- -
- -
- -
-
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
- -
- -
- -
-
-
-
- -
- -
- -
-
-
-
- -
- -
- -
-
-
-
- -
- no port bindings specified -
-
- Publish all Exposed Ports -
-
- Privileged -
-
- pull -
-
-
- - -
-
- - -
-
- -
- -
-
-
-
- - -
-
- -
- -
- -
-
-
-
-
-
- - {{label}} -
- -
Deploy
-
-
-
- diff --git a/controller/static/app/containers/details.controller.js b/controller/static/app/containers/details.controller.js deleted file mode 100644 index 947fd97c5..000000000 --- a/controller/static/app/containers/details.controller.js +++ /dev/null @@ -1,134 +0,0 @@ -(function() { - 'use strict'; - - angular - .module('shipyard.containers') - .controller('ContainerDetailsController', ContainerDetailsController); - - ContainerDetailsController.$inject = ['$scope', '$location', '$routeParams', 'flash', 'Container']; - - function ContainerDetailsController($scope, $location, $routeParams, flash, Container) { - $scope.showX = function(){ - return function(d){ - return d.key; - }; - }; - $scope.showRemoveContainerDialog = function() { - $('.basic.modal.removeContainer') - .modal('show'); - }; - $scope.showStopContainerDialog = function() { - $('.basic.modal.stopContainer') - .modal('show'); - }; - $scope.showRestartContainerDialog = function() { - $('.basic.modal.restartContainer') - .modal('show'); - }; - $scope.showScaleContainerDialog = function() { - $('.basic.modal.scaleContainer') - .modal('show'); - }; - $scope.destroyContainer = function() { - Container.destroy({id: $routeParams.id}).$promise.then(function() { - // we must remove the modal or it will come back - // the next time the modal is shown - $('.basic.modal').remove(); - $location.path("/containers"); - }, function(err) { - flash.error = 'error destroying container: ' + err.data; - }); - }; - $scope.stopContainer = function() { - Container.control({id: $routeParams.id, action: 'stop'}).$promise.then(function() { - // we must remove the modal or it will come back - // the next time the modal is shown - $('.basic.modal').remove(); - $location.path("/containers/"); - }, function(err) { - flash.error = 'error stopping container: ' + err.data; - }); - }; - $scope.restartContainer = function() { - Container.control({id: $routeParams.id, action: 'restart'}).$promise.then(function() { - // we must remove the modal or it will come back - // the next time the modal is shown - $('.basic.modal').remove(); - $location.path("/containers/"); - }, function(err) { - flash.error = 'error restarting container: ' + err.data; - }); - }; - $scope.showProgress = function() { - $('.ui.form').addClass('hide'); - $('.progress').removeClass('hide'); - }; - $scope.hideProgress = function() { - $('.ui.form').removeClass('hide'); - $('.progress').addClass('hide'); - }; - $scope.scale = function() { - var valid = $(".ui.form").form('validate form'); - if (!valid) { - return false; - } - $scope.showProgress(); - Container.control({id: $routeParams.id, action: 'scale', count: $scope.count}).$promise.then(function() { - // we must remove the modal or it will come back - // the next time the modal is shown - $('.basic.modal').modal('hide'); - $('.basic.modal').remove(); - $location.path("/containers"); - }, function(err) { - flash.error = 'error scaling container: ' + err.data; - $scope.hideProgress(); - $('.basic.modal').modal('hide'); - $('.basic.modal').remove(); - }); - }; - var portLinks = []; - Container.query({id: $routeParams.id}, function(data){ - $scope.container = data; - // build port links - $scope.tooltipFunction = function(){ - return function(key, x, y, e, graph) { - return "
Reserved
" + '

' + y + '

'; - } - }; - angular.forEach(data.ports, function(p) { - var h = document.createElement('a'); - h.href = data.engine.addr; - var l = {}; - l.hostname = h.hostname; - l.protocol = p.proto; - l.port = p.port; - l.container_port = p.container_port; - l.link = 'http://' + h.hostname + ':' + p.port; - this.push(l); - }, portLinks); - $scope.portLinks = portLinks; - $scope.predicate = 'container_port'; - $scope.cpuMax = data.engine.cpus; - $scope.memoryMax = data.engine.memory; - $scope.chartOptions = {}; - $scope.containerCpuData = { - labels: ["Reserved"], - datasets: [ - { - fillColor: "#6D91AD", - data: [ $scope.container.image.cpus ] - } - ] - }; - $scope.containerMemoryData = { - labels: ["Reserved"], - datasets: [ - { - fillColor: "#6D91AD", - data: [ $scope.container.image.memory ] - } - ] - }; - }); - } -})() diff --git a/controller/static/app/containers/details.html b/controller/static/app/containers/details.html deleted file mode 100644 index 3337e0a93..000000000 --- a/controller/static/app/containers/details.html +++ /dev/null @@ -1,201 +0,0 @@ - - - - - - - - - - -
- - - - - - - -
- - {{flash.message}} -
-
-
-
-

Name

-
{{container.name|container_name}}
-
-
-
-

Image

-
{{container.image.name}}
-
-
-
-

Type

-
{{container.image.type}}
-
-
-

Environment

-
No environment variables
-
- {{k}}
Show
-
{{v}}
-
-
-
-
-

Hostname

-
{{container.image.hostname}}
-
-
-
-

Engine

-
{{container.engine.id}}
-
-
-
-

Network Mode

-
{{container.image.network_mode}}
-
-
-
-

Restart Policy

-
- {{container.image.restart_policy.name || "no"}}:{{container.image.restart_policy.maximum_retry}} -
-
-
-
-

Privileged

-
- {{container.image.privileged}} -
-
-
-

Ports

- -
-
-

CPU

-
- -
-
-
-

Memory

-
- -
-
-
- diff --git a/controller/static/app/containers/logs.controller.js b/controller/static/app/containers/logs.controller.js deleted file mode 100644 index ffd6e65c3..000000000 --- a/controller/static/app/containers/logs.controller.js +++ /dev/null @@ -1,20 +0,0 @@ -(function() { - 'use strict'; - - angular - .module('shipyard.containers') - .controller('ContainerLogsController', ContainerLogsController); - - ContainerLogsController.$inject = ['$scope', '$location', '$routeParams', '$http', 'flash', 'Container', 'ansi2html']; - - function ContainerLogsController($scope, $location, $routeParams, $http, flash, Container, ansi2html) { - $http.get('/api/containers/' + $routeParams.id + "/logs").success(function(data){ - $scope.logs = ansi2html.toHtml(data.replace(/\n/g, '
')); - }); - Container.query({id: $routeParams.id}, function(data){ - $scope.container = data; - }); - } - -})() - diff --git a/controller/static/app/containers/logs.html b/controller/static/app/containers/logs.html deleted file mode 100644 index 75851629e..000000000 --- a/controller/static/app/containers/logs.html +++ /dev/null @@ -1,14 +0,0 @@ - -
- - {{flash.message}} -
-
-
-
diff --git a/controller/static/app/controllers.js b/controller/static/app/controllers.js deleted file mode 100644 index cebbc633c..000000000 --- a/controller/static/app/controllers.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; - -angular.module('shipyard.controllers', ['ngCookies']) - .controller('LoginController', function($scope, $cookieStore, $window, flash, Login, authtoken) { - $scope.template = 'templates/login.html'; - $scope.login = function() { - Login.login({username: $scope.username, password: $scope.password}).$promise.then(function(data){ - authtoken.save($scope.username, data.auth_token); - $window.location.href = '/#/dashboard'; - $window.location.reload(); - }, function() { - flash.error = 'invalid username/password'; - }); - } - }) - .controller('LogoutController', function($scope, $window, authtoken) { - authtoken.delete(); - $window.location.href = '/#/login'; - $window.location.reload(); - }) - -$(function(){ - $('.message .close').on('click', function() { - $(this).closest('.message').fadeOut(); - }); -}); diff --git a/controller/static/app/dashboard/config.route.js b/controller/static/app/dashboard/config.route.js deleted file mode 100644 index 41bc09029..000000000 --- a/controller/static/app/dashboard/config.route.js +++ /dev/null @@ -1,18 +0,0 @@ -(function() { - 'use strict'; - - angular - .module('shipyard.dashboard') - .config(dashboardRoute); - - dashboardRoute.$inject = ['$routeProvider']; - - function dashboardRoute($routeProvider) { - $routeProvider.when('/dashboard', { - templateUrl: 'app/dashboard/dashboard.html', - controller: 'DashboardController', - controllerAs: 'vm' - }) - }; -})() - diff --git a/controller/static/app/dashboard/dashboard.controller.js b/controller/static/app/dashboard/dashboard.controller.js deleted file mode 100644 index d5475238d..000000000 --- a/controller/static/app/dashboard/dashboard.controller.js +++ /dev/null @@ -1,56 +0,0 @@ -(function() { - 'use strict'; - - angular - .module('shipyard.dashboard') - .controller('DashboardController', DashboardController); - - DashboardController.$inject = ['$location', 'Containers', 'Events', 'ClusterInfo', 'authtoken']; - - function DashboardController($location, Containers, Events, ClusterInfo, authtoken) { - var vm = this; - - Containers.query().$promise.then(function(data){ - if (data != undefined && data.length == 0) { - $location.path("/engines"); - } - }, function() { - }); - - Events.query(function(data){ - vm.events = data; - }); - vm.showX = function(){ - return function(d){ - return d.key; - }; - }; - vm.showY = function(){ - return function(d){ - return d.y; - }; - }; - - ClusterInfo.query(function(data){ - vm.chartOptions = {}; - vm.clusterInfo = data; - vm.clusterCpuData = [ - { label: "Free", value: data.cpus, color: "#184465" }, - { label: "Reserved", value: 0, color: "#6D91AD" } - ]; - if (data.cpus != undefined && data.reserved_cpus != undefined) { - vm.clusterCpuData[0].value = data.cpus - data.reserved_cpus; - vm.clusterCpuData[1].value = data.reserved_cpus; - } - vm.clusterMemoryData = [ - { label: "Free", value: data.memory, color: "#184465" }, - { label: "Reserved", value: 0, color: "#6D91AD" } - ]; - if (data.memory != undefined && data.reserved_memory != undefined) { - vm.clusterMemoryData[0].value = data.memory - data.reserved_memory; - vm.clusterMemoryData[1].value = data.reserved_memory; - } - }); - } - -})() diff --git a/controller/static/app/dashboard/dashboard.html b/controller/static/app/dashboard/dashboard.html deleted file mode 100644 index f9fe35540..000000000 --- a/controller/static/app/dashboard/dashboard.html +++ /dev/null @@ -1,32 +0,0 @@ -
-
-
-

CPU

-
- -
-
- -
-

Memory

-
- -
-
- -
-
-
-
- -
-
-
- {{event.time|date:'short'}} {{event|formatEvent}} -
-
-
-
-
-
-
diff --git a/controller/static/app/dashboard/dashboard.module.js b/controller/static/app/dashboard/dashboard.module.js deleted file mode 100644 index 1de45c99e..000000000 --- a/controller/static/app/dashboard/dashboard.module.js +++ /dev/null @@ -1,6 +0,0 @@ -(function() { - 'use strict'; - - angular. - module('shipyard.dashboard', []); -})() diff --git a/controller/static/app/engines/add.controller.js b/controller/static/app/engines/add.controller.js deleted file mode 100644 index f4ab7dccb..000000000 --- a/controller/static/app/engines/add.controller.js +++ /dev/null @@ -1,46 +0,0 @@ -(function() { - 'use strict'; - - angular - .module('shipyard.engines') - .controller('EngineAddController', EngineAddController); - - EngineAddController.$inject = ['$scope', '$location', '$routeParams', 'flash', 'Engines']; - - function EngineAddController($scope, $location, $routeParams, flash, Engines) { - $scope.id = ""; - $scope.addr = ""; - $scope.cpus = 1.0; - $scope.memory = 1024; - $scope.labels = ""; - $scope.ssl_cert = ""; - $scope.ssl_key = ""; - $scope.ca_cert = ""; - $scope.addEngine = function() { - var valid = $(".ui.form").form('validate form'); - if (!valid) { - return false; - } - var params = { - engine: { - id: $scope.id, - addr: $scope.addr, - cpus: parseFloat($scope.cpus), - memory: parseFloat($scope.memory), - labels: $scope.labels.split(" ") - }, - ssl_cert: $scope.ssl_cert, - ssl_key: $scope.ssl_key, - ca_cert: $scope.ca_cert - }; - Engines.save({}, params).$promise.then(function(c){ - $location.path("/engines"); - }, function(err){ - $scope.error = err.data; - return false; - }); - }; - } - -})() - diff --git a/controller/static/app/engines/add.html b/controller/static/app/engines/add.html deleted file mode 100644 index da2ae83cf..000000000 --- a/controller/static/app/engines/add.html +++ /dev/null @@ -1,130 +0,0 @@ -
-
-
-
-
Error
-

{{error}}

-
-
- -
- -
- -
-
-
-
- -
- -
- -
-
-
-
-
- -
- -
- -
-
-
-
- -
- -
- -
-
-
-
-
- -
- -
- -
-
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
Add
-
-
-
- diff --git a/controller/static/app/engines/config.route.js b/controller/static/app/engines/config.route.js deleted file mode 100644 index 8e9c7743f..000000000 --- a/controller/static/app/engines/config.route.js +++ /dev/null @@ -1,25 +0,0 @@ -(function() { - 'use strict'; - - angular - .module('shipyard.engines') - .config(enginesRoute); - - enginesRoute.$inject = ['$routeProvider']; - - function enginesRoute($routeProvider) { - $routeProvider.when('/engines', { - templateUrl: 'app/engines/engines.html', - controller: 'EnginesController', - controllerAs: 'vm' - }) - $routeProvider.when('/engines/add', { - templateUrl: 'app/engines/add.html', - controller: 'EngineAddController' - }); - $routeProvider.when('/engines/:id', { - templateUrl: 'app/engines/details.html', - controller: 'EngineDetailsController' - }); - }; -})() diff --git a/controller/static/app/engines/details.controller.js b/controller/static/app/engines/details.controller.js deleted file mode 100644 index 45a644dbb..000000000 --- a/controller/static/app/engines/details.controller.js +++ /dev/null @@ -1,66 +0,0 @@ -(function() { - 'use strict'; - - angular - .module('shipyard.engines') - .controller('EngineDetailsController', EngineDetailsController); - - EngineDetailsController.$inject = ['$scope', '$location', '$routeParams', 'flash', 'Containers', 'Engine']; - - function EngineDetailsController($scope, $location, $routeParams, flash, Containers, Engine) { - $scope.showX = function(){ - return function(d){ - return d.key; - }; - }; - $scope.showY = function(){ - return function(d){ - return d.y; - }; - }; - $scope.showRemoveEngineDialog = function() { - $('.basic.modal') - .modal('show'); - }; - $scope.removeEngine = function() { - Engine.remove({id: $routeParams.id}).$promise.then(function() { - // we must remove the modal or it will come back - // the next time the modal is shown - $('.basic.modal').remove(); - $location.path("/engines"); - }, function(err) { - flash.error = 'error removing engine: ' + err.data; - }); - }; - Engine.query({id: $routeParams.id}, function(data){ - $scope.engine = data; - // load container data - Containers.query(function(d){ - var cpuData = []; - var memoryData = []; - $scope.chartOptions = {}; - for (var i=0; i -
- Remove Engine -
-
- Remove {{engine.engine.id}} from cluster? -
-
-
Cancel
- Ok -
- - - - -
- - {{flash.message}} -
-
-
-
-

Name

-
{{engine.engine.id}}
-
-
-
-

Resources

-
CPU: {{engine.engine.cpus}}
-
Memory: {{engine.engine.memory}}
-
-
-
-

Address

-
{{engine.engine.addr}}
-
-
-
-

Docker Version

-
{{engine.docker_version}}
-
-
-
-

Labels

-
{{label}}
-
-
-
-
-

CPU Usage

-
- -
-
-
-
-

Memory Usage

-
- -
-
-
- diff --git a/controller/static/app/engines/engines.controller.js b/controller/static/app/engines/engines.controller.js deleted file mode 100644 index 8459449a2..000000000 --- a/controller/static/app/engines/engines.controller.js +++ /dev/null @@ -1,23 +0,0 @@ -(function() { - 'use strict'; - - angular - .module('shipyard.engines') - .controller('EnginesController', EnginesController); - - EnginesController.$inject = ['$location', 'Engines', 'tablesort']; - - function EnginesController($location, Engines, tablesort) { - - var vm = this; - vm.tablesort = tablesort; - - vm.go = function(engine) { - $location.path("/engines/" + engine.id) - }; - - Engines.query(function(data){ - vm.engines = data; - }); - } -})(); diff --git a/controller/static/app/engines/engines.html b/controller/static/app/engines/engines.html deleted file mode 100644 index 9b6d1a180..000000000 --- a/controller/static/app/engines/engines.html +++ /dev/null @@ -1,47 +0,0 @@ - - -
- -
-
- Engines -
-

There are no engines in the cluster.

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - -
NameCPUsMemoryAddrLabelsResponse Time (ms)Docker Version
diff --git a/controller/static/app/engines/engines.module.js b/controller/static/app/engines/engines.module.js deleted file mode 100644 index d7e1117e5..000000000 --- a/controller/static/app/engines/engines.module.js +++ /dev/null @@ -1,6 +0,0 @@ -(function() { - 'use strict'; - - angular. - module('shipyard.engines', []); -})() diff --git a/controller/static/app/engines/engines.service.js b/controller/static/app/engines/engines.service.js deleted file mode 100644 index e650b08a8..000000000 --- a/controller/static/app/engines/engines.service.js +++ /dev/null @@ -1,17 +0,0 @@ -(function() { - 'use strict'; - - angular - .module('shipyard.engines') - .factory('Engines', function($resource) { - return $resource('/api/engines'); - }) - .factory('Engine', function($resource) { - return $resource('/api/engines/:id', {id: '@id'}, { - remove: { method: 'DELETE' }, - 'save': { isArray: true, method: 'POST' }, - query: { isArray: false } - }); - }); -})() - diff --git a/controller/static/app/events/config.route.js b/controller/static/app/events/config.route.js deleted file mode 100644 index 401eb642b..000000000 --- a/controller/static/app/events/config.route.js +++ /dev/null @@ -1,18 +0,0 @@ -(function() { - 'use strict'; - - angular - .module('shipyard.events') - .config(eventsRoute); - - eventsRoute.$inject = ['$routeProvider']; - - function eventsRoute($routeProvider) { - $routeProvider.when('/events', { - templateUrl: 'app/events/events.html', - controller: 'EventsController', - controllerAs: 'vm' - }) - }; -})() - diff --git a/controller/static/app/events/events.controller.js b/controller/static/app/events/events.controller.js deleted file mode 100644 index 7326be187..000000000 --- a/controller/static/app/events/events.controller.js +++ /dev/null @@ -1,39 +0,0 @@ -(function() { - - angular - .module('shipyard.events') - .controller('EventsController', EventsController); - - EventsController.$inject = ['$location', '$window', 'Events', 'tablesort']; - - function EventsController($location, $window, Events, tablesort) { - var vm = this; - vm.tablesort = tablesort; - - vm.initSort = function() { - vm.tablesort.sortBy('time'); - vm.tablesort.setReverseSort(true); - } - - vm.showPurgeEventsDialog = function() { - $('.basic.modal.purgeEvents') - .modal('show'); - }; - - vm.purgeEvents = function() { - Events.purge().$promise.then(function(c) { - // we must remove the modal or it will come back - // the next time the modal is shown - $('.basic.modal').remove(); - $location.path('/events'); - $window.location.reload(); - }, function(err) { - flash.error = 'error purging events: ' + err.data; - }); - }; - - Events.query(function(data){ - vm.events = data; - }); - } -})() diff --git a/controller/static/app/events/events.html b/controller/static/app/events/events.html deleted file mode 100644 index e8147a23c..000000000 --- a/controller/static/app/events/events.html +++ /dev/null @@ -1,50 +0,0 @@ - - - - -
- -
-
- Events -
-

There are no events recorded.

-
-
- - - - - - - - - - - - - - - - - - - - -
TimeContainerEngineTypeTags
{{e.time}}{{e.container.id|truncate}}{{e.engine.id}}{{e.type}}{{e.tags.join(', ')}}
diff --git a/controller/static/app/events/events.module.js b/controller/static/app/events/events.module.js deleted file mode 100644 index 84ddbe74a..000000000 --- a/controller/static/app/events/events.module.js +++ /dev/null @@ -1,6 +0,0 @@ -(function() { - 'use strict'; - - angular. - module('shipyard.events', []); -})() diff --git a/controller/static/app/events/events.service.js b/controller/static/app/events/events.service.js deleted file mode 100644 index 3b5b2137e..000000000 --- a/controller/static/app/events/events.service.js +++ /dev/null @@ -1,13 +0,0 @@ -(function(){ - 'use strict'; - - angular - .module('shipyard.events') - .factory('Events', function($resource) { - return $resource('/api/events', {}, { - 'purge': { isArray: false, method: 'DELETE' }, - query: { isArray: true } - }); - }); - -})() diff --git a/controller/static/app/filters.js b/controller/static/app/filters.js deleted file mode 100644 index e68fbfc09..000000000 --- a/controller/static/app/filters.js +++ /dev/null @@ -1,126 +0,0 @@ -'use strict'; -function truncate(t) { - if (t == undefined) { - return t; - } - if (t.length < 12) { - return t; - } - return t.substring(0, 12); -} - -angular.module('shipyard.filters', []) - .filter('truncate', function () { - return function (t) { - if (t == undefined) { - return ""; - } - return truncate(t); - }; - }) - .filter('container_name', function () { - return function (t) { - if (t == undefined) { - return ""; - } - return t.slice(1); - }; - }) - .filter('parseUrl', function () { - return function (u) { - if (u == undefined) { - return ""; - } - var h = document.createElement('a'); - h.href = u; - var l = {}; - l.protocol = h.proto; - l.port = h.port; - l.hostname = h.hostname; - return l - }; - }) - .filter('formatMemory', function () { - return function (s) { - if (s == undefined) { - return ""; - } - return s + " MB"; - }; - }) - .filter('unsafe', function ($sce) { - return function(val) { - return $sce.trustAsHtml(val); - }; - }) - .filter('default', function () { - return function(input, defaultValue) { - if (!input) return defaultValue; - return input; - }; - }) - .filter('formatEvent', function () { - return function (e) { - var evt = ""; - evt += e.type + " " + e.message; - return evt; - }; - }) - .filter('eventCssClass', function () { - return function (t) { - var cls = ""; - switch(t) { - case 'die': - cls = "off red"; - break; - case 'kill': - cls = "off red"; - break; - case 'start': - cls = "checkmark green"; - break; - case 'create': - cls = "add blue"; - break; - case 'restart': - cls = "refresh blue"; - break; - case 'add-engine': - cls = "cloud green"; - break; - case 'remove-engine': - cls = "cloud"; - break; - case 'add-service-key': - cls = "lock green"; - break; - case 'remove-service-key': - cls = "remove red"; - break; - case 'add-account': - cls = "user blue"; - break; - case 'delete-account': - cls = "user"; - break; - case 'add-role': - cls = "user blue"; - break; - case 'delete-role': - cls = "user"; - break; - case 'add-webhook-key': - cls = "exchange blue"; - break; - case 'delete-webhook-key': - cls = "exchange"; - break; - case 'deploy': - cls = "cloud upload green"; - break; - default: - cls = "text file" - } - return cls; - }; - }); diff --git a/controller/static/app/layout/error.html b/controller/static/app/layout/error.html deleted file mode 100644 index 1ae8ebe81..000000000 --- a/controller/static/app/layout/error.html +++ /dev/null @@ -1,2 +0,0 @@ -

An error occurred

-

Please try again later.

diff --git a/controller/static/app/layout/header.controller.js b/controller/static/app/layout/header.controller.js deleted file mode 100644 index ac433715f..000000000 --- a/controller/static/app/layout/header.controller.js +++ /dev/null @@ -1,16 +0,0 @@ -(function(){ - 'use strict'; - - angular - .module('shipyard.layout') - .controller('HeaderController', HeaderController); - - HeaderController.$inject = ['$http', '$scope', 'authtoken']; - - function HeaderController($http, $scope, authtoken) { - $scope.template = 'app/layout/header.html'; - $scope.username = authtoken.getUsername(); - $scope.isLoggedIn = authtoken.isLoggedIn(); - $http.defaults.headers.common['X-Access-Token'] = authtoken.get(); - } -})() diff --git a/controller/static/app/layout/header.html b/controller/static/app/layout/header.html deleted file mode 100644 index ea22a81ae..000000000 --- a/controller/static/app/layout/header.html +++ /dev/null @@ -1,16 +0,0 @@ -
-
- -
-
- -
-
- diff --git a/controller/static/app/layout/layout.module.js b/controller/static/app/layout/layout.module.js deleted file mode 100644 index de34154ec..000000000 --- a/controller/static/app/layout/layout.module.js +++ /dev/null @@ -1,6 +0,0 @@ -(function() { - 'use strict'; - - angular - .module('shipyard.layout', []); -})() diff --git a/controller/static/app/layout/menu.controller.js b/controller/static/app/layout/menu.controller.js deleted file mode 100644 index e1ffe618d..000000000 --- a/controller/static/app/layout/menu.controller.js +++ /dev/null @@ -1,18 +0,0 @@ -(function(){ - 'use strict'; - - angular - .module('shipyard.layout') - .controller('MenuController', MenuController); - - function MenuController($scope, $location, $cookieStore, authtoken) { - $scope.template = 'app/layout/menu.html'; - $scope.isActive = function(path){ - if ($location.path().substr(0, path.length) == path) { - return true - } - return false - } - $scope.isLoggedIn = authtoken.isLoggedIn(); - } -})() diff --git a/controller/static/app/layout/menu.html b/controller/static/app/layout/menu.html deleted file mode 100644 index 82d393bbb..000000000 --- a/controller/static/app/layout/menu.html +++ /dev/null @@ -1,16 +0,0 @@ - diff --git a/controller/static/app/services.js b/controller/static/app/services.js deleted file mode 100644 index 1e1635eb2..000000000 --- a/controller/static/app/services.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -angular.module('shipyard.services', ['ngResource', 'ngRoute']) - .factory('Login', function($resource) { - return $resource('/auth/login', [], { - query: { isArray: false }, - 'login': { method: 'POST', isArray: false } - }); - }) - .factory('ClusterInfo', function($resource) { - return $resource('/api/cluster/info', [], { - query: { isArray: false } - }); - }); diff --git a/controller/static/app/shared/accordion.directive.js b/controller/static/app/shared/accordion.directive.js deleted file mode 100644 index e7fd93079..000000000 --- a/controller/static/app/shared/accordion.directive.js +++ /dev/null @@ -1,16 +0,0 @@ -(function(){ - 'use strict'; - - angular - .module('shipyard.shared') - .directive('accordion', accordion); - - function accordion() { - return { - restrict: 'A', - link: function(scope, element, attrs) { - $(element).accordion(scope.$eval(attrs.accordion)); - } - }; - } -})() diff --git a/controller/static/app/shared/authtoken.service.js b/controller/static/app/shared/authtoken.service.js deleted file mode 100644 index a8124a573..000000000 --- a/controller/static/app/shared/authtoken.service.js +++ /dev/null @@ -1,38 +0,0 @@ -(function(){ - 'use strict'; - - angular - .module('shipyard.shared') - .factory('authtoken', authtoken); - - authtoken.$inject = ['$cookieStore']; - - function authtoken($cookieStore) { - return { - 'get': function() { - return $cookieStore.get('auth_token'); - }, - 'getUsername': function() { - return $cookieStore.get('auth_username'); - }, - 'save': function(username, token) { - var token = username + ":" + token; - $cookieStore.put('auth_username', username); - $cookieStore.put('auth_token', token); - }, - 'delete': function() { - $cookieStore.remove('auth_username'); - $cookieStore.remove('auth_token'); - }, - 'isLoggedIn': function() { - var loggedIn = false; - var token = $cookieStore.get('auth_token'); - if (token != undefined) { - loggedIn = true; - } - return loggedIn; - } - }; - } - -})() diff --git a/controller/static/app/shared/checkbox.directive.js b/controller/static/app/shared/checkbox.directive.js deleted file mode 100644 index 192829ce9..000000000 --- a/controller/static/app/shared/checkbox.directive.js +++ /dev/null @@ -1,77 +0,0 @@ -(function(){ - 'use strict'; - - angular - .module('shipyard.shared') - .directive('checkbox', checkbox); - - // this is from https://github.com/angularify/angular-semantic-ui/tree/master/src/checkbox - function checkbox() { - return { - restrict: 'E', - replace: true, - transclude: true, - scope :{ - type: "@", - size: "@", - checked: "@", - model: '=ngModel' - }, - template: "
" + - "" + - "" + - "
", - link: function(scope, element, attrs, ngModel) { - if (scope.type == 'standard' || scope.type == undefined){ - scope.type = 'standard'; - scope.checkbox_class = 'ui checkbox'; - } else if (scope.type == 'slider'){ - scope.type = 'slider'; - scope.checkbox_class = 'ui slider checkbox'; - } else if (scope.type == 'toggle'){ - scope.type = 'toggle'; - scope.checkbox_class = 'ui toggle checkbox'; - } else { - scope.type = 'standard'; - scope.checkbox_class = 'ui checkbox'; - } - if (scope.size == 'large'){ - scope.checkbox_class = scope.checkbox_class + ' large'; - } else if (scope.size == 'huge') { - scope.checkbox_class = scope.checkbox_class + ' huge'; - } - if (scope.checked == 'false' || scope.checked == undefined) { - scope.checked = false; - } else { - scope.checked = true; - element.children()[0].setAttribute('checked', ''); - } - element.bind('click', function () { - scope.$apply(function() { - if (scope.checked == true){ - scope.checked = true; - scope.model = false; - element.children()[0].removeAttribute('checked'); - } else { - scope.checked = true; - scope.model = true; - element.children()[0].setAttribute('checked', 'true'); - } - }) - }); - scope.$watch('model', function(val){ - if (val == undefined) - return; - if (val == true){ - scope.checked = true; - element.children()[0].setAttribute('checked', 'true'); - } else { - scope.checked = false; - element.children()[0].removeAttribute('checked'); - } - }); - } - } - } - -})() diff --git a/controller/static/app/shared/dropdown.directive.js b/controller/static/app/shared/dropdown.directive.js deleted file mode 100644 index 9e01f1348..000000000 --- a/controller/static/app/shared/dropdown.directive.js +++ /dev/null @@ -1,18 +0,0 @@ -(function(){ - 'use strict'; - - angular - .module('shipyard.shared') - .directive('dropdown', dropdown); - - function dropdown() { - return { - restrict: 'A', - link: function(scope, element, attrs) { - $(element).dropdown(scope.$eval(attrs.dropdown)); - } - }; - }; - -})() - diff --git a/controller/static/app/shared/envvar.directive.js b/controller/static/app/shared/envvar.directive.js deleted file mode 100644 index 0dfe519d6..000000000 --- a/controller/static/app/shared/envvar.directive.js +++ /dev/null @@ -1,25 +0,0 @@ -(function(){ - 'use strict'; - - angular - .module('shipyard.shared') - .directive('envvar', envvar); - - function envvar() { - return { - restrict: 'A', - link: function(scope, element, attrs) { - element.bind('click', function() { - $(element).parent().children('.hide').transition(); - if ($(element).parent().children('.hide').hasClass('visible')) { - $(element).text('Show'); - } else { - $(element).text('Hide'); - } - }); - } - }; - }; - -})() - diff --git a/controller/static/app/shared/popup.directive.js b/controller/static/app/shared/popup.directive.js deleted file mode 100644 index 70da5bb84..000000000 --- a/controller/static/app/shared/popup.directive.js +++ /dev/null @@ -1,18 +0,0 @@ -(function(){ - 'use strict'; - - angular - .module('shipyard.shared') - .directive('popup', popup); - - function popup() { - return { - restrict: 'A', - link: function(scope, element, attrs) { - $(element).popup(scope.$eval(attrs.popup)); - } - }; - }; - -})() - diff --git a/controller/static/app/shared/shared.module.js b/controller/static/app/shared/shared.module.js deleted file mode 100644 index 074962a24..000000000 --- a/controller/static/app/shared/shared.module.js +++ /dev/null @@ -1,6 +0,0 @@ -(function() { - 'use strict'; - - angular. - module('shipyard.shared', []); -})() diff --git a/controller/static/app/shared/tablesort.service.js b/controller/static/app/shared/tablesort.service.js deleted file mode 100644 index 8b2dd41ef..000000000 --- a/controller/static/app/shared/tablesort.service.js +++ /dev/null @@ -1,54 +0,0 @@ -(function() { - 'use strict'; - - angular - .module('shipyard.shared') - .factory('tablesort', tablesort); - - function tablesort() { - var sortField = ""; - var reverseSort = false; - - function sortBy(field) { - if(sortField == field) { - reverseSort = !reverseSort; - } else { - sortField = field; - } - } - - function setReverseSort(reverse) { - reverseSort = reverse; - } - - function semanticHeaderClass(field) { - if(field != sortField) { - return ""; - } - if(reverseSort == false) { - return "ascending"; - } else { - return "descending"; - } - } - - function isReverseSorted() { - return reverseSort; - } - - function getSortField() { - return sortField; - } - - var service = { - sortBy: sortBy, - semanticHeaderClass: semanticHeaderClass, - isReverseSorted: isReverseSorted, - getSortField: getSortField, - setReverseSort: setReverseSort, - }; - - return service; - } - -})() diff --git a/controller/static/app/utils.js b/controller/static/app/utils.js deleted file mode 100644 index cf53db02e..000000000 --- a/controller/static/app/utils.js +++ /dev/null @@ -1,112 +0,0 @@ -'use strict'; - -function setActiveMenuItem(name) { - $("div#menu-main a.item").removeClass("active"); - $("div#menu-main a#"+name).addClass("active"); -} - -function drawDashboardCharts(reservedCpu, totalCpu, reservedMemory, totalMemory) { - var cpuData = [ - { - "label": "Used", - "value": reservedCpu || 0.0 - }, - { - "label": "Total", - "value": totalCpu || 0.0 - } - ] - var memoryData = [ - { - "label": "Used", - "value": reservedMemory || 0.0 - }, - { - "label": "Total", - "value": totalMemory || 0.0 - } - ] - nv.addGraph(function() { - var chart = nv.models.pieChart() - .x(function(d) { return d.label }) - .y(function(d) { return d.value }) - .donut(true) - .donutRatio(0.35) - .showLabels(true); - - d3.select("#chart-cpu svg") - .datum(cpuData) - .transition().duration(1200) - .call(chart); - return chart; - }); - nv.addGraph(function() { - var chart = nv.models.pieChart() - .x(function(d) { return d.label }) - .y(function(d) { return d.value }) - .donut(true) - .donutRatio(0.35) - .showLabels(true); - - d3.select("#chart-memory svg") - .datum(memoryData) - .transition().duration(1200) - .call(chart); - return chart; - }); -} - -function getToken() { - //return 'admin:$2a$10$0QTBoG3R/rxj.Aasmo4oHOIWu/5Vi9HwQjCQbWFnwQB9/9K3kgHHK'; - return 'admin:foo'; -} - -function isLoggedIn() { - return true; -} - -function getRandomColor() { - var colors = d3.scale.category20c().range(); - var rand = colors[Math.floor(Math.random() * colors.length)]; - return rand -} - -function addPortDefinition() { - $(".ui.ports.default-label").remove(); - var idx = $(".ui.segment.ports").children("div").length; - $(".ui.segment.ports").append(' \ -
\ -
\ - \ - \ -
\ -
\ - \ -
\ - \ -
\ -
\ -
\ - \ -
\ - \ -
\ -
\ -
\ - \ -
\ - \ -
\ -
\ -
'); - $(".ui.dropdown").dropdown(); - setValidationRules(); -} diff --git a/controller/static/bower.json b/controller/static/bower.json deleted file mode 100644 index b580d98fd..000000000 --- a/controller/static/bower.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "shipyard", - "version": "1.0.0", - "homepage": "https://github.com/shipyard/shipyard", - "authors": [ - "Evan Hazlett " - ], - "description": "Docker Management", - "main": "app/app.js", - "keywords": [ - "shipyard", - "docker" - ], - "license": "Apache 2", - "private": true, - "ignore": [ - "**/.*", - "node_modules", - "bower_components", - "test", - "tests" - ], - "dependencies": { - "angular": "~1.2.23", - "semantic-ui": "~0.19.0", - "d3": "~3.4.11", - "angular-route": "~1.2.23", - "angular-resource": "~1.2.23", - "angular-loader": "~1.2.23", - "angular-cookies": "~1.2.23", - "angular-flash": "~0.1.13", - "angles": "*", - "ansi-to-html": "jorgeecardona/ansi-to-html" - } -} diff --git a/controller/static/css/app.css b/controller/static/css/app.css deleted file mode 100644 index 5053d1bf2..000000000 --- a/controller/static/css/app.css +++ /dev/null @@ -1,72 +0,0 @@ -html, -body { - font-size: 15px; -} -body { - font-family: "Open Sans", "Helvetica Neue", "Helvetica", "Arial", sans-serif; - background: #FFFFFF; - margin: 0px; - padding: 5px 45px 0px 20px; - color: #555555; - text-rendering: optimizeLegibility; - min-width: 320px; -} -th a { - color: #000000; -} -a { - cursor: pointer; -} -.logo { - font-family: 'Poiret One', arial; - text-rendering: optimizeLegibility; - font-size: 32px; -} -.logo a, .logo a:visited { - text-decoration: none; - color: #818181; -} -.ui.blue.button { - background-color: #9AAFCB; -} -.ui.blue.button:hover { - background-color: #6581A7; -} -div.container { - margin-top: 10px; -} -.ui.dropdown .menu { - margin-top: 5px; -} -.hide { - display: none; -} -.toolbar { - margin-bottom: 5px; -} -.checkbox { - padding-right: 10px; -} -.logs { - font-family: Menlo, curier; - font-size: 12px; - word-break: break-word; - width: 100%; -} -.rowlink:hover { - cursor: pointer; - background-color: rgba(0, 0, 0, 0.04) !important; -} -.healthcolumn { - width: 1px; -} -td.healthcolumn { - text-align: center; -} -.ui.sortable.table thead th.healthcolumn:after { - width: 0; - margin: 0; -} -[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { - display: none !important; -} diff --git a/controller/static/css/nv.d3.css b/controller/static/css/nv.d3.css deleted file mode 100644 index cae834827..000000000 --- a/controller/static/css/nv.d3.css +++ /dev/null @@ -1,769 +0,0 @@ - -/******************** - * HTML CSS - */ - - -.chartWrap { - margin: 0; - padding: 0; - overflow: hidden; -} - -/******************** - Box shadow and border radius styling -*/ -.nvtooltip.with-3d-shadow, .with-3d-shadow .nvtooltip { - -moz-box-shadow: 0 5px 10px rgba(0,0,0,.2); - -webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2); - box-shadow: 0 5px 10px rgba(0,0,0,.2); - - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} - -/******************** - * TOOLTIP CSS - */ - -.nvtooltip { - position: absolute; - background-color: rgba(255,255,255,1.0); - padding: 1px; - border: 1px solid rgba(0,0,0,.2); - z-index: 10000; - - font-family: Arial; - font-size: 13px; - text-align: left; - pointer-events: none; - - white-space: nowrap; - - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -/*Give tooltips that old fade in transition by - putting a "with-transitions" class on the container div. -*/ -.nvtooltip.with-transitions, .with-transitions .nvtooltip { - transition: opacity 250ms linear; - -moz-transition: opacity 250ms linear; - -webkit-transition: opacity 250ms linear; - - transition-delay: 250ms; - -moz-transition-delay: 250ms; - -webkit-transition-delay: 250ms; -} - -.nvtooltip.x-nvtooltip, -.nvtooltip.y-nvtooltip { - padding: 8px; -} - -.nvtooltip h3 { - margin: 0; - padding: 4px 14px; - line-height: 18px; - font-weight: normal; - background-color: rgba(247,247,247,0.75); - text-align: center; - - border-bottom: 1px solid #ebebeb; - - -webkit-border-radius: 5px 5px 0 0; - -moz-border-radius: 5px 5px 0 0; - border-radius: 5px 5px 0 0; -} - -.nvtooltip p { - margin: 0; - padding: 5px 14px; - text-align: center; -} - -.nvtooltip span { - display: inline-block; - margin: 2px 0; -} - -.nvtooltip table { - margin: 6px; - border-spacing:0; -} - - -.nvtooltip table td { - padding: 2px 9px 2px 0; - vertical-align: middle; -} - -.nvtooltip table td.key { - font-weight:normal; -} -.nvtooltip table td.value { - text-align: right; - font-weight: bold; -} - -.nvtooltip table tr.highlight td { - padding: 1px 9px 1px 0; - border-bottom-style: solid; - border-bottom-width: 1px; - border-top-style: solid; - border-top-width: 1px; -} - -.nvtooltip table td.legend-color-guide div { - width: 8px; - height: 8px; - vertical-align: middle; -} - -.nvtooltip .footer { - padding: 3px; - text-align: center; -} - - -.nvtooltip-pending-removal { - position: absolute; - pointer-events: none; -} - - -/******************** - * SVG CSS - */ - - -svg { - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - /* Trying to get SVG to act like a greedy block in all browsers */ - display: block; - width:100%; - height:100%; -} - - -svg text { - font: normal 12px Arial; -} - -svg .title { - font: bold 14px Arial; -} - -.nvd3 .nv-background { - fill: white; - fill-opacity: 0; - /* - pointer-events: none; - */ -} - -.nvd3.nv-noData { - font-size: 18px; - font-weight: bold; -} - - -/********** -* Brush -*/ - -.nv-brush .extent { - fill-opacity: .125; - shape-rendering: crispEdges; -} - - - -/********** -* Legend -*/ - -.nvd3 .nv-legend .nv-series { - cursor: pointer; -} - -.nvd3 .nv-legend .disabled circle { - fill-opacity: 0; -} - - - -/********** -* Axes -*/ -.nvd3 .nv-axis { - pointer-events:none; -} - -.nvd3 .nv-axis path { - fill: none; - stroke: #000; - stroke-opacity: .75; - shape-rendering: crispEdges; -} - -.nvd3 .nv-axis path.domain { - stroke-opacity: .75; -} - -.nvd3 .nv-axis.nv-x path.domain { - stroke-opacity: 0; -} - -.nvd3 .nv-axis line { - fill: none; - stroke: #e5e5e5; - shape-rendering: crispEdges; -} - -.nvd3 .nv-axis .zero line, -/*this selector may not be necessary*/ .nvd3 .nv-axis line.zero { - stroke-opacity: .75; -} - -.nvd3 .nv-axis .nv-axisMaxMin text { - font-weight: bold; -} - -.nvd3 .x .nv-axis .nv-axisMaxMin text, -.nvd3 .x2 .nv-axis .nv-axisMaxMin text, -.nvd3 .x3 .nv-axis .nv-axisMaxMin text { - text-anchor: middle -} - - - -/********** -* Brush -*/ - -.nv-brush .resize path { - fill: #eee; - stroke: #666; -} - - - -/********** -* Bars -*/ - -.nvd3 .nv-bars .negative rect { - zfill: brown; -} - -.nvd3 .nv-bars rect { - zfill: steelblue; - fill-opacity: .75; - - transition: fill-opacity 250ms linear; - -moz-transition: fill-opacity 250ms linear; - -webkit-transition: fill-opacity 250ms linear; -} - -.nvd3 .nv-bars rect.hover { - fill-opacity: 1; -} - -.nvd3 .nv-bars .hover rect { - fill: lightblue; -} - -.nvd3 .nv-bars text { - fill: rgba(0,0,0,0); -} - -.nvd3 .nv-bars .hover text { - fill: rgba(0,0,0,1); -} - - -/********** -* Bars -*/ - -.nvd3 .nv-multibar .nv-groups rect, -.nvd3 .nv-multibarHorizontal .nv-groups rect, -.nvd3 .nv-discretebar .nv-groups rect { - stroke-opacity: 0; - - transition: fill-opacity 250ms linear; - -moz-transition: fill-opacity 250ms linear; - -webkit-transition: fill-opacity 250ms linear; -} - -.nvd3 .nv-multibar .nv-groups rect:hover, -.nvd3 .nv-multibarHorizontal .nv-groups rect:hover, -.nvd3 .nv-discretebar .nv-groups rect:hover { - fill-opacity: 1; -} - -.nvd3 .nv-discretebar .nv-groups text, -.nvd3 .nv-multibarHorizontal .nv-groups text { - font-weight: bold; - fill: rgba(0,0,0,1); - stroke: rgba(0,0,0,0); -} - -/*********** -* Pie Chart -*/ - -.nvd3.nv-pie path { - stroke-opacity: 0; - transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear; - -moz-transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear; - -webkit-transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear; - -} - -.nvd3.nv-pie .nv-slice text { - stroke: #000; - stroke-width: 0; -} - -.nvd3.nv-pie path { - stroke: #fff; - stroke-width: 1px; - stroke-opacity: 1; -} - -.nvd3.nv-pie .hover path { - fill-opacity: .7; -} -.nvd3.nv-pie .nv-label { - pointer-events: none; -} -.nvd3.nv-pie .nv-label rect { - fill-opacity: 0; - stroke-opacity: 0; -} - -/********** -* Lines -*/ - -.nvd3 .nv-groups path.nv-line { - fill: none; - stroke-width: 1.5px; - /* - stroke-linecap: round; - shape-rendering: geometricPrecision; - - transition: stroke-width 250ms linear; - -moz-transition: stroke-width 250ms linear; - -webkit-transition: stroke-width 250ms linear; - - transition-delay: 250ms - -moz-transition-delay: 250ms; - -webkit-transition-delay: 250ms; - */ -} - -.nvd3 .nv-groups path.nv-line.nv-thin-line { - stroke-width: 1px; -} - - -.nvd3 .nv-groups path.nv-area { - stroke: none; - /* - stroke-linecap: round; - shape-rendering: geometricPrecision; - - stroke-width: 2.5px; - transition: stroke-width 250ms linear; - -moz-transition: stroke-width 250ms linear; - -webkit-transition: stroke-width 250ms linear; - - transition-delay: 250ms - -moz-transition-delay: 250ms; - -webkit-transition-delay: 250ms; - */ -} - -.nvd3 .nv-line.hover path { - stroke-width: 6px; -} - -/* -.nvd3.scatter .groups .point { - fill-opacity: 0.1; - stroke-opacity: 0.1; -} - */ - -.nvd3.nv-line .nvd3.nv-scatter .nv-groups .nv-point { - fill-opacity: 0; - stroke-opacity: 0; -} - -.nvd3.nv-scatter.nv-single-point .nv-groups .nv-point { - fill-opacity: .5 !important; - stroke-opacity: .5 !important; -} - - -.with-transitions .nvd3 .nv-groups .nv-point { - transition: stroke-width 250ms linear, stroke-opacity 250ms linear; - -moz-transition: stroke-width 250ms linear, stroke-opacity 250ms linear; - -webkit-transition: stroke-width 250ms linear, stroke-opacity 250ms linear; - -} - -.nvd3.nv-scatter .nv-groups .nv-point.hover, -.nvd3 .nv-groups .nv-point.hover { - stroke-width: 7px; - fill-opacity: .95 !important; - stroke-opacity: .95 !important; -} - - -.nvd3 .nv-point-paths path { - stroke: #aaa; - stroke-opacity: 0; - fill: #eee; - fill-opacity: 0; -} - - - -.nvd3 .nv-indexLine { - cursor: ew-resize; -} - - -/********** -* Distribution -*/ - -.nvd3 .nv-distribution { - pointer-events: none; -} - - - -/********** -* Scatter -*/ - -/* **Attempting to remove this for useVoronoi(false), need to see if it's required anywhere -.nvd3 .nv-groups .nv-point { - pointer-events: none; -} -*/ - -.nvd3 .nv-groups .nv-point.hover { - stroke-width: 20px; - stroke-opacity: .5; -} - -.nvd3 .nv-scatter .nv-point.hover { - fill-opacity: 1; -} - -/* -.nv-group.hover .nv-point { - fill-opacity: 1; -} -*/ - - -/********** -* Stacked Area -*/ - -.nvd3.nv-stackedarea path.nv-area { - fill-opacity: .7; - /* - stroke-opacity: .65; - fill-opacity: 1; - */ - stroke-opacity: 0; - - transition: fill-opacity 250ms linear, stroke-opacity 250ms linear; - -moz-transition: fill-opacity 250ms linear, stroke-opacity 250ms linear; - -webkit-transition: fill-opacity 250ms linear, stroke-opacity 250ms linear; - - /* - transition-delay: 500ms; - -moz-transition-delay: 500ms; - -webkit-transition-delay: 500ms; - */ - -} - -.nvd3.nv-stackedarea path.nv-area.hover { - fill-opacity: .9; - /* - stroke-opacity: .85; - */ -} -/* -.d3stackedarea .groups path { - stroke-opacity: 0; -} - */ - - - -.nvd3.nv-stackedarea .nv-groups .nv-point { - stroke-opacity: 0; - fill-opacity: 0; -} - -/* -.nvd3.nv-stackedarea .nv-groups .nv-point.hover { - stroke-width: 20px; - stroke-opacity: .75; - fill-opacity: 1; -}*/ - - - -/********** -* Line Plus Bar -*/ - -.nvd3.nv-linePlusBar .nv-bar rect { - fill-opacity: .75; -} - -.nvd3.nv-linePlusBar .nv-bar rect:hover { - fill-opacity: 1; -} - - -/********** -* Bullet -*/ - -.nvd3.nv-bullet { font: 10px sans-serif; } -.nvd3.nv-bullet .nv-measure { fill-opacity: .8; } -.nvd3.nv-bullet .nv-measure:hover { fill-opacity: 1; } -.nvd3.nv-bullet .nv-marker { stroke: #000; stroke-width: 2px; } -.nvd3.nv-bullet .nv-markerTriangle { stroke: #000; fill: #fff; stroke-width: 1.5px; } -.nvd3.nv-bullet .nv-tick line { stroke: #666; stroke-width: .5px; } -.nvd3.nv-bullet .nv-range.nv-s0 { fill: #eee; } -.nvd3.nv-bullet .nv-range.nv-s1 { fill: #ddd; } -.nvd3.nv-bullet .nv-range.nv-s2 { fill: #ccc; } -.nvd3.nv-bullet .nv-title { font-size: 14px; font-weight: bold; } -.nvd3.nv-bullet .nv-subtitle { fill: #999; } - - -.nvd3.nv-bullet .nv-range { - fill: #bababa; - fill-opacity: .4; -} -.nvd3.nv-bullet .nv-range:hover { - fill-opacity: .7; -} - - - -/********** -* Sparkline -*/ - -.nvd3.nv-sparkline path { - fill: none; -} - -.nvd3.nv-sparklineplus g.nv-hoverValue { - pointer-events: none; -} - -.nvd3.nv-sparklineplus .nv-hoverValue line { - stroke: #333; - stroke-width: 1.5px; - } - -.nvd3.nv-sparklineplus, -.nvd3.nv-sparklineplus g { - pointer-events: all; -} - -.nvd3 .nv-hoverArea { - fill-opacity: 0; - stroke-opacity: 0; -} - -.nvd3.nv-sparklineplus .nv-xValue, -.nvd3.nv-sparklineplus .nv-yValue { - /* - stroke: #666; - */ - stroke-width: 0; - font-size: .9em; - font-weight: normal; -} - -.nvd3.nv-sparklineplus .nv-yValue { - stroke: #f66; -} - -.nvd3.nv-sparklineplus .nv-maxValue { - stroke: #2ca02c; - fill: #2ca02c; -} - -.nvd3.nv-sparklineplus .nv-minValue { - stroke: #d62728; - fill: #d62728; -} - -.nvd3.nv-sparklineplus .nv-currentValue { - /* - stroke: #444; - fill: #000; - */ - font-weight: bold; - font-size: 1.1em; -} - -/********** -* historical stock -*/ - -.nvd3.nv-ohlcBar .nv-ticks .nv-tick { - stroke-width: 2px; -} - -.nvd3.nv-ohlcBar .nv-ticks .nv-tick.hover { - stroke-width: 4px; -} - -.nvd3.nv-ohlcBar .nv-ticks .nv-tick.positive { - stroke: #2ca02c; -} - -.nvd3.nv-ohlcBar .nv-ticks .nv-tick.negative { - stroke: #d62728; -} - -.nvd3.nv-historicalStockChart .nv-axis .nv-axislabel { - font-weight: bold; -} - -.nvd3.nv-historicalStockChart .nv-dragTarget { - fill-opacity: 0; - stroke: none; - cursor: move; -} - -.nvd3 .nv-brush .extent { - /* - cursor: ew-resize !important; - */ - fill-opacity: 0 !important; -} - -.nvd3 .nv-brushBackground rect { - stroke: #000; - stroke-width: .4; - fill: #fff; - fill-opacity: .7; -} - - - -/********** -* Indented Tree -*/ - - -/** - * TODO: the following 3 selectors are based on classes used in the example. I should either make them standard and leave them here, or move to a CSS file not included in the library - */ -.nvd3.nv-indentedtree .name { - margin-left: 5px; -} - -.nvd3.nv-indentedtree .clickable { - color: #08C; - cursor: pointer; -} - -.nvd3.nv-indentedtree span.clickable:hover { - color: #005580; - text-decoration: underline; -} - - -.nvd3.nv-indentedtree .nv-childrenCount { - display: inline-block; - margin-left: 5px; -} - -.nvd3.nv-indentedtree .nv-treeicon { - cursor: pointer; - /* - cursor: n-resize; - */ -} - -.nvd3.nv-indentedtree .nv-treeicon.nv-folded { - cursor: pointer; - /* - cursor: s-resize; - */ -} - -/********** -* Parallel Coordinates -*/ - -.nvd3 .background path { - fill: none; - stroke: #ccc; - stroke-opacity: .4; - shape-rendering: crispEdges; -} - -.nvd3 .foreground path { - fill: none; - stroke: steelblue; - stroke-opacity: .7; -} - -.nvd3 .brush .extent { - fill-opacity: .3; - stroke: #fff; - shape-rendering: crispEdges; -} - -.nvd3 .axis line, .axis path { - fill: none; - stroke: #000; - shape-rendering: crispEdges; -} - -.nvd3 .axis text { - text-shadow: 0 1px 0 #fff; -} - -/**** -Interactive Layer -*/ -.nvd3 .nv-interactiveGuideLine { - pointer-events:none; -} -.nvd3 line.nv-guideline { - stroke: #ccc; -} \ No newline at end of file diff --git a/controller/static/css/semantic.min.css b/controller/static/css/semantic.min.css deleted file mode 100644 index f8f077981..000000000 --- a/controller/static/css/semantic.min.css +++ /dev/null @@ -1,14 +0,0 @@ -/* -* # Semantic UI -* Version: 0.16.1 -* http://github.com/jlukic/semantic-ui -* -* -* Copyright 2014 Contributors -* Released under the MIT license -* http://opensource.org/licenses/MIT -* -* Released: 04/22/2014 -*/ - -.ui.breadcrumb{margin:1em 0;display:inline-block;vertical-align:middle}.ui.breadcrumb:first-child{margin-top:0}.ui.breadcrumb:last-child{margin-bottom:0}.ui.breadcrumb .divider{display:inline-block;opacity:.5;margin:0 .15em;font-size:1em;color:rgba(0,0,0,.3)}.ui.breadcrumb a.section{cursor:pointer}.ui.breadcrumb .section{display:inline-block;margin:0;padding:0}.ui.breadcrumb.segment{display:inline-block;padding:.5em 1em}.ui.breadcrumb .active.section{font-weight:700}.ui.small.breadcrumb{font-size:.75em}.ui.large.breadcrumb{font-size:1.1em}.ui.huge.breadcrumb{font-size:1.3em}.ui.form{position:relative;max-width:100%}.ui.form :first-child{margin-top:0}.ui.form :last-child{margin-bottom:0}.ui.form>p{margin:1em 0}.ui.form .field{clear:both;margin:0 0 1em}.ui.form .field>label{margin:0 0 .3em;display:block;color:#555;font-size:.875em}.ui.form .ui.input,.ui.form input[type=date],.ui.form input[type=email],.ui.form input[type=number],.ui.form input[type=password],.ui.form input[type=tel],.ui.form input[type=text],.ui.form input[type=url],.ui.form textarea{width:100%}.ui.form input[type=date],.ui.form input[type=email],.ui.form input[type=number],.ui.form input[type=password],.ui.form input[type=tel],.ui.form input[type=text],.ui.form input[type=url],.ui.form textarea{margin:0;padding:.65em 1em;font-size:1em;background-color:#FFF;border:1px solid rgba(0,0,0,.15);outline:0;color:rgba(0,0,0,.7);border-radius:.3125em;-webkit-transition:background-color .3s ease-out,-webkit-box-shadow .2s ease,border-color .2s ease;-moz-transition:background-color .3s ease-out,box-shadow .2s ease,border-color .2s ease;transition:background-color .3s ease-out,box-shadow .2s ease,border-color .2s ease;-webkit-box-shadow:0 0 rgba(0,0,0,.3)inset;box-shadow:0 0 rgba(0,0,0,.3)inset;-webkit-appearance:none;-webkit-tap-highlight-color:rgba(255,255,255,0);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.ui.form textarea,.ui.textarea{line-height:1.33;min-height:8em;height:12em;max-height:24em;resize:vertical}.ui.form input[type=checkbox],.ui.form textarea{vertical-align:top}.ui.form .divider{clear:both;margin:1em 0}.ui.form .error.message,.ui.form .info.message,.ui.form .warning.message{display:none}.ui.form .message:first-child{margin-top:0}.ui.form .field .prompt.label{white-space:nowrap}.ui.form .inline.field .prompt{margin-top:0;margin-left:1em}.ui.form .inline.field .prompt:before{margin-top:-.3em;bottom:auto;right:auto;top:50%;left:0}.ui.form input[type=date]:focus,.ui.form input[type=email]:focus,.ui.form input[type=number]:focus,.ui.form input[type=password]:focus,.ui.form input[type=tel]:focus,.ui.form input[type=text]:focus,.ui.form input[type=url]:focus,.ui.form textarea:focus{color:rgba(0,0,0,.85);border-color:rgba(0,0,0,.2);border-bottom-left-radius:0;border-top-left-radius:0;-webkit-appearance:none;-webkit-box-shadow:.3em 0 0 0 rgba(0,0,0,.2)inset;box-shadow:.3em 0 0 0 rgba(0,0,0,.2)inset}.ui.form.error .error.message,.ui.form.warning .warning.message{display:block}.ui.form .field.error .input,.ui.form .field.error label,.ui.form .fields.error .field .input,.ui.form .fields.error .field label{color:#D95C5C}.ui.form .field.error .corner.label,.ui.form .fields.error .field .corner.label{border-color:#D95C5C;color:#FFF}.ui.form .field.error input[type=date],.ui.form .field.error input[type=email],.ui.form .field.error input[type=number],.ui.form .field.error input[type=password],.ui.form .field.error input[type=tel],.ui.form .field.error input[type=text],.ui.form .field.error input[type=url],.ui.form .field.error textarea,.ui.form .fields.error .field input[type=date],.ui.form .fields.error .field input[type=email],.ui.form .fields.error .field input[type=number],.ui.form .fields.error .field input[type=password],.ui.form .fields.error .field input[type=tel],.ui.form .fields.error .field input[type=text],.ui.form .fields.error .field input[type=url],.ui.form .fields.error .field textarea{background-color:snow;border-color:#E7BEBE;border-left:none;color:#D95C5C;padding-left:1.2em;border-bottom-left-radius:0;border-top-left-radius:0;-webkit-box-shadow:.3em 0 0 0 #D95C5C inset;box-shadow:.3em 0 0 0 #D95C5C inset}.ui.form .field.error input[type=date]:focus,.ui.form .field.error input[type=email]:focus,.ui.form .field.error input[type=number]:focus,.ui.form .field.error input[type=password]:focus,.ui.form .field.error input[type=tel]:focus,.ui.form .field.error input[type=text]:focus,.ui.form .field.error input[type=url]:focus,.ui.form .field.error textarea:focus{border-color:#ff5050;color:#ff5050;-webkit-appearance:none;-webkit-box-shadow:.3em 0 0 0 #FF5050 inset;box-shadow:.3em 0 0 0 #FF5050 inset}.ui.form .field.error .ui.dropdown,.ui.form .field.error .ui.dropdown .item,.ui.form .fields.error .field .ui.dropdown,.ui.form .fields.error .field .ui.dropdown .item{background-color:snow;color:#D95C5C}.ui.form .field.error .ui.dropdown,.ui.form .field.error .ui.dropdown:hover,.ui.form .fields.error .field .ui.dropdown,.ui.form .fields.error .field .ui.dropdown:hover{-webkit-box-shadow:0 0 0 1px #E7BEBE!important;box-shadow:0 0 0 1px #E7BEBE!important}.ui.form .field.error .ui.dropdown:hover .menu,.ui.form .fields.error .field .ui.dropdown:hover .menu{-webkit-box-shadow:0 1px 0 1px #E7BEBE;box-shadow:0 1px 0 1px #E7BEBE}.ui.form .field.error .ui.dropdown .menu .item:hover,.ui.form .fields.error .field .ui.dropdown .menu .item:hover{background-color:#FFF2F2}.ui.form .field.error .ui.dropdown .menu .active.item,.ui.form .fields.error .field .ui.dropdown .menu .active.item{background-color:#FDCFCF!important}.ui.form ::-webkit-input-placeholder{color:#AAA}.ui.form ::-moz-placeholder{color:#AAA}.ui.form :focus::-webkit-input-placeholder{color:#999}.ui.form :focus::-moz-placeholder{color:#999}.ui.form .error ::-webkit-input-placeholder{color:rgba(255,80,80,.4)}.ui.form .error ::-moz-placeholder{color:rgba(255,80,80,.4)}.ui.form .error :focus::-webkit-input-placeholder{color:rgba(255,80,80,.7)}.ui.form .error :focus::-moz-placeholder{color:rgba(255,80,80,.7)}.ui.form .field :disabled,.ui.form .field.disabled,.ui.form .field.disabled label{opacity:.5}.ui.form .field.disabled :disabled{opacity:1}.ui.form.loading{position:relative}.ui.form.loading:after{position:absolute;top:0;left:0;content:'';width:100%;height:100%;background:rgba(255,255,255,.8)url(../images/loader-large.gif) no-repeat 50% 50%;visibility:visible}.ui.form.fluid{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.ui.form input.attached{width:auto}.ui.form .date.field>label{position:relative}.ui.form .date.field>label:after{position:absolute;top:2em;right:.5em;font-family:Icons;content:'\f133';font-size:1.2em;font-weight:400;color:#CCC}.ui.inverted.form label{color:#FFF}.ui.inverted.form .field.error input[type=date],.ui.inverted.form .field.error input[type=email],.ui.inverted.form .field.error input[type=number],.ui.inverted.form .field.error input[type=password],.ui.inverted.form .field.error input[type=tel],.ui.inverted.form .field.error input[type=text],.ui.inverted.form .field.error input[type=url],.ui.inverted.form .field.error textarea{background-color:#FCC}.ui.inverted.form .ui.checkbox label{color:rgba(255,255,255,.8)}.ui.inverted.form .ui.checkbox .box:hover,.ui.inverted.form .ui.checkbox label:hover{color:#FFF}.ui.form .grouped.fields{margin:0 0 1em}.ui.form .grouped.fields .field{display:block;float:none;margin:.5em 0;padding:0}.ui.form .fields{clear:both}.ui.form .fields:after{content:' ';display:block;clear:both;visibility:hidden;line-height:0;height:0}.ui.form .fields>.field{clear:none;float:left;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.ui.form .fields>.field:first-child{border-left:none;-webkit-box-shadow:none;box-shadow:none}.ui.form .two.fields>.field,.ui.form .two.fields>.fields{width:50%;padding-left:1%;padding-right:1%}.ui.form .three.fields>.field,.ui.form .three.fields>.fields{width:33.333%;padding-left:1%;padding-right:1%}.ui.form .four.fields>.field,.ui.form .four.fields>.fields{width:25%;padding-left:1%;padding-right:1%}.ui.form .five.fields>.field,.ui.form .five.fields>.fields{width:20%;padding-left:1%;padding-right:1%}.ui.form .fields .field:first-child{padding-left:0}.ui.form .fields .field:last-child{padding-right:0}.ui.form .fields .wide.field{width:6.25%;padding-left:1%;padding-right:1%}.ui.form .fields .wide.field:first-child{padding-left:0}.ui.form .fields .wide.field:last-child{padding-right:0}.ui.form .fields>.one.wide.field{width:6.25%}.ui.form .fields>.two.wide.field{width:12.5%}.ui.form .fields>.three.wide.field{width:18.75%}.ui.form .fields>.four.wide.field{width:25%}.ui.form .fields>.five.wide.field{width:31.25%}.ui.form .fields>.six.wide.field{width:37.5%}.ui.form .fields>.seven.wide.field{width:43.75%}.ui.form .fields>.eight.wide.field{width:50%}.ui.form .fields>.nine.wide.field{width:56.25%}.ui.form .fields>.ten.wide.field{width:62.5%}.ui.form .fields>.eleven.wide.field{width:68.75%}.ui.form .fields>.twelve.wide.field{width:75%}.ui.form .fields>.thirteen.wide.field{width:81.25%}.ui.form .fields>.fourteen.wide.field{width:87.5%}.ui.form .fields>.fifteen.wide.field{width:93.75%}.ui.form .fields>.sixteen.wide.field{width:100%}@media only screen and (max-width:767px){.ui.form .fields>.eight.wide.field,.ui.form .fields>.eleven.wide.field,.ui.form .fields>.fifteen.wide.field,.ui.form .fields>.five.wide.field,.ui.form .fields>.four.wide.field,.ui.form .fields>.fourteen.wide.field,.ui.form .fields>.nine.wide.field,.ui.form .fields>.seven.wide.field,.ui.form .fields>.six.wide.field,.ui.form .fields>.sixteen.wide.field,.ui.form .fields>.ten.wide.field,.ui.form .fields>.thirteen.wide.field,.ui.form .fields>.three.wide.field,.ui.form .fields>.twelve.wide.field,.ui.form .fields>.two.wide.field,.ui.form .five.fields>.field,.ui.form .five.fields>.fields,.ui.form .four.fields>.field,.ui.form .four.fields>.fields,.ui.form .three.fields>.field,.ui.form .three.fields>.fields,.ui.form .two.fields>.field,.ui.form .two.fields>.fields{width:100%;padding-left:0;padding-right:0}}.ui.form .inline.fields .field{min-height:1.3em;margin-right:.5em}.ui.form .inline.field>input,.ui.form .inline.field>label,.ui.form .inline.field>p,.ui.form .inline.fields .field>input,.ui.form .inline.fields .field>label,.ui.form .inline.fields .field>p{display:inline-block;width:auto;margin-top:0;margin-bottom:0;vertical-align:middle;font-size:1em}.ui.form .inline.field>input,.ui.form .inline.fields .field>input{font-size:.875em}.ui.form .inline.field>:first-child,.ui.form .inline.fields .field>:first-child{margin:0 .5em 0 0}.ui.form .inline.field>:only-child,.ui.form .inline.fields .field>:only-child{margin:0}.ui.small.form{font-size:.875em}.ui.small.form input[type=date],.ui.small.form input[type=email],.ui.small.form input[type=number],.ui.small.form input[type=password],.ui.small.form input[type=tel],.ui.small.form input[type=text],.ui.small.form input[type=url],.ui.small.form label,.ui.small.form textarea{font-size:1em}.ui.large.form{font-size:1.125em}.ui.grid{display:block;text-align:left;font-size:0;margin:0 -1.5%;padding:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}body>.ui.grid{margin-left:0!important;margin-right:0!important}.ui.grid:after,.ui.row:after{content:".";display:block;height:0;clear:both;visibility:hidden}.ui.grid>.column,.ui.grid>.row>.column{display:inline-block;text-align:left;font-size:1rem;width:6.25%;padding-left:1.5%;padding-right:1.5%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;vertical-align:top}.ui.grid>.column{margin-top:1rem;margin-bottom:1rem}.ui.grid>.row{display:block;width:100%!important;margin-top:1.5%;padding:1rem 0 0;font-size:0}.ui.grid>.row:first-child{padding-top:0;margin-top:0}.ui.grid>.row>.column>img,.ui.grid>.row>img{max-width:100%}.ui.grid .column>.ui.segment:only-child{margin:0}.ui.page.grid{min-width:320px;margin-left:0;margin-right:0}@media only screen and (max-width:991px){.ui.page.grid{padding:0 4%}}@media only screen and (min-width:992px){.ui.page.grid{padding:0 8%}}@media only screen and (min-width:1500px){.ui.page.grid{padding:0 13%}}@media only screen and (min-width:1750px){.ui.page.grid{padding:0 18%}}@media only screen and (min-width:2000px){.ui.page.grid{padding:0 23%}}.ui.column.grid>.one.wide.column,.ui.grid>.column.row>.one.wide.column,.ui.grid>.one.wide.column,.ui.grid>.row>.one.wide.column{width:6.25%}.ui.column.grid>.two.wide.column,.ui.grid>.column.row>.two.wide.column,.ui.grid>.row>.two.wide.column,.ui.grid>.two.wide.column{width:12.5%}.ui.column.grid>.three.wide.column,.ui.grid>.column.row>.three.wide.column,.ui.grid>.row>.three.wide.column,.ui.grid>.three.wide.column{width:18.75%}.ui.column.grid>.four.wide.column,.ui.grid>.column.row>.four.wide.column,.ui.grid>.four.wide.column,.ui.grid>.row>.four.wide.column{width:25%}.ui.column.grid>.five.wide.column,.ui.grid>.column.row>.five.wide.column,.ui.grid>.five.wide.column,.ui.grid>.row>.five.wide.column{width:31.25%}.ui.column.grid>.six.wide.column,.ui.grid>.column.row>.six.wide.column,.ui.grid>.row>.six.wide.column,.ui.grid>.six.wide.column{width:37.5%}.ui.column.grid>.seven.wide.column,.ui.grid>.column.row>.seven.wide.column,.ui.grid>.row>.seven.wide.column,.ui.grid>.seven.wide.column{width:43.75%}.ui.column.grid>.eight.wide.column,.ui.grid>.column.row>.eight.wide.column,.ui.grid>.eight.wide.column,.ui.grid>.row>.eight.wide.column{width:50%}.ui.column.grid>.nine.wide.column,.ui.grid>.column.row>.nine.wide.column,.ui.grid>.nine.wide.column,.ui.grid>.row>.nine.wide.column{width:56.25%}.ui.column.grid>.ten.wide.column,.ui.grid>.column.row>.ten.wide.column,.ui.grid>.row>.ten.wide.column,.ui.grid>.ten.wide.column{width:62.5%}.ui.column.grid>.eleven.wide.column,.ui.grid>.column.row>.eleven.wide.column,.ui.grid>.eleven.wide.column,.ui.grid>.row>.eleven.wide.column{width:68.75%}.ui.column.grid>.twelve.wide.column,.ui.grid>.column.row>.twelve.wide.column,.ui.grid>.row>.twelve.wide.column,.ui.grid>.twelve.wide.column{width:75%}.ui.column.grid>.thirteen.wide.column,.ui.grid>.column.row>.thirteen.wide.column,.ui.grid>.row>.thirteen.wide.column,.ui.grid>.thirteen.wide.column{width:81.25%}.ui.column.grid>.fourteen.wide.column,.ui.grid>.column.row>.fourteen.wide.column,.ui.grid>.fourteen.wide.column,.ui.grid>.row>.fourteen.wide.column{width:87.5%}.ui.column.grid>.fifteen.wide.column,.ui.grid>.column.row>.fifteen.wide.column,.ui.grid>.fifteen.wide.column,.ui.grid>.row>.fifteen.wide.column{width:93.75%}.ui.column.grid>.sixteen.wide.column,.ui.grid>.column.row>.sixteen.wide.column,.ui.grid>.one.column.row>.column,.ui.grid>.row>.sixteen.wide.column,.ui.grid>.sixteen.wide.column,.ui.one.column.grid>.column,.ui.one.column.grid>.row>.column{width:100%}.ui.grid>.two.column.row>.column,.ui.two.column.grid>.column,.ui.two.column.grid>.row>.column{width:50%}.ui.grid>.three.column.row>.column,.ui.three.column.grid>.column,.ui.three.column.grid>.row>.column{width:33.3333%}.ui.four.column.grid>.column,.ui.four.column.grid>.row>.column,.ui.grid>.four.column.row>.column{width:25%}.ui.five.column.grid>.column,.ui.five.column.grid>.row>.column,.ui.grid>.five.column.row>.column{width:20%}.ui.grid>.six.column.row>.column,.ui.six.column.grid>.column,.ui.six.column.grid>.row>.column{width:16.66667%}.ui.grid>.seven.column.row>.column,.ui.seven.column.grid>.column,.ui.seven.column.grid>.row>.column{width:14.2857%}.ui.eight.column.grid>.column,.ui.eight.column.grid>.row>.column,.ui.grid>.eight.column.row>.column{width:12.5%}.ui.grid>.nine.column.row>.column,.ui.nine.column.grid>.column,.ui.nine.column.grid>.row>.column{width:11.1111%}.ui.grid>.ten.column.row>.column,.ui.ten.column.grid>.column,.ui.ten.column.grid>.row>.column{width:10%}.ui.eleven.column.grid>.column,.ui.eleven.column.grid>.row>.column,.ui.grid>.eleven.column.row>.column{width:9.0909%}.ui.grid>.twelve.column.row>.column,.ui.twelve.column.grid>.column,.ui.twelve.column.grid>.row>.column{width:8.3333%}.ui.grid>.thirteen.column.row>.column,.ui.thirteen.column.grid>.column,.ui.thirteen.column.grid>.row>.column{width:7.6923%}.ui.fourteen.column.grid>.column,.ui.fourteen.column.grid>.row>.column,.ui.grid>.fourteen.column.row>.column{width:7.1428%}.ui.fifteen.column.grid>.column,.ui.fifteen.column.grid>.row>.column,.ui.grid>.fifteen.column.row>.column{width:6.6666%}.ui.grid>.sixteen.column.row>.column,.ui.sixteen.column.grid>.column,.ui.sixteen.column.grid>.row>.column{width:6.25%}.ui.grid>.column:only-child,.ui.grid>.row>.column:only-child{width:100%}.ui.relaxed.grid{margin:0 -2.5%}.ui.relaxed.grid>.column,.ui.relaxed.grid>.row>.column{padding-left:2.5%;padding-right:2.5%}.ui.grid .left.floated.column{float:left}.ui.grid .right.floated.column{float:right}.ui.divided.grid,.ui.divided.grid>.row{display:table;width:100%;margin-left:0!important;margin-right:0!important}.ui.divided.grid>.column:not(.row),.ui.divided.grid>.row>.column{display:table-cell;-webkit-box-shadow:-1px 0 0 0 rgba(0,0,0,.1),-2px 0 0 0 rgba(255,255,255,.8);box-shadow:-1px 0 0 0 rgba(0,0,0,.1),-2px 0 0 0 rgba(255,255,255,.8)}.ui.divided.grid>.column.row{display:table}.ui.divided.grid>.column:first-child,.ui.divided.grid>.row>.column:first-child{-webkit-box-shadow:none;box-shadow:none}.ui.vertically.divided.grid>.row{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1),0 -2px 0 0 rgba(255,255,255,.8)!important;box-shadow:0 -1px 0 0 rgba(0,0,0,.1),0 -2px 0 0 rgba(255,255,255,.8)!important}.ui.vertically.divided.grid>.column:not(.row),.ui.vertically.divided.grid>.row:first-child,.ui.vertically.divided.grid>.row>.column{-webkit-box-shadow:none!important;box-shadow:none!important}.ui.celled.grid{display:table;width:100%;margin-left:0!important;margin-right:0!important;-webkit-box-shadow:0 0 0 1px #DFDFDF;box-shadow:0 0 0 1px #DFDFDF}.ui.celled.grid>.column.row,.ui.celled.grid>.column.row:first-child,.ui.celled.grid>.row{display:table;width:100%;margin-top:0;padding-top:0;-webkit-box-shadow:0 -1px 0 0 #dfdfdf;box-shadow:0 -1px 0 0 #dfdfdf}.ui.celled.grid>.column:not(.row),.ui.celled.grid>.row>.column{display:table-cell;padding:.75em;-webkit-box-shadow:-1px 0 0 0 #dfdfdf;box-shadow:-1px 0 0 0 #dfdfdf}.ui.celled.grid>.column:first-child,.ui.celled.grid>.row>.column:first-child,.ui.celled.page.grid{-webkit-box-shadow:none;box-shadow:none}.ui.grid .left.aligned.column,.ui.grid>.left.aligned.row>.column,.ui.left.aligned.grid,.ui.left.aligned.grid>.column,.ui.left.aligned.grid>.row>.column{text-align:left}.ui.center.aligned.grid,.ui.center.aligned.grid>.column,.ui.center.aligned.grid>.row>.column,.ui.grid .center.aligned.column,.ui.grid>.center.aligned.row>.column{text-align:center}.ui.grid .right.aligned.column,.ui.grid>.right.aligned.row>.column,.ui.right.aligned.grid,.ui.right.aligned.grid>.column,.ui.right.aligned.grid>.row>.column{text-align:right}.ui.grid .justified.column,.ui.grid>.justified.row>.column,.ui.justified.grid,.ui.justified.grid>.column,.ui.justified.grid>.row>.column{text-align:justify;-webkit-hyphens:auto;-moz-hyphens:auto;-ms-hyphens:auto;hyphens:auto}.ui.grid .top.aligned.column,.ui.grid>.top.aligned.row>.column,.ui.top.aligned.grid,.ui.top.aligned.grid>.column,.ui.top.aligned.grid>.row>.column{vertical-align:top}.ui.grid .middle.aligned.column,.ui.grid>.middle.aligned.row>.column,.ui.middle.aligned.grid,.ui.middle.aligned.grid>.column,.ui.middle.aligned.grid>.row>.column{vertical-align:middle}.ui.bottom.aligned.grid,.ui.bottom.aligned.grid>.column,.ui.bottom.aligned.grid>.row>.column,.ui.grid .bottom.aligned.column,.ui.grid>.bottom.aligned.row>.column{vertical-align:bottom}.ui.grid>.equal.height.row{display:table;width:100%}.ui.grid>.equal.height.row>.column{display:table-cell}@media only screen and (max-width:767px){.ui.grid>.mobile.only.row,.ui.mobile.only.grid{display:block!important}.ui.grid>.row>.mobile.only.column{display:inline-block!important}.ui.celled.grid .mobile.only.row,.ui.celled.mobile.only.grid,.ui.celled.mobile.only.grid .row,.ui.divided.grid .mobile.only.row,.ui.divided.mobile.only.grid,.ui.divided.mobile.only.grid .row,.ui.grid .mobile.only.equal.height.row,.ui.mobile.only.grid .equal.height.row{display:table!important}.ui.celled.grid>.row>.mobile.only.column,.ui.celled.mobile.only.grid>.column,.ui.celled.mobile.only.grid>.row>.column,.ui.divided.grid>.row>.mobile.only.column,.ui.divided.mobile.only.grid>.column,.ui.divided.mobile.only.grid>.row>.column{display:table-cell!important}}@media only screen and (min-width:768px){.ui.grid>.mobile.only.column,.ui.grid>.mobile.only.row,.ui.grid>.row>.mobile.only.column,.ui.mobile.only.grid{display:none}}@media only screen and (min-width:768px) and (max-width:991px){.ui.grid>.tablet.only.row,.ui.tablet.only.grid{display:block!important}.ui.grid>.row>.tablet.only.column{display:inline-block!important}.ui.celled.grid .tablet.only.row,.ui.celled.tablet.only.grid,.ui.celled.tablet.only.grid .row,.ui.divided.grid .tablet.only.row,.ui.divided.tablet.only.grid,.ui.divided.tablet.only.grid .row,.ui.grid .tablet.only.equal.height.row,.ui.tablet.only.grid .equal.height.row{display:table!important}.ui.celled.grid>.row>.tablet.only.column,.ui.celled.tablet.only.grid>.column,.ui.celled.tablet.only.grid>.row>.column,.ui.divided.grid>.row>.tablet.only.column,.ui.divided.tablet.only.grid>.column,.ui.divided.tablet.only.grid>.row>.column{display:table-cell!important}}@media only screen and (max-width:767px),(min-width:992px){.ui.grid>.row>.tablet.only.column,.ui.grid>.tablet.only.column,.ui.grid>.tablet.only.row,.ui.tablet.only.grid{display:none}}@media only screen and (min-width:992px){.ui.computer.only.grid,.ui.grid>.computer.only.row{display:block!important}.ui.grid>.row>.computer.only.column{display:inline-block!important}.ui.celled.computer.only.grid,.ui.celled.computer.only.grid .row,.ui.celled.grid .computer.only.row,.ui.computer.only.grid .equal.height.row,.ui.divided.computer.only.grid,.ui.divided.computer.only.grid .row,.ui.divided.grid .computer.only.row,.ui.grid .computer.only.equal.height.row{display:table!important}.ui.celled.computer.only.grid>.column,.ui.celled.computer.only.grid>.row>.column,.ui.celled.grid>.row>.computer.only.column,.ui.divided.computer.only.grid>.column,.ui.divided.computer.only.grid>.row>.column,.ui.divided.grid>.row>.computer.only.column{display:table-cell!important}}@media only screen and (max-width:991px){.ui.computer.only.grid,.ui.grid>.computer.only.column,.ui.grid>.computer.only.row,.ui.grid>.row>.computer.only.column{display:none}}@media only screen and (max-width:767px){.ui.five.column.doubling.grid>.column,.ui.five.column.doubling.grid>.row>.column,.ui.four.column.doubling.grid>.column,.ui.four.column.doubling.grid>.row>.column,.ui.grid>.five.column.doubling.row>.column,.ui.grid>.four.column.doubling.row>.column,.ui.grid>.three.column.doubling.row>.column,.ui.grid>.two.column.doubling.row>.column,.ui.three.column.doubling.grid>.column,.ui.three.column.doubling.grid>.row>.column,.ui.two.column.doubling.grid>.column,.ui.two.column.doubling.grid>.row>.column{width:100%}.ui.eight.column.doubling.grid>.column,.ui.eight.column.doubling.grid>.row>.column,.ui.grid>.eight.column.doubling.row>.column,.ui.grid>.nine.column.doubling.row>.column,.ui.grid>.seven.column.doubling.row>.column,.ui.grid>.six.column.doubling.row>.column,.ui.grid>.ten.column.doubling.row>.column,.ui.nine.column.doubling.grid>.column,.ui.nine.column.doubling.grid>.row>.column,.ui.seven.column.doubling.grid>.column,.ui.seven.column.doubling.grid>.row>.column,.ui.six.column.doubling.grid>.column,.ui.six.column.doubling.grid>.row>.column,.ui.ten.column.doubling.grid>.column,.ui.ten.column.doubling.grid>.row>.column{width:50%}.ui.fourteen.column.doubling.grid>.column,.ui.fourteen.column.doubling.grid>.row>.column,.ui.grid>.fourteen.column.doubling.row>.column,.ui.grid>.twelve.column.doubling.row>.column,.ui.twelve.column.doubling.grid>.column,.ui.twelve.column.doubling.grid>.row>.column{width:33.3333333333333%}.ui.grid>.sixteen.column.doubling.row>.column,.ui.sixteen.column.doubling.grid>.column,.ui.sixteen.column.doubling.grid>.row>.column{width:25%}}@media only screen and (min-width:768px) and (max-width:991px){.ui.grid>.two.column.doubling.row>.column,.ui.two.column.doubling.grid>.column,.ui.two.column.doubling.grid>.row>.column{width:100%}.ui.four.column.doubling.grid>.column,.ui.four.column.doubling.grid>.row>.column,.ui.grid>.four.column.doubling.row>.column,.ui.grid>.three.column.doubling.row>.column,.ui.three.column.doubling.grid>.column,.ui.three.column.doubling.grid>.row>.column{width:50%}.ui.eight.column.doubling.grid>.column,.ui.eight.column.doubling.grid>.row>.column,.ui.five.column.doubling.grid>.column,.ui.five.column.doubling.grid>.row>.column,.ui.grid>.eight.column.doubling.row>.column,.ui.grid>.five.column.doubling.row>.column,.ui.grid>.six.column.doubling.row>.column,.ui.six.column.doubling.grid>.column,.ui.six.column.doubling.grid>.row>.column{width:33.3333333%}.ui.eight.column.doubling.grid>.column,.ui.eight.column.doubling.grid>.row>.column,.ui.grid>.eight.column.doubling.row>.column,.ui.grid>.nine.column.doubling.row>.column,.ui.nine.column.doubling.grid>.column,.ui.nine.column.doubling.grid>.row>.column{width:25%}.ui.grid>.ten.column.doubling.row>.column,.ui.ten.column.doubling.grid>.column,.ui.ten.column.doubling.grid>.row>.column{width:20%}.ui.grid>.twelve.column.doubling.row>.column,.ui.twelve.column.doubling.grid>.column,.ui.twelve.column.doubling.grid>.row>.column{width:16.6666666%}.ui.fourteen.column.doubling.grid>.column,.ui.fourteen.column.doubling.grid>.row>.column,.ui.grid>.fourteen.column.doubling.row>.column{width:14.28571428571429%}.ui.grid>.sixteen.column.doubling.row>.column,.ui.sixteen.column.doubling.grid>.column,.ui.sixteen.column.doubling.grid>.row>.column{width:12.5%}}@media only screen and (max-width:767px){.ui.stackable.grid{display:block!important;padding:0;margin:0}.ui.stackable.grid>.column,.ui.stackable.grid>.row>.column{display:block!important;width:auto!important;margin:1em 0 0!important;padding:1em 0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}.ui.stackable.celled.grid .column,.ui.stackable.divided.grid .column{border-top:1px dotted rgba(0,0,0,.1)}.ui.stackable.grid>.column:first-child,.ui.stackable.grid>.row:first-child>.column:first-child{margin-top:0!important;padding-top:0!important}.ui.stackable.celled.grid>.column:first-child,.ui.stackable.celled.grid>.row:first-child>.column:first-child,.ui.stackable.divided.grid>.column:first-child,.ui.stackable.divided.grid>.row:first-child>.column:first-child{border-top:none!important}.ui.stackable.page.grid>.column,.ui.stackable.page.grid>.row>.column{padding-left:1em!important;padding-right:1em!important}.ui.stackable.grid .vertical.pointing.menu .item:after{display:none}}.ui.menu{margin:1rem 0;background-color:#FFF;font-size:0;font-weight:400;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.1);box-shadow:0 0 0 1px rgba(0,0,0,.1);border-radius:.1875rem}.ui.menu:first-child{margin-top:0}.ui.menu:last-child{margin-bottom:0}.ui.menu:after{content:".";display:block;height:0;clear:both;visibility:hidden}.ui.menu>.item:first-child{border-radius:.1875em 0 0 .1875em}.ui.menu>.item:last-child{border-radius:0 .1875em .1875em 0}.ui.menu .item{vertical-align:middle;line-height:1;text-decoration:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-transition:opacity .2s ease,background .2s ease,-webkit-box-shadow .2s ease;-moz-transition:opacity .2s ease,background .2s ease,box-shadow .2s ease;transition:opacity .2s ease,background .2s ease,box-shadow .2s ease}.ui.menu .item,.ui.menu .item>a:not(.button){color:rgba(0,0,0,.75)}.ui.menu .item .item,.ui.menu .item .item>a:not(.button){color:rgba(30,30,30,.7)}.ui.menu .item .item .item,.ui.menu .item .item .item>a:not(.button){color:rgba(30,30,30,.6)}.ui.menu .dropdown .menu .item,.ui.menu .dropdown .menu .item a:not(.button){color:rgba(0,0,0,.75)}.ui.menu .dropdown .menu .item a:not(.button):hover,.ui.menu .item .menu .link.item:hover,.ui.menu .item .menu a.item:hover{color:rgba(0,0,0,.85)}.ui.menu .active.item,.ui.menu .active.item a:not(.button){color:rgba(0,0,0,.85);border-radius:0}.ui.menu .item{position:relative;display:inline-block;padding:.83em .95em;border-top:0 solid rgba(0,0,0,0);-webkit-tap-highlight-color:rgba(0,0,0,0);-moz-user-select:-moz-none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.ui.menu .menu{margin:0}.ui.menu .item.left,.ui.menu .menu.left{float:left}.ui.menu .item.right,.ui.menu .menu.right{float:right}.ui.menu .item:before{position:absolute;content:'';top:0;left:0;width:1px;height:100%;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(0,0,0,.05)),color-stop(50%,rgba(0,0,0,.1)),to(rgba(0,0,0,.05)));background-image:-webkit-linear-gradient(rgba(0,0,0,.05)0,rgba(0,0,0,.1)50%,rgba(0,0,0,.05)100%);background-image:-moz-linear-gradient(rgba(0,0,0,.05)0,rgba(0,0,0,.1)50%,rgba(0,0,0,.05)100%);background-image:linear-gradient(rgba(0,0,0,.05)0,rgba(0,0,0,.1)50%,rgba(0,0,0,.05)100%)}.ui.menu .item:first-child:before,.ui.menu>.menu:not(.right):first-child>.item:first-child:before{display:none}.ui.menu .item.right:before,.ui.menu .menu.right .item:before{right:auto;left:0}.ui.menu .item>p:only-child,.ui.menu .text.item>*{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;line-height:1.3;color:rgba(0,0,0,.6)}.ui.menu .item>p:first-child{margin-top:0}.ui.menu .item>p:last-child{margin-bottom:0}.ui.menu:not(.vertical) .item>.button{position:relative;top:-.05em;margin:-.55em 0;padding-bottom:.55em;padding-top:.55em;font-size:.875em}.ui.menu:not(.vertical) .item>.input{margin-top:-.85em;margin-bottom:-.85em;padding-top:.3em;padding-bottom:.3em;width:100%;vertical-align:top}.ui.menu .item>.input input{padding-top:.35em;padding-bottom:.35em}.ui.vertical.menu .item>.input input{margin:0;padding-top:.63em;padding-bottom:.63em}.ui.menu:not(.vertical) .item>.button.labeled>.icon{padding-top:.6em}.ui.menu:not(.vertical) .item .action.input>.button{font-size:.8em;padding:.55em .8em}.ui.small.menu:not(.vertical) .item>.input input{padding-top:.4em;padding-bottom:.4em}.ui.large.menu:not(.vertical) .item>.input input{top:-.125em;padding-bottom:.6em;padding-top:.6em}.ui.large.menu:not(.vertical) .item .action.input>.button{font-size:.8em;padding:.9em}.ui.large.menu:not(.vertical) .item .action.input>.button>.icon{padding-top:.8em}.ui.menu .header.item{background-color:rgba(0,0,0,.04);margin:0}.ui.vertical.menu .header.item{font-weight:700}.ui.menu .dropdown .menu .item .icon{float:none;margin:0 .75em 0 0}.ui.menu .dropdown.item .menu{left:1px;margin:0;min-width:-webkit-calc(99%);min-width:-moz-calc(99%);min-width:calc(99%);-webkit-box-shadow:0 1px 1px 1px rgba(0,0,0,.1);box-shadow:0 1px 1px 1px rgba(0,0,0,.1)}.ui.secondary.menu .dropdown.item .menu{left:0;min-width:100%}.ui.menu .pointing.dropdown.item .menu{margin-top:.75em}.ui.menu .simple.dropdown.item .menu{margin:0!important}.ui.menu .dropdown.item .menu .item{width:100%;color:rgba(0,0,0,.75)}.ui.menu .dropdown.item .menu .active.item{-webkit-box-shadow:none!important;box-shadow:none!important}.ui.menu .ui.dropdown .menu .item:before{display:none}.ui.menu .item>.label{background-color:rgba(0,0,0,.35);color:#FFF;margin:-.15em 0 -.15em .5em;padding:.3em .8em;vertical-align:baseline}.ui.menu .item>.floating.label{padding:.3em .8em}.ui.menu .item>img:only-child{display:block;max-width:100%;margin:0 auto}.ui.link.menu .item:hover,.ui.menu .link.item:hover,.ui.menu .ui.dropdown .menu .item:hover,.ui.menu a.item:hover{cursor:pointer;background-color:rgba(0,0,0,.02)}.ui.menu .ui.dropdown.item.active{background-color:rgba(0,0,0,.02);-webkit-box-shadow:none;box-shadow:none;-moz-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-bottom-left-radius:0;border-bottom-left-radius:0}.ui.link.menu .item:active,.ui.menu .link.item:active,.ui.menu .ui.dropdown .menu .item:active,.ui.menu a.item:active{background-color:rgba(0,0,0,.05)}.ui.menu .active.item{background-color:rgba(0,0,0,.01);color:rgba(0,0,0,.95);-webkit-box-shadow:0 .2em 0 inset;box-shadow:0 .2em 0 inset}.ui.vertical.menu .active.item{border-radius:0;-webkit-box-shadow:.2em 0 0 inset;box-shadow:.2em 0 0 inset}.ui.vertical.menu>.active.item:first-child{border-radius:0 .1875em 0 0}.ui.vertical.menu>.active.item:last-child{border-radius:0 0 .1875em}.ui.vertical.menu>.active.item:only-child{border-radius:0 .1875em .1875em 0}.ui.vertical.menu .active.item .menu .active.item{border-left:none;padding-left:1.5rem}.ui.vertical.menu .item .menu .active.item{background-color:rgba(0,0,0,.03);-webkit-box-shadow:none;box-shadow:none}.ui.menu .item.disabled,.ui.menu .item.disabled:hover{cursor:default;color:rgba(0,0,0,.2);background-color:transparent!important}.ui.menu.loading{position:relative}.ui.menu.loading:after{position:absolute;top:0;left:0;content:'';width:100%;height:100%;background:rgba(255,255,255,.8)url(../images/loader-large.gif) no-repeat 50% 50%;visibility:visible}.ui.vertical.menu .item{display:block;height:auto!important;border-top:none;border-left:0 solid rgba(0,0,0,0);border-right:none}.ui.vertical.menu>.item:first-child{border-radius:.1875em .1875em 0 0}.ui.vertical.menu>.item:last-child{border-radius:0 0 .1875em .1875em}.ui.vertical.menu .item>.label{float:right;text-align:center}.ui.vertical.menu .item>i.icon{float:right;width:1.22em;margin:0 0 0 .5em}.ui.vertical.menu .item>.label+i.icon{float:none;margin:0 .25em 0 0}.ui.vertical.menu .item:before{position:absolute;content:'';top:0;left:0;width:100%;height:1px;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.03)0,rgba(0,0,0,.1)1.5em,rgba(0,0,0,.03)100%);background-image:-moz-linear-gradient(left,rgba(0,0,0,.03)0,rgba(0,0,0,.1)1.5em,rgba(0,0,0,.03)100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.03)),color-stop(1.5em,rgba(0,0,0,.1)),to(rgba(0,0,0,.03)));background-image:linear-gradient(to right,rgba(0,0,0,.03)0,rgba(0,0,0,.1)1.5em,rgba(0,0,0,.03)100%)}.ui.vertical.menu .item:first-child:before{background-image:none!important}.ui.vertical.menu .dropdown.item>i{float:right;content:"\f0da"}.ui.vertical.menu .dropdown.item .menu{top:0!important;left:100%;margin:0 0 0 1px;-webkit-box-shadow:0 0 1px 1px #DDD;box-shadow:0 0 1px 1px #DDD}.ui.vertical.menu .dropdown.item.active{border-top-right-radius:0;border-bottom-right-radius:0}.ui.vertical.menu .dropdown.item .menu .item{font-size:1rem}.ui.vertical.menu .dropdown.item .menu .item i.icon{margin-right:0}.ui.vertical.menu .dropdown.item.active{-webkit-box-shadow:none;box-shadow:none}.ui.vertical.menu .item>.menu{margin:.5em -.95em 0}.ui.vertical.menu .item>.menu>.item{padding:.5rem 1.5rem;font-size:.875em}.ui.vertical.menu .item>.menu>.item:before{display:none}.ui.tiered.menu>.sub.menu>.item{color:rgba(0,0,0,.4)}.ui.tiered.menu .item.active,.ui.tiered.menu>.menu>.item:hover{color:rgba(0,0,0,.8)}.ui.tiered.menu>.menu .item.active:after{position:absolute;content:'';margin-top:-1px;top:100%;left:0;width:100%;height:2px;background-color:#FBFBFB}.ui.tiered.menu .sub.menu{background-color:rgba(0,0,0,.01);border-radius:0;border-top:1px solid rgba(0,0,0,.1);-webkit-box-shadow:none;box-shadow:none;color:#FFF}.ui.tiered.menu .sub.menu .item{font-size:.875rem}.ui.tiered.menu .sub.menu .item:before{background-image:none}.ui.tiered.menu .sub.menu .active.item{padding-top:.83em;background-color:transparent;border-radius:0;border-top:medium none;-webkit-box-shadow:none;box-shadow:none;color:rgba(0,0,0,.7)!important}.ui.tiered.menu .sub.menu .active.item:after{display:none}.ui.inverted.tiered.menu>.menu>.item{color:rgba(255,255,255,.5)}.ui.inverted.tiered.menu .sub.menu{background-color:rgba(0,0,0,.2)}.ui.inverted.tiered.menu .sub.menu .item{color:rgba(255,255,255,.6)}.ui.inverted.tiered.menu>.menu>.item:hover{color:rgba(255,255,255,.9)}.ui.inverted.tiered.menu .active.item:after{display:none}.ui.inverted.tiered.menu>.menu>.active.item,.ui.inverted.tiered.menu>.sub.menu>.active.item{color:#fff!important;-webkit-box-shadow:none;box-shadow:none}.ui.pointing.tiered.menu>.menu>.item:after{display:none}.ui.pointing.tiered.menu>.sub.menu>.item:after{display:block}.ui.tabular.menu{background-color:transparent;border-bottom:1px solid #DCDDDE;border-radius:0;-webkit-box-shadow:none!important;box-shadow:none!important}.ui.tabular.menu .item{background-color:transparent;border-left:1px solid transparent;border-right:1px solid transparent;border-top:1px solid transparent;padding-left:1.4em;padding-right:1.4em;color:rgba(0,0,0,.6)}.ui.tabular.menu .item:before{display:none}.ui.tabular.menu .item:hover{background-color:transparent;color:rgba(0,0,0,.8)}.ui.tabular.menu .active.item{position:relative;background-color:#FFF;color:rgba(0,0,0,.8);border-color:#DCDDDE;font-weight:700;margin-bottom:-1px;border-bottom:1px solid #FFF;-webkit-box-shadow:none;box-shadow:none;border-radius:5px 5px 0 0}.ui.attached.tabular.menu{position:relative;z-index:2}.ui.tabular.menu~.bottom.attached.segment{margin:1px 0 0 1px}.ui.pagination.menu{margin:0;display:inline-block;vertical-align:middle}.ui.pagination.menu .item{min-width:3em;text-align:center}.ui.pagination.menu .icon.item i.icon{vertical-align:top}.ui.pagination.menu.floated{display:block}.ui.pagination.menu .active.item{border-top:none;padding-top:.83em;background-color:rgba(0,0,0,.05);-webkit-box-shadow:none;box-shadow:none}.ui.secondary.menu{background-color:transparent;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.ui.secondary.menu>.item,.ui.secondary.menu>.menu>.item{-webkit-box-shadow:none;box-shadow:none;border:none;height:auto!important;margin:0 .25em;padding:.5em 1em;border-radius:.3125em}.ui.secondary.menu>.item:before,.ui.secondary.menu>.menu>.item:before{display:none!important}.ui.secondary.menu .item>.input input{background-color:transparent;border:none}.ui.secondary.menu .link.item,.ui.secondary.menu a.item{opacity:.8;-webkit-transition:none;-moz-transition:none;transition:none}.ui.secondary.menu .header.item{border-right:.1em solid rgba(0,0,0,.1);background-color:transparent;border-radius:0}.ui.secondary.menu .link.item:hover,.ui.secondary.menu a.item:hover{opacity:1}.ui.secondary.menu>.active.item,.ui.secondary.menu>.menu>.active.item{background-color:rgba(0,0,0,.08);opacity:1;-webkit-box-shadow:none;box-shadow:none}.ui.secondary.vertical.menu>.active.item{border-radius:.3125em}.ui.secondary.inverted.menu .link.item,.ui.secondary.inverted.menu a.item{color:rgba(255,255,255,.5)}.ui.secondary.inverted.menu .link.item:hover,.ui.secondary.inverted.menu a.item:hover{color:rgba(255,255,255,.9)}.ui.secondary.inverted.menu .active.item{background-color:rgba(255,255,255,.1)}.ui.secondary.item.menu>.item{margin:0}.ui.secondary.attached.menu{-webkit-box-shadow:none;box-shadow:none}.ui.secondary.pointing.menu{border-bottom:3px solid rgba(0,0,0,.1)}.ui.secondary.pointing.menu>.item,.ui.secondary.pointing.menu>.menu>.item{margin:0 0 -3px;padding:.6em .95em;border-bottom:3px solid rgba(0,0,0,0);border-radius:0;-webkit-transition:color .2s;-moz-transition:color .2s;transition:color .2s}.ui.secondary.pointing.menu .header.item{margin-bottom:-3px;background-color:transparent!important;border-right-width:0!important;font-weight:700!important;color:rgba(0,0,0,.8)!important}.ui.secondary.pointing.menu .text.item{-webkit-box-shadow:none!important;box-shadow:none!important}.ui.secondary.pointing.menu>.item:after,.ui.secondary.pointing.menu>.menu>.item:after{display:none}.ui.secondary.pointing.menu>.link.item:hover,.ui.secondary.pointing.menu>.menu>.link.item:hover,.ui.secondary.pointing.menu>.menu>a.item:hover,.ui.secondary.pointing.menu>a.item:hover{background-color:transparent;color:rgba(0,0,0,.7)}.ui.secondary.pointing.menu>.link.item:active,.ui.secondary.pointing.menu>.menu>.link.item:active,.ui.secondary.pointing.menu>.menu>a.item:active,.ui.secondary.pointing.menu>a.item:active{background-color:transparent;border-color:rgba(0,0,0,.2)}.ui.secondary.pointing.menu>.item.active,.ui.secondary.pointing.menu>.menu>.item.active{background-color:transparent;border-color:rgba(0,0,0,.4);-webkit-box-shadow:none;box-shadow:none}.ui.secondary.vertical.pointing.menu{border:none;border-right:3px solid rgba(0,0,0,.1)}.ui.secondary.vertical.menu>.item{border:none;margin:0 0 .3em;padding:.6em .8em;border-radius:.1875em}.ui.secondary.vertical.menu>.header.item{border-radius:0}.ui.secondary.vertical.pointing.menu>.item{margin:0 -3px 0 0;border-bottom:none;border-right:3px solid transparent;border-radius:0}.ui.secondary.vertical.pointing.menu>.item:hover{background-color:transparent;color:rgba(0,0,0,.7)}.ui.secondary.vertical.pointing.menu>.item:active{background-color:transparent;border-color:rgba(0,0,0,.2)}.ui.secondary.vertical.pointing.menu>.item.active{background-color:transparent;border-color:rgba(0,0,0,.4);color:rgba(0,0,0,.85)}.ui.secondary.inverted.menu{background-color:transparent}.ui.secondary.inverted.pointing.menu{border-bottom:3px solid rgba(255,255,255,.1)}.ui.secondary.inverted.pointing.menu>.item{color:rgba(255,255,255,.7)}.ui.secondary.inverted.pointing.menu>.header.item{color:#FFF!important}.ui.secondary.inverted.pointing.menu>.item:hover,.ui.secondary.inverted.pointing.menu>.menu>.item:hover{color:rgba(255,255,255,.85)}.ui.secondary.inverted.pointing.menu>.item:active,.ui.secondary.inverted.pointing.menu>.menu>.item:active{border-color:rgba(255,255,255,.4)}.ui.secondary.inverted.pointing.menu>.item.active,.ui.secondary.inverted.pointing.menu>.menu>.item.active{border-color:rgba(255,255,255,.8);color:#fff}.ui.secondary.inverted.vertical.pointing.menu{border-right:3px solid rgba(255,255,255,.1);border-bottom:none}.ui.text.menu{background-color:transparent;margin:1rem -1rem;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.ui.text.menu>.item{opacity:.8;margin:0 1em;padding:0;height:auto!important;border-radius:0;-webkit-box-shadow:none;box-shadow:none;-webkit-transition:opacity .2s ease;-moz-transition:opacity .2s ease;transition:opacity .2s ease}.ui.text.menu>.item:before{display:none!important}.ui.text.menu .header.item{background-color:transparent;opacity:1;color:rgba(50,50,50,.8);font-size:.875rem;padding:0;text-transform:uppercase;font-weight:700}.ui.text.item.menu .item{margin:0}.ui.vertical.text.menu{margin:1rem 0}.ui.vertical.text.menu:first-child{margin-top:0}.ui.vertical.text.menu:last-child{margin-bottom:0}.ui.vertical.text.menu .item{float:left;clear:left;margin:.5em 0}.ui.vertical.text.menu .item>i.icon{float:none;margin:0 .83em 0 0}.ui.vertical.text.menu .header.item{margin:.8em 0}.ui.text.menu .item:hover{opacity:1;background-color:transparent}.ui.text.menu .active.item{background-color:transparent;padding:0;border:none;opacity:1;font-weight:700;-webkit-box-shadow:none;box-shadow:none}.ui.text.attached.menu,.ui.text.pointing.menu .active.item:after{-webkit-box-shadow:none;box-shadow:none}.ui.inverted.text.menu,.ui.inverted.text.menu .item,.ui.inverted.text.menu .item.active,.ui.inverted.text.menu .item:hover{background-color:transparent}.ui.icon.menu,.ui.vertical.icon.menu{width:auto;display:inline-block;height:auto}.ui.icon.menu>.item{height:auto;text-align:center;color:rgba(60,60,60,.7)}.ui.icon.menu>.item>.icon{display:block;float:none!important;opacity:1;margin:0 auto!important}.ui.icon.menu .icon:before{opacity:1}.ui.menu .icon.item .icon{margin:0}.ui.vertical.icon.menu{float:none}.ui.inverted.icon.menu .item{color:rgba(255,255,255,.8)}.ui.inverted.icon.menu .icon{color:#fff}.ui.labeled.icon.menu{text-align:center}.ui.labeled.icon.menu>.item>.icon{display:block;font-size:1.5em!important;margin:0 auto .3em!important}.ui.green.menu .active.item,.ui.menu .green.active.item{border-color:#A1CF64!important;color:#A1CF64!important}.ui.menu .red.active.item,.ui.red.menu .active.item{border-color:#D95C5C!important;color:#D95C5C!important}.ui.blue.menu .active.item,.ui.menu .blue.active.item{border-color:#6ECFF5!important;color:#6ECFF5!important}.ui.menu .purple.active.item,.ui.purple.menu .active.item{border-color:#564F8A!important;color:#564F8A!important}.ui.menu .orange.active.item,.ui.orange.menu .active.item{border-color:#F05940!important;color:#F05940!important}.ui.menu .teal.active.item,.ui.teal.menu .active.item{border-color:#00B5AD!important;color:#00B5AD!important}.ui.inverted.menu{background-color:#333;-webkit-box-shadow:none;box-shadow:none}.ui.inverted.menu .header.item{margin:0;background-color:rgba(0,0,0,.3);-webkit-box-shadow:none;box-shadow:none}.ui.inverted.menu .item,.ui.inverted.menu .item>a{color:#FFF}.ui.inverted.menu .item .item,.ui.inverted.menu .item .item>a{color:rgba(255,255,255,.8)}.ui.inverted.menu .dropdown .menu .item,.ui.inverted.menu .dropdown .menu .item a{color:rgba(0,0,0,.75)!important}.ui.inverted.menu .item.disabled,.ui.inverted.menu .item.disabled:hover{color:rgba(255,255,255,.2)}.ui.inverted.menu .item:before{background-image:-webkit-linear-gradient(rgba(255,255,255,.03)0,rgba(255,255,255,.1)50%,rgba(255,255,255,.03)100%);background-image:-moz-linear-gradient(rgba(255,255,255,.03)0,rgba(255,255,255,.1)50%,rgba(255,255,255,.03)100%);background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.03)),color-stop(50%,rgba(255,255,255,.1)),to(rgba(255,255,255,.03)));background-image:linear-gradient(rgba(255,255,255,.03)0,rgba(255,255,255,.1)50%,rgba(255,255,255,.03)100%)}.ui.vertical.inverted.menu .item:before{background-image:-webkit-linear-gradient(left,rgba(255,255,255,.03)0,rgba(255,255,255,.1)50%,rgba(255,255,255,.03)100%);background-image:-moz-linear-gradient(left,rgba(255,255,255,.03)0,rgba(255,255,255,.1)50%,rgba(255,255,255,.03)100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(255,255,255,.03)),color-stop(50%,rgba(255,255,255,.1)),to(rgba(255,255,255,.03)));background-image:linear-gradient(to right,rgba(255,255,255,.03)0,rgba(255,255,255,.1)50%,rgba(255,255,255,.03)100%)}.ui.inverted.menu .dropdown.item:hover,.ui.inverted.menu .link.item:hover,.ui.inverted.menu a.item:hover,.ui.link.inverted.menu .item:hover{background-color:rgba(255,255,255,.1)}.ui.inverted.menu .item .menu .link.item:hover,.ui.inverted.menu .item .menu a.item:hover,.ui.inverted.menu .item>a:hover,.ui.inverted.menu a.item:hover{color:#fff}.ui.inverted.menu .dropdown.item:active,.ui.inverted.menu .link.item:active,.ui.inverted.menu a.item:active{background-color:rgba(255,255,255,.15)}.ui.inverted.menu .active.item{-webkit-box-shadow:none!important;box-shadow:none!important;background-color:rgba(255,255,255,.2)}.ui.inverted.menu .active.item,.ui.inverted.menu .active.item a{color:#fff!important}.ui.inverted.vertical.menu .item .menu .active.item{background-color:rgba(255,255,255,.2);color:#fff}.ui.inverted.pointing.menu .active.item:after{background-color:#5B5B5B;-webkit-box-shadow:none;box-shadow:none}.ui.inverted.pointing.menu .active.item:hover:after{background-color:#4A4A4A}.ui.selection.menu>.item{color:rgba(0,0,0,.4)}.ui.selection.menu>.item:hover{color:rgba(0,0,0,.6)}.ui.selection.menu>.item.active{color:rgba(0,0,0,.85)}.ui.inverted.selection.menu>.item{color:rgba(255,255,255,.4)}.ui.inverted.selection.menu>.item:hover{color:rgba(255,255,255,.9)}.ui.inverted.selection.menu>.item.active{color:#FFF}.ui.floated.menu{float:left;margin:0 .5rem 0 0}.ui.right.floated.menu{float:right;margin:0 0 0 .5rem}.ui.grey.menu{background-color:#F0F0F0}.ui.inverted.green.menu,.ui.inverted.green.pointing.menu .active.item:after{background-color:#A1CF64}.ui.inverted.red.menu{background-color:#D95C5C}.ui.inverted.red.pointing.menu .active.item:after{background-color:#F16883}.ui.inverted.blue.menu,.ui.inverted.blue.pointing.menu .active.item:after{background-color:#6ECFF5}.ui.inverted.purple.menu,.ui.inverted.purple.pointing.menu .active.item:after{background-color:#564F8A}.ui.inverted.orange.menu,.ui.inverted.orange.pointing.menu .active.item:after{background-color:#F05940}.ui.inverted.teal.menu,.ui.inverted.teal.pointing.menu .active.item:after{background-color:#00B5AD}.ui.fitted.menu .item,.ui.fitted.menu .item .menu .item,.ui.menu .fitted.item{padding:0}.ui.horizontally.fitted.menu .item,.ui.horizontally.fitted.menu .item .menu .item,.ui.menu .horizontally.fitted.item{padding-top:.83em;padding-bottom:.83em}.ui.menu .vertically.fitted.item,.ui.vertically.fitted.menu .item,.ui.vertically.fitted.menu .item .menu .item{padding-left:.95em;padding-right:.95em}.ui.borderless.menu .item .menu .item:before,.ui.borderless.menu .item:before,.ui.menu .borderless.item:before{background-image:none}.ui.compact.menu{display:inline-block;margin:0;vertical-align:middle}.ui.compact.vertical.menu{width:auto!important}.ui.compact.vertical.menu .item:last-child::before{display:block}.ui.menu.fluid,.ui.vertical.menu.fluid{display:block;width:100%!important}.ui.item.menu,.ui.item.menu .item{width:100%;padding-left:0!important;padding-right:0!important;text-align:center}.ui.menu.two.item .item{width:50%}.ui.menu.three.item .item{width:33.333%}.ui.menu.four.item .item{width:25%}.ui.menu.five.item .item{width:20%}.ui.menu.six.item .item{width:16.666%}.ui.menu.seven.item .item{width:14.285%}.ui.menu.eight.item .item{width:12.5%}.ui.menu.nine.item .item{width:11.11%}.ui.menu.ten.item .item{width:10%}.ui.menu.eleven.item .item{width:9.09%}.ui.menu.twelve.item .item{width:8.333%}.ui.menu.fixed{position:fixed;z-index:999;margin:0;border:none;width:100%}.ui.menu.fixed,.ui.menu.fixed .item:first-child,.ui.menu.fixed .item:last-child{border-radius:0!important}.ui.menu.fixed.top{top:0;left:0;right:auto;bottom:auto}.ui.menu.fixed.right{top:0;right:0;left:auto;bottom:auto;width:auto;height:100%}.ui.menu.fixed.bottom{bottom:0;left:0;top:auto;right:auto}.ui.menu.fixed.left{top:0;left:0;right:auto;bottom:auto;width:auto;height:100%}.ui.fixed.menu+.ui.grid{padding-top:2.75rem}.ui.pointing.menu .active.item:after{position:absolute;bottom:-.3em;left:50%;content:"";margin-left:-.3em;width:.6em;height:.6em;border:none;border-bottom:1px solid rgba(0,0,0,.1);border-right:1px solid rgba(0,0,0,.1);background-image:none;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);z-index:2;-webkit-transition:background .2s ease;-moz-transition:background .2s ease;transition:background .2s ease}.ui.pointing.menu .active.item .menu .active.item:after{display:none}.ui.vertical.pointing.menu .active.item:after{position:absolute;top:50%;margin-top:-.3em;right:-.4em;bottom:auto;left:auto;border:none;border-top:1px solid rgba(0,0,0,.1);border-right:1px solid rgba(0,0,0,.1)}.ui.pointing.menu .active.item:after{background-color:#FCFCFC}.ui.pointing.menu .active.item:hover:after{background-color:#FAFAFA}.ui.vertical.pointing.menu .menu .active.item:after{background-color:#F4F4F4}.ui.pointing.menu a.active.item:active:after{background-color:#F0F0F0}.ui.menu.attached{margin:0;border-radius:0;-webkit-box-shadow:0 0 0 1px #DDD;box-shadow:0 0 0 1px #DDD}.ui.top.attached.menu{border-radius:.1875em .1875em 0 0}.ui.menu.bottom.attached{border-radius:0 0 .1875em .1875em}.ui.small.menu .item{font-size:.875rem}.ui.small.vertical.menu{width:13rem}.ui.menu .item{font-size:1rem}.ui.vertical.menu{width:15rem}.ui.large.menu .item{font-size:1.125rem}.ui.large.menu .item .item{font-size:.875rem}.ui.large.menu .dropdown .item{font-size:1rem}.ui.large.vertical.menu{width:18rem}.ui.message{position:relative;min-height:18px;margin:1em 0;height:auto;background-color:#EFEFEF;padding:1em;line-height:1.33;color:rgba(0,0,0,.6);-webkit-transition:opacity .1s ease,color .1s ease,background .1s ease,-webkit-box-shadow .1s ease;-moz-transition:opacity .1s ease,color .1s ease,background .1s ease,box-shadow .1s ease;transition:opacity .1s ease,color .1s ease,background .1s ease,box-shadow .1s ease;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;border-radius:.325em}.ui.message:first-child{margin-top:0}.ui.message:last-child{margin-bottom:0}.ui.message .header{margin:0;font-size:1.33em;font-weight:700}.ui.message p{opacity:.85;margin:1em 0}.ui.message p:first-child{margin-top:0}.ui.message p:last-child{margin-bottom:0}.ui.message .header+p{margin-top:.3em}.ui.message>:first-child{margin-top:0}.ui.message>:last-child{margin-bottom:0}.ui.message ul.list{opacity:.85;list-style-position:inside;margin:.2em 0;padding:0}.ui.message ul.list li{position:relative;list-style-type:none;margin:0 0 .3em 1em;padding:0}.ui.message ul.list li:before{position:absolute;content:'\2022';top:-.05em;left:-.8em;height:100%;vertical-align:baseline;opacity:.5}.ui.message ul.list li:first-child{margin-top:0}.ui.message>.close.icon{cursor:pointer;position:absolute;right:0;top:0;width:2.5em;height:2.5em;opacity:.7;padding:.75em 0 0 .75em;-webkit-transition:opacity .1s linear;-moz-transition:opacity .1s linear;transition:opacity .1s linear;z-index:10}.ui.message>.close.icon:hover{opacity:1}.ui.message.visible{display:block!important}.ui.icon.message.animating,.ui.icon.message.visible{display:table!important}.ui.message.hidden{display:none!important}.ui.compact.message{display:inline-block}.ui.attached.message{margin-left:-1px;margin-right:-1px;margin-bottom:-1px;border-radius:.325em .325em 0 0;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.1)inset;box-shadow:0 0 0 1px rgba(0,0,0,.1)inset}.ui.attached+.ui.attached.message:not(.top):not(.bottom){margin-top:-1px;border-radius:0}.ui.bottom.attached.message{margin-top:-1px;border-radius:0 0 .325em .325em}.ui.bottom.attached.message:not(:last-child){margin-bottom:1em}.ui.attached.icon.message{display:block;width:auto}.ui.icon.message{display:table;width:100%}.ui.icon.message>.icon:not(.close){display:table-cell;vertical-align:middle;font-size:3.8em;opacity:.5}.ui.icon.message>.icon+.content{padding-left:1em}.ui.icon.message>.content{display:table-cell;vertical-align:middle}.ui.inverted.message{background-color:rgba(255,255,255,.05);color:rgba(255,255,255,.95)}.ui.floating.message{-webkit-box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 0 0 1px rgba(0,0,0,.05)inset;box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 0 0 1px rgba(0,0,0,.05)inset}.ui.black.message{background-color:#333;color:rgba(255,255,255,.95)}.ui.blue.message,.ui.info.message{background-color:#E6F4F9;color:#4D8796}.ui.green.message{background-color:#DEFCD5;color:#52A954}.ui.warning.message,.ui.yellow.message{background-color:#F6F3D5;color:#96904D}.ui.red.message{background-color:#F1D7D7;color:#A95252}.ui.positive.message,.ui.success.message{background-color:#DEFCD5;color:#52A954}.ui.error.message,.ui.negative.message{background-color:#F1D7D7;color:#A95252}.ui.small.message{font-size:.875em}.ui.message{font-size:1em}.ui.large.message{font-size:1.125em}.ui.huge.message{font-size:1.5em}.ui.massive.message{font-size:2em}.ui.table{width:100%;border-collapse:collapse}.ui.table td,.ui.table th,.ui.table tr{border-collapse:collapse;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-transition:all .1s ease-out;-moz-transition:all .1s ease-out;transition:all .1s ease-out}.ui.table thead{border-bottom:1px solid rgba(0,0,0,.03)}.ui.table tfoot th{background-color:rgba(0,0,0,.03)}.ui.table th{cursor:auto;background-color:rgba(0,0,0,.05);text-align:left;color:rgba(0,0,0,.8);padding:.5em .7em;vertical-align:middle}.ui.table thead th:first-child{border-radius:5px 0 0}.ui.table thead th:last-child{border-radius:0 5px 0 0}.ui.table tfoot th:first-child{border-radius:0 0 0 5px}.ui.table tfoot th:last-child{border-radius:0 0 5px}.ui.table tfoot th:only-child{border-radius:0 0 5px 5px}.ui.table td{padding:.4em .7em;vertical-align:middle}.ui.table tfoot{border-top:1px solid rgba(0,0,0,.03)}.ui.table tfoot th{font-weight:400;font-style:italic}.ui.table tbody tr:nth-child(2n){background-color:rgba(0,0,50,.02)}.ui.table>.icon{vertical-align:baseline}.ui.table>.icon:only-child{margin:0}.ui.table.segment:after{display:none}.ui.table.segment.stacked:after{display:block}@media only screen and (max-width:768px){.ui.table{display:block;padding:0}.ui.table tfoot,.ui.table thead{display:none}.ui.table tbody,.ui.table tr{display:block}.ui.table tr>td{width:100%!important;display:block;border:none!important;padding:.25em .75em;-webkit-box-shadow:0 1px 0 0 rgba(0,0,0,.05)!important;box-shadow:0 1px 0 0 rgba(0,0,0,.05)!important}.ui.table td:first-child{font-weight:700;padding-top:1em}.ui.table td:last-child{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1)inset!important;box-shadow:0 -1px 0 0 rgba(0,0,0,.1)inset!important;padding-bottom:1em}.ui.table tr>td.active,.ui.table tr>td.error,.ui.table tr>td.negative,.ui.table tr>td.positive,.ui.table tr>td.warning{background-color:transparent!important}}.ui.sortable.table th.disabled:hover{cursor:auto;text-align:left;font-weight:700;color:#333;color:rgba(0,0,0,.8)}.ui.sortable.table thead th:hover{background-color:rgba(0,0,0,.13);color:rgba(0,0,0,.8)}.ui.inverted.sortable.table thead th:hover{background-color:rgba(255,255,255,.13);color:#fff}.ui.table td.positive,.ui.table tr.positive{-webkit-box-shadow:2px 0 0 #119000 inset;box-shadow:2px 0 0 #119000 inset}.ui.table td.positive,.ui.table tr.positive td{background-color:#F2F8F0!important;color:#119000!important}.ui.celled.table tr.positive:hover td,.ui.celled.table tr:hover td.positive,.ui.table td:hover.positive,.ui.table th:hover.positive,.ui.table tr.positive:hover td{background-color:#ECF5E9!important;color:#119000!important}.ui.table td.negative,.ui.table tr.negative{-webkit-box-shadow:2px 0 0 #CD2929 inset;box-shadow:2px 0 0 #CD2929 inset}.ui.table td.negative,.ui.table tr.negative td{background-color:#F9F4F4;color:#CD2929!important}.ui.celled.table tr.negative:hover td,.ui.celled.table tr:hover td.negative,.ui.table td:hover.negative,.ui.table th:hover.negative,.ui.table tr.negative:hover td{background-color:#F2E8E8;color:#CD2929}.ui.table td.error,.ui.table tr.error{-webkit-box-shadow:2px 0 0 #CD2929 inset;box-shadow:2px 0 0 #CD2929 inset}.ui.table td.error,.ui.table th.error,.ui.table tr.error td{background-color:#F9F4F4;color:#CD2929}.ui.celled.table tr.error:hover td,.ui.celled.table tr:hover td.error,.ui.table td:hover.error,.ui.table th:hover.error,.ui.table tr.error:hover td{background-color:#F2E8E8;color:#CD2929}.ui.table td.warning,.ui.table tr.warning{-webkit-box-shadow:2px 0 0 #7D6C00 inset;box-shadow:2px 0 0 #7D6C00 inset}.ui.table td.warning,.ui.table th.warning,.ui.table tr.warning td{background-color:#FBF6E9;color:#7D6C00}.ui.celled.table tr.warning:hover td,.ui.celled.table tr:hover td.warning,.ui.table td:hover.warning,.ui.table th:hover.warning,.ui.table tr.warning:hover td{background-color:#F3EDDC;color:#7D6C00}.ui.table td.active,.ui.table tr.active{-webkit-box-shadow:2px 0 0 rgba(50,50,50,.9)inset;box-shadow:2px 0 0 rgba(50,50,50,.9)inset}.ui.table tr td.active,.ui.table tr.active td{background-color:#E0E0E0;color:rgba(50,50,50,.9)}.ui.table tr td.disabled,.ui.table tr.disabled td,.ui.table tr.disabled:hover td,.ui.table tr:hover td.disabled{color:rgba(150,150,150,.3)}.ui.two.column.table td{width:50%}.ui.three.column.table td{width:33.3333%}.ui.four.column.table td{width:25%}.ui.five.column.table td{width:20%}.ui.six.column.table td{width:16.66667%}.ui.seven.column.table td{width:14.2857%}.ui.eight.column.table td{width:12.5%}.ui.nine.column.table td{width:11.1111%}.ui.ten.column.table td{width:10%}.ui.eleven.column.table td{width:9.0909%}.ui.twelve.column.table td{width:8.3333%}.ui.thirteen.column.table td{width:7.6923%}.ui.fourteen.column.table td{width:7.1428%}.ui.fifteen.column.table td{width:6.6666%}.ui.sixteen.column.table td,.ui.table td.one.wide,.ui.table th.one.wide{width:6.25%}.ui.table td.two.wide,.ui.table th.two.wide{width:12.5%}.ui.table td.three.wide,.ui.table th.three.wide{width:18.75%}.ui.table td.four.wide,.ui.table th.four.wide{width:25%}.ui.table td.five.wide,.ui.table th.five.wide{width:31.25%}.ui.table td.six.wide,.ui.table th.six.wide{width:37.5%}.ui.table td.seven.wide,.ui.table th.seven.wide{width:43.75%}.ui.table td.eight.wide,.ui.table th.eight.wide{width:50%}.ui.table td.nine.wide,.ui.table th.nine.wide{width:56.25%}.ui.table td.ten.wide,.ui.table th.ten.wide{width:62.5%}.ui.table td.eleven.wide,.ui.table th.eleven.wide{width:68.75%}.ui.table td.twelve.wide,.ui.table th.twelve.wide{width:75%}.ui.table td.thirteen.wide,.ui.table th.thirteen.wide{width:81.25%}.ui.table td.fourteen.wide,.ui.table th.fourteen.wide{width:87.5%}.ui.table td.fifteen.wide,.ui.table th.fifteen.wide{width:93.75%}.ui.table td.sixteen.wide,.ui.table th.sixteen.wide{width:100%}.ui.celled.table{color:rgba(0,0,0,.8)}.ui.celled.table tbody tr,.ui.celled.table tfoot tr{border:none}.ui.celled.table td,.ui.celled.table th{border:1px solid rgba(0,0,0,.1)}.ui.celled.table.segment td:first-child,.ui.celled.table.segment th:first-child{border-left:none}.ui.celled.table.segment td:last-child,.ui.celled.table.segment th:last-child{border-right:none}.ui.sortable.table thead th{cursor:pointer;white-space:nowrap}.ui.sortable.table thead th.sorted,.ui.sortable.table thead th.sorted:hover{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ui.sortable.table thead th:after{display:inline-block;content:'';width:1em;opacity:.8;margin:0 0 0 .5em;font-family:Icons;font-style:normal;font-weight:400;text-decoration:inherit}.ui.sortable.table thead th.ascending:after{content:'\25b4'}.ui.sortable.table thead th.descending:after{content:'\25be'}.ui.inverted.table td{color:rgba(255,255,255,.9)}.ui.inverted.table th{background-color:rgba(0,0,0,.15);color:rgba(255,255,255,.9)}.ui.inverted.table tbody tr:nth-child(2n){background-color:rgba(255,255,255,.06)}.ui.definition.table td:first-child{font-weight:700}.ui.collapsing.table{width:auto}.ui.basic.table th{background-color:transparent;padding:.5em}.ui.basic.table tbody tr{border-bottom:1px solid rgba(0,0,0,.03)}.ui.basic.table td{padding:.8em .5em}.ui.basic.table tbody tr:nth-child(2n){background-color:transparent!important}.ui.padded.table td,.ui.padded.table th{padding:.8em 1em}.ui.compact.table th{padding:.3em .5em}.ui.compact.table td{padding:.2em .5em}.ui.small.table{font-size:.875em}.ui.table{font-size:1em}.ui.large.table{font-size:1.1em}@font-face{font-family:'Basic Icons';src:url(../fonts/basic.icons.eot);src:url(../fonts/basic.icons.eot?#iefix) format('embedded-opentype'),url(../fonts/basic.icons.svg#basic.icons) format('svg'),url(../fonts/basic.icons.woff) format('woff'),url(../fonts/basic.icons.ttf) format('truetype');font-style:normal;font-weight:400;font-variant:normal;text-decoration:inherit;text-transform:none}i.basic.icon{display:inline-block;opacity:.75;margin:0 .25em 0 0;width:1.23em;height:1em;font-family:'Basic Icons';font-style:normal;line-height:1;font-weight:400;text-decoration:inherit;text-align:center;speak:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-font-smoothing:antialiased;-moz-font-smoothing:antialiased;font-smoothing:antialiased}i.basic.icon.circle.attention:before{content:'\2757'}i.basic.icon.circle.help:before{content:'\e704'}i.basic.icon.circle.info:before{content:'\e705'}i.basic.icon.add:before{content:'\2795'}i.basic.icon.chart:before{content:'📈'}i.basic.icon.chart.bar:before{content:'📊'}i.basic.icon.chart.pie:before{content:'\e7a2'}i.basic.icon.resize.full:before{content:'\e744'}i.basic.icon.resize.horizontal:before{content:'\2b0d'}i.basic.icon.resize.small:before{content:'\e746'}i.basic.icon.resize.vertical:before{content:'\2b0c'}i.basic.icon.down:before{content:'\2193'}i.basic.icon.down.triangle:before{content:'\25be'}i.basic.icon.down.arrow:before{content:'\e75c'}i.basic.icon.left:before{content:'\2190'}i.basic.icon.left.triangle:before{content:'\25c2'}i.basic.icon.left.arrow:before{content:'\e75d'}i.basic.icon.right:before{content:'\2192'}i.basic.icon.right.triangle:before{content:'\25b8'}i.basic.icon.right.arrow:before{content:'\e75e'}i.basic.icon.up:before{content:'\2191'}i.basic.icon.up.triangle:before{content:'\25b4'}i.basic.icon.up.arrow:before{content:'\e75f'}i.basic.icon.folder:before{content:'\e810'}i.basic.icon.open.folder:before{content:'📂'}i.basic.icon.desk.globe:before{content:'🌐'}i.basic.icon.star:before{content:'\e801'}i.basic.icon.star.empty:before{content:'\e800'}i.basic.icon.star.half:before{content:'\e701'}i.basic.icon.lock:before{content:'🔒'}i.basic.icon.unlock:before{content:'🔓'}i.basic.icon.layout.grid:before{content:'\e80c'}i.basic.icon.layout.block:before{content:'\e708'}i.basic.icon.layout.list:before{content:'\e80b'}i.basic.icon.heart.empty:before{content:'\2661'}i.basic.icon.heart:before{content:'\2665'}i.basic.icon.asterisk:before{content:'\2731'}i.basic.icon.attachment:before{content:'📎'}i.basic.icon.attention:before{content:'\26a0'}i.basic.icon.trophy:before{content:'🏉'}i.basic.icon.barcode:before{content:'\e792'}i.basic.icon.cart:before{content:'\e813'}i.basic.icon.block:before{content:'🚫'}i.basic.icon.book:before{content:'📖'}i.basic.icon.bookmark:before{content:'🔖'}i.basic.icon.calendar:before{content:'📅'}i.basic.icon.cancel:before{content:'\2716'}i.basic.icon.close:before{content:'\e80d'}i.basic.icon.color:before{content:'\e794'}i.basic.icon.chat:before{content:'\e720'}i.basic.icon.check:before{content:'\2611'}i.basic.icon.time:before{content:'🕔'}i.basic.icon.cloud:before{content:'\2601'}i.basic.icon.code:before{content:'\e714'}i.basic.icon.email:before{content:'\40'}i.basic.icon.settings:before{content:'\26ef'}i.basic.icon.setting:before{content:'\2699'}i.basic.icon.comment:before{content:'\e802'}i.basic.icon.clockwise.counter:before{content:'\27f2'}i.basic.icon.clockwise:before{content:'\27f3'}i.basic.icon.cube:before{content:'\e807'}i.basic.icon.direction:before{content:'\27a2'}i.basic.icon.doc:before{content:'📄'}i.basic.icon.docs:before{content:'\e736'}i.basic.icon.dollar:before{content:'💵'}i.basic.icon.paint:before{content:'\e7b5'}i.basic.icon.edit:before{content:'\270d'}i.basic.icon.eject:before{content:'\2ecf'}i.basic.icon.export:before{content:'\e715'}i.basic.icon.hide:before{content:'\e70b'}i.basic.icon.unhide:before{content:'\e80f'}i.basic.icon.facebook:before{content:'\f301'}i.basic.icon.fast-forward:before{content:'\e804'}i.basic.icon.fire:before{content:'🔥'}i.basic.icon.flag:before{content:'\2691'}i.basic.icon.lightning:before{content:'\26a1'}i.basic.icon.lab:before{content:'\68'}i.basic.icon.flight:before{content:'\2708'}i.basic.icon.forward:before{content:'\27a6'}i.basic.icon.gift:before{content:'🎁'}i.basic.icon.github:before{content:'\f308'}i.basic.icon.globe:before{content:'\e817'}i.basic.icon.headphones:before{content:'🎧'}i.basic.icon.question:before{content:'\2753'}i.basic.icon.home:before{content:'\2302'}i.basic.icon.i:before{content:'\2139'}i.basic.icon.idea:before{content:'💡'}i.basic.icon.open:before{content:'🔗'}i.basic.icon.content:before{content:'\e782'}i.basic.icon.location:before{content:'\e724'}i.basic.icon.mail:before{content:'\2709'}i.basic.icon.mic:before{content:'🎤'}i.basic.icon.minus:before{content:'\2d'}i.basic.icon.money:before{content:'💰'}i.basic.icon.off:before{content:'\e78e'}i.basic.icon.pause:before{content:'\e808'}i.basic.icon.photos:before{content:'\e812'}i.basic.icon.photo:before{content:'🌄'}i.basic.icon.pin:before{content:'📌'}i.basic.icon.play:before{content:'\e809'}i.basic.icon.plus:before{content:'\2b'}i.basic.icon.print:before{content:'\e716'}i.basic.icon.rss:before{content:'\e73a'}i.basic.icon.search:before{content:'🔍'}i.basic.icon.shuffle:before{content:'\e803'}i.basic.icon.tag:before{content:'\e80a'}i.basic.icon.tags:before{content:'\e70d'}i.basic.icon.terminal:before{content:'\e7ac'}i.basic.icon.thumbs.down:before{content:'👎'}i.basic.icon.thumbs.up:before{content:'👍'}i.basic.icon.to-end:before{content:'\e806'}i.basic.icon.to-start:before{content:'\e805'}i.basic.icon.top.list:before{content:'🏆'}i.basic.icon.trash:before{content:'\e729'}i.basic.icon.twitter:before{content:'\f303'}i.basic.icon.upload:before{content:'\e711'}i.basic.icon.user.add:before{content:'\e700'}i.basic.icon.user:before{content:'👤'}i.basic.icon.community:before{content:'\e814'}i.basic.icon.users:before{content:'👥'}i.basic.icon.id:before{content:'\e722'}i.basic.icon.url:before{content:'🔗'}i.basic.icon.zoom.in:before{content:'\e750'}i.basic.icon.zoom.out:before{content:'\e751'}i.dropdown.basic.icon{margin:0 0 0 .5em}i.basic.icon.star{width:auto;margin:0}i.basic.icon.left{width:auto;margin:0 .5em 0 0}i.basic.icon.down,i.basic.icon.right,i.basic.icon.search,i.basic.icon.up{width:auto;margin:0 0 0 .5em}i.basic.icon.delete:before{content:'\e80d'}i.basic.icon.dropdown:before{content:'\25be'}i.basic.icon.help:before{content:'\e704'}i.basic.icon.info:before{content:'\e705'}i.basic.icon.error:before{content:'\e80d'}i.basic.icon.dislike:before{content:'\2661'}i.basic.icon.like:before{content:'\2665'}i.basic.icon.eye:before{content:'\e80f'}i.basic.icon.eye.hidden:before{content:'\e70b'}i.basic.icon.date:before{content:'📅'}i.basic.icon.active,i.basic.icon.hover,i.emphasized.basic.icon{opacity:1}i.basic.icon.disabled{opacity:.3}i.link.basic.icon{cursor:pointer;opacity:.7;-webkit-transition:opacity .3s ease-out;-moz-transition:opacity .3s ease-out;transition:opacity .3s ease-out}.link.basic.icon:hover{opacity:1!important}i.circular.basic.icon{border-radius:500px!important;padding:.5em 0!important;-webkit-box-shadow:0 0 0 .1em rgba(0,0,0,.1)inset;box-shadow:0 0 0 .1em rgba(0,0,0,.1)inset;line-height:1!important;width:2em!important;height:2em!important}i.circular.inverted.basic.icon{border:none;-webkit-box-shadow:none;box-shadow:none}i.vertically.flipped.basic.icon{-webkit-transform:scale(1,-1);-moz-transform:scale(1,-1);-ms-transform:scale(1,-1);transform:scale(1,-1)}i.horizontally.flipped.basic.icon{-webkit-transform:scale(-1,1);-moz-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}i.left.rotated.basic.icon{-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);transform:rotate(-90deg)}i.right.rotated.basic.icon{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}i.square.basic.icon{width:2em;height:2em;padding:.5em .35em!important;-webkit-box-shadow:0 0 0 .1em rgba(0,0,0,.1)inset;box-shadow:0 0 0 .1em rgba(0,0,0,.1)inset;vertical-align:baseline}i.square.basic.icon:before{vertical-align:middle}i.square.inverted.basic.icon{border:none;-webkit-box-shadow:none;box-shadow:none}i.inverted.basic.icon{background-color:#222;color:#FFF}i.blue.basic.icon{color:#6ECFF5!important}i.black.basic.icon{color:#5C6166!important}i.green.basic.icon{color:#A1CF64!important}i.red.basic.icon{color:#D95C5C!important}i.purple.basic.icon{color:#564F8A!important}i.teal.basic.icon{color:#00B5AD!important}i.inverted.black.basic.icon{background-color:#5C6166!important;color:#FFF!important}i.inverted.blue.basic.icon{background-color:#6ECFF5!important;color:#FFF!important}i.inverted.green.basic.icon{background-color:#A1CF64!important;color:#FFF!important}i.inverted.red.basic.icon{background-color:#D95C5C!important;color:#FFF!important}i.inverted.purple.basic.icon{background-color:#564F8A!important;color:#FFF!important}i.inverted.teal.basic.icon{background-color:#00B5AD!important;color:#FFF!important}i.small.basic.icon{font-size:.875em}i.basic.icon{font-size:1em}i.large.basic.icon{font-size:1.5em;margin-right:.2em;vertical-align:middle}i.big.basic.icon{font-size:2em;margin-right:.5em;vertical-align:middle}i.huge.basic.icon{font-size:4em;margin-right:.75em;vertical-align:middle}i.massive.basic.icon{font-size:8em;margin-right:1em;vertical-align:middle}.ui.button{cursor:pointer;display:inline-block;vertical-align:middle;min-height:1em;outline:0;border:none;background-color:#FAFAFA;color:gray;margin:0;padding:.8em 1.5em;text-transform:uppercase;line-height:1;font-weight:700;font-style:normal;text-align:center;text-decoration:none;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(0,0,0,0)),to(rgba(0,0,0,.05)));background-image:-webkit-linear-gradient(rgba(0,0,0,0),rgba(0,0,0,.05));background-image:-moz-linear-gradient(rgba(0,0,0,0),rgba(0,0,0,.05));background-image:linear-gradient(rgba(0,0,0,0),rgba(0,0,0,.05));border-radius:.25em;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.08)inset;box-shadow:0 0 0 1px rgba(0,0,0,.08)inset;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-transition:opacity .25s ease,background-color .25s ease,color .25s ease,background .25s ease,-webkit-box-shadow .25s ease;-moz-transition:opacity .25s ease,background-color .25s ease,color .25s ease,background .25s ease,box-shadow .25s ease;transition:opacity .25s ease,background-color .25s ease,color .25s ease,background .25s ease,box-shadow .25s ease}.ui.active.button,.ui.buttons .active.button{background-color:#EAEAEA;background-image:none;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.05)inset!important;box-shadow:0 0 0 1px rgba(0,0,0,.05)inset!important;color:rgba(0,0,0,.7)}.ui.button:hover{background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(0,0,0,0)),to(rgba(0,0,0,.08)));background-image:-webkit-linear-gradient(rgba(0,0,0,0),rgba(0,0,0,.08));background-image:-moz-linear-gradient(rgba(0,0,0,0),rgba(0,0,0,.08));background-image:linear-gradient(rgba(0,0,0,0),rgba(0,0,0,.08));color:rgba(0,0,0,.7)}.ui.button.active:hover{background-image:none}.ui.button.hover .icon,.ui.button:hover .icon{opacity:.85}.ui.active.button:active,.ui.button:active{background-color:#F1F1F1;color:rgba(0,0,0,.7);-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.05)inset!important;box-shadow:0 0 0 1px rgba(0,0,0,.05)inset!important}.ui.loading.button{position:relative;cursor:default;background-color:#FFF!important;color:transparent!important;-webkit-transition:all 0s linear;-moz-transition:all 0s linear;transition:all 0s linear}.ui.loading.button:after{position:absolute;top:0;left:0;width:100%;height:100%;content:'';background:transparent url(../images/loader-mini.gif) no-repeat 50% 50%}.ui.labeled.icon.loading.button .icon{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.ui.disabled.button,.ui.disabled.button.active,.ui.disabled.button:hover{background-color:#DDD!important;cursor:default;color:rgba(0,0,0,.5)!important;opacity:.3!important;background-image:none!important;-webkit-box-shadow:none!important;box-shadow:none!important}.ui.animated.button{position:relative;overflow:hidden}.ui.animated.button .visible.content{position:relative}.ui.animated.button .hidden.content{position:absolute;width:100%}.ui.animated.button .hidden.content,.ui.animated.button .visible.content{-webkit-transition:right .3s ease 0s;-moz-transition:right .3s ease 0s;transition:right .3s ease 0s}.ui.animated.button .visible.content{left:auto;right:0}.ui.animated.button .hidden.content{top:50%;left:auto;right:-100%;margin-top:-.55em}.ui.animated.button:hover .visible.content{left:auto;right:200%}.ui.animated.button:hover .hidden.content{left:auto;right:0}.ui.vertical.animated.button .hidden.content,.ui.vertical.animated.button .visible.content{-webkit-transition:top .3s ease 0s,-webkit-transform .3s ease 0s;-moz-transition:top .3s ease 0s,-moz-transform .3s ease 0s;transition:top .3s ease 0s,transform .3s ease 0s}.ui.vertical.animated.button .visible.content{-webkit-transform:translateY(0%);-moz-transform:translateY(0%);-ms-transform:translateY(0%);transform:translateY(0%);right:auto}.ui.vertical.animated.button .hidden.content{top:-100%;left:0;right:auto}.ui.vertical.animated.button:hover .visible.content{-webkit-transform:translateY(200%);-moz-transform:translateY(200%);-ms-transform:translateY(200%);transform:translateY(200%);right:auto}.ui.vertical.animated.button:hover .hidden.content{top:50%;right:auto}.ui.fade.animated.button .hidden.content,.ui.fade.animated.button .visible.content{-webkit-transition:opacity .3s ease 0s,-webkit-transform .3s ease 0s;-moz-transition:opacity .3s ease 0s,-moz-transform .3s ease 0s;transition:opacity .3s ease 0s,transform .3s ease 0s}.ui.fade.animated.button .visible.content{left:auto;right:auto;opacity:1;-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}.ui.fade.animated.button .hidden.content{opacity:0;left:0;right:auto;-webkit-transform:scale(1.2);-moz-transform:scale(1.2);-ms-transform:scale(1.2);transform:scale(1.2)}.ui.fade.animated.button:hover .visible.content{left:auto;right:auto;opacity:0;-webkit-transform:scale(0.7);-moz-transform:scale(0.7);-ms-transform:scale(0.7);transform:scale(0.7)}.ui.fade.animated.button:hover .hidden.content{left:0;right:auto;opacity:1;-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}.ui.primary.button,.ui.primary.buttons .button{background-color:#D95C5C;color:#FFF}.ui.primary.button.active,.ui.primary.button:hover,.ui.primary.buttons .active.button,.ui.primary.buttons .button:hover{background-color:#E75859;color:#FFF}.ui.primary.button:active,.ui.primary.buttons .button:active{background-color:#D24B4C;color:#FFF}.ui.secondary.button,.ui.secondary.buttons .button{background-color:#00B5AD;color:#FFF}.ui.secondary.button.active,.ui.secondary.button:hover,.ui.secondary.buttons .active.button,.ui.secondary.buttons .button:hover{background-color:#009A93;color:#FFF}.ui.secondary.button:active,.ui.secondary.buttons .button:active{background-color:#00847E;color:#FFF}.ui.facebook.button{background-color:#3B579D;color:#FFF}.ui.facebook.button:hover{background-color:#3A59A9;color:#FFF}.ui.facebook.button:active{background-color:#334F95;color:#FFF}.ui.twitter.button{background-color:#4092CC;color:#FFF}.ui.twitter.button:hover{background-color:#399ADE;color:#FFF}.ui.twitter.button:active{background-color:#3283BC;color:#FFF}.ui.google.plus.button{background-color:#D34836;color:#FFF}.ui.google.plus.button:hover{background-color:#E3432E;color:#FFF}.ui.google.plus.button:active{background-color:#CA3A27;color:#FFF}.ui.linkedin.button{background-color:#1F88BE;color:#FFF}.ui.linkedin.button:hover{background-color:#1394D6;color:#FFF}.ui.linkedin.button:active{background-color:#1179AE;color:#FFF}.ui.youtube.button{background-color:#CC181E;color:#FFF}.ui.youtube.button:hover{background-color:#DF0209;color:#FFF}.ui.youtube.button:active{background-color:#A50006;color:#FFF}.ui.instagram.button{background-color:#49769C;color:#FFF}.ui.instagram.button:hover{background-color:#4781B1;color:#FFF}.ui.instagram.button:active{background-color:#38658A;color:#FFF}.ui.pinterest.button{background-color:#00ACED;color:#FFF}.ui.pinterest.button:hover{background-color:#00B9FF;color:#FFF}.ui.pinterest.button:active{background-color:#009EDA;color:#FFF}.ui.vk.button{background-color:#4D7198;color:#FFF}.ui.vk.button:hover{background-color:#537AA5;color:#FFF}.ui.vk.button:active{background-color:#405E7E;color:#FFF}.ui.button>.icon{margin-right:.6em;line-height:1;-webkit-transition:opacity .1s ease;-moz-transition:opacity .1s ease;transition:opacity .1s ease}.ui.left.floated.button,.ui.left.floated.buttons{float:left;margin-right:.25em}.ui.right.floated.button,.ui.right.floated.buttons{float:right;margin-left:.25em}.ui.button,.ui.buttons .button{font-size:1rem}.ui.mini.button,.ui.mini.buttons .button,.ui.mini.buttons .or{font-size:.8rem}.ui.mini.button,.ui.mini.buttons .button{padding:.6em .8em}.ui.mini.buttons .icon.button,.ui.mini.icon.buttons .button{padding:.6em}.ui.tiny.button,.ui.tiny.buttons .button,.ui.tiny.buttons .or{font-size:.875em}.ui.tiny.button,.ui.tiny.buttons .button{padding:.6em .8em}.ui.tiny.buttons .icon.button,.ui.tiny.icon.buttons .button{padding:.6em}.ui.small.button,.ui.small.buttons .button,.ui.small.buttons .or{font-size:.875rem}.ui.large.button,.ui.large.buttons .button,.ui.large.buttons .or{font-size:1.125rem}.ui.big.button,.ui.big.buttons .button,.ui.big.buttons .or{font-size:1.25rem}.ui.huge.button,.ui.huge.buttons .button,.ui.huge.buttons .or{font-size:1.375rem}.ui.massive.button,.ui.massive.buttons .button,.ui.massive.buttons .or{font-size:1.5rem;font-weight:700}.ui.mini.buttons .or:before,.ui.tiny.buttons .or:before{width:1.45em;height:1.55em;line-height:1.4;margin-left:-.725em;margin-top:-.25em}.ui.mini.buttons .or:after,.ui.tiny.buttons .or:after{height:1.45em}.ui.huge.loading.button.active:after,.ui.huge.loading.button:after{background-image:url(../images/loader-small.gif)}.ui.gigantic.buttons .loading.button.active:after,.ui.gigantic.buttons .loading.button:after,.ui.gigantic.loading.button.active:after,.ui.gigantic.loading.button:after,.ui.massive.buttons .loading.button.active:after,.ui.massive.buttons .loading.button:after,.ui.massive.loading.button.active:after,.ui.massive.loading.button:after{background-image:url(../images/loader-medium.gif)}.ui.icon.button,.ui.icon.buttons .button{padding:.8em}.ui.icon.button>.icon,.ui.icon.buttons .button>.icon{opacity:.9;margin:0;vertical-align:top}.ui.basic.button,.ui.basic.buttons .button{background-color:transparent!important;background-image:none;color:gray!important;font-weight:400;text-transform:none;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.1)inset;box-shadow:0 0 0 1px rgba(0,0,0,.1)inset}.ui.basic.buttons{-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.1)inset;box-shadow:0 0 0 1px rgba(0,0,0,.1)inset;border-radius:.25em}.ui.basic.button:hover,.ui.basic.buttons .button:hover{background-image:none;color:#7F7F7F!important;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.18)inset;box-shadow:0 0 0 1px rgba(0,0,0,.18)inset}.ui.basic.button:active,.ui.basic.buttons .button:active{background-color:rgba(0,0,0,.02)!important;color:#7F7F7F!important;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.1)inset;box-shadow:0 0 0 1px rgba(0,0,0,.1)inset}.ui.basic.button.active,.ui.basic.buttons .button.active{background-color:rgba(0,0,0,.05);color:#7F7F7F;-webkit-box-shadow:0 0 0 1px #BDBDBD inset;box-shadow:0 0 0 1px #BDBDBD inset}.ui.basic.button.active:hover,.ui.basic.buttons .button.active:hover{background-color:rgba(0,0,0,.1)}.ui.basic.inverted.button,.ui.basic.inverted.buttons .button{color:#FAFAFA!important;-webkit-box-shadow:0 0 0 1px rgba(255,255,255,.3)inset;box-shadow:0 0 0 1px rgba(255,255,255,.3)inset}.ui.basic.inverted.button:hover,.ui.basic.inverted.buttons .button:hover{background-image:none;color:#FFF!important;-webkit-box-shadow:0 0 0 1px rgba(255,255,255,.5)inset;box-shadow:0 0 0 1px rgba(255,255,255,.5)inset}.ui.basic.inverted.button:active,.ui.basic.inverted.buttons .button:active{background-color:rgba(255,255,255,.05)!important;color:#FFF!important;-webkit-box-shadow:0 0 0 1px rgba(255,255,255,.8)inset!important;box-shadow:0 0 0 1px rgba(255,255,255,.8)inset!important}.ui.basic.inverted.button.active,.ui.basic.inverted.buttons .button.active{background-color:rgba(255,255,255,.5);color:#FFF;-webkit-box-shadow:none;box-shadow:none}.ui.basic.inverted.button.active:hover,.ui.basic.inverted.buttons .button.active:hover{background-color:rgba(0,0,0,.1)}.ui.basic.buttons .button{border-left:1px solid rgba(0,0,0,.1);-webkit-box-shadow:none;box-shadow:none}.ui.basic.buttons .button:active,.ui.basic.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none}.ui.basic.buttons .button.active,.ui.basic.buttons .button.active:hover{-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.2)inset;box-shadow:0 0 0 1px rgba(0,0,0,.2)inset}.ui.labeled.icon.button,.ui.labeled.icon.buttons .button{position:relative;padding-left:4em!important;padding-right:1.4em!important}.ui.labeled.icon.button>.icon,.ui.labeled.icon.buttons>.button>.icon{position:absolute;top:0;left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;width:2.75em;height:100%;padding-top:.8em;background-color:rgba(0,0,0,.05);text-align:center;border-radius:.25em 0 0 .25em;line-height:1;-webkit-box-shadow:-1px 0 0 0 rgba(0,0,0,.05)inset;box-shadow:-1px 0 0 0 rgba(0,0,0,.05)inset}.ui.labeled.icon.buttons .button>.icon{border-radius:0}.ui.labeled.icon.buttons .button:first-child>.icon{border-top-left-radius:.25em;border-bottom-left-radius:.25em}.ui.labeled.icon.buttons .button:last-child>.icon{border-top-right-radius:.25em;border-bottom-right-radius:.25em}.ui.vertical.labeled.icon.buttons .button:first-child>.icon{border-radius:0;border-top-left-radius:.25em}.ui.vertical.labeled.icon.buttons .button:last-child>.icon{border-radius:0;border-bottom-left-radius:.25em}.ui.right.labeled.icon.button{padding-left:1.4em!important;padding-right:4em!important}.ui.left.fluid.labeled.icon.button,.ui.right.fluid.labeled.icon.button{padding-left:1.4em!important;padding-right:1.4em!important}.ui.right.labeled.icon.button .icon{left:auto;right:0;border-radius:0 .25em .25em 0;-webkit-box-shadow:1px 0 0 0 rgba(0,0,0,.05)inset;box-shadow:1px 0 0 0 rgba(0,0,0,.05)inset}.ui.button.toggle.active,.ui.buttons .button.toggle.active,.ui.toggle.buttons .active.button{background-color:#5BBD72!important;color:#FFF!important;-webkit-box-shadow:none!important;box-shadow:none!important}.ui.button.toggle.active:hover{background-color:#58CB73!important;color:#FFF!important;-webkit-box-shadow:none!important;box-shadow:none!important}.ui.circular.button{border-radius:10em}.ui.attached.button{display:block;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.1)!important;box-shadow:0 0 0 1px rgba(0,0,0,.1)!important}.ui.attached.top.button{border-radius:.25em .25em 0 0}.ui.attached.bottom.button{border-radius:0 0 .25em .25em}.ui.attached.left.button{display:inline-block;border-left:none;padding-right:.75em;text-align:right;border-radius:.25em 0 0 .25em}.ui.attached.right.button{display:inline-block;padding-left:.75em;text-align:left;border-radius:0 .25em .25em 0}.ui.buttons .or{position:relative;float:left;width:.3em;height:1.1em;z-index:3}.ui.buttons .or:before{position:absolute;top:50%;left:50%;content:'or';background-color:#FFF;margin-top:-.1em;margin-left:-.9em;width:1.8em;height:1.8em;line-height:1.55;color:#AAA;font-style:normal;font-weight:400;text-align:center;border-radius:500px;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.1);box-shadow:0 0 0 1px rgba(0,0,0,.1);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.ui.buttons .or[data-text]:before{content:attr(data-text)}.ui.buttons .or:after{position:absolute;top:0;left:0;content:' ';width:.3em;height:1.7em;background-color:transparent;border-top:.5em solid #FFF;border-bottom:.5em solid #FFF}.ui.fluid.buttons .or{width:0!important}.ui.fluid.buttons .or:after{display:none}.attached.ui.buttons{margin:0;border-radius:4px 4px 0 0}.attached.ui.buttons .button:first-child{border-radius:4px 0 0}.attached.ui.buttons .button:last-child{border-radius:0 4px 0 0}.bottom.attached.ui.buttons{margin-top:-1px;border-radius:0 0 4px 4px}.bottom.attached.ui.buttons .button:first-child{border-radius:0 0 0 4px}.bottom.attached.ui.buttons .button:last-child{border-radius:0 0 4px}.left.attached.ui.buttons{margin-left:-1px;border-radius:0 4px 4px 0}.left.attached.ui.buttons .button:first-child{margin-left:-1px;border-radius:0 4px 0 0}.left.attached.ui.buttons .button:last-child{margin-left:-1px;border-radius:0 0 4px}.right.attached.ui.buttons,.right.attached.ui.buttons .button{margin-right:-1px;border-radius:4px 0 0 4px}.right.attached.ui.buttons .button:first-child{margin-left:-1px;border-radius:4px 0 0}.right.attached.ui.buttons .button:last-child{margin-left:-1px;border-radius:0 0 0 4px}.ui.button.fluid,.ui.fluid.buttons,.ui.fluid.buttons>.button{display:block;width:100%}.ui.\32.buttons>.button,.ui.two.buttons>.button{width:50%}.ui.\33.buttons>.button,.ui.three.buttons>.button{width:33.333%}.ui.\34.buttons>.button,.ui.four.buttons>.button{width:25%}.ui.\35.buttons>.button,.ui.five.buttons>.button{width:20%}.ui.\36.buttons>.button,.ui.six.buttons>.button{width:16.666%}.ui.\37.buttons>.button,.ui.seven.buttons>.button{width:14.285%}.ui.\38.buttons>.button,.ui.eight.buttons>.button{width:12.5%}.ui.\39.buttons>.button,.ui.nine.buttons>.button{width:11.11%}.ui.\31\30.buttons>.button,.ui.ten.buttons>.button{width:10%}.ui.\31\31.buttons>.button,.ui.eleven.buttons>.button{width:9.09%}.ui.\31\32.buttons>.button,.ui.twelve.buttons>.button{width:8.3333%}.ui.fluid.vertical.buttons,.ui.fluid.vertical.buttons>.button{display:block;width:auto;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.ui.\32.vertical.buttons>.button,.ui.two.vertical.buttons>.button{height:50%}.ui.\33.vertical.buttons>.button,.ui.three.vertical.buttons>.button{height:33.333%}.ui.\34.vertical.buttons>.button,.ui.four.vertical.buttons>.button{height:25%}.ui.\35.vertical.buttons>.button,.ui.five.vertical.buttons>.button{height:20%}.ui.\36.vertical.buttons>.button,.ui.six.vertical.buttons>.button{height:16.666%}.ui.\37.vertical.buttons>.button,.ui.seven.vertical.buttons>.button{height:14.285%}.ui.\38.vertical.buttons>.button,.ui.eight.vertical.buttons>.button{height:12.5%}.ui.\39.vertical.buttons>.button,.ui.nine.vertical.buttons>.button{height:11.11%}.ui.\31\30.vertical.buttons>.button,.ui.ten.vertical.buttons>.button{height:10%}.ui.\31\31.vertical.buttons>.button,.ui.eleven.vertical.buttons>.button{height:9.09%}.ui.\31\32.vertical.buttons>.button,.ui.twelve.vertical.buttons>.button{height:8.3333%}.ui.black.button,.ui.black.buttons .button{background-color:#5C6166;color:#FFF}.ui.black.button:hover,.ui.black.buttons .button:hover{background-color:#4C4C4C;color:#FFF}.ui.black.button:active,.ui.black.buttons .button:active{background-color:#333;color:#FFF}.ui.green.button,.ui.green.buttons .button{background-color:#5BBD72;color:#FFF}.ui.green.button.active,.ui.green.button:hover,.ui.green.buttons .active.button,.ui.green.buttons .button:hover{background-color:#58cb73;color:#FFF}.ui.green.button:active,.ui.green.buttons .button:active{background-color:#4CB164;color:#FFF}.ui.red.button,.ui.red.buttons .button{background-color:#D95C5C;color:#FFF}.ui.red.button.active,.ui.red.button:hover,.ui.red.buttons .active.button,.ui.red.buttons .button:hover{background-color:#E75859;color:#FFF}.ui.red.button:active,.ui.red.buttons .button:active{background-color:#D24B4C;color:#FFF}.ui.orange.button,.ui.orange.buttons .button{background-color:#E96633;color:#FFF}.ui.orange.button.active,.ui.orange.button:hover,.ui.orange.buttons .active.button,.ui.orange.buttons .button:hover{background-color:#FF7038;color:#FFF}.ui.orange.button:active,.ui.orange.buttons .button:active{background-color:#DA683B;color:#FFF}.ui.blue.button,.ui.blue.buttons .button{background-color:#6ECFF5;color:#FFF}.ui.blue.button.active,.ui.blue.button:hover,.ui.blue.buttons .active.button,.ui.blue.buttons .button:hover{background-color:#1AB8F3;color:#FFF}.ui.blue.button:active,.ui.blue.buttons .button:active{background-color:#0AA5DF;color:#FFF}.ui.purple.button,.ui.purple.buttons .button{background-color:#564F8A;color:#FFF}.ui.purple.button.active,.ui.purple.button:hover,.ui.purple.buttons .active.button,.ui.purple.buttons .button:hover{background-color:#3E3773;color:#FFF}.ui.purple.button:active,.ui.purple.buttons .button:active{background-color:#2E2860;color:#FFF}.ui.teal.button,.ui.teal.buttons .button{background-color:#00B5AD;color:#FFF}.ui.teal.button.active,.ui.teal.button:hover,.ui.teal.buttons .active.button,.ui.teal.buttons .button:hover{background-color:#009A93;color:#FFF}.ui.teal.button:active,.ui.teal.buttons .button:active{background-color:#00847E;color:#FFF}.ui.positive.button,.ui.positive.buttons .button{background-color:#5BBD72!important;color:#FFF}.ui.positive.button.active,.ui.positive.button:hover,.ui.positive.buttons .active.button,.ui.positive.buttons .button:hover{background-color:#58CB73!important;color:#FFF}.ui.positive.button:active,.ui.positive.buttons .button:active{background-color:#4CB164!important;color:#FFF}.ui.negative.button,.ui.negative.buttons .button{background-color:#D95C5C!important;color:#FFF}.ui.negative.button.active,.ui.negative.button:hover,.ui.negative.buttons .active.button,.ui.negative.buttons .button:hover{background-color:#E75859!important;color:#FFF}.ui.negative.button:active,.ui.negative.buttons .button:active{background-color:#D24B4C!important;color:#FFF}.ui.buttons{display:inline-block;vertical-align:middle}.ui.buttons:after{content:".";display:block;height:0;clear:both;visibility:hidden}.ui.buttons .button:first-child{border-left:none}.ui.buttons .button{float:left;border-radius:0}.ui.buttons .button:first-child{margin-left:0;border-top-left-radius:.25em;border-bottom-left-radius:.25em}.ui.buttons .button:last-child{border-top-right-radius:.25em;border-bottom-right-radius:.25em}.ui.vertical.buttons{display:inline-block}.ui.vertical.buttons .button{display:block;float:none;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.1)inset;box-shadow:0 0 0 1px rgba(0,0,0,.1)inset}.ui.vertical.buttons .button:first-child,.ui.vertical.buttons .huge.button:first-child,.ui.vertical.buttons .massive.button:first-child,.ui.vertical.buttons .mini.button:first-child,.ui.vertical.buttons .small.button:first-child,.ui.vertical.buttons .tiny.button:first-child{margin-top:0;border-radius:.25em .25em 0 0}.ui.vertical.buttons .button:last-child,.ui.vertical.buttons .gigantic.button:last-child,.ui.vertical.buttons .huge.button:last-child,.ui.vertical.buttons .massive.button:last-child,.ui.vertical.buttons .mini.button:last-child,.ui.vertical.buttons .small.button:last-child,.ui.vertical.buttons .tiny.button:last-child{border-radius:0 0 .25em .25em}.ui.divider{margin:1rem 0;border-top:1px solid rgba(0,0,0,.1);border-bottom:1px solid rgba(255,255,255,.8);line-height:1;height:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:rgba(0,0,0,0)}.ui.horizontal.divider,.ui.vertical.divider{border:none;background-color:transparent;font-size:.875rem;font-weight:700;text-align:center;text-transform:uppercase;color:rgba(0,0,0,.8)}.ui.vertical.divider{position:absolute;z-index:2;top:50%;left:50%;margin:0 0 0 -3%;width:6%;height:50%;line-height:0;padding:0}.ui.vertical.divider:after,.ui.vertical.divider:before{position:absolute;left:50%;content:" ";z-index:3;border-left:1px solid rgba(0,0,0,.1);border-right:1px solid rgba(255,255,255,.8);width:0;height:80%}.ui.vertical.divider:before{top:-100%}.ui.vertical.divider:after{top:auto;bottom:0}.ui.horizontal.divider{position:relative;top:0;left:0;margin:1rem 1.5rem;height:auto;padding:0;line-height:1}.ui.horizontal.divider:after,.ui.horizontal.divider:before{position:absolute;content:" ";z-index:3;width:50%;top:50%;height:0;border-top:1px solid rgba(0,0,0,.1);border-bottom:1px solid rgba(255,255,255,.8)}.ui.horizontal.divider:before{left:0;margin-left:-1.5rem}.ui.horizontal.divider:after{left:auto;right:0;margin-right:-1.5rem}.ui.divider>.icon{margin:0;font-size:1rem;vertical-align:middle}.ui.divider.inverted{color:#fff}.ui.horizontal.inverted.divider,.ui.vertical.inverted.divider{color:rgba(255,255,255,.9)}.ui.divider.inverted,.ui.divider.inverted:after,.ui.divider.inverted:before{border-top-color:rgba(0,0,0,.15);border-bottom-color:rgba(255,255,255,.15);border-left-color:rgba(0,0,0,.15);border-right-color:rgba(255,255,255,.15)}.ui.fitted.divider{margin:0}.ui.clearing.divider{clear:both}.ui.section.divider{margin-top:2rem;margin-bottom:2rem}.ui.header{border:none;margin:1em 0 1rem;padding:0;font-size:1.33em;font-weight:700;line-height:1.33}.ui.header .sub.header{font-size:1rem;font-weight:400;margin:0;padding:0;line-height:1.2;color:rgba(0,0,0,.5)}.ui.header .icon{display:table-cell;vertical-align:middle;padding-right:.5em}.ui.header .icon:only-child{display:inline-block;vertical-align:baseline}.ui.header .content{display:inline-block;vertical-align:top}.ui.header .icon+.content{padding-left:.5em;display:table-cell}.ui.header:first-child{margin-top:0}.ui.header:last-child{margin-bottom:0}.ui.header+p{margin-top:0}h1.ui.header{min-height:1rem;line-height:1.33;font-size:2rem}h2.ui.header{line-height:1.33;font-size:1.75rem}h3.ui.header{line-height:1.33;font-size:1.33rem}h4.ui.header{line-height:1.33;font-size:1.1rem}h5.ui.header{line-height:1.2;font-size:1rem}.ui.huge.header{min-height:1em;font-size:2em}.ui.large.header{font-size:1.75em}.ui.medium.header{font-size:1.33em}.ui.small.header{font-size:1.1em}.ui.tiny.header{font-size:1em}.ui.icon.header{display:inline-block;text-align:center}.ui.icon.header .icon{float:none;display:block;font-size:3em;margin:0 auto .2em;padding:0}.ui.icon.header .content{display:block}.ui.icon.header .circular.icon,.ui.icon.header .square.icon{font-size:2em}.ui.block.icon.header .icon{margin-bottom:0}.ui.icon.header.aligned{margin-left:auto;margin-right:auto;display:block}.ui.disabled.header{opacity:.5}.ui.blue.header{color:#6ECFF5!important}.ui.black.header{color:#5C6166!important}.ui.green.header{color:#A1CF64!important}.ui.red.header{color:#D95C5C!important}.ui.purple.header{color:#564F8A!important}.ui.teal.header{color:#00B5AD!important}.ui.blue.dividing.header{border-bottom:3px solid #6ECFF5}.ui.black.dividing.header{border-bottom:3px solid #5C6166}.ui.green.dividing.header{border-bottom:3px solid #A1CF64}.ui.red.dividing.header{border-bottom:3px solid #D95C5C}.ui.purple.dividing.header{border-bottom:3px solid #564F8A}.ui.teal.dividing.header{border-bottom:3px solid #00B5AD}.ui.inverted.header{color:#FFF}.ui.inverted.header .sub.header{color:rgba(255,255,255,.85)}.ui.inverted.black.header{background-color:#5C6166!important;color:#FFF!important}.ui.inverted.blue.header{background-color:#6ECFF5!important;color:#FFF!important}.ui.inverted.green.header{background-color:#A1CF64!important;color:#FFF!important}.ui.inverted.red.header{background-color:#D95C5C!important;color:#FFF!important}.ui.inverted.purple.header{background-color:#564F8A!important;color:#FFF!important}.ui.inverted.teal.header{background-color:#00B5AD!important;color:#FFF!important}.ui.inverted.block.header{border-bottom:none}.ui.left.aligned.header{text-align:left}.ui.right.aligned.header{text-align:right}.ui.center.aligned.header{text-align:center}.ui.justified.header{text-align:justify}.ui.justified.header:after{display:inline-block;content:'';width:100%}.ui.floated.header,.ui.left.floated.header{float:left;margin-top:0;margin-right:.5em}.ui.right.floated.header{float:right;margin-top:0;margin-left:.5em}.ui.fitted.header{padding:0}.ui.dividing.header{padding-bottom:.2rem;border-bottom:1px solid rgba(0,0,0,.1)}.ui.dividing.header .sub.header{padding-bottom:.5em}.ui.dividing.header .icon{margin-bottom:.2em}.ui.block.header{background-color:rgba(0,0,0,.05);padding:.5em 1em}.ui.attached.header{background-color:#E0E0E0;padding:.5em 1rem;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.1);box-shadow:0 0 0 1px rgba(0,0,0,.1)}.ui.top.attached.header{margin-bottom:0;border-radius:.3125em .3125em 0 0}.ui.bottom.attached.header{margin-top:0;border-radius:0 0 .3125em .3125em}@font-face{font-family:Icons;src:url(../fonts/icons.eot);src:url(../fonts/icons.eot?#iefix) format('embedded-opentype'),url(../fonts/icons.svg#icons) format('svg'),url(../fonts/icons.woff) format('woff'),url(../fonts/icons.ttf) format('truetype');font-style:normal;font-weight:400;font-variant:normal;text-decoration:inherit;text-transform:none}i.icon{display:inline-block;opacity:.75;margin:0 .25em 0 0;width:1.23em;height:1em;font-family:Icons;font-style:normal;line-height:1;font-weight:400;text-decoration:inherit;text-align:center;speak:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}i.icon.left:before{content:"\f060"}i.icon.right:before{content:"\f061"}i.icon.add.sign.box:before{content:"\f0fe"}i.icon.add.sign:before{content:"\f055"}i.icon.add:before{content:"\f067"}i.icon.adjust:before{content:"\f042"}i.icon.adn:before{content:"\f170"}i.icon.align.center:before{content:"\f037"}i.icon.align.justify:before{content:"\f039"}i.icon.align.left:before{content:"\f036"}i.icon.align.right:before{content:"\f038"}i.icon.ambulance:before{content:"\f0f9"}i.icon.anchor:before{content:"\f13d"}i.icon.android:before{content:"\f17b"}i.icon.angle.down:before{content:"\f107"}i.icon.angle.left:before{content:"\f104"}i.icon.angle.right:before{content:"\f105"}i.icon.angle.up:before{content:"\f106"}i.icon.apple:before{content:"\f179"}i.icon.archive:before{content:"\f187"}i.icon.arrow.down:before{content:"\f078"}i.icon.arrow.left:before{content:"\f053"}i.icon.arrow.right:before{content:"\f054"}i.icon.arrow.sign.down:before{content:"\f13a"}i.icon.arrow.sign.left:before{content:"\f137"}i.icon.arrow.sign.right:before{content:"\f138"}i.icon.arrow.sign.up:before{content:"\f139"}i.icon.arrow.up:before{content:"\f077"}i.icon.asterisk:before{content:"\f069"}i.icon.attachment:before{content:"\f0c6"}i.icon.attention:before{content:"\f06a"}i.icon.backward:before{content:"\f04a"}i.icon.ban.circle:before{content:"\f05e"}i.icon.bar.chart:before{content:"\f080"}i.icon.barcode:before{content:"\f02a"}i.icon.beer:before{content:"\f0fc"}i.icon.bell.outline:before{content:"\f0a2"}i.icon.bell:before{content:"\f0f3"}i.icon.bitbucket.sign:before{content:"\f172"}i.icon.bitbucket:before{content:"\f171"}i.icon.bitcoin:before{content:"\f15a"}i.icon.bold:before{content:"\f032"}i.icon.bolt:before{content:"\f0e7"}i.icon.book:before{content:"\f02d"}i.icon.bookmark.empty:before{content:"\f097"}i.icon.bookmark:before{content:"\f02e"}i.icon.box.arrow.down:before{content:"\f150"}i.icon.box.arrow.right:before{content:"\f152"}i.icon.box.arrow.up:before{content:"\f151"}i.icon.briefcase:before{content:"\f0b1"}i.icon.browser:before{content:"\f022"}i.icon.bug:before{content:"\f188"}i.icon.building:before{content:"\f0f7"}i.icon.bullhorn:before{content:"\f0a1"}i.icon.bullseye:before{content:"\f140"}i.icon.calendar.empty:before{content:"\f133"}i.icon.calendar:before{content:"\f073"}i.icon.camera.retro:before{content:"\f083"}i.icon.camera:before{content:"\f030"}i.icon.triangle.down:before{content:"\f0d7"}i.icon.triangle.left:before{content:"\f0d9"}i.icon.triangle.right:before{content:"\f0da"}i.icon.triangle.up:before{content:"\f0d8"}i.icon.cart:before{content:"\f07a"}i.icon.certificate:before{content:"\f0a3"}i.icon.chat.outline:before{content:"\f0e6"}i.icon.chat:before{content:"\f086"}i.icon.checkbox.empty:before{content:"\f096"}i.icon.checkbox.minus:before{content:"\f147"}i.icon.checked.checkbox:before{content:"\f046"}i.icon.checkmark.sign:before{content:"\f14a"}i.icon.checkmark:before{content:"\f00c"}i.icon.circle.blank:before{content:"\f10c"}i.icon.circle.down:before{content:"\f0ab"}i.icon.circle.left:before{content:"\f0a8"}i.icon.circle.right:before{content:"\f0a9"}i.icon.circle.up:before{content:"\f0aa"}i.icon.circle:before{content:"\f111"}i.icon.cloud.download:before{content:"\f0ed"}i.icon.cloud.upload:before{content:"\f0ee"}i.icon.cloud:before{content:"\f0c2"}i.icon.code.fork:before{content:"\f126"}i.icon.code:before{content:"\f121"}i.icon.coffee:before{content:"\f0f4"}i.icon.collapse:before{content:"\f117"}i.icon.comment.outline:before{content:"\f0e5"}i.icon.comment:before{content:"\f075"}i.icon.copy:before{content:"\f0c5"}i.icon.crop:before{content:"\f125"}i.icon.css3:before{content:"\f13c"}i.icon.cut:before{content:"\f0c4"}i.icon.dashboard:before{content:"\f0e4"}i.icon.desktop:before{content:"\f108"}i.icon.doctor:before{content:"\f0f0"}i.icon.dollar:before{content:"\f155"}i.icon.double.angle.down:before{content:"\f103"}i.icon.double.angle.left:before{content:"\f100"}i.icon.double.angle.right:before{content:"\f101"}i.icon.double.angle.up:before{content:"\f102"}i.icon.down:before{content:"\f063"}i.icon.download.disk:before{content:"\f019"}i.icon.download:before{content:"\f01a"}i.icon.dribbble:before{content:"\f17d"}i.icon.dropbox:before{content:"\f16b"}i.icon.edit.sign:before{content:"\f14b"}i.icon.edit:before{content:"\f044"}i.icon.eject:before{content:"\f052"}i.icon.ellipsis.horizontal:before{content:"\f141"}i.icon.ellipsis.vertical:before{content:"\f142"}i.icon.eraser:before{content:"\f12d"}i.icon.euro:before{content:"\f153"}i.icon.exchange:before{content:"\f0ec"}i.icon.exclamation:before{content:"\f12a"}i.icon.expand:before{content:"\f116"}i.icon.external.url.sign:before{content:"\f14c"}i.icon.external.url:before{content:"\f08e"}i.icon.facebook.sign:before{content:"\f082"}i.icon.facebook:before{content:"\f09a"}i.icon.facetime.video:before{content:"\f03d"}i.icon.fast.backward:before{content:"\f049"}i.icon.fast.forward:before{content:"\f050"}i.icon.female:before{content:"\f182"}i.icon.fighter.jet:before{content:"\f0fb"}i.icon.file.outline:before{content:"\f016"}i.icon.file.text.outline:before{content:"\f0f6"}i.icon.file.text:before{content:"\f15c"}i.icon.file:before{content:"\f15b"}i.icon.filter:before{content:"\f0b0"}i.icon.fire.extinguisher:before{content:"\f134"}i.icon.fire:before{content:"\f06d"}i.icon.flag.checkered:before{content:"\f11e"}i.icon.flag.empty:before{content:"\f11d"}i.icon.flag:before{content:"\f024"}i.icon.flickr:before{content:"\f16e"}i.icon.folder.open.outline:before{content:"\f115"}i.icon.folder.open:before{content:"\f07c"}i.icon.folder.outline:before{content:"\f114"}i.icon.folder:before{content:"\f07b"}i.icon.font:before{content:"\f031"}i.icon.food:before{content:"\f0f5"}i.icon.forward.mail:before{content:"\f064"}i.icon.forward:before{content:"\f04e"}i.icon.foursquare:before{content:"\f180"}i.icon.frown:before{content:"\f119"}i.icon.fullscreen:before{content:"\f0b2"}i.icon.gamepad:before{content:"\f11b"}i.icon.gift:before{content:"\f06b"}i.icon.github.alternate:before{content:"\f09b"}i.icon.github.sign:before{content:"\f092"}i.icon.github:before{content:"\f113"}i.icon.gittip:before{content:"\f184"}i.icon.glass:before{content:"\f000"}i.icon.globe:before{content:"\f0ac"}i.icon.google.plus.sign:before{content:"\f0d4"}i.icon.google.plus:before{content:"\f0d5"}i.icon.h.sign:before{content:"\f0fd"}i.icon.hand.down:before{content:"\f0a7"}i.icon.hand.left:before{content:"\f0a5"}i.icon.hand.right:before{content:"\f0a4"}i.icon.hand.up:before{content:"\f0a6"}i.icon.hdd:before{content:"\f0a0"}i.icon.headphones:before{content:"\f025"}i.icon.heart.empty:before{content:"\f08a"}i.icon.heart:before{content:"\f004"}i.icon.help:before{content:"\f059"}i.icon.hide:before{content:"\f070"}i.icon.home:before{content:"\f015"}i.icon.hospital:before{content:"\f0f8"}i.icon.html5:before{content:"\f13b"}i.icon.inbox:before{content:"\f01c"}i.icon.indent.left:before{content:"\f03b"}i.icon.indent.right:before{content:"\f03c"}i.icon.info.letter:before{content:"\f129"}i.icon.info:before{content:"\f05a"}i.icon.instagram:before{content:"\f16d"}i.icon.italic:before{content:"\f033"}i.icon.key:before{content:"\f084"}i.icon.keyboard:before{content:"\f11c"}i.icon.lab:before{content:"\f0c3"}i.icon.laptop:before{content:"\f109"}i.icon.layout.block:before{content:"\f009"}i.icon.layout.column:before{content:"\f0db"}i.icon.layout.grid:before{content:"\f00a"}i.icon.layout.list:before{content:"\f00b"}i.icon.leaf:before{content:"\f06c"}i.icon.legal:before{content:"\f0e3"}i.icon.lemon:before{content:"\f094"}i.icon.level.down:before{content:"\f149"}i.icon.level.up:before{content:"\f148"}i.icon.lightbulb:before{content:"\f0eb"}i.icon.linkedin.sign:before{content:"\f08c"}i.icon.linkedin:before{content:"\f0e1"}i.icon.linux:before{content:"\f17c"}i.icon.list.ordered:before{content:"\f0cb"}i.icon.list.unordered:before{content:"\f0ca"}i.icon.list:before{content:"\f03a"}i.icon.loading:before{content:"\f110"}i.icon.location:before{content:"\f124"}i.icon.lock:before{content:"\f023"}i.icon.long.arrow.down:before{content:"\f175"}i.icon.long.arrow.left:before{content:"\f177"}i.icon.long.arrow.right:before{content:"\f178"}i.icon.long.arrow.up:before{content:"\f176"}i.icon.magic:before{content:"\f0d0"}i.icon.magnet:before{content:"\f076"}i.icon.mail.outline:before{content:"\f003"}i.icon.mail.reply:before{content:"\f112"}i.icon.mail:before{content:"\f0e0"}i.icon.male:before{content:"\f183"}i.icon.map.marker:before{content:"\f041"}i.icon.map:before{content:"\f14e"}i.icon.maxcdn:before{content:"\f136"}i.icon.medkit:before{content:"\f0fa"}i.icon.meh:before{content:"\f11a"}i.icon.minus.sign.alternate:before{content:"\f146"}i.icon.minus.sign:before{content:"\f056"}i.icon.minus:before{content:"\f068"}i.icon.mobile:before{content:"\f10b"}i.icon.money:before{content:"\f0d6"}i.icon.moon:before{content:"\f186"}i.icon.move:before{content:"\f047"}i.icon.music:before{content:"\f001"}i.icon.mute:before{content:"\f131"}i.icon.off:before{content:"\f011"}i.icon.ok.circle:before{content:"\f05d"}i.icon.ok.sign:before{content:"\f058"}i.icon.paste:before{content:"\f0ea"}i.icon.pause:before{content:"\f04c"}i.icon.payment:before{content:"\f09d"}i.icon.pencil:before{content:"\f040"}i.icon.phone.sign:before{content:"\f098"}i.icon.phone:before{content:"\f095"}i.icon.photo:before{content:"\f03e"}i.icon.pin:before{content:"\f08d"}i.icon.pinterest.sign:before{content:"\f0d3"}i.icon.pinterest:before{content:"\f0d2"}i.icon.plane:before{content:"\f072"}i.icon.play.circle:before{content:"\f01d"}i.icon.play.sign:before{content:"\f144"}i.icon.play:before{content:"\f04b"}i.icon.pound:before{content:"\f154"}i.icon.print:before{content:"\f02f"}i.icon.puzzle.piece:before{content:"\f12e"}i.icon.qr.code:before{content:"\f029"}i.icon.question:before{content:"\f128"}i.icon.quote.left:before{content:"\f10d"}i.icon.quote.right:before{content:"\f10e"}i.icon.refresh:before{content:"\f021"}i.icon.remove.circle:before{content:"\f05c"}i.icon.remove.sign:before{content:"\f057"}i.icon.remove:before{content:"\f00d"}i.icon.renren:before{content:"\f18b"}i.icon.reorder:before{content:"\f0c9"}i.icon.repeat:before{content:"\f01e"}i.icon.reply.all.mail:before{content:"\f122"}i.icon.resize.full:before{content:"\f065"}i.icon.resize.horizontal:before{content:"\f07e"}i.icon.resize.small:before{content:"\f066"}i.icon.resize.vertical:before{content:"\f07d"}i.icon.retweet:before{content:"\f079"}i.icon.road:before{content:"\f018"}i.icon.rocket:before{content:"\f135"}i.icon.rss.sign:before{content:"\f143"}i.icon.rss:before{content:"\f09e"}i.icon.rupee:before{content:"\f156"}i.icon.save:before{content:"\f0c7"}i.icon.screenshot:before{content:"\f05b"}i.icon.search:before{content:"\f002"}i.icon.setting:before{content:"\f013"}i.icon.settings:before{content:"\f085"}i.icon.share.sign:before{content:"\f14d"}i.icon.share:before{content:"\f045"}i.icon.shield:before{content:"\f132"}i.icon.shuffle:before{content:"\f074"}i.icon.sign.in:before{content:"\f090"}i.icon.sign.out:before{content:"\f08b"}i.icon.sign:before{content:"\f0c8"}i.icon.signal:before{content:"\f012"}i.icon.sitemap:before{content:"\f0e8"}i.icon.skype:before{content:"\f17e"}i.icon.smile:before{content:"\f118"}i.icon.sort.ascending:before{content:"\f0de"}i.icon.sort.descending:before{content:"\f0dd"}i.icon.sort.alphabet.descending:before{content:"\f15e"}i.icon.sort.alphabet:before{content:"\f15d"}i.icon.sort.attributes.descending:before{content:"\f161"}i.icon.sort.attributes:before{content:"\f160"}i.icon.sort.order.descending:before{content:"\f163"}i.icon.sort.order:before{content:"\f162"}i.icon.sort:before{content:"\f0dc"}i.icon.stackexchange:before{content:"\f16c"}i.icon.star.empty:before{content:"\f006"}i.icon.star.half.empty:before{content:"\f123"}i.icon.star.half.full:before,i.icon.star.half:before{content:"\f089"}i.icon.star:before{content:"\f005"}i.icon.step.backward:before{content:"\f048"}i.icon.step.forward:before{content:"\f051"}i.icon.stethoscope:before{content:"\f0f1"}i.icon.stop:before{content:"\f04d"}i.icon.strikethrough:before{content:"\f0cc"}i.icon.subscript:before{content:"\f12c"}i.icon.suitcase:before{content:"\f0f2"}i.icon.sun:before{content:"\f185"}i.icon.superscript:before{content:"\f12b"}i.icon.table:before{content:"\f0ce"}i.icon.tablet:before{content:"\f10a"}i.icon.tag:before{content:"\f02b"}i.icon.tags:before{content:"\f02c"}i.icon.tasks:before{content:"\f0ae"}i.icon.terminal:before{content:"\f120"}i.icon.text.height:before{content:"\f034"}i.icon.text.width:before{content:"\f035"}i.icon.thumbs.down.outline:before{content:"\f088"}i.icon.thumbs.down:before{content:"\f165"}i.icon.thumbs.up.outline:before{content:"\f087"}i.icon.thumbs.up:before{content:"\f164"}i.icon.ticket:before{content:"\f145"}i.icon.time:before{content:"\f017"}i.icon.tint:before{content:"\f043"}i.icon.trash:before{content:"\f014"}i.icon.trello:before{content:"\f181"}i.icon.trophy:before{content:"\f091"}i.icon.truck:before{content:"\f0d1"}i.icon.tumblr.sign:before{content:"\f174"}i.icon.tumblr:before{content:"\f173"}i.icon.twitter.sign:before{content:"\f081"}i.icon.twitter:before{content:"\f099"}i.icon.umbrella:before{content:"\f0e9"}i.icon.underline:before{content:"\f0cd"}i.icon.undo:before{content:"\f0e2"}i.icon.unhide:before{content:"\f06e"}i.icon.unlink:before{content:"\f127"}i.icon.unlock.alternate:before{content:"\f13e"}i.icon.unlock:before{content:"\f09c"}i.icon.unmute:before{content:"\f130"}i.icon.up:before{content:"\f062"}i.icon.upload.disk:before{content:"\f093"}i.icon.upload:before{content:"\f01b"}i.icon.url:before{content:"\f0c1"}i.icon.user:before{content:"\f007"}i.icon.users:before{content:"\f0c0"}i.icon.video:before{content:"\f008"}i.icon.vk:before{content:"\f189"}i.icon.volume.down:before{content:"\f027"}i.icon.volume.off:before{content:"\f026"}i.icon.volume.up:before{content:"\f028"}i.icon.warning:before{content:"\f071"}i.icon.weibo:before{content:"\f18a"}i.icon.windows:before{content:"\f17a"}i.icon.won:before{content:"\f159"}i.icon.wrench:before{content:"\f0ad"}i.icon.xing.sign:before{content:"\f169"}i.icon.xing:before{content:"\f168"}i.icon.yen:before{content:"\f157"}i.icon.youtube.play:before{content:"\f16a"}i.icon.youtube.sign:before{content:"\f166"}i.icon.youtube:before{content:"\f167"}i.icon.yuan:before{content:"\f158"}i.icon.zoom.in:before{content:"\f00e"}i.icon.zoom.out:before{content:"\f010"}i.icon.check:before{content:"\f00c"}i.icon.close:before,i.icon.delete:before{content:"\f00d"}i.icon.like:before{content:"\f004"}i.icon.plus:before{content:"\f067"}i.icon.signup:before{content:"\f044"}i.icon.star{width:auto;margin:0}i.icon.left{width:auto;margin:0 .5em 0 0}i.icon.right,i.icon.search{width:auto;margin:0 0 0 .5em}i.icon.loading{-webkit-animation:icon-loading 2s linear infinite;-moz-animation:icon-loading 2s linear infinite;-ms-animation:icon-loading 2s linear infinite;animation:icon-loading 2s linear infinite}@keyframes icon-loading{from{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes icon-loading{from{-moz-transform:rotate(0deg);transform:rotate(0deg)}to{-moz-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes icon-loading{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-ms-keyframes icon-loading{from{-ms-transform:rotate(0deg);transform:rotate(0deg)}to{-ms-transform:rotate(360deg);transform:rotate(360deg)}}i.emphasized.icon,i.icon.active,i.icon.hover{opacity:1}i.icon.disabled{opacity:.3}i.link.icon{cursor:pointer;opacity:.7;-webkit-transition:opacity .3s ease-out;-moz-transition:opacity .3s ease-out;transition:opacity .3s ease-out}i.link.icon:hover{opacity:1!important}i.circular.icon{border-radius:500em!important;padding:.5em .35em!important;-webkit-box-shadow:0 0 0 .1em rgba(0,0,0,.1)inset;box-shadow:0 0 0 .1em rgba(0,0,0,.1)inset;line-height:1!important;width:2em!important;height:2em!important}i.circular.inverted.icon{border:none;-webkit-box-shadow:none;box-shadow:none}i.flipped.icon,i.horizontally.flipped.icon{-webkit-transform:scale(-1,1);-moz-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}i.vertically.flipped.icon{-webkit-transform:scale(1,-1);-moz-transform:scale(1,-1);-ms-transform:scale(1,-1);transform:scale(1,-1)}i.clockwise.rotated.icon,i.right.rotated.icon,i.rotated.icon{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}i.counterclockwise.rotated.icon,i.left.rotated.icon{-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);transform:rotate(-90deg)}i.square.icon{width:2em;height:2em;padding:.5em .35em!important;-webkit-box-shadow:0 0 0 .1em rgba(0,0,0,.1)inset;box-shadow:0 0 0 .1em rgba(0,0,0,.1)inset;vertical-align:baseline}i.square.inverted.icon{border:none;-webkit-box-shadow:none;box-shadow:none}i.inverted.icon{background-color:#222;color:#FFF;-moz-osx-font-smoothing:grayscale}i.blue.icon{color:#6ECFF5!important}i.black.icon{color:#5C6166!important}i.green.icon{color:#A1CF64!important}i.red.icon{color:#D95C5C!important}i.purple.icon{color:#564F8A!important}i.orange.icon{color:#F05940!important}i.teal.icon{color:#00B5AD!important}i.inverted.black.icon{background-color:#5C6166!important;color:#FFF!important}i.inverted.blue.icon{background-color:#6ECFF5!important;color:#FFF!important}i.inverted.green.icon{background-color:#A1CF64!important;color:#FFF!important}i.inverted.red.icon{background-color:#D95C5C!important;color:#FFF!important}i.inverted.purple.icon{background-color:#564F8A!important;color:#FFF!important}i.inverted.orange.icon{background-color:#F05940!important;color:#FFF!important}i.inverted.teal.icon{background-color:#00B5AD!important;color:#FFF!important}i.small.icon{font-size:.875em}i.icon{font-size:1em}i.large.icon{font-size:1.5em;vertical-align:middle}i.big.icon{font-size:2em;vertical-align:middle}i.huge.icon{font-size:4em;vertical-align:middle}i.massive.icon{font-size:8em;vertical-align:middle}.ui.image{position:relative;display:inline-block;vertical-align:middle;max-width:100%;background-color:rgba(0,0,0,.05)}img.ui.image{display:block;background:0 0}.ui.image img{display:block;max-width:100%;height:auto}.ui.disabled.image{cursor:default;opacity:.3}.ui.rounded.image,.ui.rounded.image img,.ui.rounded.images .image,.ui.rounded.images img{border-radius:.3125em}.ui.circular.image,.ui.circular.image img,.ui.circular.images .image,.ui.circular.images img{border-radius:500rem}.ui.fluid.image,.ui.fluid.image img,.ui.fluid.images,.ui.fluid.images img{display:block;width:100%}.ui.avatar.image,.ui.avatar.image img,.ui.avatar.images .image,.ui.avatar.images img{margin-right:.5em;display:inline-block;width:2em;height:2em;border-radius:500rem}.ui.floated.image,.ui.floated.images{float:left;margin-right:1em;margin-bottom:1em}.ui.right.floated.image,.ui.right.floated.images{float:right;margin-bottom:1em;margin-left:1em}.ui.tiny.image,.ui.tiny.images .image,.ui.tiny.images img{width:20px;font-size:.7rem}.ui.mini.image,.ui.mini.images .image,.ui.mini.images img{width:35px;font-size:.8rem}.ui.small.image,.ui.small.images .image,.ui.small.images img{width:80px;font-size:.9rem}.ui.medium.image,.ui.medium.images .image,.ui.medium.images img{width:300px;font-size:1rem}.ui.large.image,.ui.large.images .image,.ui.large.images img{width:450px;font-size:1.1rem}.ui.huge.image,.ui.huge.images .image,.ui.huge.images img{width:600px;font-size:1.2rem}.ui.images{font-size:0;margin:0 -.25rem 0}.ui.images .image,.ui.images img{display:inline-block;margin:0 .25em .5em}.ui.input{display:inline-block;position:relative;color:rgba(0,0,0,.7)}.ui.input input{width:100%;font-family:"Helvetica Neue",Helvetica,Arial;margin:0;padding:.65em 1em;font-size:1em;background-color:#FFF;border:1px solid rgba(0,0,0,.15);outline:0;color:rgba(0,0,0,.7);border-radius:.3125em;-webkit-transition:background-color .3s ease-out,-webkit-box-shadow .2s ease,border-color .2s ease;-moz-transition:background-color .3s ease-out,box-shadow .2s ease,border-color .2s ease;transition:background-color .3s ease-out,box-shadow .2s ease,border-color .2s ease;-webkit-tap-highlight-color:rgba(255,255,255,0);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.ui.input::-webkit-input-placeholder{color:#BBB}.ui.input::-moz-placeholder{color:#BBB}.ui.input input:active,.ui.input.down input{border-color:rgba(0,0,0,.3);background-color:#FAFAFA}.ui.loading.input>.icon{background:url(../images/loader-mini.gif) no-repeat 50% 50%}.ui.loading.input>.icon:after,.ui.loading.input>.icon:before{display:none}.ui.input input:focus,.ui.input.focus input{border-color:rgba(0,0,0,.2);color:rgba(0,0,0,.85)}.ui.input input:focus input::-webkit-input-placeholder,.ui.input.focus input input::-webkit-input-placeholder{color:#AAA}.ui.input input:focus input::-moz-placeholder,.ui.input.focus input input::-moz-placeholder{color:#AAA}.ui.input.error input{background-color:snow;border-color:#E7BEBE;color:#D95C5C}.ui.input.error input ::-webkit-input-placeholder{color:rgba(255,80,80,.4)}.ui.input.error input ::-moz-placeholder{color:rgba(255,80,80,.4)}.ui.input.error input :focus::-webkit-input-placeholder{color:rgba(255,80,80,.7)}.ui.input.error input :focus::-moz-placeholder{color:rgba(255,80,80,.7)}.ui.transparent.input input{border:none;background-color:transparent}.ui.icon.input>.icon{cursor:default;position:absolute;opacity:.5;top:0;right:0;margin:0;width:2.6em;height:100%;padding-top:.82em;text-align:center;border-radius:0 .3125em .3125em 0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-transition:opacity .3s ease-out;-moz-transition:opacity .3s ease-out;transition:opacity .3s ease-out}.ui.icon.input>.link.icon{cursor:pointer}.ui.icon.input input{padding-right:3em!important}.ui.icon.input>.circular.icon{top:.35em;right:.5em}.ui.left.icon.input>.icon{right:auto;left:1px;border-radius:.3125em 0 0 .3125em}.ui.left.icon.input>.circular.icon{right:auto;left:.5em}.ui.left.icon.input>input{padding-left:3em!important;padding-right:1.2em!important}.ui.icon.input>input:focus~.icon{opacity:1}.ui.labeled.input .corner.label{font-size:.7em;border-radius:0 .3125em}.ui.labeled.input .left.corner.label{border-radius:.3125em 0}.ui.labeled.input input{padding-right:2.5em!important}.ui.labeled.icon.input:not(.left)>input{padding-right:3.25em!important}.ui.labeled.icon.input:not(.left)>.icon{margin-right:1.25em}.ui.action.input{display:table}.ui.action.input>input{display:table-cell;border-top-right-radius:0!important;border-bottom-right-radius:0!important;border-right:none}.ui.action.input>.button,.ui.action.input>.buttons{display:table-cell;border-top-left-radius:0;border-bottom-left-radius:0;white-space:nowrap}.ui.action.input>.button>.icon,.ui.action.input>.buttons>.button>.icon{display:inline;vertical-align:top}.ui.fluid.action.input{display:table;width:100%}.ui.fluid.action.input>.button{width:.01%}.ui.fluid.input{display:block}.ui.mini.input{font-size:.8125em}.ui.small.input{font-size:.875em}.ui.input{font-size:1em}.ui.large.input{font-size:1.125em}.ui.big.input{font-size:1.25em}.ui.huge.input{font-size:1.375em}.ui.massive.input{font-size:1.5em}.ui.label{display:inline-block;vertical-align:middle;margin:-.25em .25em 0;background-color:#E8E8E8;border-color:#E8E8E8;padding:.5em .8em;color:rgba(0,0,0,.65);text-transform:uppercase;font-weight:400;border-radius:.325em;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-transition:background .1s linear;-moz-transition:background .1s linear;transition:background .1s linear}.ui.label:first-child{margin-left:0}.ui.label:last-child{margin-right:0}a.ui.label{cursor:pointer}.ui.label a{cursor:pointer;color:inherit;opacity:.8;-webkit-transition:.2s opacity ease;-moz-transition:.2s opacity ease;transition:.2s opacity ease}.ui.label a:hover{opacity:1}.ui.label .detail{display:inline-block;margin-left:.5em;font-weight:700;opacity:.8}.ui.label .icon{width:auto}.ui.label .delete.icon{cursor:pointer;margin:0 0 0 .5em;opacity:.7;-webkit-transition:background .1s linear;-moz-transition:background .1s linear;transition:background .1s linear}.ui.label .delete.icon:hover{opacity:.99}.ui.segment>.attached.label:first-child+*{margin-top:2.5em}.ui.segment>.bottom.attached.label:first-child~:last-child{margin-top:0;margin-bottom:2.5em}.ui.image.label{width:auto!important;margin-top:0;margin-bottom:0;padding-top:.4em;padding-bottom:.4em;line-height:1.5em;vertical-align:baseline;text-transform:none;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.1)inset;box-shadow:0 0 0 1px rgba(0,0,0,.1)inset}.ui.image.label img{display:inline-block;height:2.25em;margin:-.4em .8em -.4em -.8em;vertical-align:top;border-radius:.325em 0 0 .325em}.ui.label.disabled{opacity:.5}a.ui.label:hover,a.ui.labels .label:hover{background-color:#E0E0E0;border-color:#E0E0E0;color:rgba(0,0,0,.7)}.ui.labels a.label:hover:before,a.ui.label:hover:before{background-color:#E0E0E0;color:rgba(0,0,0,.7)}.ui.label.visible,.ui.labels.visible .label{display:inline-block!important}.ui.label.hidden,.ui.labels.hidden .label{display:none!important}.ui.tag.label,.ui.tag.labels .label{margin-left:1em;position:relative;padding:.33em 1.3em .33em 1.4em;border-radius:0 3px 3px 0}.ui.tag.label:before,.ui.tag.labels .label:before{position:absolute;top:.3em;left:.3em;content:'';margin-left:-1em;background-image:none;width:1.5em;height:1.5em;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);-webkit-transition:background .1s linear;-moz-transition:background .1s linear;transition:background .1s linear}.ui.tag.label:after,.ui.tag.labels .label:after{position:absolute;content:'';top:50%;left:-.25em;margin-top:-.3em;background-color:#FFF;width:.55em;height:.55em;-webkit-box-shadow:0 -1px 1px 0 rgba(0,0,0,.3);box-shadow:0 -1px 1px 0 rgba(0,0,0,.3);border-radius:100px}.ui.ribbon.label{position:relative;margin:0 .2em;left:-2rem;padding-left:2rem;border-radius:0 4px 4px 0;border-color:rgba(0,0,0,.15)}.ui.ribbon.label:after{position:absolute;content:"";top:100%;left:0;border-top:0 solid transparent;border-right-width:1em;border-right-color:inherit;border-right-style:solid;border-bottom:1em solid transparent;border-left:0 solid transparent;width:0;height:0}.ui.attached.label,.ui.top.attached.label{width:100%;position:absolute;margin:0;top:0;left:0;padding:.75em 1em;border-radius:4px 4px 0 0}.ui.bottom.attached.label{top:auto;bottom:0;border-radius:0 0 4px 4px}.ui.top.left.attached.label{width:auto;margin-top:0!important;border-radius:4px 0}.ui.top.right.attached.label{width:auto;left:auto;right:0;border-radius:0 4px}.ui.bottom.left.attached.label{width:auto;top:auto;bottom:0;border-radius:4px 0 0 4px}.ui.bottom.right.attached.label{top:auto;bottom:0;left:auto;right:0;width:auto;border-radius:4px 0}.ui.corner.label{background-color:transparent;position:absolute;top:0;right:0;z-index:10;margin:0;width:3em;height:3em;padding:0;text-align:center;-webkit-transition:color .2s ease;-moz-transition:color .2s ease;transition:color .2s ease}.ui.corner.label:after{position:absolute;content:"";right:0;top:0;z-index:-1;width:0;height:0;border-top:0 solid transparent;border-right:3em solid transparent;border-bottom:3em solid transparent;border-left:0 solid transparent;border-right-color:inherit;-webkit-transition:border-color .2s ease;-moz-transition:border-color .2s ease;transition:border-color .2s ease}.ui.corner.label .icon{font-size:.875em;margin:.5em 0 0 1.25em}.ui.corner.label .text{display:inline-block;font-weight:700;margin:.5em 0 0 1em;width:2.5em;font-size:.875em;text-align:center;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.ui.input>.ui.corner.label,.ui.rounded.image>.ui.corner.label,.ui.segment>.ui.corner.label{overflow:hidden}.ui.segment>.ui.corner.label{top:-1px;right:-1px}.ui.segment>.ui.left.corner.label{right:auto;left:-1px}.ui.input>.ui.corner.label{top:1px;right:1px}.ui.input>.ui.right.corner.label{right:auto;left:1px}.ui.left.corner.label,.ui.left.corner.label:after{right:auto;left:0}.ui.left.corner.label:after{border-top:3em solid transparent;border-right:3em solid transparent;border-bottom:0 solid transparent;border-left:0 solid transparent;border-top-color:inherit}.ui.left.corner.label .icon{margin:.5em 0 0 -1em}.ui.left.corner.label .text{margin:.5em 0 0 -1em;-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);transform:rotate(-45deg)}.ui.corner.label:hover{background-color:transparent}.ui.fluid.labels>.label,.ui.label.fluid{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.ui.inverted.label,.ui.inverted.labels .label{color:#FFF!important}.ui.black.label,.ui.black.labels .label{background-color:#5C6166!important;border-color:#5C6166!important;color:#FFF!important}.ui.black.label:before,.ui.black.labels .label:before,.ui.labels .black.label:before{background-color:#5C6166!important}a.ui.black.label:hover,a.ui.black.labels .label:hover{background-color:#333!important;border-color:#333!important}.ui.black.labels a.label:hover:before,.ui.labels a.black.label:hover:before,a.ui.black.label:hover:before{background-color:#333!important}.ui.black.corner.label,.ui.black.corner.label:hover{background-color:transparent!important}.ui.black.ribbon.label{border-color:#333!important}.ui.green.label,.ui.green.labels .label{background-color:#A1CF64!important;border-color:#A1CF64!important;color:#FFF!important}.ui.green.label:before,.ui.green.labels .label:before,.ui.labels .green.label:before{background-color:#A1CF64!important}a.ui.green.label:hover,a.ui.green.labels .label:hover{background-color:#89B84C!important;border-color:#89B84C!important}.ui.green.labels a.label:hover:before,.ui.labels a.green.label:hover:before,a.ui.green.label:hover:before{background-color:#89B84C!important}.ui.green.corner.label,.ui.green.corner.label:hover{background-color:transparent!important}.ui.green.ribbon.label{border-color:#89B84C!important}.ui.red.label,.ui.red.labels .label{background-color:#D95C5C!important;border-color:#D95C5C!important;color:#FFF!important}.ui.labels .red.label:before,.ui.red.label:before,.ui.red.labels .label:before{background-color:#D95C5C!important}.ui.red.corner.label,.ui.red.corner.label:hover{background-color:transparent!important}a.ui.red.label:hover,a.ui.red.labels .label:hover{background-color:#DE3859!important;border-color:#DE3859!important;color:#FFF!important}.ui.labels a.red.label:hover:before,.ui.red.labels a.label:hover:before,a.ui.red.label:hover:before{background-color:#DE3859!important}.ui.red.ribbon.label{border-color:#DE3859!important}.ui.blue.label,.ui.blue.labels .label{background-color:#6ECFF5!important;border-color:#6ECFF5!important;color:#FFF!important}.ui.blue.label:before,.ui.blue.labels .label:before,.ui.labels .blue.label:before{background-color:#6ECFF5!important}.ui.blue.labels a.label:hover,a.ui.blue.label:hover,a.ui.blue.labels .label:hover{background-color:#1AB8F3!important;border-color:#1AB8F3!important;color:#FFF!important}.ui.blue.labels a.label:hover:before,.ui.labels a.blue.label:hover:before,a.ui.blue.label:hover:before{background-color:#1AB8F3!important}.ui.blue.corner.label,.ui.blue.corner.label:hover{background-color:transparent!important}.ui.blue.ribbon.label{border-color:#1AB8F3!important}.ui.purple.label,.ui.purple.labels .label{background-color:#564F8A!important;border-color:#564F8A!important;color:#FFF!important}.ui.labels .purple.label:before,.ui.purple.label:before,.ui.purple.labels .label:before{background-color:#564F8A!important}.ui.purple.labels a.label:hover,a.ui.purple.label:hover,a.ui.purple.labels .label:hover{background-color:#3E3773!important;border-color:#3E3773!important;color:#FFF!important}.ui.labels a.purple.label:hover:before,.ui.purple.labels a.label:hover:before,a.ui.purple.label:hover:before{background-color:#3E3773!important}.ui.purple.corner.label,.ui.purple.corner.label:hover{background-color:transparent!important}.ui.purple.ribbon.label{border-color:#3E3773!important}.ui.orange.label,.ui.orange.labels .label{background-color:#F05940!important;border-color:#F05940!important;color:#FFF!important}.ui.labels .orange.label:before,.ui.orange.label:before,.ui.orange.labels .label:before{background-color:#F05940!important}.ui.orange.labels a.label:hover,a.ui.orange.label:hover,a.ui.orange.labels .label:hover{background-color:#FF4121!important;border-color:#FF4121!important;color:#FFF!important}.ui.labels a.orange.label:hover:before,.ui.orange.labels a.label:hover:before,a.ui.orange.label:hover:before{background-color:#FF4121!important}.ui.orange.corner.label,.ui.orange.corner.label:hover{background-color:transparent!important}.ui.orange.ribbon.label{border-color:#FF4121!important}.ui.teal.label,.ui.teal.labels .label{background-color:#00B5AD!important;border-color:#00B5AD!important;color:#FFF!important}.ui.labels .teal.label:before,.ui.teal.label:before,.ui.teal.labels .label:before{background-color:#00B5AD!important}.ui.teal.labels a.label:hover,a.ui.teal.label:hover,a.ui.teal.labels .label:hover{background-color:#009A93!important;border-color:#009A93!important;color:#FFF!important}.ui.labels a.teal.label:hover:before,.ui.teal.labels a.label:hover:before,a.ui.teal.label:hover:before{background-color:#009A93!important}.ui.teal.corner.label,.ui.teal.corner.label:hover{background-color:transparent!important}.ui.teal.ribbon.label{border-color:#009A93!important}.ui.horizontal.label,.ui.horizontal.labels .label{margin:-.125em .5em -.125em 0;padding:.35em 1em;min-width:6em;text-align:center}.ui.circular.label,.ui.circular.labels .label{min-height:1em;max-height:2em;padding:.5em!important;line-height:1em;text-align:center;border-radius:500rem}.ui.pointing.label{position:relative}.ui.attached.pointing.label{position:absolute}.ui.pointing.label:before{position:absolute;content:"";width:.6em;height:.6em;background-image:none;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);z-index:2;-webkit-transition:background .1s linear;-moz-transition:background .1s linear;transition:background .1s linear;background-color:#E8E8E8}.ui.pointing.above.label,.ui.pointing.label{margin-top:1em}.ui.pointing.above.label:before,.ui.pointing.label:before{margin-left:-.3em;top:-.3em;left:50%}.ui.pointing.below.label{margin-top:0;margin-bottom:1em}.ui.pointing.below.label:before{margin-left:-.3em;top:auto;right:auto;bottom:-.3em;left:50%}.ui.pointing.left.label{margin-top:0;margin-left:1em}.ui.pointing.left.label:before{margin-top:-.3em;bottom:auto;right:auto;top:50%;left:0}.ui.pointing.right.label{margin-top:0;margin-right:1em}.ui.pointing.right.label:before{margin-top:-.3em;right:-.3em;top:50%;bottom:auto;left:auto}.ui.floating.label{position:absolute;z-index:100;top:-1em;left:100%;margin:0 0 0 -1.5em!important}.ui.small.label,.ui.small.labels .label{font-size:.75rem}.ui.label{font-size:.8125rem}.ui.large.label,.ui.large.labels .label{font-size:.875rem}.ui.huge.label,.ui.huge.labels .label{font-size:1rem}.ui.loader{display:none;position:absolute;top:50%;left:50%;margin:0;z-index:1000;-webkit-transform:translateX(-50%) translateY(-50%);-moz-transform:translateX(-50%) translateY(-50%);-ms-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%)}.ui.dimmer .loader{display:block}.ui.text.loader{width:auto!important;height:auto!important;text-align:center;font-style:normal}.ui.mini.text.loader{min-width:16px;padding-top:2em;font-size:.875em}.ui.small.text.loader{min-width:24px;padding-top:2.5em;font-size:.875em}.ui.text.loader{min-width:32px;font-size:1em;padding-top:3em}.ui.large.text.loader{min-width:64px;padding-top:5em;font-size:1.2em}.ui.loader.active,.ui.loader.visible{display:block}.ui.loader.disabled,.ui.loader.hidden{display:none}.ui.dimmer .ui.text.loader,.ui.inverted.text.loader{color:rgba(255,255,255,.8)}.ui.inverted.dimmer .ui.text.loader{color:rgba(0,0,0,.8)}.ui.dimmer .mini.ui.loader,.ui.inverted .mini.ui.loader{background-image:url(../images/loader-mini-inverted.gif)}.ui.dimmer .small.ui.loader,.ui.inverted .small.ui.loader{background-image:url(../images/loader-small-inverted.gif)}.ui.dimmer .ui.loader,.ui.inverted.loader{background-image:url(../images/loader-medium-inverted.gif)}.ui.dimmer .large.ui.loader,.ui.inverted .large.ui.loader{background-image:url(../images/loader-large-inverted.gif)}.ui.inverted.dimmer .ui.mini.loader,.ui.mini.loader{width:16px;height:16px;background-image:url(../images/loader-mini.gif)}.ui.inverted.dimmer .ui.small.loader,.ui.small.loader{width:24px;height:24px;background-image:url(../images/loader-small.gif)}.ui.inverted.dimmer .ui.loader,.ui.loader{width:32px;height:32px;background:url(../images/loader-medium.gif) no-repeat;background-position:48% 0}.ui.inverted.dimmer .ui.loader.large,.ui.loader.large{width:64px;height:64px;background-image:url(../images/loader-large.gif)}.ui.inline.loader{position:static;vertical-align:middle;margin:0;-webkit-transform:none;-moz-transform:none;-ms-transform:none;transform:none}.ui.inline.loader.active,.ui.inline.loader.visible{display:inline-block}.ui.progress{border:1px solid rgba(0,0,0,.1);width:100%;height:35px;background-color:#FAFAFA;padding:5px;border-radius:.3125em;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.ui.progress .bar{display:inline-block;height:100%;background-color:#CCC;border-radius:3px;-webkit-transition:width 1s ease-in-out,background-color 1s ease-out;-moz-transition:width 1s ease-in-out,background-color 1s ease-out;transition:width 1s ease-in-out,background-color 1s ease-out}.ui.successful.progress .bar{background-color:#73E064!important}.ui.successful.progress .bar,.ui.successful.progress .bar::after{-webkit-animation:none!important;-moz-animation:none!important;animation:none!important}.ui.warning.progress .bar{background-color:#E96633!important}.ui.warning.progress .bar,.ui.warning.progress .bar::after{-webkit-animation:none!important;-moz-animation:none!important;animation:none!important}.ui.failed.progress .bar{background-color:#DF9BA4!important}.ui.failed.progress .bar,.ui.failed.progress .bar::after{-webkit-animation:none!important;-moz-animation:none!important;animation:none!important}.ui.active.progress .bar{position:relative}.ui.active.progress .bar::after{content:'';opacity:0;position:absolute;top:0;left:0;right:0;bottom:0;background:#FFF;border-radius:3px;-webkit-animation:progress-active 2s ease-out infinite;-moz-animation:progress-active 2s ease-out infinite;animation:progress-active 2s ease-out infinite}@-webkit-keyframes progress-active{0%{opacity:0;width:0}50%{opacity:.3}100%{opacity:0;width:95%}}@-moz-keyframes progress-active{0%{opacity:0;width:0}50%{opacity:.3}100%{opacity:0;width:100%}}@keyframes progress-active{0%{opacity:0;width:0}50%{opacity:.3}100%{opacity:0;width:100%}}.ui.disabled.progress{opacity:.35}.ui.disabled.progress .bar,.ui.disabled.progress .bar::after{-webkit-animation:none!important;-moz-animation:none!important;animation:none!important}.ui.progress.attached{position:relative;border:none}.ui.progress.attached,.ui.progress.attached .bar{display:block;height:3px;padding:0;overflow:hidden;border-radius:0 0 .3125em .3125em}.ui.progress.attached .bar{border-radius:0}.ui.progress.top.attached,.ui.progress.top.attached .bar{top:-2px;border-radius:.3125em .3125em 0 0}.ui.progress.top.attached .bar{border-radius:0}.ui.blue.progress .bar{background-color:#6ECFF5}.ui.black.progress .bar{background-color:#5C6166}.ui.green.progress .bar{background-color:#A1CF64}.ui.red.progress .bar{background-color:#EF4D6D}.ui.purple.progress .bar{background-color:#564F8A}.ui.teal.progress .bar{background-color:#00B5AD}.ui.progress.striped .bar{-webkit-background-size:30px 30px;background-size:30px 30px;background-image:-webkit-gradient(linear,left top,right bottom,color-stop(0.25,rgba(255,255,255,.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,.15)),color-stop(0.75,rgba(255,255,255,.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(135deg,rgba(255,255,255,.15)25%,transparent 25%,transparent 50%,rgba(255,255,255,.15)50%,rgba(255,255,255,.15)75%,transparent 75%,transparent);background-image:-moz-linear-gradient(135deg,rgba(255,255,255,.15)25%,transparent 25%,transparent 50%,rgba(255,255,255,.15)50%,rgba(255,255,255,.15)75%,transparent 75%,transparent);background-image:-webkit-linear-gradient(315deg,rgba(255,255,255,.15)25%,transparent 25%,transparent 50%,rgba(255,255,255,.15)50%,rgba(255,255,255,.15)75%,transparent 75%,transparent);background-image:-moz-linear-gradient(315deg,rgba(255,255,255,.15)25%,transparent 25%,transparent 50%,rgba(255,255,255,.15)50%,rgba(255,255,255,.15)75%,transparent 75%,transparent);background-image:linear-gradient(135deg,rgba(255,255,255,.15)25%,transparent 25%,transparent 50%,rgba(255,255,255,.15)50%,rgba(255,255,255,.15)75%,transparent 75%,transparent)}.ui.progress.active.striped .bar:after{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}.ui.progress.active.striped .bar{-webkit-animation:progress-striped 3s linear infinite;-moz-animation:progress-striped 3s linear infinite;animation:progress-striped 3s linear infinite}@-webkit-keyframes progress-striped{0%{background-position:0 0}100%{background-position:60px 0}}@-moz-keyframes progress-striped{0%{background-position:0 0}100%{background-position:60px 0}}@keyframes progress-striped{0%{background-position:0 0}100%{background-position:60px 0}}.ui.small.progress .bar{height:14px}.ui.reveal{display:inline-block;position:relative!important;z-index:2!important;font-size:0!important}.ui.reveal>.content{font-size:1rem!important}.ui.reveal>.visible.content{position:absolute!important;top:0!important;left:0!important;z-index:4!important;-webkit-transition:all .8s cubic-bezier(0.175,.885,.32,1) .15s;-moz-transition:all .8s cubic-bezier(0.175,.885,.32,1) .15s;transition:all .8s cubic-bezier(0.175,.885,.32,1) .15s}.ui.reveal>.hidden.content{position:relative!important;z-index:3!important}.ui.reveal.button{overflow:hidden}.ui.slide.reveal{position:relative!important;display:block;overflow:hidden!important;white-space:nowrap}.ui.slide.reveal>.content{display:block;float:left;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;margin:0;-webkit-transition:top .8s cubic-bezier(0.175,.885,.32,1) .15s,left .8s cubic-bezier(0.175,.885,.32,1) .15s,right .8s cubic-bezier(0.175,.885,.32,1) .15s,bottom .8s cubic-bezier(0.175,.885,.32,1) .15s;-moz-transition:top .8s cubic-bezier(0.175,.885,.32,1) .15s,left .8s cubic-bezier(0.175,.885,.32,1) .15s,right .8s cubic-bezier(0.175,.885,.32,1) .15s,bottom .8s cubic-bezier(0.175,.885,.32,1) .15s;transition:top .8s cubic-bezier(0.175,.885,.32,1) .15s,left .8s cubic-bezier(0.175,.885,.32,1) .15s,right .8s cubic-bezier(0.175,.885,.32,1) .15s,bottom .8s cubic-bezier(0.175,.885,.32,1) .15s}.ui.slide.reveal>.visible.content{position:relative!important}.ui.slide.reveal>.hidden.content{position:absolute!important;left:100%!important;width:100%!important}.ui.slide.reveal:focus>.visible.content,.ui.slide.reveal:hover>.visible.content{left:-100%!important}.ui.slide.reveal:focus>.hidden.content,.ui.slide.reveal:hover>.hidden.content{left:0!important}.ui.right.slide.reveal>.visible.content{left:0}.ui.right.slide.reveal>.hidden.content{left:auto!important;right:100%!important}.ui.right.slide.reveal:focus>.visible.content,.ui.right.slide.reveal:hover>.visible.content{left:100%!important;right:auto!important}.ui.right.slide.reveal:focus>.hidden.content,.ui.right.slide.reveal:hover>.hidden.content{left:auto!important;right:0!important}.ui.up.slide.reveal>.visible.content{top:0!important;left:0!important;right:auto!important;bottom:auto!important}.ui.up.slide.reveal>.hidden.content{top:100%!important;left:0!important;right:auto!important;bottom:auto!important}.ui.slide.up.reveal:focus>.visible.content,.ui.slide.up.reveal:hover>.visible.content{top:-100%!important;left:0!important}.ui.slide.up.reveal:focus>.hidden.content,.ui.slide.up.reveal:hover>.hidden.content{top:0!important;left:0!important}.ui.down.slide.reveal>.visible.content{top:auto!important;right:auto!important;bottom:auto!important;bottom:0!important}.ui.down.slide.reveal>.hidden.content{top:auto!important;right:auto!important;bottom:100%!important;left:0!important}.ui.slide.down.reveal:focus>.visible.content,.ui.slide.down.reveal:hover>.visible.content{left:0!important;bottom:-100%!important}.ui.slide.down.reveal:focus>.hidden.content,.ui.slide.down.reveal:hover>.hidden.content{left:0!important;bottom:0!important}.ui.fade.reveal>.hidden.content{-webkit-transition:opacity .8s cubic-bezier(0.175,.885,.32,1) .15s;-moz-transition:opacity .8s cubic-bezier(0.175,.885,.32,1) .15s;transition:opacity .8s cubic-bezier(0.175,.885,.32,1) .15s;z-index:5!important;opacity:0}.ui.fade.reveal:hover>.hidden.content{opacity:1}.ui.move.left.reveal>.visible.content,.ui.move.reveal>.visible.content{left:auto!important;top:auto!important;bottom:auto!important;right:0!important}.ui.move.left.reveal:focus>.visible.content,.ui.move.left.reveal:hover>.visible.content,.ui.move.reveal:focus>.visible.content,.ui.move.reveal:hover>.visible.content{right:100%!important}.ui.move.right.reveal>.visible.content{right:auto!important;top:auto!important;bottom:auto!important;left:0!important}.ui.move.right.reveal:focus>.visible.content,.ui.move.right.reveal:hover>.visible.content{left:100%!important}.ui.move.up.reveal>.visible.content{right:auto!important;left:auto!important;top:auto!important;bottom:0!important}.ui.move.up.reveal:focus>.visible.content,.ui.move.up.reveal:hover>.visible.content{bottom:100%!important}.ui.move.down.reveal>.visible.content{right:auto!important;left:auto!important;top:0!important;bottom:auto!important}.ui.move.down.reveal:focus>.visible.content,.ui.move.down.reveal:hover>.visible.content{top:100%!important}.ui.rotate.reveal>.visible.content{-webkit-transition-duration:.8s;-moz-transition-duration:.8s;transition-duration:.8s;-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg)}.ui.rotate.reveal>.visible.content,.ui.rotate.right.reveal>.visible.content{-webkit-transform-origin:bottom right;-moz-transform-origin:bottom right;-ms-transform-origin:bottom right;transform-origin:bottom right}.ui.rotate.reveal:focus>.visible.content,.ui.rotate.reveal:hover>.visible.content,.ui.rotate.right.reveal:focus>.visible.content,.ui.rotate.right.reveal:hover>.visible.content{-webkit-transform:rotate(110deg);-moz-transform:rotate(110deg);-ms-transform:rotate(110deg);transform:rotate(110deg)}.ui.rotate.left.reveal>.visible.content{-webkit-transform-origin:bottom left;-moz-transform-origin:bottom left;-ms-transform-origin:bottom left;transform-origin:bottom left}.ui.rotate.left.reveal:focus>.visible.content,.ui.rotate.left.reveal:hover>.visible.content{-webkit-transform:rotate(-110deg);-moz-transform:rotate(-110deg);-ms-transform:rotate(-110deg);transform:rotate(-110deg)}.ui.disabled.reveal{opacity:1!important}.ui.disabled.reveal>.content{-webkit-transition:none!important;-moz-transition:none!important;transition:none!important}.ui.disabled.reveal:focus>.visible.content,.ui.disabled.reveal:hover>.visible.content{position:static!important;display:block!important;opacity:1!important;top:0!important;left:0!important;right:auto!important;bottom:auto!important;-webkit-transform:none!important;-moz-transform:none!important;-ms-transform:none!important;transform:none!important}.ui.disabled.reveal:focus>.hidden.content,.ui.disabled.reveal:hover>.hidden.content{display:none!important}.ui.masked.reveal{overflow:hidden}.ui.instant.reveal>.content{-webkit-transition-delay:0s!important;-moz-transition-delay:0s!important;transition-delay:0s!important}.ui.segment{position:relative;background-color:#FFF;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.1);box-shadow:0 0 0 1px rgba(0,0,0,.1);margin:1em 0;padding:1em;border-radius:5px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.ui.segment:first-child{margin-top:0}.ui.segment:last-child{margin-bottom:0}.ui.segment:after{content:'';display:block;height:0;clear:both;visibility:hidden}.ui.vertical.segment{margin:0;padding-left:0;padding-right:0;background-color:transparent;border-radius:0;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.1);box-shadow:0 1px 0 rgba(0,0,0,.1)}.ui.vertical.segment:first-child{padding-top:0}.ui.horizontal.segment{margin:0;padding-top:0;padding-bottom:0;background-color:transparent;border-radius:0;-webkit-box-shadow:1px 0 0 rgba(0,0,0,.1);box-shadow:1px 0 0 rgba(0,0,0,.1)}.ui.horizontal.segment:first-child{padding-left:0}.ui.pointing.menu~.ui.attached.segment{top:1px}.ui.page.grid.segment .ui.grid .ui.segment.column{padding-top:2rem;padding-bottom:2rem}.ui.grid .ui.segment.column,.ui.grid .ui.segment.row,.ui.grid.segment{border-radius:0;-webkit-box-shadow:none;box-shadow:none;border:none}.ui.segment>:first-child{margin-top:0}.ui.segment>:last-child{margin-bottom:0}.ui.piled.segment{margin:2em 0;-webkit-box-shadow:0 0 1px 1px rgba(0,0,0,.15);-ms-box-shadow:0 0 1px 1px rgba(0,0,0,.15);-o-box-shadow:0 0 1px 1px rgba(0,0,0,.15);box-shadow:0 0 1px 1px rgba(0,0,0,.15)}.ui.piled.segment:first-child{margin-top:0}.ui.piled.segment:last-child{margin-bottom:0}.ui.piled.segment:after,.ui.piled.segment:before{background-color:#FFF;visibility:visible;content:"";display:block;height:100%;left:-1px;position:absolute;width:100%;-webkit-box-shadow:0 0 1px 1px rgba(0,0,0,.1);box-shadow:0 0 1px 1px rgba(0,0,0,.1)}.ui.piled.segment:after{-webkit-transform:rotate(1.2deg);-moz-transform:rotate(1.2deg);-ms-transform:rotate(1.2deg);transform:rotate(1.2deg);top:0;z-index:-1}.ui.piled.segment:before{-webkit-transform:rotate(-1.2deg);-moz-transform:rotate(-1.2deg);-ms-transform:rotate(-1.2deg);transform:rotate(-1.2deg);top:0;z-index:-2}.ui.stacked.segment{padding-bottom:1.7em}.ui.stacked.segment:after,.ui.stacked.segment:before{content:'';position:absolute;bottom:-3px;left:0;border-top:1px solid rgba(0,0,0,.1);background-color:rgba(0,0,0,.02);width:100%;height:5px;visibility:visible}.ui.stacked.segment:before{bottom:0}.ui.stacked.inverted.segment:after,.ui.stacked.inverted.segment:before{background-color:rgba(255,255,255,.1);border-top:1px solid rgba(255,255,255,.35)}.ui.circular.segment{display:table-cell;padding:2em;text-align:center;vertical-align:middle;border-radius:500em}.ui.raised.segment{-webkit-box-shadow:0 1px 2px 1px rgba(0,0,0,.1);box-shadow:0 1px 2px 1px rgba(0,0,0,.1)}.ui.disabled.segment{opacity:.8;color:#DDD}.ui.basic.segment{position:relative;background-color:transparent;-webkit-box-shadow:none;box-shadow:none;border-radius:0}.ui.basic.segment:first-child{padding-top:0}.ui.basic.segment:last-child{padding-bottom:0}.ui.fitted.segment{padding:0}.ui.blue.segment{border-top:.2em solid #6ECFF5}.ui.green.segment{border-top:.2em solid #A1CF64}.ui.red.segment{border-top:.2em solid #D95C5C}.ui.orange.segment{border-top:.2em solid #F05940}.ui.purple.segment{border-top:.2em solid #564F8A}.ui.teal.segment{border-top:.2em solid #00B5AD}.ui.inverted.black.segment{background-color:#5C6166!important;color:#FFF!important}.ui.inverted.blue.segment{background-color:#6ECFF5!important;color:#FFF!important}.ui.inverted.green.segment{background-color:#A1CF64!important;color:#FFF!important}.ui.inverted.red.segment{background-color:#D95C5C!important;color:#FFF!important}.ui.inverted.orange.segment{background-color:#F05940!important;color:#FFF!important}.ui.inverted.purple.segment{background-color:#564F8A!important;color:#FFF!important}.ui.inverted.teal.segment{background-color:#00B5AD!important;color:#FFF!important}.ui.left.aligned.segment{text-align:left}.ui.right.aligned.segment{text-align:right}.ui.center.aligned.segment{text-align:center}.ui.justified.segment{text-align:justify;-webkit-hyphens:auto;-moz-hyphens:auto;-ms-hyphens:auto;hyphens:auto}.ui.floated.segment,.ui.left.floated.segment{float:left}.ui.right.floated.segment{float:right}.ui.inverted.segment{border:none;-webkit-box-shadow:none;box-shadow:none}.ui.inverted.segment .segment{color:rgba(0,0,0,.7)}.ui.inverted.segment .inverted.segment{color:#FFF}.ui.inverted.segment,.ui.primary.inverted.segment{background-color:#222;color:#FFF}.ui.primary.segment{background-color:#FFF;color:#555}.ui.secondary.segment{background-color:#FAF9FA;color:#777}.ui.tertiary.segment{background-color:#EBEBEB;color:#B0B0B0}.ui.secondary.inverted.segment{background-color:#555;background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.3)),to(rgba(255,255,255,.3)));background-image:-webkit-linear-gradient(rgba(255,255,255,.3)0,rgba(255,255,255,.3)100%);background-image:-moz-linear-gradient(rgba(255,255,255,.3)0,rgba(255,255,255,.3)100%);background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.3)),to(rgba(255,255,255,.3)));background-image:linear-gradient(rgba(255,255,255,.3)0,rgba(255,255,255,.3)100%);color:#FAFAFA}.ui.tertiary.inverted.segment{background-color:#555;background-image:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.6)),to(rgba(255,255,255,.6)));background-image:-webkit-linear-gradient(rgba(255,255,255,.6)0,rgba(255,255,255,.6)100%);background-image:-moz-linear-gradient(rgba(255,255,255,.6)0,rgba(255,255,255,.6)100%);background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,.6)),to(rgba(255,255,255,.6)));background-image:linear-gradient(rgba(255,255,255,.6)0,rgba(255,255,255,.6)100%);color:#EEE}.ui.segment.attached{top:-1px;bottom:-1px;border-radius:0;margin:0;-webkit-box-shadow:0 0 0 1px #DDD;box-shadow:0 0 0 1px #DDD}.ui.top.attached.segment{top:0;bottom:-1px;margin-top:1em;margin-bottom:0;border-radius:5px 5px 0 0}.ui.segment.top.attached:first-child{margin-top:0}.ui.segment.bottom.attached{top:-1px;bottom:0;margin-top:0;margin-bottom:1em;border-radius:0 0 5px 5px}.ui.segment.bottom.attached:last-child{margin-bottom:0}.ui.step,.ui.steps .step{display:inline-block;position:relative;padding:1em 2em 1em 3em;vertical-align:top;background-color:#FFF;color:#888;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.ui.step:after,.ui.steps .step:after{position:absolute;z-index:2;content:'';top:.42em;right:-1em;border:medium none;background-color:#FFF;width:2.2em;height:2.2em;-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);transform:rotate(-45deg);-webkit-box-shadow:-1px -1px 0 0 rgba(0,0,0,.15)inset;box-shadow:-1px -1px 0 0 rgba(0,0,0,.15)inset}.ui.step,.ui.steps .step,.ui.steps .step:after{-webkit-transition:background-color .1s ease,opacity .1s ease,color .1s ease,-webkit-box-shadow .1s ease;-moz-transition:background-color .1s ease,opacity .1s ease,color .1s ease,box-shadow .1s ease;transition:background-color .1s ease,opacity .1s ease,color .1s ease,box-shadow .1s ease}.ui.vertical.steps{overflow:visible}.ui.vertical.steps .step{display:block;border-radius:0;padding:1em 2em}.ui.vertical.steps .step:first-child{padding:1em 2em;border-radius:0;border-top-left-radius:.3125rem;border-top-right-radius:.3125rem}.ui.vertical.steps .active.step:first-child{border-top-right-radius:0}.ui.vertical.steps .step:last-child{border-radius:0;border-bottom-left-radius:.3125rem;border-bottom-right-radius:.3125rem}.ui.vertical.steps .active.step:last-child{border-bottom-right-radius:0}.ui.vertical.steps .step:after{display:none}.ui.vertical.steps .active.step:after{display:block}.ui.vertical.steps .two.line.step{line-height:1.3}.ui.vertical.steps .two.line.active.step:after{position:absolute;z-index:2;content:'';top:0;right:-1.45em;background-color:transparent;border-bottom:2.35em solid transparent;border-left:1.55em solid #555;border-top:2.35em solid transparent;width:0;height:0;-webkit-transform:none;-moz-transform:none;-ms-transform:none;transform:none}.ui.steps{cursor:pointer;display:inline-block;font-size:0;overflow:hidden;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.1);box-shadow:0 0 0 1px rgba(0,0,0,.1);line-height:1;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;border-radius:.3125rem}.ui.steps .step:first-child{padding-left:1.35em;border-radius:.3125em 0 0 .3125em}.ui.steps .step:last-child{border-radius:0 .3125em .3125em 0}.ui.steps .step:only-child{border-radius:.3125em}.ui.steps .step:last-child{margin-right:0}.ui.steps .step:last-child:after{display:none}.ui.step.hover,.ui.step:hover{background-color:#F7F7F7;color:rgba(0,0,0,.8)}.ui.step.hover::after,.ui.step:hover,.ui.steps .step.hover:after,.ui.steps .step:hover:after{background-color:#F7F7F7}.ui.step.down,.ui.step:active,.ui.steps .step.down,.ui.steps .step.down:after,.ui.steps .step:active,.ui.steps .step:active:after,.ui.steps.down::after,.ui.steps:active::after{background-color:#F0F0F0}.ui.active.step,.ui.steps .step.active{cursor:auto;background-color:#555;color:#FFF;font-weight:700}.ui.active.steps:after,.ui.steps .step.active:after{background-color:#555;-webkit-box-shadow:none;box-shadow:none}.ui.disabled.step,.ui.steps .disabled.step{cursor:auto;background-color:#FFF;color:#CBCBCB}.ui.disabled.step:after,.ui.steps .disabled.step:after{background-color:#FFF}.attached.ui.steps{margin:0;border-radius:.3125em .3125em 0 0}.attached.ui.steps .step:first-child{border-radius:.3125em 0 0}.attached.ui.steps .step:last-child{border-radius:0 .3125em 0 0}.bottom.attached.ui.steps{margin-top:-1px;border-radius:0 0 .3125em .3125em}.bottom.attached.ui.steps .step:first-child{border-radius:0 0 0 .3125em}.bottom.attached.ui.steps .step:last-child{border-radius:0 0 .3125em}.ui.eight.steps,.ui.five.steps,.ui.four.steps,.ui.one.steps,.ui.seven.steps,.ui.six.steps,.ui.three.steps,.ui.two.steps{display:block}.ui.one.steps>.step{width:100%}.ui.two.steps>.step{width:50%}.ui.three.steps>.step{width:33.333%}.ui.four.steps>.step{width:25%}.ui.five.steps>.step{width:20%}.ui.six.steps>.step{width:16.666%}.ui.seven.steps>.step{width:14.285%}.ui.eight.steps>.step{width:12.5%}.ui.small.step,.ui.small.steps .step{font-size:.8rem}.ui.step,.ui.steps .step{font-size:1rem}.ui.large.step,.ui.large.steps .step{font-size:1.25rem}.ui.accordion,.ui.accordion .accordion{width:600px;max-width:100%;font-size:1rem;border-radius:.3125em;background-color:#FFF;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.1);box-shadow:0 0 0 1px rgba(0,0,0,.1)}.ui.accordion .accordion .title,.ui.accordion .title{cursor:pointer;margin:0;padding:.75em 1em;color:rgba(0,0,0,.6);border-top:1px solid rgba(0,0,0,.05);-webkit-transition:background-color .2s ease-out;-moz-transition:background-color .2s ease-out;transition:background-color .2s ease-out}.ui.accordion .accordion .title:first-child,.ui.accordion .title:first-child{border-top:none}.ui.accordion .accordion .content,.ui.accordion .content{display:none;margin:0;padding:1.3em 1em}.ui.accordion .accordion .title .dropdown.icon,.ui.accordion .title .dropdown.icon{display:inline-block;float:none;margin:0 .5em 0 0;-webkit-transition:-webkit-transform .2s ease,opacity .2s ease;-moz-transition:-moz-transform .2s ease,opacity .2s ease;transition:transform .2s ease,opacity .2s ease;-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg)}.ui.accordion .accordion .title .dropdown.icon:before,.ui.accordion .title .dropdown.icon:before{content:'\f0da'}.ui.basic.accordion.menu{background-color:#FFF;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.1);box-shadow:0 0 0 1px rgba(0,0,0,.1)}.ui.basic.accordion.menu .content,.ui.basic.accordion.menu .title{padding:0}.ui.basic.accordion{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.ui.basic.accordion .accordion .title,.ui.basic.accordion .title{background-color:transparent;border-top:none;padding-left:0;padding-right:0}.ui.basic.accordion .accordion .content,.ui.basic.accordion .content{padding:.5em 0}.ui.basic.accordion .accordion .active.title,.ui.basic.accordion .active.title{background-color:transparent}.ui.accordion .accordion .active.title,.ui.accordion .accordion .title:hover,.ui.accordion .active.title,.ui.accordion .title:hover{color:rgba(0,0,0,.8)}.ui.accordion .accordion .active.title,.ui.accordion .active.title{background-color:rgba(0,0,0,.1);color:rgba(0,0,0,.8)}.ui.accordion .accordion .active.title .dropdown.icon,.ui.accordion .active.title .dropdown.icon{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.ui.accordion .accordion .active.content,.ui.accordion .active.content{display:block}.ui.fluid.accordion,.ui.fluid.accordion .accordion{width:100%}.ui.chatroom{background-color:#F8F8F8;padding:0}.ui.chatroom .room{position:relative;background-color:#FFF;overflow:hidden;height:286px;border:1px solid rgba(0,0,0,.1);border-top:none;border-bottom:none}.ui.chatroom .room .loader{display:none;margin:-25px 0 0 -25px}.ui.chatroom .actions{overflow:hidden;background-color:#EEE;padding:4px;border:1px solid rgba(0,0,0,.1);border-radius:5px 5px 0 0}.ui.chatroom .actions .button{float:right;margin-left:3px}.ui.chatroom .actions .message{float:left;margin-left:6px;font-size:11px;color:#AAA;text-shadow:0 -1px 0 rgba(255,255,255,.8);line-height:28px}.ui.chatroom .actions .message .loader{display:inline-block;margin-right:8px}.ui.chatroom .log{float:left;overflow:auto;overflow-x:hidden;overflow-y:auto}.ui.chatroom .log .message{padding:3px 0;border-top:1px dotted #DADADA}.ui.chatroom .log .message:first-child{border-top:none}.ui.chatroom .status{padding:5px 0;color:#AAA;font-size:12px;font-style:italic;line-height:1.33;border-top:1px dotted #DADADA}.ui.chatroom .log .status:first-child{border-top:none}.ui.chatroom .log .flag{float:left}.ui.chatroom .log p{margin-left:0}.ui.chatroom .log .author{font-weight:700;-webkit-transition:color .3s ease-out;-moz-transition:color .3s ease-out;transition:color .3s ease-out}.ui.chatroom .log a.author:hover{opacity:.8}.ui.chatroom .log .message.admin p{font-weight:700;margin:1px 0 0 23px}.ui.chatroom .log .divider{margin:-1px 0;font-size:11px;padding:10px 0;border-top:1px solid #F8F8F8;border-bottom:1px solid #F8F8F8}.ui.chatroom .log .divider .rule{top:50%;width:15%}.ui.chatroom .log .divider .label{color:#777;margin:0}.ui.chatroom .room .list{position:relative;overflow:auto;overflow-x:hidden;overflow-y:auto;float:left;background-color:#EEE;border-left:1px solid #DDD}.ui.chatroom .room .list .user{display:table;padding:3px 7px;border-bottom:1px solid #DDD}.ui.chatroom .room .list .user:hover{background-color:#F8F8F8}.ui.chatroom .room .list .image{display:table-cell;vertical-align:middle;width:20px}.ui.chatroom .room .list .image img{width:20px;height:20px;vertical-align:middle}.ui.chatroom .room .list p{display:table-cell;vertical-align:middle;padding-left:7px;padding-right:14px;font-size:11px;line-height:1.2;font-weight:700}.ui.chatroom .room .list a:hover{opacity:.8}.ui.chatroom .talk{border:1px solid rgba(0,0,0,.1);padding:5px 0 0;background-color:#EEE;border-radius:0 0 5px 5px}.ui.chatroom .talk .avatar,.ui.chatroom .talk .button,.ui.chatroom .talk input{float:left}.ui.chatroom .talk .avatar img{display:block;width:30px;height:30px;margin-right:4px;border-radius:500rem}.ui.chatroom .talk input{border:1px solid #CCC;margin:0;width:196px;height:14px;padding:8px 5px;font-size:12px;color:#555}.ui.chatroom .talk input.focus{border:1px solid #AAA}.ui.chatroom .send{width:80px;height:32px;margin-left:-1px;padding:4px 12px;font-size:12px;line-height:23px;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.1)inset;box-shadow:0 0 0 1px rgba(0,0,0,.1)inset;border-radius:0 5px 5px 0}.ui.chatroom .talk .log-in.button{display:block;float:none;margin-top:-6px;height:22px;border-radius:0 0 4px 4px}.ui.chatroom .talk .log-in.button i{vertical-align:text-top}.ui.chatroom .log .team.flag{width:18px}.ui.chatroom.loading .loader{display:block}.ui.chatroom{width:330px;height:370px}.ui.chatroom .room .container{width:3000px}.ui.chatroom .log{width:314px;height:278px;padding:4px 7px}.ui.chatroom .room .list{width:124px;height:278px;padding:4px 0}.ui.chatroom .room .list .user{width:110px}.ui.chatroom .talk{height:40px}.ui.checkbox{position:relative;display:inline-block;min-width:1em;min-height:1.25em;line-height:1em;outline:0;vertical-align:middle}.ui.checkbox input{position:absolute;top:0;left:0;opacity:0;outline:0}.ui.checkbox .box,.ui.checkbox label{cursor:pointer;padding-left:2em;outline:0}.ui.checkbox .box:before,.ui.checkbox label:before{position:absolute;top:0;line-height:1;width:1em;height:1em;left:0;content:'';border-radius:4px;background:#FFF;-webkit-transition:background-color .3s ease,-webkit-box-shadow .3s ease;-moz-transition:background-color .3s ease,box-shadow .3s ease;transition:background-color .3s ease,box-shadow .3s ease;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.2);box-shadow:0 0 0 1px rgba(0,0,0,.2)}.ui.checkbox .box:after,.ui.checkbox label:after{-ms-filter:"alpha(Opacity=0)";filter:alpha(opacity=0);opacity:0;content:'';position:absolute;background:0 0;border:.2em solid #333;border-top:none;border-right:none;-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);transform:rotate(-45deg);top:.275em;left:.2em;width:.45em;height:.15em}.ui.checkbox label{display:block;color:rgba(0,0,0,.6);-webkit-transition:color .2s ease;-moz-transition:color .2s ease;transition:color .2s ease}.ui.checkbox input:focus~label,.ui.checkbox label:hover{color:rgba(0,0,0,.8)}.ui.checkbox~label{cursor:pointer;opacity:.85;vertical-align:middle}.ui.checkbox~label:hover{opacity:1}.ui.checkbox .box:hover::before,.ui.checkbox label:hover::before{-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.3);box-shadow:0 0 0 1px rgba(0,0,0,.3)}.ui.checkbox .box:active::before,.ui.checkbox label:active::before{background-color:#F5F5F5}.ui.checkbox input:focus~.box:before,.ui.checkbox input:focus~label:before{-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.3);box-shadow:0 0 0 1px rgba(0,0,0,.3)}.ui.checkbox input:checked~.box:after,.ui.checkbox input:checked~label:after{-ms-filter:"alpha(Opacity=100)";filter:alpha(opacity=100);opacity:1}.ui.checkbox input[disabled]~.box:after,.ui.checkbox input[disabled]~label,.ui.disabled.checkbox label,.ui.disabled.checkbox~.box:after{opacity:.4;color:rgba(0,0,0,.3)}.ui.radio.checkbox .box:before,.ui.radio.checkbox label:before{min-width:1em;height:1em;border-radius:500px;-webkit-transform:none;-moz-transform:none;-ms-transform:none;transform:none}.ui.radio.checkbox .box:after,.ui.radio.checkbox label:after{border:none;top:.2em;left:.2em;width:.6em;height:.6em;background-color:#555;-webkit-transform:none;-moz-transform:none;-ms-transform:none;transform:none;border-radius:500px}.ui.slider.checkbox{cursor:pointer;min-width:3em}.ui.slider.checkbox:after{position:absolute;top:.5em;left:0;content:'';width:3em;height:2px;background-color:rgba(0,0,0,.1)}.ui.slider.checkbox .box,.ui.slider.checkbox label{padding-left:4em}.ui.slider.checkbox .box:before,.ui.slider.checkbox label:before{cursor:pointer;display:block;position:absolute;top:-.25em;left:0;z-index:1;width:1.5em;height:1.5em;-webkit-transform:none;-moz-transform:none;-ms-transform:none;transform:none;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.1)inset;box-shadow:0 0 0 1px rgba(0,0,0,.1)inset;border-radius:50rem;-webkit-transition:left .3s ease 0s;-moz-transition:left .3s ease 0s;transition:left .3s ease 0s}.ui.slider.checkbox .box:after,.ui.slider.checkbox label:after{opacity:1;position:absolute;content:'';top:.15em;left:0;z-index:2;margin-left:.375em;border:none;width:.75em;height:.75em;border-radius:50rem;-webkit-transform:none;-moz-transform:none;-ms-transform:none;transform:none;-webkit-transition:background .3s ease 0s,left .3s ease 0s;-moz-transition:background .3s ease 0s,left .3s ease 0s;transition:background .3s ease 0s,left .3s ease 0s}.ui.slider.checkbox input:checked~.box:after,.ui.slider.checkbox input:checked~.box:before,.ui.slider.checkbox input:checked~label:after,.ui.slider.checkbox input:checked~label:before{left:1.75em}.ui.slider.checkbox .box:after,.ui.slider.checkbox label:after{background-color:#D95C5C}.ui.slider.checkbox input:checked~.box:after,.ui.slider.checkbox input:checked~label:after{background-color:#89B84C}.ui.toggle.checkbox{cursor:pointer}.ui.toggle.checkbox .box,.ui.toggle.checkbox label{padding-left:4em}.ui.toggle.checkbox .box:before,.ui.toggle.checkbox label:before{cursor:pointer;display:block;position:absolute;content:'';top:-.25em;left:0;z-index:1;background-color:#FFF;width:3em;height:1.5em;-webkit-transform:none;-moz-transform:none;-ms-transform:none;transform:none;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.1)inset;box-shadow:0 0 0 1px rgba(0,0,0,.1)inset;border-radius:50rem}.ui.toggle.checkbox .box:after,.ui.toggle.checkbox label:after{opacity:1;-webkit-box-shadow:none;box-shadow:none;content:'';position:absolute;top:.15em;left:.5em;z-index:2;border:none;width:.75em;height:.75em;background-color:#D95C5C;border-radius:50rem;-webkit-transition:background .3s ease 0s,left .3s ease 0s;-moz-transition:background .3s ease 0s,left .3s ease 0s;transition:background .3s ease 0s,left .3s ease 0s}.ui.toggle.checkbox:active .box:before,.ui.toggle.checkbox:active label:before{background-color:#F5F5F5}.ui.toggle.checkbox input:checked~.box:after,.ui.toggle.checkbox input:checked~label:after{left:1.75em;background-color:#89B84C}.ui.checkbox{font-size:1em}.ui.large.checkbox{font-size:1.25em}.ui.huge.checkbox{font-size:1.5em}.ui.dimmable{position:relative}.ui.dimmer{display:none;position:absolute;top:0!important;left:0!important;width:0;height:0;text-align:center;vertical-align:middle;background-color:rgba(0,0,0,.85);opacity:0;line-height:1;-webkit-animation-fill-mode:both;-moz-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.5s;-moz-animation-duration:.5s;animation-duration:.5s;-webkit-transition:background-color .5s linear;-moz-transition:background-color .5s linear;transition:background-color .5s linear;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;z-index:1000}.ui.dimmer>.content{width:100%;height:100%;display:table;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.ui.dimmer>.content>div{display:table-cell;vertical-align:middle;color:#FFF}.ui.horizontal.segment>.ui.dimmer,.ui.segment>.ui.dimmer,.ui.vertical.segment>.ui.dimmer{border-radius:5px}.ui.dimmed.dimmable:not(body){overflow:hidden}.ui.active.dimmer,.ui.dimmed.dimmable>.ui.animating.dimmer,.ui.dimmed.dimmable>.ui.visible.dimmer{display:block;width:100%;height:100%;opacity:1}.ui.disabled.dimmer{width:0!important;height:0!important}.ui.page.dimmer{position:fixed;-webkit-transform-style:preserve-3d;-moz-transform-style:preserve-3d;-ms-transform-style:preserve-3d;transform-style:preserve-3d;-webkit-perspective:2000px;-moz-perspective:2000px;-ms-perspective:2000px;perspective:2000px;-webkit-transform-origin:center center;-moz-transform-origin:center center;-ms-transform-origin:center center;transform-origin:center center}.ui.scrolling.dimmable>.dimmer,.ui.scrolling.page.dimmer{position:absolute}.ui.dimmer>.top.aligned.content>*{vertical-align:top}.ui.dimmer>.bottom.aligned.content>*{vertical-align:bottom}.ui.inverted.dimmer{background-color:rgba(255,255,255,.85)}.ui.inverted.dimmer>.content>*{color:rgba(0,0,0,.8)}.ui.simple.dimmer{display:block;overflow:hidden;opacity:1;z-index:-100;background-color:rgba(0,0,0,0)}.ui.dimmed.dimmable>.ui.simple.dimmer{overflow:visible;opacity:1;width:100%;height:100%;background-color:rgba(0,0,0,.85);z-index:1}.ui.simple.inverted.dimmer{background-color:rgba(255,255,255,0)}.ui.dimmed.dimmable>.ui.simple.inverted.dimmer{background-color:rgba(255,255,255,.85)}.ui.dropdown{cursor:pointer;position:relative;display:inline-block;line-height:1;-webkit-transition:border-radius .1s ease,width .2s ease;-moz-transition:border-radius .1s ease,width .2s ease;transition:border-radius .1s ease,width .2s ease;-webkit-tap-highlight-color:rgba(0,0,0,0);-moz-tap-highlight-color:rgba(0,0,0,0);tap-highlight-color:rgba(0,0,0,0)}.ui.dropdown .menu{cursor:auto;position:absolute;display:none;top:100%;margin:0;background-color:#FFF;min-width:100%;white-space:nowrap;font-size:.875em;text-shadow:none;-webkit-box-shadow:0 0 1px 1px rgba(0,0,0,.1);box-shadow:0 0 1px 1px rgba(0,0,0,.1);border-radius:0 0 .325em .325em;-webkit-transition:opacity .2s ease;-moz-transition:opacity .2s ease;transition:opacity .2s ease;z-index:11}.ui.dropdown>.dropdown.icon{width:auto;margin:0 0 0 1em}.ui.dropdown>.dropdown.icon:before{content:"\f0d7"}.ui.dropdown .menu .item .dropdown.icon{width:auto;float:right;margin:0 0 0 .5em}.ui.dropdown .menu .item .dropdown.icon:before{content:"\f0da"}.ui.dropdown>.text{display:inline-block;-webkit-transition:color .2s ease;-moz-transition:color .2s ease;transition:color .2s ease}.ui.dropdown .menu{left:0}.ui.dropdown .menu .menu{top:0!important;left:100%!important;margin:0!important;border-radius:0 .325em .325em 0!important}.ui.dropdown .menu .menu:after{display:none}.ui.dropdown .menu .item{cursor:pointer;border:none;border-top:1px solid rgba(0,0,0,.05);height:auto;display:block;color:rgba(0,0,0,.75);padding:.85em 1em!important;font-size:.875rem;text-transform:none;font-weight:400;text-align:left;-webkit-touch-callout:none}.ui.dropdown .menu .item:before{display:none}.ui.dropdown .menu .item .icon{margin-right:.75em}.ui.dropdown .menu .item:first-child{border-top:none}.ui.buttons>.ui.dropdown:last-child .menu,.ui.menu .right.menu .dropdown:last-child .menu{left:auto;right:0}.ui.vertical.menu .dropdown.item>.dropdown.icon:before{content:"\f0da"}.ui.dropdown.icon.button>.dropdown.icon{margin:0}.ui.visible.dropdown>.menu{display:block}.ui.dropdown .menu .item:hover{background-color:rgba(0,0,0,.02);z-index:12}.ui.dropdown .menu .active.item{background-color:rgba(0,0,0,.06)!important;border-left:none;border-color:transparent!important;-moz-shadow:none;-webkit-box-shadow:none;box-shadow:none;z-index:12}.ui.default.dropdown>.text,.ui.dropdown>.default.text{color:rgba(0,0,0,.5)}.ui.default.dropdown:hover>.text,.ui.dropdown:hover>.default.text{color:rgba(0,0,0,.8)}.ui.dropdown.error,.ui.dropdown.error>.default.text,.ui.dropdown.error>.text{color:#D95C5C!important}.ui.selection.dropdown.error{background-color:snow;-webkit-box-shadow:0 0 0 1px #e7bebe!important;box-shadow:0 0 0 1px #e7bebe!important}.ui.selection.dropdown.error:hover{-webkit-box-shadow:0 0 0 1px #e7bebe!important;box-shadow:0 0 0 1px #e7bebe!important}.ui.dropdown.error>.menu,.ui.dropdown.error>.menu .menu{-webkit-box-shadow:0 0 1px 1px #E7BEBE!important;box-shadow:0 0 1px 1px #E7BEBE!important}.ui.dropdown.error>.menu .item{color:#D95C5C!important}.ui.dropdown.error>.menu .item:hover{background-color:#FFF2F2!important}.ui.dropdown.error>.menu .active.item{background-color:#FDCFCF!important}.ui.simple.dropdown .menu:after,.ui.simple.dropdown .menu:before{display:none}.ui.simple.dropdown .menu{display:block;overflow:hidden;top:-9999px!important;position:absolute;opacity:0;width:0;height:0;-webkit-transition:opacity .2s ease-out;-moz-transition:opacity .2s ease-out;transition:opacity .2s ease-out}.ui.simple.active.dropdown,.ui.simple.dropdown:hover{border-bottom-left-radius:0!important;border-bottom-right-radius:0!important}.ui.simple.active.dropdown>.menu,.ui.simple.dropdown:hover>.menu{overflow:visible;width:auto;height:auto;top:100%!important;opacity:1}.ui.simple.dropdown:hover>.menu .item:hover>.menu,.ui.simple.dropdown>.menu .item:active>.menu{overflow:visible;width:auto;height:auto;top:0!important;left:100%!important;opacity:1}.ui.simple.disabled.dropdown:hover .menu{display:none;height:0;width:0;overflow:hidden}.ui.selection.dropdown{cursor:pointer;display:inline-block;word-wrap:break-word;white-space:normal;background-color:#FFF;padding:.65em 1em;line-height:1.33;color:rgba(0,0,0,.8);-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.1)!important;box-shadow:0 0 0 1px rgba(0,0,0,.1)!important;border-radius:.3125em!important}.ui.selection.dropdown select{display:none}.ui.selection.dropdown>.dropdown.icon{opacity:.7;margin:.2em 0 .2em 1.25em;-webkit-transition:opacity .2s ease-out;-moz-transition:opacity .2s ease-out;transition:opacity .2s ease-out}.ui.selection.dropdown,.ui.selection.dropdown .menu{-webkit-transition:-webkit-box-shadow .2s ease-out;-moz-transition:box-shadow .2s ease-out;transition:box-shadow .2s ease-out}.ui.selection.dropdown .menu{top:100%;max-height:312px;overflow-x:hidden;overflow-y:auto;-webkit-box-shadow:0 1px 0 1px #E0E0E0;box-shadow:0 1px 0 1px #E0E0E0;border-radius:0 0 .325em .325em}.ui.selection.dropdown .menu:after,.ui.selection.dropdown .menu:before{display:none}.ui.selection.dropdown .menu img{height:2.5em;display:inline-block;vertical-align:middle;margin-right:.5em}.ui.visible.selection.dropdown{border-bottom-left-radius:0!important;border-bottom-right-radius:0!important}.ui.active.selection.dropdown{border-radius:.3125em .3125em 0 0!important}.ui.active.selection.dropdown>.dropdown.icon{opacity:1}.ui.fluid.dropdown{display:block}.ui.fluid.dropdown>.dropdown.icon{float:right}.ui.inline.dropdown{cursor:pointer;display:inline-block;color:inherit}.ui.inline.dropdown .dropdown.icon{margin:0 .5em 0 .25em}.ui.inline.dropdown .text{font-weight:700}.ui.inline.dropdown .menu{cursor:auto;margin-top:.25em;border-radius:.325em}.ui.floating.dropdown .menu{left:0;right:auto;margin-top:.5em!important;border-radius:.325em}.ui.pointing.dropdown .menu{top:100%;margin-top:.75em;border-radius:.325em}.ui.pointing.dropdown .menu:after{display:block;position:absolute;pointer-events:none;content:" ";visibility:visible;width:.5em;height:.5em;-webkit-box-shadow:-1px -1px 0 1px rgba(0,0,0,.05);box-shadow:-1px -1px 0 1px rgba(0,0,0,.05);background-image:none;background-color:#FFF;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);z-index:2}.ui.pointing.dropdown .menu .active.item:first-child{background:transparent -webkit-linear-gradient(transparent,rgba(0,0,0,.03));background:transparent -moz-linear-gradient(transparent,rgba(0,0,0,.03));background:transparent linear-gradient(transparent,rgba(0,0,0,.03))}.ui.pointing.dropdown .menu:after{top:-.25em;left:50%;margin:0 0 0 -.25em}.ui.top.left.pointing.dropdown .menu{top:100%;bottom:auto;left:0;right:auto;margin:.75em 0 0}.ui.top.left.pointing.dropdown .menu:after{top:-.25em;left:1.25em;right:auto;margin:0;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.ui.top.right.pointing.dropdown .menu{top:100%;bottom:auto;right:0;left:auto;margin:.75em 0 0}.ui.top.right.pointing.dropdown .menu:after{top:-.25em;left:auto;right:1.25em;margin:0;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.ui.left.pointing.dropdown .menu{top:0;left:100%;right:auto;margin:0 0 0 .75em}.ui.left.pointing.dropdown .menu:after{top:1em;left:-.25em;margin:0;-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);transform:rotate(-45deg)}.ui.right.pointing.dropdown .menu{top:0;left:auto;right:100%;margin:0 .75em 0 0}.ui.right.pointing.dropdown .menu:after{top:1em;left:auto;right:-.25em;margin:0;-webkit-transform:rotate(135deg);-moz-transform:rotate(135deg);-ms-transform:rotate(135deg);transform:rotate(135deg)}.ui.modal{display:none;position:fixed;z-index:1001;top:50%;left:50%;text-align:left;width:90%;margin-left:-45%;background-color:#FFF;border:1px solid #DDD;border-radius:5px;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.ui.modal>.close{cursor:pointer;position:absolute;z-index:1;opacity:.8;font-size:1.25em;top:-1.75em;right:-1.75em;color:#FFF}.ui.modal>.close:hover{opacity:1}.ui.modal>.header{margin:0;padding:1.5rem 2rem;font-size:1.6em;font-weight:700;border-radius:.325em .325em 0 0}.ui.modal>.content{display:table;width:100%;position:relative;padding:2em;background-color:#F4F4F4;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.ui.modal>.content>.left:not(.ui){display:table-cell;padding-right:1.5%;min-width:25%}.ui.modal>.content>.right:not(.ui){display:table-cell;padding-left:1.5%;vertical-align:top}.ui.modal>.content>.left:not(.ui)>i.icon{font-size:8em;margin:0}.ui.modal>.content p{line-height:1.6}.ui.modal .actions{padding:1rem 2rem;text-align:right}.ui.modal .actions>.button{margin-left:.75em}@media only screen and (max-width:768px){.ui.modal .content>.left:not(.ui){display:block;padding:0 0 1em}.ui.modal .content>.right:not(.ui){display:block;padding:1em 0 0;-webkit-box-shadow:none;box-shadow:none}.ui.modal .content .image{width:auto!important;max-width:100%}.ui.modal .actions{padding-bottom:0}.ui.modal .actions>.button,.ui.modal .actions>.buttons{margin-bottom:1em}}@media only screen and (max-width:998px){.ui.modal{width:92%;margin-left:-46%}.ui.modal>.close{color:rgba(0,0,0,.8);top:1.5rem;right:1rem}}@media only screen and (min-width:998px){.ui.modal{width:74%;margin-left:-37%}}@media only screen and (min-width:1500px){.ui.modal{width:56%;margin-left:-28%}}@media only screen and (min-width:1750px){.ui.modal{width:42%;margin-left:-21%}}@media only screen and (min-width:2000px){.ui.modal{width:36%;margin-left:-18%}}.ui.basic.modal{background-color:transparent;border:none;color:#FFF}.ui.basic.modal>.close{top:1.5rem;right:1rem}.ui.basic.modal .content{background-color:transparent}.ui.modal.scrolling{position:absolute;margin-top:10px}.ui.active.modal{display:block}.ui.small.modal>.header{font-size:1.3em}@media only screen and (min-width:998px){.ui.small.modal{width:58%;margin-left:-29%}}@media only screen and (min-width:1500px){.ui.small.modal{width:40%;margin-left:-20%}}@media only screen and (min-width:1750px){.ui.small.modal{width:26%;margin-left:-13%}}@media only screen and (min-width:2000px){.ui.small.modal{width:20%;margin-left:-10%}}@media only screen and (min-width:998px){.ui.large.modal{width:74%;margin-left:-37%}}@media only screen and (min-width:1500px){.ui.large.modal{width:64%;margin-left:-32%}}@media only screen and (min-width:1750px){.ui.large.modal{width:54%;margin-left:-27%}}@media only screen and (min-width:2000px){.ui.large.modal{width:44%;margin-left:-22%}}.ui.nag{display:none;opacity:.95;position:relative;top:0;left:0;z-index:101;min-height:0;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;margin:0;line-height:3em;padding:0 1em;background-color:#555;-webkit-box-shadow:0 1px 2px 0 rgba(0,0,0,.2);box-shadow:0 1px 2px 0 rgba(0,0,0,.2);font-size:1em;text-align:center;color:rgba(255,255,255,.8);border-radius:0 0 5px 5px;-webkit-transition:.2s background;-moz-transition:.2s background;transition:.2s background}a.ui.nag{cursor:pointer}.ui.nag>.title{display:inline-block;margin:0 .5em;color:#FFF}.ui.nag>.close.icon{cursor:pointer;opacity:.4;position:absolute;top:50%;right:1em;margin-top:-.5em;color:#FFF;-webkit-transition:.1s opacity;-moz-transition:.1s opacity;transition:.1s opacity}.ui.nag .close:hover,.ui.nag:hover{opacity:1}.ui.overlay.nag{position:absolute;display:block}.ui.fixed.nag{position:fixed}.ui.bottom.nag{border-radius:5px 5px 0 0}.ui.fixed.bottom.nag,.ui.fixed.bottom.nags{top:auto;bottom:0}.ui.white.nag,.ui.white.nags .nag{background-color:#F1F1F1;text-shadow:0 1px 0 rgba(255,255,255,.8);color:#ACACAC}.ui.white.nag .close,.ui.white.nag .title,.ui.white.nags .nag .close,.ui.white.nags .nag .title{color:#333}.ui.nags .nag{border-radius:0}.ui.popup{display:none;position:absolute;top:0;right:0;z-index:900;border:1px solid rgba(0,0,0,.1);max-width:250px;background-color:#FFF;padding:.8em 1.2em;font-size:.875rem;font-weight:400;font-style:normal;color:rgba(0,0,0,.7);border-radius:.2em;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.1);box-shadow:0 1px 2px rgba(0,0,0,.1)}.ui.popup .header{padding:0 0 .5em;font-size:1.125em;line-height:1.2;font-weight:700}.ui.popup:before{position:absolute;content:"";width:.75em;height:.75rem;background-image:none;background-color:#FFF;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);z-index:2;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-box-shadow:1px 1px 1px rgba(0,0,0,.1);box-shadow:1px 1px 1px rgba(0,0,0,.1)}.ui.popup .ui.button{width:100%}.ui.popup{margin:0}.ui.popup.bottom{margin:.75em 0 0}.ui.popup.top{margin:0 0 .75em}.ui.popup.left.center{margin:0 .75em 0 0}.ui.popup.right.center{margin:0 0 0 .75em}.ui.popup.center{margin-left:-1.25em}.ui.bottom.center.popup:before{margin-left:-.4em;top:-.4em;left:50%;right:auto;bottom:auto;-webkit-box-shadow:-1px -1px 1px rgba(0,0,0,.2);box-shadow:-1px -1px 1px rgba(0,0,0,.2)}.ui.bottom.left.popup{margin-right:-2em}.ui.bottom.left.popup:before{top:-.4em;right:1em;bottom:auto;left:auto;margin-left:0;-webkit-box-shadow:-1px -1px 1px rgba(0,0,0,.2);box-shadow:-1px -1px 1px rgba(0,0,0,.2)}.ui.bottom.right.popup{margin-left:-2em}.ui.bottom.right.popup:before{top:-.4em;left:1em;right:auto;bottom:auto;margin-left:0;-webkit-box-shadow:-1px -1px 1px rgba(0,0,0,.2);box-shadow:-1px -1px 1px rgba(0,0,0,.2)}.ui.top.center.popup:before{top:auto;right:auto;bottom:-.4em;left:50%;margin-left:-.4em}.ui.top.left.popup{margin-right:-2em}.ui.top.left.popup:before{bottom:-.4em;right:1em;top:auto;left:auto;margin-left:0}.ui.top.right.popup{margin-left:-2em}.ui.top.right.popup:before{bottom:-.4em;left:1em;top:auto;right:auto;margin-left:0}.ui.left.center.popup:before{top:50%;right:-.35em;bottom:auto;left:auto;margin-top:-.4em;-webkit-box-shadow:1px -1px 1px rgba(0,0,0,.2);box-shadow:1px -1px 1px rgba(0,0,0,.2)}.ui.right.center.popup:before{top:50%;left:-.35em;bottom:auto;right:auto;margin-top:-.4em;-webkit-box-shadow:-1px 1px 1px rgba(0,0,0,.2);box-shadow:-1px 1px 1px rgba(0,0,0,.2)}.ui.loading.popup{display:block;visibility:hidden}.ui.animating.popup,.ui.visible.popup{display:block}.ui.small.popup{font-size:.75rem}.ui.large.popup{font-size:1rem}.ui.inverted.popup{background-color:#333;border:none;color:#FFF;-webkit-box-shadow:none;box-shadow:none}.ui.inverted.popup .header{background-color:rgba(0,0,0,.2);color:#FFF}.ui.inverted.popup:before{background-color:#333;-webkit-box-shadow:none;box-shadow:none}.ui.rating{display:inline-block;font-size:0;vertical-align:middle;margin:0 .5rem 0 0}.ui.rating:last-child{margin-right:0}.ui.rating:before{display:block;content:'';visibility:hidden;clear:both;height:0}.ui.rating .icon{cursor:pointer;margin:0;width:1em;height:auto;padding:0;color:rgba(0,0,0,.15);font-weight:400;font-style:normal}.ui.rating .icon:before{content:"\2605"}.ui.star.rating .icon{width:1.2em}.ui.star.rating .icon:before{content:'\f006';font-family:Icons}.ui.star.rating .active.icon:before{content:'\f005';font-family:Icons}.ui.heart.rating .icon{width:1.2em}.ui.heart.rating .icon:before{content:'\f08a';font-family:Icons}.ui.heart.rating .active.icon:before{content:'\f004';font-family:Icons}.ui.heart.rating .active.icon{color:#EF404A!important}.ui.heart.rating .active.hover.icon,.ui.heart.rating .hover.icon{color:#FF2733!important}.ui.disabled.rating .icon{cursor:default}.ui.rating .active.icon{color:#FFCB08!important}.ui.rating.hover .active.icon{opacity:.5}.ui.rating .icon.hover,.ui.rating .icon.hover.active{opacity:1;color:#FFB70A!important}.ui.small.rating .icon{font-size:.75rem}.ui.rating .icon{font-size:1rem}.ui.large.rating .icon{font-size:1.5rem;vertical-align:middle}.ui.huge.rating .icon{font-size:2rem;vertical-align:middle}.ui.search{position:relative;text-shadow:none;font-style:normal;font-weight:400}.ui.search input{border-radius:500rem}.ui.search>.button{position:relative;z-index:2;float:right;margin:0 0 0 -15px;padding:6px 15px 7px;border-radius:0 15px 15px 0;-webkit-box-shadow:none;box-shadow:none}.ui.search .results{display:none;position:absolute;z-index:999;top:100%;left:0;overflow:hidden;background-color:#FFF;margin-top:.5em;width:380px;font-size:.875em;line-height:1.2;color:#555;border-radius:3px;-webkit-box-shadow:0 0 1px 1px rgba(0,0,0,.1),0 -2px 0 0 rgba(0,0,0,.1)inset;box-shadow:0 0 1px 1px rgba(0,0,0,.1),0 -2px 0 0 rgba(0,0,0,.1)inset}.ui.search .result{cursor:pointer;overflow:hidden;padding:.5em 1em}.ui.search .result:first-child{border-top:none}.ui.search .result .image{background:#F0F0F0;margin-right:10px;float:left;overflow:hidden;border-radius:3px;width:38px;height:38px}.ui.search .result .image img{display:block;width:38px;height:38px}.ui.search .result .image~.info{float:none;margin-left:50px}.ui.search .result .info{float:left}.ui.search .result .title{font-weight:700;color:rgba(0,0,0,.8)}.ui.search .result .description{color:rgba(0,0,0,.6)}.ui.search .result .price{float:right;color:#5BBD72;font-weight:700}.ui.search .message{padding:1em}.ui.search .message .text .title{margin:0 0 .5rem;font-size:1.25rem;font-weight:700;color:rgba(0,0,0,.8)}.ui.search .message .text .description{margin:0;font-size:1rem;color:rgba(0,0,0,.5)}.ui.search .results .category{background-color:#FAFAFA;border-top:1px solid rgba(0,0,0,.1);-webkit-transition:background .2s ease-in;-moz-transition:background .2s ease-in;transition:background .2s ease-in}.ui.search .results .category:first-child{border-top:none}.ui.search .results .category>.name{float:left;padding:12px 0 0 8px;font-weight:700;color:#777;text-shadow:0 1px 0 rgba(255,255,255,.8)}.ui.search .results .category .result{background-color:#FFF;margin-left:80px;border-left:1px solid rgba(0,0,0,.1)}.ui.search .all{display:block;border-top:1px solid rgba(0,0,0,.1);background-color:#FAFAFA;height:2em;line-height:2em;color:rgba(0,0,0,.6);font-weight:700;text-align:center}.ui.search .category .result:hover,.ui.search .result:hover{background-color:#F8F8F8}.ui.search .all:hover{background-color:#F0F0F0}.ui.search.loading .input .icon{background:url(../images/loader-mini.gif) no-repeat 50% 50%}.ui.search.loading .input .icon:after,.ui.search.loading .input .icon:before{display:none}.ui.search .results .category.active{background-color:#F1F1F1}.ui.search .results .category.active>.name{color:#333}.ui.search .category .result.active,.ui.search .result.active{background-color:#FBFBFB}.ui.search .result.active .title{color:#000}.ui.search .result.active .description{color:#555}.ui.search .large.result .image,.ui.search .large.result .image img{width:50px;height:50px}.ui.search .large.results .indented.info{margin-left:65px}.ui.search .large.results .info .title{font-size:16px}.ui.search .large.results .info .description{font-size:11px}.ui.shape{display:inline-block;position:relative;-webkit-perspective:2000px;-moz-perspective:2000px;-ms-perspective:2000px;perspective:2000px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.ui.shape .sides{-webkit-transform-style:preserve-3d;-moz-transform-style:preserve-3d;-ms-transform-style:preserve-3d;transform-style:preserve-3d}.ui.shape .side{opacity:1;width:100%;margin:0!important;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;-ms-backface-visibility:hidden;backface-visibility:hidden;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;display:none}.ui.cube.shape .side{min-width:15em;height:15em;padding:2em;background-color:#E6E6E6;color:rgba(0,0,0,.6);-webkit-box-shadow:0 0 2px rgba(0,0,0,.3);box-shadow:0 0 2px rgba(0,0,0,.3)}.ui.cube.shape .side>.content{width:100%;height:100%;display:table;text-align:center;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.ui.cube.shape .side>.content>div{display:table-cell;vertical-align:middle;font-size:2em}.ui.text.shape.animating .sides{position:static}.ui.text.shape .side{white-space:nowrap}.ui.text.shape .side>*{white-space:normal}.ui.loading.shape{position:absolute;top:-9999px;left:-9999px}.ui.shape .animating.side{position:absolute;top:0;left:0;z-index:100}.ui.shape .hidden.side{opacity:.4}.ui.shape.animating{-webkit-transition:all .6s ease-in-out;-moz-transition:all .6s ease-in-out;transition:all .6s ease-in-out}.ui.shape.animating .sides{position:absolute;-webkit-transition:all .6s ease-in-out;-moz-transition:all .6s ease-in-out;transition:all .6s ease-in-out}.ui.shape.animating .side{-webkit-transition:opacity .6s ease-in-out;-moz-transition:opacity .6s ease-in-out;transition:opacity .6s ease-in-out}.ui.shape .active.side{display:block}body{-webkit-transition:margin .3s ease,-webkit-transform .3s ease;-moz-transition:margin .3s ease,-moz-transform .3s ease;transition:margin .3s ease,transform .3s ease}.ui.sidebar{position:fixed;margin:0!important;height:100%!important;border-radius:0!important;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-ms-overflow-y:auto;overflow-y:auto;top:0;left:0;z-index:999;-webkit-transition:margin-left .3s ease,margin-top .3s ease;-moz-transition:margin-left .3s ease,margin-top .3s ease;transition:margin-left .3s ease,margin-top .3s ease}body.pushed.scrolling.ui.dimmable{position:static}.ui.right.sidebar,.ui.right.thin.sidebar,.ui.right.very.thin.sidebar,.ui.right.very.wide.sidebar,.ui.right.wide.sidebar{left:100%;margin:0!important}.ui.top.sidebar{width:100%!important}.ui.bottom.sidebar{width:100%!important;top:100%;margin:0!important}.ui.active.bottom.sidebar,.ui.active.top.sidebar{margin-top:0!important}.ui.styled.sidebar{padding:1em 1.5em;background-color:#FFF;-webkit-box-shadow:1px 0 0 rgba(0,0,0,.1);box-shadow:1px 0 0 rgba(0,0,0,.1)}.ui.styled.very.thin.sidebar{padding:.5em}.ui.styled.thin.sidebar{padding:1em}.ui.floating.sidebar{-webkit-box-shadow:2px 0 2px rgba(0,0,0,.2);box-shadow:2px 0 2px rgba(0,0,0,.2)}.ui.right.floating.sidebar{-webkit-box-shadow:-2px 0 2px rgba(0,0,0,.2);box-shadow:-2px 0 2px rgba(0,0,0,.2)}.ui.top.floating.sidebar{-webkit-box-shadow:0 4px 4px rgba(0,0,0,.2);box-shadow:0 4px 4px rgba(0,0,0,.2)}.ui.bottom.floating.sidebar{-webkit-box-shadow:0 -4px 4px rgba(0,0,0,.2);box-shadow:0 -4px 4px rgba(0,0,0,.2)}.ui.very.thin.sidebar{width:60px!important;margin-left:-60px!important}.ui.active.very.thin.sidebar{margin-left:0!important}.ui.active.right.very.thin.sidebar{margin-left:-60px!important}.ui.thin.sidebar{width:200px!important;margin-left:-200px!important}.ui.active.thin.sidebar{margin-left:0!important}.ui.active.right.thin.sidebar{margin-left:-200px!important}.ui.sidebar{width:275px!important;margin-left:-275px!important}.ui.active.sidebar{margin-left:0!important}.ui.active.right.sidebar{margin-left:-275px!important}.ui.wide.sidebar{width:350px!important;margin-left:-350px!important}.ui.active.wide.sidebar{margin-left:0!important}.ui.active.right.wide.sidebar{margin-left:-350px!important}.ui.very.wide.sidebar{width:475px!important;margin-left:-475px!important}.ui.active.very.wide.sidebar{margin-left:0!important}.ui.active.right.very.wide.sidebar{margin-left:-475px!important}.ui.top.sidebar{margin:-40px 0 0 0!important}.ui.bottom.sidebar,.ui.top.sidebar{height:40px!important}.ui.active.bottom.sidebar{margin-top:-40px!important}.ui.tab{display:none}.ui.tab.active,.ui.tab.open{display:block}.ui.tab.loading{position:relative;overflow:hidden;display:block;min-height:250px;text-indent:-10000px}.ui.tab.loading *{position:relative!important;left:-10000px!important}.ui.tab.loading:after{position:absolute;top:50px;left:50%;content:'Loading...';margin-left:-32px;text-indent:5px;color:rgba(0,0,0,.4);width:100%;height:100%;padding-top:75px;background:url(../images/loader-large.gif) no-repeat 0 0;visibility:visible}.ui.transition{-webkit-animation-iteration-count:1;-moz-animation-iteration-count:1;animation-iteration-count:1;-webkit-animation-duration:1s;-moz-animation-duration:1s;animation-duration:1s;-webkit-animation-timing-function:ease;-moz-animation-timing-function:ease;animation-timing-function:ease;-webkit-animation-fill-mode:both;-moz-animation-fill-mode:both;animation-fill-mode:both}.ui.animating.transition{display:block;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;-ms-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0)}.ui.loading.transition{position:absolute;top:-999999px;left:-99999px}.ui.hidden.transition{display:none!important}.ui.visible.transition{display:block;visibility:visible}.ui.disabled.transition{-webkit-animation-play-state:paused;-moz-animation-play-state:paused;animation-play-state:paused}.ui.looping.transition{-webkit-animation-iteration-count:infinite;-moz-animation-iteration-count:infinite;animation-iteration-count:infinite}.ui.flash.transition{-webkit-animation-name:flash;-moz-animation-name:flash;animation-name:flash}.ui.shake.transition{-webkit-animation-name:shake;-moz-animation-name:shake;animation-name:shake}.ui.bounce.transition{-webkit-animation-name:bounce;-moz-animation-name:bounce;animation-name:bounce}.ui.tada.transition{-webkit-animation-name:tada;-moz-animation-name:tada;animation-name:tada}.ui.pulse.transition{-webkit-animation-name:pulse;-moz-animation-name:pulse;animation-name:pulse}.ui.flip.transition.in,.ui.flip.transition.out{-webkit-perspective:2000px;-moz-perspective:2000px;-ms-perspective:2000px;perspective:2000px}.ui.horizontal.flip.transition.in,.ui.horizontal.flip.transition.out{-webkit-animation-name:horizontalFlip;-moz-animation-name:horizontalFlip;animation-name:horizontalFlip}.ui.horizontal.flip.transition.out{-webkit-animation-name:horizontalFlipOut;-moz-animation-name:horizontalFlipOut;animation-name:horizontalFlipOut}.ui.vertical.flip.transition.in,.ui.vertical.flip.transition.out{-webkit-animation-name:verticalFlip;-moz-animation-name:verticalFlip;animation-name:verticalFlip}.ui.vertical.flip.transition.out{-webkit-animation-name:verticalFlipOut;-moz-animation-name:verticalFlipOut;animation-name:verticalFlipOut}.ui.fade.transition.in{-webkit-animation-name:fade;-moz-animation-name:fade;animation-name:fade}.ui.fade.transition.out{-webkit-animation-name:fadeOut;-moz-animation-name:fadeOut;animation-name:fadeOut}.ui.fade.up.transition.in{-webkit-animation-name:fadeUp;-moz-animation-name:fadeUp;animation-name:fadeUp}.ui.fade.up.transition.out{-webkit-animation-name:fadeUpOut;-moz-animation-name:fadeUpOut;animation-name:fadeUpOut}.ui.fade.down.transition.in{-webkit-animation-name:fadeDown;-moz-animation-name:fadeDown;animation-name:fadeDown}.ui.fade.down.transition.out{-webkit-animation-name:fadeDownOut;-moz-animation-name:fadeDownOut;animation-name:fadeDownOut}.ui.scale.transition.in{-webkit-animation-name:scale;-moz-animation-name:scale;animation-name:scale}.ui.scale.transition.out{-webkit-animation-name:scaleOut;-moz-animation-name:scaleOut;animation-name:scaleOut}.ui.slide.down.transition.in{-webkit-animation-name:slide;-moz-animation-name:slide;animation-name:slide;-moz-transform-origin:50% 0;transform-origin:50% 0;-ms-transform-origin:50% 0;-webkit-transform-origin:50% 0}.ui.slide.down.transition.out{-webkit-animation-name:slideOut;-moz-animation-name:slideOut;animation-name:slideOut;-webkit-transform-origin:50% 0;-moz-transform-origin:50% 0;-ms-transform-origin:50% 0;transform-origin:50% 0}.ui.slide.up.transition.in{-webkit-animation-name:slide;-moz-animation-name:slide;animation-name:slide;-webkit-transform-origin:50% 100%;-moz-transform-origin:50% 100%;-ms-transform-origin:50% 100%;transform-origin:50% 100%}.ui.slide.up.transition.out{-webkit-animation-name:slideOut;-moz-animation-name:slideOut;animation-name:slideOut;-webkit-transform-origin:50% 100%;-moz-transform-origin:50% 100%;-ms-transform-origin:50% 100%;transform-origin:50% 100%}@-webkit-keyframes slide{0%{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}100%{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1)}}@-moz-keyframes slide{0%{opacity:0;-moz-transform:scaleY(0);transform:scaleY(0)}100%{opacity:1;-moz-transform:scaleY(1);transform:scaleY(1)}}@keyframes slide{0%{opacity:0;-webkit-transform:scaleY(0);-moz-transform:scaleY(0);transform:scaleY(0)}100%{opacity:1;-webkit-transform:scaleY(1);-moz-transform:scaleY(1);transform:scaleY(1)}}@-webkit-keyframes slideOut{0%{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1)}100%{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}}@-moz-keyframes slideOut{0%{opacity:1;-moz-transform:scaleY(1);transform:scaleY(1)}100%{opacity:0;-moz-transform:scaleY(0);transform:scaleY(0)}}@keyframes slideOut{0%{opacity:1;-webkit-transform:scaleY(1);-moz-transform:scaleY(1);transform:scaleY(1)}100%{opacity:0;-webkit-transform:scaleY(0);-moz-transform:scaleY(0);transform:scaleY(0)}}@-webkit-keyframes flash{0%,100%,50%{opacity:1}25%,75%{opacity:0}}@-moz-keyframes flash{0%,100%,50%{opacity:1}25%,75%{opacity:0}}@keyframes flash{0%,100%,50%{opacity:1}25%,75%{opacity:0}}@-webkit-keyframes shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}@-moz-keyframes shake{0%,100%{-moz-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-moz-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-moz-transform:translateX(10px);transform:translateX(10px)}}@keyframes shake{0%,100%{-webkit-transform:translateX(0);-moz-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);-moz-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);-moz-transform:translateX(10px);transform:translateX(10px)}}@-webkit-keyframes bounce{0%,100%,20%,50%,80%{-webkit-transform:translateY(0);transform:translateY(0)}40%{-webkit-transform:translateY(-30px);transform:translateY(-30px)}60%{-webkit-transform:translateY(-15px);transform:translateY(-15px)}}@-moz-keyframes bounce{0%,100%,20%,50%,80%{-moz-transform:translateY(0);transform:translateY(0)}40%{-moz-transform:translateY(-30px);transform:translateY(-30px)}60%{-moz-transform:translateY(-15px);transform:translateY(-15px)}}@keyframes bounce{0%,100%,20%,50%,80%{-webkit-transform:translateY(0);-moz-transform:translateY(0);transform:translateY(0)}40%{-webkit-transform:translateY(-30px);-moz-transform:translateY(-30px);transform:translateY(-30px)}60%{-webkit-transform:translateY(-15px);-moz-transform:translateY(-15px);transform:translateY(-15px)}}@-webkit-keyframes tada{0%{-webkit-transform:scale(1);transform:scale(1)}10%,20%{-webkit-transform:scale(0.9) rotate(-3deg);transform:scale(0.9) rotate(-3deg)}30%,50%,70%,90%{-webkit-transform:scale(1.1) rotate(3deg);transform:scale(1.1) rotate(3deg)}40%,60%,80%{-webkit-transform:scale(1.1) rotate(-3deg);transform:scale(1.1) rotate(-3deg)}100%{-webkit-transform:scale(1) rotate(0);transform:scale(1) rotate(0)}}@-moz-keyframes tada{0%{-moz-transform:scale(1);transform:scale(1)}10%,20%{-moz-transform:scale(0.9) rotate(-3deg);transform:scale(0.9) rotate(-3deg)}30%,50%,70%,90%{-moz-transform:scale(1.1) rotate(3deg);transform:scale(1.1) rotate(3deg)}40%,60%,80%{-moz-transform:scale(1.1) rotate(-3deg);transform:scale(1.1) rotate(-3deg)}100%{-moz-transform:scale(1) rotate(0);transform:scale(1) rotate(0)}}@keyframes tada{0%{-webkit-transform:scale(1);-moz-transform:scale(1);transform:scale(1)}10%,20%{-webkit-transform:scale(0.9) rotate(-3deg);-moz-transform:scale(0.9) rotate(-3deg);transform:scale(0.9) rotate(-3deg)}30%,50%,70%,90%{-webkit-transform:scale(1.1) rotate(3deg);-moz-transform:scale(1.1) rotate(3deg);transform:scale(1.1) rotate(3deg)}40%,60%,80%{-webkit-transform:scale(1.1) rotate(-3deg);-moz-transform:scale(1.1) rotate(-3deg);transform:scale(1.1) rotate(-3deg)}100%{-webkit-transform:scale(1) rotate(0);-moz-transform:scale(1) rotate(0);transform:scale(1) rotate(0)}}@-webkit-keyframes pulse{0%{-webkit-transform:scale(1);transform:scale(1);opacity:1}50%{-webkit-transform:scale(0.9);transform:scale(0.9);opacity:.7}100%{-webkit-transform:scale(1);transform:scale(1);opacity:1}}@-moz-keyframes pulse{0%{-moz-transform:scale(1);transform:scale(1);opacity:1}50%{-moz-transform:scale(0.9);transform:scale(0.9);opacity:.7}100%{-moz-transform:scale(1);transform:scale(1);opacity:1}}@keyframes pulse{0%{-webkit-transform:scale(1);-moz-transform:scale(1);transform:scale(1);opacity:1}50%{-webkit-transform:scale(0.9);-moz-transform:scale(0.9);transform:scale(0.9);opacity:.7}100%{-webkit-transform:scale(1);-moz-transform:scale(1);transform:scale(1);opacity:1}}@-webkit-keyframes horizontalFlip{0%{-webkit-transform:rotateY(-90deg);transform:rotateY(-90deg);opacity:0}100%{-webkit-transform:rotateY(0deg);transform:rotateY(0deg);opacity:1}}@-moz-keyframes horizontalFlip{0%{-moz-transform:rotateY(-90deg);transform:rotateY(-90deg);opacity:0}100%{-moz-transform:rotateY(0deg);transform:rotateY(0deg);opacity:1}}@keyframes horizontalFlip{0%{-webkit-transform:rotateY(-90deg);-moz-transform:rotateY(-90deg);transform:rotateY(-90deg);opacity:0}100%{-webkit-transform:rotateY(0deg);-moz-transform:rotateY(0deg);transform:rotateY(0deg);opacity:1}}@-webkit-keyframes horizontalFlipOut{0%{-webkit-transform:rotateY(0deg);transform:rotateY(0deg);opacity:1}100%{-webkit-transform:rotateY(90deg);transform:rotateY(90deg);opacity:0}}@-moz-keyframes horizontalFlipOut{0%{-moz-transform:rotateY(0deg);transform:rotateY(0deg);opacity:1}100%{-moz-transform:rotateY(90deg);transform:rotateY(90deg);opacity:0}}@keyframes horizontalFlipOut{0%{-webkit-transform:rotateY(0deg);-moz-transform:rotateY(0deg);transform:rotateY(0deg);opacity:1}100%{-webkit-transform:rotateY(90deg);-moz-transform:rotateY(90deg);transform:rotateY(90deg);opacity:0}}@-webkit-keyframes verticalFlip{0%{-webkit-transform:rotateX(-90deg);transform:rotateX(-90deg);opacity:0}100%{-webkit-transform:rotateX(0deg);transform:rotateX(0deg);opacity:1}}@-moz-keyframes verticalFlip{0%{-moz-transform:rotateX(-90deg);transform:rotateX(-90deg);opacity:0}100%{-moz-transform:rotateX(0deg);transform:rotateX(0deg);opacity:1}}@keyframes verticalFlip{0%{-webkit-transform:rotateX(-90deg);-moz-transform:rotateX(-90deg);transform:rotateX(-90deg);opacity:0}100%{-webkit-transform:rotateX(0deg);-moz-transform:rotateX(0deg);transform:rotateX(0deg);opacity:1}}@-webkit-keyframes verticalFlipOut{0%{-webkit-transform:rotateX(0deg);transform:rotateX(0deg);opacity:1}100%{-webkit-transform:rotateX(-90deg);transform:rotateX(-90deg);opacity:0}}@-moz-keyframes verticalFlipOut{0%{-moz-transform:rotateX(0deg);transform:rotateX(0deg);opacity:1}100%{-moz-transform:rotateX(-90deg);transform:rotateX(-90deg);opacity:0}}@keyframes verticalFlipOut{0%{-webkit-transform:rotateX(0deg);-moz-transform:rotateX(0deg);transform:rotateX(0deg);opacity:1}100%{-webkit-transform:rotateX(-90deg);-moz-transform:rotateX(-90deg);transform:rotateX(-90deg);opacity:0}}@-webkit-keyframes fade{0%{opacity:0}100%{opacity:1}}@-moz-keyframes fade{0%{opacity:0}100%{opacity:1}}@keyframes fade{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes fadeOut{0%{opacity:1}100%{opacity:0}}@-moz-keyframes fadeOut{0%{opacity:1}100%{opacity:0}}@keyframes fadeOut{0%{opacity:1}100%{opacity:0}}@-webkit-keyframes fadeUp{0%{opacity:0;-webkit-transform:translateY(20px);transform:translateY(20px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@-moz-keyframes fadeUp{0%{opacity:0;-moz-transform:translateY(20px);transform:translateY(20px)}100%{opacity:1;-moz-transform:translateY(0);transform:translateY(0)}}@keyframes fadeUp{0%{opacity:0;-webkit-transform:translateY(20px);-moz-transform:translateY(20px);transform:translateY(20px)}100%{opacity:1;-webkit-transform:translateY(0);-moz-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes fadeUpOut{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(20px);transform:translateY(20px)}}@-moz-keyframes fadeUpOut{0%{opacity:1;-moz-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-moz-transform:translateY(20px);transform:translateY(20px)}}@keyframes fadeUpOut{0%{opacity:1;-webkit-transform:translateY(0);-moz-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(20px);-moz-transform:translateY(20px);transform:translateY(20px)}}@-webkit-keyframes fadeDown{0%{opacity:0;-webkit-transform:translateY(-20px);transform:translateY(-20px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@-moz-keyframes fadeDown{0%{opacity:0;-moz-transform:translateY(-20px);transform:translateY(-20px)}100%{opacity:1;-moz-transform:translateY(0);transform:translateY(0)}}@keyframes fadeDown{0%{opacity:0;-webkit-transform:translateY(-20px);-moz-transform:translateY(-20px);transform:translateY(-20px)}100%{opacity:1;-webkit-transform:translateY(0);-moz-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes fadeDownOut{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(-20px);transform:translateY(-20px)}}@-moz-keyframes fadeDownOut{0%{opacity:1;-moz-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-moz-transform:translateY(-20px);transform:translateY(-20px)}}@keyframes fadeDownOut{0%{opacity:1;-webkit-transform:translateY(0);-moz-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(-20px);-moz-transform:translateY(-20px);transform:translateY(-20px)}}@-webkit-keyframes scale{0%{opacity:0;-webkit-transform:scale(0.7);transform:scale(0.7)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@-moz-keyframes scale{0%{opacity:0;-moz-transform:scale(0.7);transform:scale(0.7)}100%{opacity:1;-moz-transform:scale(1);transform:scale(1)}}@keyframes scale{0%{opacity:0;-webkit-transform:scale(0.7);-moz-transform:scale(0.7);transform:scale(0.7)}100%{opacity:1;-webkit-transform:scale(1);-moz-transform:scale(1);transform:scale(1)}}@-webkit-keyframes scaleOut{0%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}100%{opacity:0;-webkit-transform:scale(0.7);transform:scale(0.7)}}@-moz-keyframes scaleOut{0%{opacity:1;-moz-transform:scale(1);transform:scale(1)}100%{opacity:0;-moz-transform:scale(0.7);transform:scale(0.7)}}@keyframes scaleOut{0%{opacity:1;-webkit-transform:scale(1);-moz-transform:scale(1);transform:scale(1)}100%{opacity:0;-webkit-transform:scale(0.7);-moz-transform:scale(0.7);transform:scale(0.7)}}.ui.video{position:relative;max-width:100%}.ui.video .placeholder{background-color:#333}.ui.video .play{cursor:pointer;position:absolute;top:0;left:0;z-index:10;width:100%;height:100%;-ms-filter:"alpha(Opacity=60)";filter:alpha(opacity=60);opacity:.6;-webkit-transition:opacity .3s;-moz-transition:opacity .3s;transition:opacity .3s}.ui.video .play.icon:before{position:absolute;top:50%;left:50%;z-index:11;font-size:6rem;margin:-3rem 0 0 -3rem;color:#FFF;text-shadow:0 3px 3px rgba(0,0,0,.4)}.ui.video .placeholder{display:block;width:100%;height:100%}.ui.video .embed{display:none}.ui.video .play:hover{opacity:1}.ui.video.active .placeholder,.ui.video.active .play{display:none}.ui.video.active .embed{display:block}.ui.comments a{cursor:pointer}.ui.comments .comment{position:relative;margin-top:.5em;padding-top:.5em}.ui.comments .comment:first-child{margin-top:0;padding-top:0}.ui.comments .comment .avatar{display:block;float:left;width:4em}.ui.comments .comment .avatar img{display:block;margin:0 auto;width:3em;height:3em;border-radius:500px}.ui.comments .comment>.avatar,.ui.comments .comment>.content{display:block}.ui.comments .comment .avatar~.content{padding:0 1em}.ui.comments .comment>.avatar~.content{padding-top:.25em;margin-left:3.5em}.ui.comments .comment .metadata{display:inline-block;margin-left:.3em;color:rgba(0,0,0,.4)}.ui.comments .comment .metadata>*{display:inline-block;margin:0 .3em 0 0}.ui.comments .comment .text{margin:.25em 0 .5em;word-wrap:break-word}.ui.comments .comment .actions{font-size:.9em}.ui.comments .comment .actions a{display:inline-block;margin:0 .3em 0 0;color:rgba(0,0,0,.3)}.ui.comments .comment .actions a.active,.ui.comments .comment .actions a:hover{color:rgba(0,0,0,.6)}.ui.comments .reply.form{margin-top:.75em;width:100%;max-width:30em}.ui.comments .comment .reply.form{margin-left:2em}.ui.comments>.reply.form{margin-top:1.5em;max-width:40em}.ui.comments .reply.form textarea{height:12em}.ui.comments .comment .comments{margin-top:.5em;padding-top:.5em;padding-bottom:1em}.ui.comments .comment .comments:before{position:absolute;top:0;left:0}.ui.comments>.comment .comments{margin-left:2em}.ui.comments>.comment>.comments>.comment>.comments{margin-left:1.75em}.ui.comments>.comment>.comments>.comment>.comments>.comment>.comments{margin-left:1.5em}.ui.comments>.comment>.comments>.comment>.comments>.comment>.comments>.comment .comments{margin-left:.5em}.ui.threaded.comments .comment .comments{margin-left:2em!important;padding-left:2em!important;-webkit-box-shadow:-1px 0 0 rgba(0,0,0,.05);box-shadow:-1px 0 0 rgba(0,0,0,.05)}.ui.minimal.comments .comment .actions{opacity:0;-webkit-transition:opacity .1s ease-out;-moz-transition:opacity .1s ease-out;transition:opacity .1s ease-out;-webkit-transition-delay:.1s;-moz-transition-delay:.1s;transition-delay:.1s}.ui.minimal.comments .comment>.content:hover>.actions{opacity:1}.ui.small.comments{font-size:.875em}.ui.feed a{cursor:pointer}.ui.feed,.ui.feed .content,.ui.feed .event,.ui.feed .extra,.ui.feed .label{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.ui.feed .event{width:100%;display:table}.ui.feed .event:first-child{border-top:0}.ui.feed .event:last-child{margin-bottom:1em}.ui.feed .label{width:3em;display:table-cell;vertical-align:top;text-align:left}.ui.feed .label .icon{font-size:1.5em;padding:.5em;margin:0}.ui.feed .label img{width:3em;margin:0;border-radius:50em}.ui.feed .label+.content{padding:.75em 1em 0}.ui.feed .content{display:table-cell;vertical-align:top;text-align:left;word-wrap:break-word}.ui.feed .content .date{float:right;padding-left:1em;color:rgba(0,0,0,.4)}.ui.feed .content .summary{color:rgba(0,0,0,.75)}.ui.feed .content .summary img{display:inline-block;margin-right:.25em;width:4em;border-radius:500px}.ui.feed .content .extra{margin:1em 0 0;padding:.5em 0 0;color:rgba(0,0,0,.5)}.ui.feed .content .extra.images img{display:inline-block;margin-right:.25em;width:6em}.ui.feed .content .extra.text{padding:.5em 1em;border-left:.2em solid rgba(0,0,0,.1)}.ui.small.feed{font-size:.875em}.ui.small.feed .label img{width:2.5em}.ui.small.feed .label .icon{font-size:1.25em}.ui.feed .event{padding:.75em 0}.ui.small.feed .label+.content{padding:.5em .5em 0}.ui.small.feed .content .extra.images img{width:5em}.ui.small.feed .content .extra{margin:.5em 0 0}.ui.small.feed .content .extra.text{padding:.25em .5em}.ui.items{margin:1em 0 0}.ui.items:first-child{margin-top:0}.ui.items:last-child{margin-bottom:-1em}.ui.items:after{display:block;content:' ';height:0;clear:both;overflow:hidden;visibility:hidden}.ui.items>.item,.ui.items>.row>.item{display:block;float:left;position:relative;top:0;width:316px;min-height:375px;margin:0 .5em 2.5em;background-color:#FFF;line-height:1.2;font-size:1em;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.1);box-shadow:0 0 0 1px rgba(0,0,0,.1);border-bottom:.2em solid rgba(0,0,0,.2);border-radius:.33em;-webkit-transition:-webkit-box-shadow .2s ease;-moz-transition:box-shadow .2s ease;transition:box-shadow .2s ease;padding:.5em}.ui.items .item a,.ui.items a.item{cursor:pointer}.ui.items .item,.ui.items .item>.content,.ui.items .item>.content>.extra,.ui.items .item>.content>.meta,.ui.items .item>.image,.ui.items .item>.image .overlay{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.ui.items .item>.image{display:block;position:relative;background-color:rgba(0,0,0,.05);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;border-radius:.2em}.ui.items .item>.image>img{display:block;width:100%}.ui.items .item>.content{padding:.75em .5em}.ui.items .item>.content>.name{display:block;font-size:1.25em;font-weight:700;margin-bottom:.2em;color:rgba(0,0,0,.7)}.ui.items .item>.content>.description{clear:both;margin:0;color:rgba(0,0,0,.45)}.ui.items .item>.content>.description p{margin:0 0 .2em}.ui.items .item>.content>.description p:last-child{margin-bottom:0}.ui.items .item .meta{float:right;color:rgba(0,0,0,.35)}.ui.items .item>.content>.meta+.name{float:left}.ui.items .item .star.label:hover::after{border-right-color:#F6EFC3;border-top-color:#F6EFC3}.ui.items .item .star.label:hover .icon{color:#ac9400}.ui.items .item .star.label.active::after{border-right-color:#F6EFC3;border-top-color:#F6EFC3}.ui.items .item .star.label.active .icon{color:#ac9400}.ui.items .item .like.label:hover::after{border-right-color:#F5E1E2}.ui.items .item .like.label:hover .icon{color:#ef404a}.ui.items .item .like.label.active::after{border-right-color:#F5E1E2;border-top-color:#F5E1E2}.ui.items .item .like.label.active .icon{color:#ef404a}.ui.items .item .extra{position:absolute;width:100%;padding:0 .5em;bottom:-2em;left:0;height:1.5em;color:rgba(0,0,0,.25);-webkit-transition:color .2s ease;-moz-transition:color .2s ease;transition:color .2s ease}.ui.items .item .extra>img{display:inline-block;border-radius:500px;margin-right:.25em;vertical-align:middle;width:2em}.ui.items .item .extra .left{float:left}.ui.items .item .extra .right{float:right}.ui.items .item:hover{cursor:pointer;z-index:5;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.2);box-shadow:0 0 0 1px rgba(0,0,0,.2)}.ui.items .item:hover .extra{color:rgba(0,0,0,.5)}.ui.items .item:nth-of-type(6n+1):hover{border-bottom-color:#6ECFF5!important}.ui.items .item:nth-of-type(6n+2):hover{border-bottom-color:#5C6166!important}.ui.items .item:nth-of-type(6n+3):hover{border-bottom-color:#A1CF64!important}.ui.items .item:nth-of-type(6n+4):hover{border-bottom-color:#D95C5C!important}.ui.items .item:nth-of-type(6n+5):hover{border-bottom-color:#00B5AD!important}.ui.items .item:nth-of-type(6n+6):hover{border-bottom-color:#564F8A!important}.ui.connected.items{display:table;width:100%;margin-left:0!important;margin-right:0!important}.ui.connected.items>.item,.ui.connected.items>.row>.item{float:none;display:table-cell;vertical-align:top;height:auto;border-radius:0;margin:0;width:33.33%}.ui.connected.items>.row{display:table;margin:.5em 0}.ui.connected.items>.row:first-child{margin-top:0}.ui.connected.items>.item,.ui.connected.items>.row:last-child>.item{border-bottom:.2em solid rgba(0,0,0,.2)}.ui.connected.items>.item:first-child,.ui.connected.items>.row:last-child>.item:first-child{border-radius:0 0 0 .33em}.ui.connected.items>.item:last-child,.ui.connected.items>.row:last-child>.item:last-child{border-radius:0 0 .33em}.ui.connected.items .item:hover{border-bottom-width:.2em}.ui.one.connected.items>.item,.ui.one.connected.items>.row>.item{width:50%;padding-left:2%;padding-right:2%}.ui.two.connected.items>.item,.ui.two.connected.items>.row>.item{width:50%;padding-left:1%;padding-right:1%}.ui.three.connected.items>.item,.ui.three.connected.items>.row>.item{width:33.333%;padding-left:1%;padding-right:1%}.ui.four.connected.items>.item,.ui.four.connected.items>.row>.item{width:25%;padding-left:.5%;padding-right:.5%}.ui.five.connected.items>.item,.ui.five.connected.items>.row>.item{width:20%;padding-left:.5%;padding-right:.5%}.ui.six.connected.items>.item,.ui.six.connected.items>.row>.item{width:16.66%;padding-left:.5%;padding-right:.5%}.ui.seven.connected.items>.item,.ui.seven.connected.items>.row>.item{width:14.28%;padding-left:.5%;padding-right:.5%}.ui.eight.connected.items>.item,.ui.eight.connected.items>.row>.item{width:12.5%;padding-left:.25%;padding-right:.25%}.ui.nine.connected.items>.item,.ui.nine.connected.items>.row>.item{width:11.11%;padding-left:.25%;padding-right:.25%}.ui.ten.connected.items>.item,.ui.ten.connected.items>.row>.item{width:10%;padding-left:.2%;padding-right:.2%}.ui.eleven.connected.items>.item,.ui.eleven.connected.items>.row>.item{width:9.09%;padding-left:.2%;padding-right:.2%}.ui.twelve.connected.items>.item,.ui.twelve.connected.items>.row>.item{width:8.3333%;padding-left:.1%;padding-right:.1%}@media only screen and (max-width:768px){.ui.stackable.items{display:block!important}.ui.stackable.items>.item,.ui.stackable.items>.row>.item{display:block!important;height:auto!important;width:100%!important;padding:0!important}}.ui.horizontal.items>.item,.ui.items>.horizontal.item{display:table}.ui.horizontal.items>.item>.image,.ui.items>.horizontal.item>.image{display:table-cell;width:50%}.ui.horizontal.items>.item>.image+.content,.ui.items>.horizontal.item>.image+.content{width:50%;display:table-cell}.ui.horizontal.items>.item>.content,.ui.items>.horizontal.item>.content{padding:1% 1.7% 11% 3%;vertical-align:top}.ui.horizontal.items>.item>.meta,.ui.items>.horizontal.item>.meta{position:absolute;padding:0;bottom:7%;left:3%;width:94%}.ui.horizontal.items>.item>.image+.content+.meta,.ui.items>.horizontal.item>.image+.content+.meta{bottom:7%;left:53%;width:44%}.ui.horizontal.items>.item .avatar,.ui.items>.horizontal.item .avatar{width:11.5%}.ui.items>.item .avatar{max-width:25px}.ui.one.items{margin-left:-2%;margin-right:-2%}.ui.one.items>.item{width:100%;margin-left:2%;margin-right:2%}.ui.two.items{margin-left:-1%;margin-right:-1%}.ui.two.items>.item{width:48%;margin-left:1%;margin-right:1%}.ui.two.items>.item:nth-child(2n+1){clear:left}.ui.three.items{margin-left:-1%;margin-right:-1%}.ui.three.items>.item{width:31.333%;margin-left:1%;margin-right:1%}.ui.three.items>.item:nth-child(3n+1){clear:left}.ui.four.items{margin-left:-.5%;margin-right:-.5%}.ui.four.items>.item{width:24%;margin-left:.5%;margin-right:.5%}.ui.four.items>.item:nth-child(4n+1){clear:left}.ui.five.items{margin-left:-.5%;margin-right:-.5%}.ui.five.items>.item{width:19%;margin-left:.5%;margin-right:.5%}.ui.five.items>.item:nth-child(5n+1){clear:left}.ui.six.items{margin-left:-.5%;margin-right:-.5%}.ui.six.items>.item{width:15.66%;margin-left:.5%;margin-right:.5%}.ui.six.items>.item:nth-child(6n+1){clear:left}.ui.seven.items{margin-left:-.5%;margin-right:-.5%}.ui.seven.items>.item{width:13.28%;margin-left:.5%;margin-right:.5%;font-size:11px}.ui.seven.items>.item:nth-child(7n+1){clear:left}.ui.eight.items{margin-left:-.25%;margin-right:-.25%}.ui.eight.items>.item{width:12%;margin-left:.25%;margin-right:.25%;font-size:11px}.ui.eight.items>.item:nth-child(8n+1){clear:left}.ui.nine.items{margin-left:-.25%;margin-right:-.25%}.ui.nine.items>.item{width:10.61%;margin-left:.25%;margin-right:.25%;font-size:10px}.ui.nine.items>.item:nth-child(9n+1){clear:left}.ui.ten.items{margin-left:-.2%;margin-right:-.2%}.ui.ten.items>.item{width:9.6%;margin-left:.2%;margin-right:.2%;font-size:10px}.ui.ten.items>.item:nth-child(10n+1){clear:left}.ui.eleven.items{margin-left:-.2%;margin-right:-.2%}.ui.eleven.items>.item{width:8.69%;margin-left:.2%;margin-right:.2%;font-size:9px}.ui.eleven.items>.item:nth-child(11n+1){clear:left}.ui.twelve.items{margin-left:-.1%;margin-right:-.1%}.ui.twelve.items>.item{width:8.1333%;margin-left:.1%;margin-right:.1%;font-size:9px}.ui.twelve.items>.item:nth-child(12n+1){clear:left}.ui.list,ol.ui.list,ul.ui.list{list-style-type:none;margin:1em 0;padding:0}.ui.list .list,ol.ui.list ol,ul.ui.list ul{margin:0;padding:.5em 0 .5em 1em}.ui.list:first-child,ol.ui.list:first-child,ul.ui.list:first-child{margin-top:0}.ui.list:last-child,ol.ui.list:last-child,ul.ui.list:last-child{margin-bottom:0}.ui.list .item,ol.ui.list li,ul.ui.list li{display:list-item;list-style-type:none;list-style-position:inside;padding:.3em 0;line-height:1.2em}.ui.list .item:after{content:'';display:block;height:0;clear:both;visibility:hidden}.ui.list .list{clear:both}.ui.list .item>.icon{display:block;float:left;margin:0 1em 0 0;padding:.1em 0 0}.ui.list .item>.icon:only-child{display:inline-block}.ui.horizontal.list .item>.icon{margin:0;padding:0 .25em 0 0}.ui.horizontal.list .item>.icon,.ui.horizontal.list .item>.icon+.content{float:none;display:inline-block}.ui.list .item>img{display:block;float:left;margin-right:1em;vertical-align:middle}.ui.list .item>.content{display:inline-block;vertical-align:middle;line-height:1.2em}.ui.list .item>.icon+.content{display:table-cell;vertical-align:top}.ui.list a{cursor:pointer}.ui.list a .icon{color:rgba(0,0,0,.6);-webkit-transition:color .2s ease;-moz-transition:color .2s ease;transition:color .2s ease}.ui.list .header{font-weight:700}.ui.list .description{color:rgba(0,0,0,.5)}.ui.list .item>.left.floated{margin-right:1em;float:left}.ui.list .item>.right.floated{margin-left:1em;float:right}.ui.horizontal.list{display:inline-block;font-size:0}.ui.horizontal.list>.item{display:inline-block;margin-left:1em;font-size:1rem}.ui.horizontal.list>.item:first-child{margin-left:0}.ui.horizontal.list .list{padding-left:0;padding-bottom:0}.ui.list a:hover .icon{color:rgba(0,0,0,.8)}.ui.inverted.list a .icon{color:rgba(255,255,255,.6)}.ui.inverted.list .description{color:rgba(255,255,255,.8)}.ui.inverted.link.list .item{color:rgba(255,255,255,.4)}.ui.link.list .item{color:rgba(0,0,0,.3)}.ui.link.list .item a,.ui.link.list a.item{color:rgba(0,0,0,.5)}.ui.link.list .active.item a,.ui.link.list .item a:active,.ui.link.list .item a:hover,.ui.link.list a.active.item,.ui.link.list a.item:active,.ui.link.list a.item:hover{color:rgba(0,0,0,.8)}.ui.inverted.link.list .item a,.ui.inverted.link.list a.item{color:rgba(255,255,255,.6)}.ui.inverted.link.list .item a:hover,.ui.inverted.link.list a.item:hover{color:rgba(255,255,255,.8)}.ui.inverted.link.list .item a:active,.ui.inverted.link.list a.item:active{color:rgba(255,255,255,.9)}.ui.inverted.link.list .active.item a,.ui.inverted.link.list a.active.item{color:rgba(255,255,255,.8)}.ui.selection.list .item{cursor:pointer;color:rgba(0,0,0,.4);padding:.5em;-webkit-transition:.2s color ease,.2s padding-left ease,.2s background-color ease;-moz-transition:.2s color ease,.2s padding-left ease,.2s background-color ease;transition:.2s color ease,.2s padding-left ease,.2s background-color ease}.ui.selection.list .item:hover{background-color:rgba(0,0,0,.02);color:rgba(0,0,0,.7)}.ui.selection.list .item:active{background-color:rgba(0,0,0,.05);color:rgba(0,0,0,.7)}.ui.selection.list .item.active{background-color:rgba(0,0,0,.04);color:rgba(0,0,0,.7)}.ui.animated.list .item{-webkit-transition:.2s color ease,.2s padding-left ease,.2s background-color ease;-moz-transition:.2s color ease,.2s padding-left ease,.2s background-color ease;transition:.2s color ease,.2s padding-left ease,.2s background-color ease}.ui.animated.list:not(.horizontal) .item:hover{padding-left:1em}.ui.animated.list:not(.horizontal) .item:hover .item:hover{padding-left:.5em}.ui.inverted.selection.list .item{color:rgba(255,255,255,.6)}.ui.inverted.selection.list .item:hover{background-color:rgba(255,255,255,.04);color:rgba(255,255,255,.8)}.ui.inverted.selection.list .item:active{background-color:rgba(255,255,255,.1);color:rgba(255,255,255,.7)}.ui.inverted.selection.list .item.active{background-color:rgba(255,255,255,.08);color:#FFF}.ui.bulleted.list,ul.ui.list{margin-left:1.5em}.ui.bulleted.list .item,ul.ui.list li{position:relative}.ui.bulleted.list .item:before,ul.ui.list li:before{position:absolute;left:-1.5em;content:'•'}.ui.bulleted.list .list,ul.ui.list ul{padding-left:1.5em}.ui.horizontal.bulleted.list,ul.ui.horizontal.bulleted.list{margin-left:0}.ui.horizontal.bulleted.list .item,ul.ui.horizontal.bulleted.list li{margin-left:1.5em}.ui.horizontal.bulleted.list .item:before,ul.ui.horizontal.bulleted.list li:before{left:-.9em}.ui.horizontal.bulleted.list .item:first-child,ul.ui.horizontal.bulleted.list li:first-child{margin-left:0}.ui.horizontal.bulleted.list .item:first-child::before,ul.ui.horizontal.bulleted.list li:first-child::before{display:none}.ui.ordered.list,ol.ui.list{counter-reset:ordered;margin-left:2em;list-style-type:none}.ui.ordered.list .item,ol.ui.list li{list-style-type:none;position:relative}.ui.ordered.list .item:before,ol.ui.list li:before{position:absolute;left:-2em;counter-increment:ordered;content:counters(ordered,".");text-align:right;vertical-align:top;opacity:.75}.ui.ordered.list .list,ol.ui.list ol{counter-reset:ordered;padding-left:2.5em}.ui.ordered.list .list .item:before,ol.ui.list ol li:before{left:-2.5em}.ui.ordered.horizontal.list,ol.ui.horizontal.list{margin-left:0}.ui.ordered.horizontal.list .item:before,ol.ui.horizontal.list li:before{position:static;left:0;margin:0 .5em 0 0}.ui.divided.list:not(.horizontal)>.list,.ui.divided.list>.item{border-top:1px solid rgba(0,0,0,.1);padding-left:.5em;padding-right:.5em}.ui.divided.list .item .menu .item{border-width:0}.ui.divided.list .item:first-child{border-top-width:0}.ui.divided.list:not(.horizontal) .list{margin-left:-.5em;margin-right:-.5em}.ui.divided.list:not(.horizontal) .list .item{padding-left:1em;padding-right:1em}.ui.divided.list:not(.horizontal) .list .item:first-child{border-top-width:1px}.ui.divided.bulleted.list{margin-left:0}.ui.divided.bulleted.list .item{padding-left:1.5em}.ui.divided.bulleted.list .item:before{left:.5em}.ui.divided.ordered.list{margin-left:0}.ui.divided.ordered.list>.item{padding-left:2em;padding-right:2em}.ui.divided.ordered.list>.item:before{left:.5em}.ui.divided.ordered.list .item .list{margin-left:-2em;margin-right:-2em}.ui.divided.horizontal.list{margin-left:0}.ui.divided.horizontal.list>.item{border-top:none;border-left:1px solid rgba(0,0,0,.1);margin:0;padding-left:.75em;padding-right:.75em;line-height:.6em}.ui.horizontal.divided.list>.item:first-child{border-left:none;padding-left:0}.ui.divided.inverted.horizontal.list .item,.ui.divided.inverted.list>.item,.ui.divided.inverted.list>.list{border-color:rgba(255,255,255,.2)}.ui.celled.list>.item,.ui.celled.list>.list{border-top:1px solid rgba(0,0,0,.1);padding-left:.5em;padding-right:.5em}.ui.celled.list>.item:last-child{border-bottom:1px solid rgba(0,0,0,.1)}.ui.celled.list .item .list{margin-left:-.5em;margin-right:-.5em}.ui.celled.list .item .list .item{border-width:0}.ui.celled.list .list .item:first-child{border-top-width:0}.ui.celled.bulleted.list{margin-left:0}.ui.celled.bulleted.list>.item{padding-left:1.5em}.ui.celled.bulleted.list>.item:before{left:.5em}.ui.celled.ordered.list{margin-left:0}.ui.celled.ordered.list .item{padding-left:2em;padding-right:2em}.ui.celled.ordered.list .item:before{left:.5em}.ui.celled.ordered.list .item .list{margin-left:-2em;margin-right:-2em}.ui.horizontal.celled.list{margin-left:0}.ui.horizontal.celled.list .item{border-top:none;border-left:1px solid rgba(0,0,0,.1);margin:0;padding-left:.75em;padding-right:.75em;line-height:.6em}.ui.horizontal.celled.list .item:last-child{border-bottom:none;border-right:1px solid rgba(0,0,0,.1)}.ui.celled.inverted.horizontal.list .item,.ui.celled.inverted.list>.item,.ui.celled.inverted.list>.list{border-color:rgba(255,255,255,.2)}.ui.relaxed.list:not(.horizontal) .item{padding-top:.5em;padding-bottom:.5em}.ui.relaxed.list .header{margin-bottom:.25em}.ui.horizontal.relaxed.list .item{padding-left:1.25em;padding-right:1.25em}.ui.very.relaxed.list:not(.horizontal) .item{padding-top:1em;padding-bottom:1em}.ui.very.relaxed.list .header{margin-bottom:.5em}.ui.horizontal.very.relaxed.list .item{padding-left:2em;padding-right:2em}.ui.mini.list .item{font-size:.7rem}.ui.tiny.list .item{font-size:.8125rem}.ui.small.list .item{font-size:.875rem}.ui.list .item{font-size:1em}.ui.large.list .item{font-size:1.125rem}.ui.big.list .item{font-size:1.25rem}.ui.huge.list .item{font-size:1.375rem}.ui.massive.list .item{font-size:1.5rem}.ui.statistic{text-align:center}.ui.statistic>.number{font-size:4em;font-weight:700;color:rgba(0,0,0,.7)}.ui.statistic>.description{opacity:.8} \ No newline at end of file diff --git a/controller/static/fonts/basic.icons.eot b/controller/static/fonts/basic.icons.eot deleted file mode 100644 index 25066de069a62374b39f220581d1314fa60a5f45..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40166 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 - - - -Created by FontForge 20100429 at Thu Sep 20 22:09:47 2012 - By root -Copyright (C) 2012 by original authors @ fontello.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/controller/static/fonts/basic.icons.ttf b/controller/static/fonts/basic.icons.ttf deleted file mode 100644 index 318a2643d3af55fe51b472e7769b99caf74ff7b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39924 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 J*(!_){|Uh81M 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�-A&iY%khEsGVMaq&$BJA^QAs8x>7-g_ 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 k3oQ661F8F$xZIP$lY?c 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 diff --git a/controller/static/fonts/icons.svg b/controller/static/fonts/icons.svg deleted file mode 100644 index 2edb4ec34..000000000 --- a/controller/static/fonts/icons.svg +++ /dev/null @@ -1,399 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/controller/static/fonts/icons.ttf b/controller/static/fonts/icons.ttf deleted file mode 100644 index d3659246915cacb0c9204271f1f9fc5f77049eac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79076 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 diff --git a/controller/static/images/loader-large-inverted.gif b/controller/static/images/loader-large-inverted.gif deleted file mode 100644 index ee5283f10fc717f8a2335f8b8092e1adae642f92..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10168 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 CvRK0a diff --git a/controller/static/images/loader-large.gif b/controller/static/images/loader-large.gif deleted file mode 100644 index 519621ccde30027bee14c18a2c552def9f39f9e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8492 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 diff --git a/controller/static/images/loader-mini-inverted.gif b/controller/static/images/loader-mini-inverted.gif deleted file mode 100644 index c7c95b948d30a5a8ee87741d663b2e05c77db59b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2722 zcmcJQe^8Ql9LFED zrgJ;b7M3NImX#|jYeJb>wz1}Z+MQ zu9PGt@`Fh5P4q*QLP0Qu=lS0AEP_tO<;zX8Rt=oC;h{ZSwCg_9?LSy}^NgPN{^Zfm zsgFN5r_N2S-ddfzBDecww^$?|yfO$doku!LHkJ&2Irwi9G+HT_xVUT3q4tAkd zzeC$X_dNe;$!kB99vm)3M^z>Us1O_i=2ENFZTs6mv8l1Cu&D5y>l_hBsF$ick9C4* z%dQr`9LnqHKjspywIs(=Lst_vCyR82QR$YOR7hP~cSP+_+3=$tyI#hdx=p6_2~G+W zj3ZzIMTP>T1kVXTmY2wbLA9@H6`2Z<0>QR}ZD4u*_Ii*-=Zy3php|8qt2Psqt9;T> zlt-}Gk!8n+2f5DA=?A|PD|M+f*Oa9G!nr%$j*^UaRm|wHVS(BlqATleMwYwfqE(JeB|a>slX|;O*bLdT z&{sk({rGhX4HxW#qj>KtuxJwuiAc zUb?;Mpu!1^FRpmJrQiq#we><073mK z#iaoVuz^c@qbtQWl!h@)GtGMpx9Ewc8(N1iqjK#t{wy z0H6r#1^}=FzyLhnOu!Dv2Ot4>6ei0p?-RXLfF{LbFRtcRt`9aWXh z1Y7_GL;wOrV49aDNT$oDC+|j&-8MGFCA&bnNq%b zyaI)_f+B%K0|jVL5P|TZOrdPXFHnv$@c9;PPU{lF$ft>FS{Mcvx}J?NNaJ18*an50 zf@XW7LE*^#C5rOgfimiJjfEdwv1_R*4=KqYVmW3GsRB z6Xx|?ZiWIH8$$HgC!~hX>+lcDB$t|(HCAXteqh#JAhgPK5 zq(zaX7dd<(H-z*JK_1_1e}BR$`1=#qbNv1k`OJPhHf=MX5>J<$i0#$u`*3E{WL~v> zM^ywDr8VOAXH{KI{CO?S*_BK>oBzBkrUPGzSD}s$-4bRMSwqHH z)Ufij5=^mkZ!c+Wy+4pKBv79sF~C3?-Vk8h0l;I3R17ds6<`!BKS06-qso6s80=J| zBKQxq|E$m~6pA@k)wO3wHelyfGo@#yQK#N|rqrozrLXNRCFm$EJ$n;^Qe)TH@lwKI zS)kA16>UBDzKDAtwf*8x>8bitt?ZZIM6sOMPIO^jjZ%RdOyGJKWr zp9*v#6lj5|n<0Jciyt|0Ga9o=ACe6N?)Rv(1XE5Kn!2&xg5&0stsg}t;zxtYi8h5j gW_w1fQu2_QnZPn3bFlfE(=lJv_)g~k=+Ak71CD7&bN~PV diff --git a/controller/static/images/loader-mini.gif b/controller/static/images/loader-mini.gif deleted file mode 100644 index 7f3e27c704155e5c2d5c4b7c776fd8f499f8e04f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2548 zcmb`IdrVVj7>B=8j^&igJjDWqp=;&RTB~a-3J7yW%B5%wVil*$R-8_zm{r2mWyTaN zSilR`D+I8xqIDJoM5nB}f*rA}cwx-Bg()yLqB3z#T_i;Ip3!1evP-i5eeylO_xHWu z(?oefWK=qdNc0mzWu;~I%l3hL16?<}9zK3(IBMwZ=o}sz9vK-yh(JQ>d+Jz(2a34slfzEAVXhoU*nm^hYuc(8w2NxY%(i<4IzmZBr4g>n5tZ3U{S|k$=+~l zOYPtm+(Genp_pn-T|aceu#HWPK(V%1%`N8Pr^5j1`=zh*dZ+n<*_DV3>TC&Cnv$5) z&$Lv9$W@HQG>)7XdP#SoY`VCnW^n7^h()hT5(XAIhnnbugJYpm7z)m$SQpIDs=3Jw zEC>KNXxrLqg#ZJ024GA);=V0H5mjImuU&#Bagr>kut%KtjeJpaxGqaL*vM)-6;#`0 z#Vp4)lY_EsnRAa^HD)K5Cpi1}a$&<@&>&0;Y@W22PAYp!>!u+ny;%^ko=ipeL zR@~2&`mhsYa6bU;{9X=+s{vt982nRszZXsmK!s% ztK_U{%&gjM@vSO!`-wKD=fh*+N!nJwy}f!n9p$W2H7u($k|%uF5xxvZ18&c-Ilu(q zD2*0X=1=qVabG$qn-M9T^1%2xch{D@1QeoX%nn|k#1S444Ry@S3^z_5+L#)azDIjJ z#Q)UgFec{b+%lTW$#CXalQhYGU(EC4lPd~)3YKn&5v=S~-{c7l-)eX}YD{-hYp-Fy zAF!^jqduf|6nyxA@*#DBkH;4gTo|a%CO>gL@^1SVBTD}6 zz9jr28C1F$Nx2H=6?i;LOsNi(07p`)yF;lCo(9ms7B9R>$2Lq&aXsE3TYl^#q~#KH zT2PvgmsE^1d@;uqt^%N3chliIYWwaDfD&9qH&&YiGriaqqmU|b$&9wjWtS?2ClH^J z8{)yadLUQt9!V_PM>u$YBRjYk@Ewc=!-4FezA@9mt8~b2tTpYlw}0bHKb_4eVE0wC zwN_#NirK9#3%zh9Gcug!iF(rlI0QnNSTA!ffveX?Wd~DIc5kx-d(?M81q;ETFZd3h zTQ@fA^2#kT%LH4o>NpYwx)JSl9Cm}3PA9DMcyBNJ5nV0oR!IZs6N)Tn5J``fJO5T( zHS?V5o{nQWSItoiBUL%@pUjpeB5Vf=@kjA3p?M-z;VPs(6Ihgb-#;#v%jK&MO4CWf z6!!utM~sM4)^@!*kjNVA-*v4Vt^=((T{kK^N;}Vs)^Xo&I@T(t^& zV}NO2j-HO#2CZ`+|E1}gx}wt~mly(RtO!(J@-s^##hM#grBdTUxuMR7%&4h5clq+% zi|xL``V7JK*3%`~3-hs@tE;=_?XjE4vlw1=z_XjF@cu{BMU?qx1=sV?^3=u3v|{q? z+RQ>?-8+hT?1MA+ezz%ery~5+P4@S?Ywo??AyT4kDU-jUIW23kbH)Ex&qe zBMy4^&$HKko7;Im|Ni6N<}8)OuKIKmI{pEIXCI&gRfcjyTuj`F{u3bS{KfM!Us-le zcGFu;>8a@;6%>Nb^JmY4&Y{ag^XJZQYj1l&>T{}J{FNHiOjHT7mB)YKTq4zuW?5rh_mof@kZvA@G_7!wW*8*|S6ocGzt!CbZH+n;~mo%b`B^k=GZ zB7a*x%(DB#ZqQrbP@lObv*%b3h;4ee$;4D;NK8oVIntvHRRT)p`b^;a3#wyGs!0LI z`CO{9W>h7KE|66wcx{l(s#G~+ax1)#iBM)4=Ki0GD^ZSn;Z*UsNm^OL%*_*Nf14M&R*(@2%Tvw$J=5v$=(a|%tm@jYxZN?Pvp{^x$f+H{w(PLxX$wpk8f{^bu2^4E zT2@}MZ32lu83}|c6-sab000ln0Wg3BLx2a2s;{dDhWxyIa0XH}MHq9ksF}$`)umd$ z<7u48O*Nyr!joyU4eM++NEH6TazyNoYTqK=Tr^B(Em!gnx+L?gV&^J`4()Mhd=&dD zs~Yh?lq(FU1!qUZ45?S=8T0ZO52a*F3oaYc5C|!)w6dzYrnYWJ{mzD6jc*1{08UyM zoB$A570?gM0I&cB6kr>q459}Xf|p<$keXf8>2*_Qq~<~NE>o<)vx^UEM#m&7p;JWc zx999D3@2yGqeNVt=3w8LVfx7OnP#^zoU2Db;04c2Y$D%Mf_LJEVbABs9R?3 zx#O)uZqH#| zJzaj1eR5}#im)T?Oc@LwBKO2AqJw*BRV(iAO)Y21?>FdtMl-YOO9L;?lWo~dA$w&t?=$Z2 zJaDjig4^O5cM*XT5Ml@}u$l}GSDOZqlnBtKP4$(Thw(JNXzAdYA4*rJ?Z3on8Br4F zQr6t<5~}>%?g$w2N=A_Ju->-YQ`wu2xW&!0q2{QT$Rh77`gJ2=!R%NYpX;efU@6l4 z(f(`4?_ly0QHh>*ufgC$$#c{sAU_Hb3f(queVRk2W*T{8z^V$1;OUzyw!T7*uSm>s z;cUrT?nTEdjVl$2GC zp^L*-3FJ+Zu()7K1x7>P>(F*$6FjtMJcVEmz<@1g62LGsP_}0r2^Fj`2+)*VEw-ZU zH6*=)XH_V*ljpJ8uw}$Kt~B`k#}_>`%Qpm#8T~$SDw>uYu_QdA%_k$&`$PIa%l}b8 zi|YJ^IH_T+(9@fyJ74?stZ(H*{cg?a;$?R~!o$LIToeKGHoNd~pSV)ajA<2%yaMb6 zTg$f+7PhF3EpvSKlxJGCCavxgm^VlyCpRs?y3T8EMz?(B(JZTEDi^;uWVjX;`ortS zveSDj4mCa?QLY@l;^W1?ps9`0h%&Lo4K9l8Y&!?ih0CXEu4Z3yxmSX2Ue~3+GN9O< zyxKQ5JtF&b^Th$YdB?v3|J^=O5jf8T6v0M?V-8mds)uQbpKPtUcRU@Rc_UFt z&6%)$NBw@f@af}ZaaH>2cQf{OX8v?+1eYL~J=P)&YZcQ6outEDGDAI4DJGqa=PCt! z4^#>`4Uj*$_U1kTWde=}lnLk=f2mB2pA0#ijHj})Bu|^pmo#d4U%a=VSR*GPVpOXr zY@UnRl_}G@de`vjSNk4_lIfNUhlS*En_duYY7~)bcwnJ6z`lcYh!;F#&>Uc5ufP4 z@5{WU`XFqUl6+9!=H%{&vv%P(#Rh_}%-)k59$s>qpPMRnE{WY`hY?WsoJO%Uda7BflQ3T*ep9T+`4g$nu4@qsWmfk za!4EIp%j)ff~`wc*k^awM(fnm6GYAmp4QT0m-A(5g~PxY%hGGb$Eh>efl03UTn>fC zX49w~F26v`L-}dr{)k5t43cy$8r7hnw7)s1*^Ni}UP6oawTo@Uk7S+{ zR+27>f~}_qex*(bU8bb}b>gqNh!KENli|EFG1F*qK`M~n5Y-e;8(`mJ}^xXVIpZeO&Y z(E~rH5OuxTwlc#3RnTt@e*eSDo^M9n$PX>pa)H#3lMwbzZ>f;gD1Wo2vFJBORlk$> zzONm6f;)Ei20miHVaE~|3gnyPgbW|LleI2@NoF7jgG>@@ZBgdi@nU*BGs}JfcR8Cv zrL|j4g!X0C0OI=JtAQcP#KRhSZ9v*2U@WaA7^|8>w$`Qf5Pob4^NfrB{n0mbp^sIW`kOQ|bm9 z=m!1eJQbIb7Udvctnd~(3acz#(hHaROO)wf{M{J5X25s0ji;e=t38?{Z&jo5jyv0% zc<%Pz9h}|_U2*B*uS4$IU9H=6L%-3djkVp=T`s$k{j+uSHJVpz+3f9ea2KnH`7>|= e^+Zro`5SB-+q9jw#Q_%7@+Ptm@Bg42%=-`PE%0Ih diff --git a/controller/static/images/loader-small.gif b/controller/static/images/loader-small.gif deleted file mode 100644 index 8c9f60ee2944554b57e0c79b6ad70e515c2a257b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3447 zcmchZc~nzp8i#Lg?!6=sAfaIq0TT=mAchF25nNcpBHKw26kMaC1Ox>|i&knvKo(hL zQE0|O1Pln4sf<;jPQb_*mx>zehKgvN;xeK#bz@4u0D*MIcIKR(Gx>*e63+KN@9%!^ z``n;Kf!w)ktRO4sAq16ImOpy@sO@;$&`(3MgECpQ?Abq_4gWk`US0m|$+O}2!*%ky zk&zKd_o0s%MFd1G_V?unxU)HU9QdC84j~dD62#K}kM>vynNgy4E{svID2kDSOd2AA zh>DOrxlPD7-=!trPe1x%*NYKlO-eA!y7*k28$5o3>EsDgP*wB22B37D>d-nRz$$!Th$%~E#!`GkTH zBv0jfnibuaTk_|c%s)pb5|nnZ(%tJl#qI{qvJxu3{A1hBOPkv751!Z+@ztFdZ#!1m z>RK?FY(WZ`K~5kr00~+E*dR*~DxhsU)du3!lD{7Y4UWMhOMBD%M`>vWkPZlP$ zuCHRPW;)lWdXh3ZXDP0mgm$F;(o_!WV}r|u-ZOP!m|(3!oL{cE zMW_(XQXw&rCi&cU98Fbg;k&mXZC3v5fYMv4AI!YOJ|`ZU6JydxL38u+_Z9?wjCO2f zv|xZ1%!n>rE$!ov(WV00Kr(F_1^d@E@p@%yiBG>IG6lxu7yV0RK}sxE-de#64DyX5 zEBj}*g+-?4RB#?3;jZ=3+vHqhQe%sGAVgJ7JJdJi?~$7PMt&r(ePGo|p1|eHed_VSTQh}hBdhcFP!N$Bpr(o9 zhy**PIYU7Dn}CYNDmu3-&XwZ!m>QisM8eaK*UjDVJq4mdA>7t`3C2^SDxP) zUZXa19VobW^vWl1%-N~&-6^!qXO?-g_Z|y=@a_v`B5m?u#ov z?Dx~-T$!M7U8{X(+p2a*^SY<}y& z1>cRQo!vsG&;E;$?i3qUe}?C_KDn=9?Ow2w*(`P4F{_I5K6PgwUvYQd&RIn}IHq#wvv24;5sy}e8NZHpnZM25 zF)4nyMZ8?(dbM!TN|S1I=DRC?74GbocNV&;#*P1Js zFl0Pu{Z*^A)m05xuiz?-sS4Bqn^#+^K`SyK5@3{xC=l5 z`_xWhnTZ*=Duji}e0^FGGGeH3(5+Mbh9_*L?fH1yn54AA0(u-(6#xO7Y_j2?k2UOV zbx;yzgk#EJf`>T-b;HMm5Z=<3*2!U6l_db2wa=Un0!Vz6qp!&sNd72-f!p%V9tq0l)}0^_D10}-Lth@O4|vM zIQTxgwGBkDgj1{v8ghO#e5omd#nT5hd>D3OU+)t!sm7TPs=|%!E)UX8W9Bux;&DVY z10~|b4)pPc^j>D9`mWNx5RB>y^f4wXpeWGCM6v)Bi7WZHhs(62<)tHgAyz+3zzK6P zG70f}T1JB~QkF>_FIb$Fc^IP695hTP*ZM~UR+!&FJ3>XW*OYQ?%#5V++uxGT1)|pc zfEBawIM^35|Fw3C{bbaf+BrwMCuu*>}n`sh%616hxx)5PVNT|3ZnGPX4AP>$Syzs3zzu$mioZ%ID znd4oT(z29!+=hUIo`yZ~YEuW=NdO&w4^WxBjH0fhBE^(P+rm3^kA% zED&UvqnzjhggtdZtYBraxsLjO^3$iHQ@uZLP~L(|qp~can~NVvLf;7Tx4E3jt+aE5 zx-O*gsCPn*NZfnR{UK@=$B!aSGT$Bi{cE%9xXS4GmsV%DCEhJAPIzq_FTBT!cc@KS zAypewT7;hN>p1QA55g#3ibpe$G!hzOx3thMiix_E5uZ~&=93`TV${;A>W&Z)H zhY4%R{#aSM%vwJC?t8&4v^bP@gJi<2-SinQw!C*x`N5WQ_WY|JmTfA0yDWa?s`N

xA+D)|B@z|0K+C diff --git a/controller/static/images/logo.png b/controller/static/images/logo.png deleted file mode 100644 index c65ed266e65f1349b66e8713a944681ea22d8cff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3073 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 diff --git a/controller/static/index.html b/controller/static/index.html deleted file mode 100644 index 4de10e2d3..000000000 --- a/controller/static/index.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - - Shipyard - - - - - - - - - - - - -

-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/controller/static/templates/login.html b/controller/static/templates/login.html deleted file mode 100644 index 955b96f4a..000000000 --- a/controller/static/templates/login.html +++ /dev/null @@ -1,58 +0,0 @@ -
-
-
-
- -
-
- - -
- -
-
-
-
-
- - -
- -
-
-
-
- {{flash.message}} -
-
-
Login
-
-
-
-
-
- From d117af236ead7f78364d75de22860b6a659e276c Mon Sep 17 00:00:00 2001 From: Tom Barlow Date: Sat, 4 Apr 2015 17:02:34 +0100 Subject: [PATCH 011/238] Added shipyard-ui V3 Signed-off-by: Tom Barlow --- .gitignore | 1 + controller/static/app/config.routes.js | 20 + .../static/app/containers/config.routes.js | 27 + .../app/containers/containers.controller.js | 13 + .../static/app/containers/containers.html | 38 + .../app/containers/containers.module.js | 10 + .../app/containers/containers.service.js | 13 + controller/static/app/error/error.html | 4 + controller/static/app/events/config.routes.js | 27 + .../static/app/events/events.controller.js | 13 + controller/static/app/events/events.html | 32 + controller/static/app/events/events.module.js | 10 + .../static/app/events/events.service.js | 12 + controller/static/app/layout/menu.html | 19 + controller/static/app/login/config.routes.js | 27 + .../static/app/login/login.controller.js | 24 + controller/static/app/login/login.html | 53 + controller/static/app/login/login.module.js | 12 + .../static/app/login/logout.controller.js | 15 + .../static/app/services/auth.service.js | 31 + .../static/app/services/services.module.js | 10 + controller/static/app/shipyard.config.js | 11 + controller/static/app/shipyard.js | 22 + controller/static/app/shipyard.jwt.js | 22 + controller/static/app/shipyard.module.js | 14 + controller/static/bower.json | 24 + controller/static/gulpfile.js | 49 + controller/static/index.html | 105 + controller/static/main.css | 25 + controller/static/package.json | 51 + controller/static/semantic.json | 21 + .../semantic/dist/components/accordion.css | 256 + .../semantic/dist/components/accordion.js | 578 + .../dist/components/accordion.min.css | 10 + .../semantic/dist/components/accordion.min.js | 11 + .../static/semantic/dist/components/ad.css | 276 + .../semantic/dist/components/ad.min.css | 10 + .../static/semantic/dist/components/api.js | 871 + .../semantic/dist/components/api.min.js | 11 + .../semantic/dist/components/breadcrumb.css | 124 + .../dist/components/breadcrumb.min.css | 10 + .../semantic/dist/components/button.css | 2391 ++ .../semantic/dist/components/button.min.css | 10 + .../static/semantic/dist/components/card.css | 909 + .../semantic/dist/components/card.min.css | 10 + .../semantic/dist/components/checkbox.css | 513 + .../semantic/dist/components/checkbox.js | 509 + .../semantic/dist/components/checkbox.min.css | 10 + .../semantic/dist/components/checkbox.min.js | 11 + .../semantic/dist/components/comment.css | 259 + .../semantic/dist/components/comment.min.css | 10 + .../semantic/dist/components/dimmer.css | 186 + .../static/semantic/dist/components/dimmer.js | 669 + .../semantic/dist/components/dimmer.min.css | 10 + .../semantic/dist/components/dimmer.min.js | 11 + .../semantic/dist/components/divider.css | 241 + .../semantic/dist/components/divider.min.css | 10 + .../semantic/dist/components/dropdown.css | 1104 + .../semantic/dist/components/dropdown.js | 1803 + .../semantic/dist/components/dropdown.min.css | 10 + .../semantic/dist/components/dropdown.min.js | 11 + .../static/semantic/dist/components/feed.css | 276 + .../semantic/dist/components/feed.min.css | 10 + .../static/semantic/dist/components/flag.css | 1024 + .../semantic/dist/components/flag.min.css | 10 + .../static/semantic/dist/components/form.css | 893 + .../static/semantic/dist/components/form.js | 1118 + .../semantic/dist/components/form.min.css | 10 + .../semantic/dist/components/form.min.js | 11 + .../static/semantic/dist/components/grid.css | 1857 + .../semantic/dist/components/grid.min.css | 10 + .../semantic/dist/components/header.css | 596 + .../semantic/dist/components/header.min.css | 10 + .../static/semantic/dist/components/icon.css | 2467 ++ .../semantic/dist/components/icon.min.css | 10 + .../static/semantic/dist/components/image.css | 288 + .../semantic/dist/components/image.min.css | 10 + .../static/semantic/dist/components/input.css | 445 + .../semantic/dist/components/input.min.css | 10 + .../static/semantic/dist/components/item.css | 456 + .../semantic/dist/components/item.min.css | 10 + .../static/semantic/dist/components/label.css | 963 + .../semantic/dist/components/label.min.css | 10 + .../static/semantic/dist/components/list.css | 878 + .../semantic/dist/components/list.min.css | 10 + .../semantic/dist/components/loader.css | 284 + .../semantic/dist/components/loader.min.css | 10 + .../static/semantic/dist/components/menu.css | 1608 + .../semantic/dist/components/menu.min.css | 10 + .../semantic/dist/components/message.css | 421 + .../semantic/dist/components/message.min.css | 10 + .../static/semantic/dist/components/modal.css | 432 + .../static/semantic/dist/components/modal.js | 860 + .../semantic/dist/components/modal.min.css | 10 + .../semantic/dist/components/modal.min.js | 11 + .../static/semantic/dist/components/nag.css | 148 + .../static/semantic/dist/components/nag.js | 477 + .../semantic/dist/components/nag.min.css | 10 + .../semantic/dist/components/nag.min.js | 11 + .../static/semantic/dist/components/popup.css | 293 + .../static/semantic/dist/components/popup.js | 1224 + .../semantic/dist/components/popup.min.css | 10 + .../semantic/dist/components/popup.min.js | 11 + .../semantic/dist/components/progress.css | 461 + .../semantic/dist/components/progress.js | 785 + .../semantic/dist/components/progress.min.css | 10 + .../semantic/dist/components/progress.min.js | 11 + .../static/semantic/dist/components/rail.css | 124 + .../semantic/dist/components/rail.min.css | 10 + .../semantic/dist/components/rating.css | 251 + .../static/semantic/dist/components/rating.js | 451 + .../semantic/dist/components/rating.min.css | 10 + .../semantic/dist/components/rating.min.js | 11 + .../static/semantic/dist/components/reset.css | 429 + .../semantic/dist/components/reset.min.css | 10 + .../semantic/dist/components/reveal.css | 293 + .../semantic/dist/components/reveal.min.css | 10 + .../semantic/dist/components/search.css | 329 + .../static/semantic/dist/components/search.js | 1096 + .../semantic/dist/components/search.min.css | 10 + .../semantic/dist/components/search.min.js | 11 + .../semantic/dist/components/segment.css | 635 + .../semantic/dist/components/segment.min.css | 10 + .../static/semantic/dist/components/shape.css | 154 + .../static/semantic/dist/components/shape.js | 830 + .../semantic/dist/components/shape.min.css | 10 + .../semantic/dist/components/shape.min.js | 11 + .../semantic/dist/components/sidebar.css | 625 + .../semantic/dist/components/sidebar.js | 1089 + .../semantic/dist/components/sidebar.min.css | 10 + .../semantic/dist/components/sidebar.min.js | 11 + .../static/semantic/dist/components/site.css | 160 + .../static/semantic/dist/components/site.js | 487 + .../semantic/dist/components/site.min.css | 10 + .../semantic/dist/components/site.min.js | 11 + .../static/semantic/dist/components/state.js | 695 + .../semantic/dist/components/state.min.js | 11 + .../semantic/dist/components/statistic.css | 409 + .../dist/components/statistic.min.css | 10 + .../static/semantic/dist/components/step.css | 432 + .../semantic/dist/components/step.min.css | 10 + .../semantic/dist/components/sticky.css | 79 + .../static/semantic/dist/components/sticky.js | 792 + .../semantic/dist/components/sticky.min.css | 10 + .../semantic/dist/components/sticky.min.js | 11 + .../static/semantic/dist/components/tab.css | 92 + .../static/semantic/dist/components/tab.js | 802 + .../semantic/dist/components/tab.min.css | 10 + .../semantic/dist/components/tab.min.js | 11 + .../static/semantic/dist/components/table.css | 1000 + .../semantic/dist/components/table.min.css | 10 + .../semantic/dist/components/transition.css | 1990 + .../semantic/dist/components/transition.js | 1038 + .../dist/components/transition.min.css | 10 + .../dist/components/transition.min.js | 11 + .../static/semantic/dist/components/video.css | 125 + .../static/semantic/dist/components/video.js | 540 + .../semantic/dist/components/video.min.css | 10 + .../semantic/dist/components/video.min.js | 11 + .../semantic/dist/components/visibility.js | 1037 + .../dist/components/visibility.min.js | 11 + controller/static/semantic/dist/semantic.css | 32682 ++++++++++++++++ controller/static/semantic/dist/semantic.js | 18894 +++++++++ .../static/semantic/dist/semantic.min.css | 11 + .../static/semantic/dist/semantic.min.js | 18 + .../dist/themes/basic/assets/fonts/icons.eot | Bin 0 -> 40166 bytes .../dist/themes/basic/assets/fonts/icons.svg | 450 + .../dist/themes/basic/assets/fonts/icons.ttf | Bin 0 -> 39924 bytes .../dist/themes/basic/assets/fonts/icons.woff | Bin 0 -> 24676 bytes .../themes/default/assets/fonts/icons.eot | Bin 0 -> 60767 bytes .../themes/default/assets/fonts/icons.svg | 565 + .../themes/default/assets/fonts/icons.ttf | Bin 0 -> 122092 bytes .../themes/default/assets/fonts/icons.woff | Bin 0 -> 71508 bytes .../themes/default/assets/fonts/icons.woff2 | Bin 0 -> 56780 bytes .../themes/default/assets/images/flags.png | Bin 0 -> 28123 bytes .../themes/shipyard/assets/fonts/icons.eot | Bin 0 -> 60767 bytes .../themes/shipyard/assets/fonts/icons.svg | 565 + .../themes/shipyard/assets/fonts/icons.ttf | Bin 0 -> 122092 bytes .../themes/shipyard/assets/fonts/icons.woff | Bin 0 -> 71508 bytes .../themes/shipyard/assets/fonts/icons.woff2 | Bin 0 -> 56780 bytes .../themes/shipyard/assets/images/flags.png | Bin 0 -> 28123 bytes .../semantic/src/definitions/behaviors/api.js | 871 + .../src/definitions/behaviors/colorize.js | 272 + .../src/definitions/behaviors/form.js | 1118 + .../src/definitions/behaviors/state.js | 695 + .../src/definitions/behaviors/visibility.js | 1037 + .../src/definitions/behaviors/visit.js | 515 + .../definitions/collections/breadcrumb.less | 122 + .../src/definitions/collections/form.less | 896 + .../src/definitions/collections/grid.less | 1798 + .../src/definitions/collections/menu.less | 1622 + .../src/definitions/collections/message.less | 442 + .../src/definitions/collections/table.less | 1001 + .../src/definitions/elements/button.less | 2413 ++ .../src/definitions/elements/divider.less | 254 + .../src/definitions/elements/flag.less | 53 + .../src/definitions/elements/header.less | 621 + .../src/definitions/elements/icon.less | 341 + .../src/definitions/elements/image.less | 304 + .../src/definitions/elements/input.less | 429 + .../src/definitions/elements/label.less | 969 + .../src/definitions/elements/list.less | 882 + .../src/definitions/elements/loader.less | 267 + .../src/definitions/elements/rail.less | 126 + .../src/definitions/elements/reveal.less | 258 + .../src/definitions/elements/segment.less | 629 + .../src/definitions/elements/step.less | 429 + .../src/definitions/globals/reset.less | 41 + .../semantic/src/definitions/globals/site.js | 487 + .../src/definitions/globals/site.less | 162 + .../src/definitions/modules/accordion.js | 578 + .../src/definitions/modules/accordion.less | 220 + .../src/definitions/modules/checkbox.js | 509 + .../src/definitions/modules/checkbox.less | 490 + .../src/definitions/modules/dimmer.js | 669 + .../src/definitions/modules/dimmer.less | 179 + .../src/definitions/modules/dropdown.js | 1803 + .../src/definitions/modules/dropdown.less | 1046 + .../semantic/src/definitions/modules/modal.js | 860 + .../src/definitions/modules/modal.less | 438 + .../semantic/src/definitions/modules/nag.js | 477 + .../semantic/src/definitions/modules/nag.less | 159 + .../semantic/src/definitions/modules/popup.js | 1224 + .../src/definitions/modules/popup.less | 294 + .../src/definitions/modules/progress.js | 785 + .../src/definitions/modules/progress.less | 451 + .../src/definitions/modules/rating.js | 451 + .../src/definitions/modules/rating.less | 189 + .../src/definitions/modules/search.js | 1096 + .../src/definitions/modules/search.less | 339 + .../semantic/src/definitions/modules/shape.js | 830 + .../src/definitions/modules/shape.less | 152 + .../src/definitions/modules/sidebar.js | 1089 + .../src/definitions/modules/sidebar.less | 550 + .../src/definitions/modules/sticky.js | 792 + .../src/definitions/modules/sticky.less | 75 + .../semantic/src/definitions/modules/tab.js | 802 + .../semantic/src/definitions/modules/tab.less | 95 + .../src/definitions/modules/transition.js | 1038 + .../src/definitions/modules/transition.less | 80 + .../semantic/src/definitions/modules/video.js | 540 + .../src/definitions/modules/video.less | 125 + .../semantic/src/definitions/views/ad.less | 268 + .../semantic/src/definitions/views/card.less | 927 + .../src/definitions/views/comment.less | 256 + .../semantic/src/definitions/views/feed.less | 272 + .../semantic/src/definitions/views/item.less | 465 + .../src/definitions/views/statistic.less | 414 + controller/static/semantic/src/semantic.less | 65 + .../src/site/collections/breadcrumb.overrides | 3 + .../src/site/collections/breadcrumb.variables | 3 + .../src/site/collections/form.overrides | 3 + .../src/site/collections/form.variables | 3 + .../src/site/collections/grid.overrides | 3 + .../src/site/collections/grid.variables | 3 + .../src/site/collections/menu.overrides | 3 + .../src/site/collections/menu.variables | 3 + .../src/site/collections/message.overrides | 3 + .../src/site/collections/message.variables | 3 + .../src/site/collections/table.overrides | 3 + .../src/site/collections/table.variables | 3 + .../src/site/elements/button.overrides | 3 + .../src/site/elements/button.variables | 3 + .../src/site/elements/divider.overrides | 3 + .../src/site/elements/divider.variables | 3 + .../semantic/src/site/elements/flag.overrides | 3 + .../semantic/src/site/elements/flag.variables | 3 + .../src/site/elements/header.overrides | 3 + .../src/site/elements/header.variables | 3 + .../semantic/src/site/elements/icon.overrides | 3 + .../semantic/src/site/elements/icon.variables | 3 + .../src/site/elements/image.overrides | 3 + .../src/site/elements/image.variables | 3 + .../src/site/elements/input.overrides | 3 + .../src/site/elements/input.variables | 3 + .../src/site/elements/label.overrides | 3 + .../src/site/elements/label.variables | 3 + .../semantic/src/site/elements/list.overrides | 3 + .../semantic/src/site/elements/list.variables | 3 + .../src/site/elements/loader.overrides | 3 + .../src/site/elements/loader.variables | 3 + .../semantic/src/site/elements/rail.overrides | 3 + .../semantic/src/site/elements/rail.variables | 3 + .../src/site/elements/reveal.overrides | 3 + .../src/site/elements/reveal.variables | 3 + .../src/site/elements/segment.overrides | 3 + .../src/site/elements/segment.variables | 3 + .../semantic/src/site/elements/step.overrides | 3 + .../semantic/src/site/elements/step.variables | 3 + .../semantic/src/site/globals/reset.overrides | 3 + .../semantic/src/site/globals/reset.variables | 3 + .../semantic/src/site/globals/site.overrides | 3 + .../semantic/src/site/globals/site.variables | 3 + .../src/site/modules/accordion.overrides | 3 + .../src/site/modules/accordion.variables | 3 + .../src/site/modules/chatroom.overrides | 3 + .../src/site/modules/chatroom.variables | 3 + .../src/site/modules/checkbox.overrides | 3 + .../src/site/modules/checkbox.variables | 3 + .../src/site/modules/dimmer.overrides | 3 + .../src/site/modules/dimmer.variables | 3 + .../src/site/modules/dropdown.overrides | 3 + .../src/site/modules/dropdown.variables | 3 + .../semantic/src/site/modules/modal.overrides | 3 + .../semantic/src/site/modules/modal.variables | 3 + .../semantic/src/site/modules/nag.overrides | 3 + .../semantic/src/site/modules/nag.variables | 3 + .../semantic/src/site/modules/popup.overrides | 3 + .../semantic/src/site/modules/popup.variables | 3 + .../src/site/modules/progress.overrides | 3 + .../src/site/modules/progress.variables | 3 + .../src/site/modules/rating.overrides | 3 + .../src/site/modules/rating.variables | 3 + .../src/site/modules/search.overrides | 3 + .../src/site/modules/search.variables | 3 + .../semantic/src/site/modules/shape.overrides | 3 + .../semantic/src/site/modules/shape.variables | 3 + .../src/site/modules/sidebar.overrides | 3 + .../src/site/modules/sidebar.variables | 3 + .../src/site/modules/sticky.overrides | 3 + .../src/site/modules/sticky.variables | 3 + .../semantic/src/site/modules/tab.overrides | 3 + .../semantic/src/site/modules/tab.variables | 3 + .../src/site/modules/transition.overrides | 3 + .../src/site/modules/transition.variables | 3 + .../semantic/src/site/modules/video.overrides | 3 + .../semantic/src/site/modules/video.variables | 0 .../semantic/src/site/views/ad.overrides | 3 + .../semantic/src/site/views/ad.variables | 3 + .../semantic/src/site/views/card.overrides | 3 + .../semantic/src/site/views/card.variables | 3 + .../semantic/src/site/views/comment.overrides | 3 + .../semantic/src/site/views/comment.variables | 3 + .../semantic/src/site/views/feed.overrides | 3 + .../semantic/src/site/views/feed.variables | 3 + .../semantic/src/site/views/item.overrides | 3 + .../semantic/src/site/views/item.variables | 3 + .../src/site/views/statistic.overrides | 3 + .../src/site/views/statistic.variables | 3 + controller/static/semantic/src/theme.config | 90 + controller/static/semantic/src/theme.less | 48 + .../src/themes/default/assets/fonts/icons.eot | Bin 0 -> 60767 bytes .../src/themes/default/assets/fonts/icons.svg | 565 + .../src/themes/default/assets/fonts/icons.ttf | Bin 0 -> 122092 bytes .../themes/default/assets/fonts/icons.woff | Bin 0 -> 71508 bytes .../themes/default/assets/fonts/icons.woff2 | Bin 0 -> 56780 bytes .../themes/default/assets/images/flags.png | Bin 0 -> 28123 bytes .../default/collections/breadcrumb.overrides | 3 + .../default/collections/breadcrumb.variables | 43 + .../themes/default/collections/form.overrides | 3 + .../themes/default/collections/form.variables | 193 + .../themes/default/collections/grid.overrides | 4 + .../themes/default/collections/grid.variables | 92 + .../themes/default/collections/menu.overrides | 3 + .../themes/default/collections/menu.variables | 333 + .../default/collections/message.overrides | 3 + .../default/collections/message.variables | 88 + .../default/collections/table.overrides | 3 + .../default/collections/table.variables | 228 + .../themes/default/elements/button.overrides | 3 + .../themes/default/elements/button.variables | 294 + .../themes/default/elements/divider.overrides | 3 + .../themes/default/elements/divider.variables | 54 + .../themes/default/elements/flag.overrides | 978 + .../themes/default/elements/flag.variables | 13 + .../themes/default/elements/header.overrides | 4 + .../themes/default/elements/header.variables | 147 + .../themes/default/elements/icon.overrides | 763 + .../themes/default/elements/icon.variables | 43 + .../themes/default/elements/image.overrides | 3 + .../themes/default/elements/image.variables | 42 + .../themes/default/elements/input.overrides | 3 + .../themes/default/elements/input.variables | 118 + .../themes/default/elements/label.overrides | 3 + .../themes/default/elements/label.variables | 174 + .../themes/default/elements/list.overrides | 3 + .../themes/default/elements/list.variables | 204 + .../themes/default/elements/loader.overrides | 3 + .../themes/default/elements/loader.variables | 61 + .../themes/default/elements/rail.overrides | 3 + .../themes/default/elements/rail.variables | 26 + .../themes/default/elements/reveal.overrides | 3 + .../themes/default/elements/reveal.variables | 15 + .../themes/default/elements/segment.overrides | 3 + .../themes/default/elements/segment.variables | 100 + .../themes/default/elements/step.overrides | 16 + .../themes/default/elements/step.variables | 98 + .../themes/default/globals/reset.overrides | 434 + .../themes/default/globals/reset.variables | 3 + .../src/themes/default/globals/site.overrides | 3 + .../src/themes/default/globals/site.variables | 613 + .../default/modules/accordion.overrides | 28 + .../default/modules/accordion.variables | 101 + .../themes/default/modules/chatroom.overrides | 3 + .../themes/default/modules/chatroom.variables | 3 + .../themes/default/modules/checkbox.overrides | 33 + .../themes/default/modules/checkbox.variables | 140 + .../themes/default/modules/dimmer.overrides | 3 + .../themes/default/modules/dimmer.variables | 58 + .../themes/default/modules/dropdown.overrides | 67 + .../themes/default/modules/dropdown.variables | 282 + .../themes/default/modules/modal.overrides | 3 + .../themes/default/modules/modal.variables | 145 + .../src/themes/default/modules/nag.overrides | 3 + .../src/themes/default/modules/nag.variables | 74 + .../themes/default/modules/popup.overrides | 3 + .../themes/default/modules/popup.variables | 95 + .../themes/default/modules/progress.overrides | 3 + .../themes/default/modules/progress.variables | 107 + .../themes/default/modules/rating.overrides | 68 + .../themes/default/modules/rating.variables | 65 + .../themes/default/modules/search.overrides | 3 + .../themes/default/modules/search.variables | 140 + .../themes/default/modules/shape.overrides | 3 + .../themes/default/modules/shape.variables | 35 + .../themes/default/modules/sidebar.overrides | 3 + .../themes/default/modules/sidebar.variables | 48 + .../themes/default/modules/sticky.overrides | 3 + .../themes/default/modules/sticky.variables | 12 + .../src/themes/default/modules/tab.overrides | 3 + .../src/themes/default/modules/tab.variables | 13 + .../default/modules/transition.overrides | 911 + .../default/modules/transition.variables | 10 + .../themes/default/modules/video.overrides | 3 + .../themes/default/modules/video.variables | 16 + .../src/themes/default/views/ad.overrides | 3 + .../src/themes/default/views/ad.variables | 13 + .../src/themes/default/views/card.overrides | 3 + .../src/themes/default/views/card.variables | 199 + .../themes/default/views/comment.overrides | 3 + .../themes/default/views/comment.variables | 104 + .../src/themes/default/views/feed.overrides | 3 + .../src/themes/default/views/feed.variables | 149 + .../src/themes/default/views/item.overrides | 3 + .../src/themes/default/views/item.variables | 153 + .../themes/default/views/statistic.overrides | 3 + .../themes/default/views/statistic.variables | 97 + .../themes/shipyard/assets/fonts/icons.eot | Bin 0 -> 60767 bytes .../themes/shipyard/assets/fonts/icons.svg | 565 + .../themes/shipyard/assets/fonts/icons.ttf | Bin 0 -> 122092 bytes .../themes/shipyard/assets/fonts/icons.woff | Bin 0 -> 71508 bytes .../themes/shipyard/assets/fonts/icons.woff2 | Bin 0 -> 56780 bytes .../themes/shipyard/assets/images/flags.png | Bin 0 -> 28123 bytes .../shipyard/collections/breadcrumb.overrides | 3 + .../shipyard/collections/breadcrumb.variables | 43 + .../shipyard/collections/form.overrides | 3 + .../shipyard/collections/form.variables | 193 + .../shipyard/collections/grid.overrides | 4 + .../shipyard/collections/grid.variables | 92 + .../shipyard/collections/menu.overrides | 3 + .../shipyard/collections/menu.variables | 333 + .../shipyard/collections/message.overrides | 3 + .../shipyard/collections/message.variables | 88 + .../shipyard/collections/table.overrides | 3 + .../shipyard/collections/table.variables | 228 + .../themes/shipyard/elements/button.overrides | 3 + .../themes/shipyard/elements/button.variables | 294 + .../shipyard/elements/divider.overrides | 3 + .../shipyard/elements/divider.variables | 54 + .../themes/shipyard/elements/flag.overrides | 978 + .../themes/shipyard/elements/flag.variables | 13 + .../themes/shipyard/elements/header.overrides | 4 + .../themes/shipyard/elements/header.variables | 147 + .../themes/shipyard/elements/icon.overrides | 763 + .../themes/shipyard/elements/icon.variables | 43 + .../themes/shipyard/elements/image.overrides | 3 + .../themes/shipyard/elements/image.variables | 42 + .../themes/shipyard/elements/input.overrides | 3 + .../themes/shipyard/elements/input.variables | 118 + .../themes/shipyard/elements/label.overrides | 3 + .../themes/shipyard/elements/label.variables | 174 + .../themes/shipyard/elements/list.overrides | 3 + .../themes/shipyard/elements/list.variables | 204 + .../themes/shipyard/elements/loader.overrides | 3 + .../themes/shipyard/elements/loader.variables | 61 + .../themes/shipyard/elements/rail.overrides | 3 + .../themes/shipyard/elements/rail.variables | 26 + .../themes/shipyard/elements/reveal.overrides | 3 + .../themes/shipyard/elements/reveal.variables | 15 + .../shipyard/elements/segment.overrides | 3 + .../shipyard/elements/segment.variables | 100 + .../themes/shipyard/elements/step.overrides | 16 + .../themes/shipyard/elements/step.variables | 98 + .../themes/shipyard/globals/reset.overrides | 434 + .../themes/shipyard/globals/reset.variables | 3 + .../themes/shipyard/globals/site.overrides | 3 + .../themes/shipyard/globals/site.variables | 617 + .../shipyard/modules/accordion.overrides | 28 + .../shipyard/modules/accordion.variables | 101 + .../shipyard/modules/chatroom.overrides | 3 + .../shipyard/modules/chatroom.variables | 3 + .../shipyard/modules/checkbox.overrides | 33 + .../shipyard/modules/checkbox.variables | 140 + .../themes/shipyard/modules/dimmer.overrides | 3 + .../themes/shipyard/modules/dimmer.variables | 58 + .../shipyard/modules/dropdown.overrides | 67 + .../shipyard/modules/dropdown.variables | 282 + .../themes/shipyard/modules/modal.overrides | 3 + .../themes/shipyard/modules/modal.variables | 145 + .../src/themes/shipyard/modules/nag.overrides | 3 + .../src/themes/shipyard/modules/nag.variables | 74 + .../themes/shipyard/modules/popup.overrides | 3 + .../themes/shipyard/modules/popup.variables | 95 + .../shipyard/modules/progress.overrides | 3 + .../shipyard/modules/progress.variables | 107 + .../themes/shipyard/modules/rating.overrides | 68 + .../themes/shipyard/modules/rating.variables | 65 + .../themes/shipyard/modules/search.overrides | 3 + .../themes/shipyard/modules/search.variables | 140 + .../themes/shipyard/modules/shape.overrides | 3 + .../themes/shipyard/modules/shape.variables | 35 + .../themes/shipyard/modules/sidebar.overrides | 3 + .../themes/shipyard/modules/sidebar.variables | 48 + .../themes/shipyard/modules/sticky.overrides | 3 + .../themes/shipyard/modules/sticky.variables | 12 + .../src/themes/shipyard/modules/tab.overrides | 3 + .../src/themes/shipyard/modules/tab.variables | 13 + .../shipyard/modules/transition.overrides | 911 + .../shipyard/modules/transition.variables | 10 + .../themes/shipyard/modules/video.overrides | 3 + .../themes/shipyard/modules/video.variables | 16 + .../src/themes/shipyard/views/ad.overrides | 3 + .../src/themes/shipyard/views/ad.variables | 13 + .../src/themes/shipyard/views/card.overrides | 3 + .../src/themes/shipyard/views/card.variables | 199 + .../themes/shipyard/views/comment.overrides | 3 + .../themes/shipyard/views/comment.variables | 104 + .../src/themes/shipyard/views/feed.overrides | 3 + .../src/themes/shipyard/views/feed.variables | 149 + .../src/themes/shipyard/views/item.overrides | 3 + .../src/themes/shipyard/views/item.variables | 153 + .../themes/shipyard/views/statistic.overrides | 3 + .../themes/shipyard/views/statistic.variables | 97 + controller/static/semantic/tasks/README.md | 17 + .../semantic/tasks/admin/components/create.js | 332 + .../semantic/tasks/admin/components/init.js | 170 + .../semantic/tasks/admin/components/update.js | 184 + .../tasks/admin/distributions/create.js | 219 + .../tasks/admin/distributions/init.js | 170 + .../tasks/admin/distributions/update.js | 182 + .../static/semantic/tasks/admin/publish.js | 25 + .../static/semantic/tasks/admin/register.js | 55 + .../static/semantic/tasks/admin/release.js | 29 + controller/static/semantic/tasks/build.js | 137 + .../static/semantic/tasks/check-install.js | 28 + controller/static/semantic/tasks/clean.js | 14 + .../semantic/tasks/collections/README.md | 16 + .../semantic/tasks/collections/admin.js | 49 + .../semantic/tasks/collections/internal.js | 214 + .../semantic/tasks/config/admin/github.js | 38 + .../tasks/config/admin/oauth.example.js | 10 + .../semantic/tasks/config/admin/release.js | 110 + .../tasks/config/admin/templates/README.md | 29 + .../tasks/config/admin/templates/bower.json | 29 + .../admin/templates/component-package.js | 14 + .../config/admin/templates/composer.json | 20 + .../config/admin/templates/css-package.js | 34 + .../config/admin/templates/less-package.js | 21 + .../tasks/config/admin/templates/package.json | 17 + .../static/semantic/tasks/config/defaults.js | 116 + .../static/semantic/tasks/config/docs.js | 24 + .../semantic/tasks/config/npm/gulpfile.js | 59 + .../semantic/tasks/config/project/config.js | 135 + .../semantic/tasks/config/project/install.js | 762 + .../semantic/tasks/config/project/release.js | 61 + .../semantic/tasks/config/project/tasks.js | 142 + .../static/semantic/tasks/config/user.js | 58 + .../static/semantic/tasks/docs/build.js | 158 + .../static/semantic/tasks/docs/serve.js | 236 + controller/static/semantic/tasks/install.js | 391 + controller/static/semantic/tasks/rtl/build.js | 132 + controller/static/semantic/tasks/rtl/watch.js | 221 + controller/static/semantic/tasks/version.js | 11 + controller/static/semantic/tasks/watch.js | 225 + 574 files changed, 163015 insertions(+) create mode 100644 controller/static/app/config.routes.js create mode 100644 controller/static/app/containers/config.routes.js create mode 100644 controller/static/app/containers/containers.controller.js create mode 100644 controller/static/app/containers/containers.html create mode 100644 controller/static/app/containers/containers.module.js create mode 100644 controller/static/app/containers/containers.service.js create mode 100644 controller/static/app/error/error.html create mode 100644 controller/static/app/events/config.routes.js create mode 100644 controller/static/app/events/events.controller.js create mode 100644 controller/static/app/events/events.html create mode 100644 controller/static/app/events/events.module.js create mode 100644 controller/static/app/events/events.service.js create mode 100644 controller/static/app/layout/menu.html create mode 100644 controller/static/app/login/config.routes.js create mode 100644 controller/static/app/login/login.controller.js create mode 100644 controller/static/app/login/login.html create mode 100644 controller/static/app/login/login.module.js create mode 100644 controller/static/app/login/logout.controller.js create mode 100644 controller/static/app/services/auth.service.js create mode 100644 controller/static/app/services/services.module.js create mode 100644 controller/static/app/shipyard.config.js create mode 100644 controller/static/app/shipyard.js create mode 100644 controller/static/app/shipyard.jwt.js create mode 100644 controller/static/app/shipyard.module.js create mode 100644 controller/static/bower.json create mode 100755 controller/static/gulpfile.js create mode 100644 controller/static/index.html create mode 100644 controller/static/main.css create mode 100644 controller/static/package.json create mode 100755 controller/static/semantic.json create mode 100755 controller/static/semantic/dist/components/accordion.css create mode 100755 controller/static/semantic/dist/components/accordion.js create mode 100755 controller/static/semantic/dist/components/accordion.min.css create mode 100755 controller/static/semantic/dist/components/accordion.min.js create mode 100755 controller/static/semantic/dist/components/ad.css create mode 100755 controller/static/semantic/dist/components/ad.min.css create mode 100755 controller/static/semantic/dist/components/api.js create mode 100755 controller/static/semantic/dist/components/api.min.js create mode 100755 controller/static/semantic/dist/components/breadcrumb.css create mode 100755 controller/static/semantic/dist/components/breadcrumb.min.css create mode 100755 controller/static/semantic/dist/components/button.css create mode 100755 controller/static/semantic/dist/components/button.min.css create mode 100755 controller/static/semantic/dist/components/card.css create mode 100755 controller/static/semantic/dist/components/card.min.css create mode 100755 controller/static/semantic/dist/components/checkbox.css create mode 100755 controller/static/semantic/dist/components/checkbox.js create mode 100755 controller/static/semantic/dist/components/checkbox.min.css create mode 100755 controller/static/semantic/dist/components/checkbox.min.js create mode 100755 controller/static/semantic/dist/components/comment.css create mode 100755 controller/static/semantic/dist/components/comment.min.css create mode 100755 controller/static/semantic/dist/components/dimmer.css create mode 100755 controller/static/semantic/dist/components/dimmer.js create mode 100755 controller/static/semantic/dist/components/dimmer.min.css create mode 100755 controller/static/semantic/dist/components/dimmer.min.js create mode 100755 controller/static/semantic/dist/components/divider.css create mode 100755 controller/static/semantic/dist/components/divider.min.css create mode 100755 controller/static/semantic/dist/components/dropdown.css create mode 100755 controller/static/semantic/dist/components/dropdown.js create mode 100755 controller/static/semantic/dist/components/dropdown.min.css create mode 100755 controller/static/semantic/dist/components/dropdown.min.js create mode 100755 controller/static/semantic/dist/components/feed.css create mode 100755 controller/static/semantic/dist/components/feed.min.css create mode 100755 controller/static/semantic/dist/components/flag.css create mode 100755 controller/static/semantic/dist/components/flag.min.css create mode 100755 controller/static/semantic/dist/components/form.css create mode 100755 controller/static/semantic/dist/components/form.js create mode 100755 controller/static/semantic/dist/components/form.min.css create mode 100755 controller/static/semantic/dist/components/form.min.js create mode 100755 controller/static/semantic/dist/components/grid.css create mode 100755 controller/static/semantic/dist/components/grid.min.css create mode 100755 controller/static/semantic/dist/components/header.css create mode 100755 controller/static/semantic/dist/components/header.min.css create mode 100755 controller/static/semantic/dist/components/icon.css create mode 100755 controller/static/semantic/dist/components/icon.min.css create mode 100755 controller/static/semantic/dist/components/image.css create mode 100755 controller/static/semantic/dist/components/image.min.css create mode 100755 controller/static/semantic/dist/components/input.css create mode 100755 controller/static/semantic/dist/components/input.min.css create mode 100755 controller/static/semantic/dist/components/item.css create mode 100755 controller/static/semantic/dist/components/item.min.css create mode 100755 controller/static/semantic/dist/components/label.css create mode 100755 controller/static/semantic/dist/components/label.min.css create mode 100755 controller/static/semantic/dist/components/list.css create mode 100755 controller/static/semantic/dist/components/list.min.css create mode 100755 controller/static/semantic/dist/components/loader.css create mode 100755 controller/static/semantic/dist/components/loader.min.css create mode 100755 controller/static/semantic/dist/components/menu.css create mode 100755 controller/static/semantic/dist/components/menu.min.css create mode 100755 controller/static/semantic/dist/components/message.css create mode 100755 controller/static/semantic/dist/components/message.min.css create mode 100755 controller/static/semantic/dist/components/modal.css create mode 100755 controller/static/semantic/dist/components/modal.js create mode 100755 controller/static/semantic/dist/components/modal.min.css create mode 100755 controller/static/semantic/dist/components/modal.min.js create mode 100755 controller/static/semantic/dist/components/nag.css create mode 100755 controller/static/semantic/dist/components/nag.js create mode 100755 controller/static/semantic/dist/components/nag.min.css create mode 100755 controller/static/semantic/dist/components/nag.min.js create mode 100755 controller/static/semantic/dist/components/popup.css create mode 100755 controller/static/semantic/dist/components/popup.js create mode 100755 controller/static/semantic/dist/components/popup.min.css create mode 100755 controller/static/semantic/dist/components/popup.min.js create mode 100755 controller/static/semantic/dist/components/progress.css create mode 100755 controller/static/semantic/dist/components/progress.js create mode 100755 controller/static/semantic/dist/components/progress.min.css create mode 100755 controller/static/semantic/dist/components/progress.min.js create mode 100755 controller/static/semantic/dist/components/rail.css create mode 100755 controller/static/semantic/dist/components/rail.min.css create mode 100755 controller/static/semantic/dist/components/rating.css create mode 100755 controller/static/semantic/dist/components/rating.js create mode 100755 controller/static/semantic/dist/components/rating.min.css create mode 100755 controller/static/semantic/dist/components/rating.min.js create mode 100755 controller/static/semantic/dist/components/reset.css create mode 100755 controller/static/semantic/dist/components/reset.min.css create mode 100755 controller/static/semantic/dist/components/reveal.css create mode 100755 controller/static/semantic/dist/components/reveal.min.css create mode 100755 controller/static/semantic/dist/components/search.css create mode 100755 controller/static/semantic/dist/components/search.js create mode 100755 controller/static/semantic/dist/components/search.min.css create mode 100755 controller/static/semantic/dist/components/search.min.js create mode 100755 controller/static/semantic/dist/components/segment.css create mode 100755 controller/static/semantic/dist/components/segment.min.css create mode 100755 controller/static/semantic/dist/components/shape.css create mode 100755 controller/static/semantic/dist/components/shape.js create mode 100755 controller/static/semantic/dist/components/shape.min.css create mode 100755 controller/static/semantic/dist/components/shape.min.js create mode 100755 controller/static/semantic/dist/components/sidebar.css create mode 100755 controller/static/semantic/dist/components/sidebar.js create mode 100755 controller/static/semantic/dist/components/sidebar.min.css create mode 100755 controller/static/semantic/dist/components/sidebar.min.js create mode 100755 controller/static/semantic/dist/components/site.css create mode 100755 controller/static/semantic/dist/components/site.js create mode 100755 controller/static/semantic/dist/components/site.min.css create mode 100755 controller/static/semantic/dist/components/site.min.js create mode 100755 controller/static/semantic/dist/components/state.js create mode 100755 controller/static/semantic/dist/components/state.min.js create mode 100755 controller/static/semantic/dist/components/statistic.css create mode 100755 controller/static/semantic/dist/components/statistic.min.css create mode 100755 controller/static/semantic/dist/components/step.css create mode 100755 controller/static/semantic/dist/components/step.min.css create mode 100755 controller/static/semantic/dist/components/sticky.css create mode 100755 controller/static/semantic/dist/components/sticky.js create mode 100755 controller/static/semantic/dist/components/sticky.min.css create mode 100755 controller/static/semantic/dist/components/sticky.min.js create mode 100755 controller/static/semantic/dist/components/tab.css create mode 100755 controller/static/semantic/dist/components/tab.js create mode 100755 controller/static/semantic/dist/components/tab.min.css create mode 100755 controller/static/semantic/dist/components/tab.min.js create mode 100755 controller/static/semantic/dist/components/table.css create mode 100755 controller/static/semantic/dist/components/table.min.css create mode 100755 controller/static/semantic/dist/components/transition.css create mode 100755 controller/static/semantic/dist/components/transition.js create mode 100755 controller/static/semantic/dist/components/transition.min.css create mode 100755 controller/static/semantic/dist/components/transition.min.js create mode 100755 controller/static/semantic/dist/components/video.css create mode 100755 controller/static/semantic/dist/components/video.js create mode 100755 controller/static/semantic/dist/components/video.min.css create mode 100755 controller/static/semantic/dist/components/video.min.js create mode 100755 controller/static/semantic/dist/components/visibility.js create mode 100755 controller/static/semantic/dist/components/visibility.min.js create mode 100644 controller/static/semantic/dist/semantic.css create mode 100755 controller/static/semantic/dist/semantic.js create mode 100644 controller/static/semantic/dist/semantic.min.css create mode 100755 controller/static/semantic/dist/semantic.min.js create mode 100755 controller/static/semantic/dist/themes/basic/assets/fonts/icons.eot create mode 100755 controller/static/semantic/dist/themes/basic/assets/fonts/icons.svg create mode 100755 controller/static/semantic/dist/themes/basic/assets/fonts/icons.ttf create mode 100755 controller/static/semantic/dist/themes/basic/assets/fonts/icons.woff create mode 100755 controller/static/semantic/dist/themes/default/assets/fonts/icons.eot create mode 100755 controller/static/semantic/dist/themes/default/assets/fonts/icons.svg create mode 100755 controller/static/semantic/dist/themes/default/assets/fonts/icons.ttf create mode 100755 controller/static/semantic/dist/themes/default/assets/fonts/icons.woff create mode 100755 controller/static/semantic/dist/themes/default/assets/fonts/icons.woff2 create mode 100755 controller/static/semantic/dist/themes/default/assets/images/flags.png create mode 100755 controller/static/semantic/dist/themes/shipyard/assets/fonts/icons.eot create mode 100755 controller/static/semantic/dist/themes/shipyard/assets/fonts/icons.svg create mode 100755 controller/static/semantic/dist/themes/shipyard/assets/fonts/icons.ttf create mode 100755 controller/static/semantic/dist/themes/shipyard/assets/fonts/icons.woff create mode 100755 controller/static/semantic/dist/themes/shipyard/assets/fonts/icons.woff2 create mode 100755 controller/static/semantic/dist/themes/shipyard/assets/images/flags.png create mode 100755 controller/static/semantic/src/definitions/behaviors/api.js create mode 100755 controller/static/semantic/src/definitions/behaviors/colorize.js create mode 100755 controller/static/semantic/src/definitions/behaviors/form.js create mode 100755 controller/static/semantic/src/definitions/behaviors/state.js create mode 100755 controller/static/semantic/src/definitions/behaviors/visibility.js create mode 100755 controller/static/semantic/src/definitions/behaviors/visit.js create mode 100755 controller/static/semantic/src/definitions/collections/breadcrumb.less create mode 100755 controller/static/semantic/src/definitions/collections/form.less create mode 100755 controller/static/semantic/src/definitions/collections/grid.less create mode 100755 controller/static/semantic/src/definitions/collections/menu.less create mode 100755 controller/static/semantic/src/definitions/collections/message.less create mode 100755 controller/static/semantic/src/definitions/collections/table.less create mode 100755 controller/static/semantic/src/definitions/elements/button.less create mode 100755 controller/static/semantic/src/definitions/elements/divider.less create mode 100755 controller/static/semantic/src/definitions/elements/flag.less create mode 100755 controller/static/semantic/src/definitions/elements/header.less create mode 100755 controller/static/semantic/src/definitions/elements/icon.less create mode 100755 controller/static/semantic/src/definitions/elements/image.less create mode 100755 controller/static/semantic/src/definitions/elements/input.less create mode 100755 controller/static/semantic/src/definitions/elements/label.less create mode 100755 controller/static/semantic/src/definitions/elements/list.less create mode 100755 controller/static/semantic/src/definitions/elements/loader.less create mode 100755 controller/static/semantic/src/definitions/elements/rail.less create mode 100755 controller/static/semantic/src/definitions/elements/reveal.less create mode 100755 controller/static/semantic/src/definitions/elements/segment.less create mode 100755 controller/static/semantic/src/definitions/elements/step.less create mode 100755 controller/static/semantic/src/definitions/globals/reset.less create mode 100755 controller/static/semantic/src/definitions/globals/site.js create mode 100755 controller/static/semantic/src/definitions/globals/site.less create mode 100755 controller/static/semantic/src/definitions/modules/accordion.js create mode 100755 controller/static/semantic/src/definitions/modules/accordion.less create mode 100755 controller/static/semantic/src/definitions/modules/checkbox.js create mode 100755 controller/static/semantic/src/definitions/modules/checkbox.less create mode 100755 controller/static/semantic/src/definitions/modules/dimmer.js create mode 100755 controller/static/semantic/src/definitions/modules/dimmer.less create mode 100755 controller/static/semantic/src/definitions/modules/dropdown.js create mode 100755 controller/static/semantic/src/definitions/modules/dropdown.less create mode 100755 controller/static/semantic/src/definitions/modules/modal.js create mode 100755 controller/static/semantic/src/definitions/modules/modal.less create mode 100755 controller/static/semantic/src/definitions/modules/nag.js create mode 100755 controller/static/semantic/src/definitions/modules/nag.less create mode 100755 controller/static/semantic/src/definitions/modules/popup.js create mode 100755 controller/static/semantic/src/definitions/modules/popup.less create mode 100755 controller/static/semantic/src/definitions/modules/progress.js create mode 100755 controller/static/semantic/src/definitions/modules/progress.less create mode 100755 controller/static/semantic/src/definitions/modules/rating.js create mode 100755 controller/static/semantic/src/definitions/modules/rating.less create mode 100755 controller/static/semantic/src/definitions/modules/search.js create mode 100755 controller/static/semantic/src/definitions/modules/search.less create mode 100755 controller/static/semantic/src/definitions/modules/shape.js create mode 100755 controller/static/semantic/src/definitions/modules/shape.less create mode 100755 controller/static/semantic/src/definitions/modules/sidebar.js create mode 100755 controller/static/semantic/src/definitions/modules/sidebar.less create mode 100755 controller/static/semantic/src/definitions/modules/sticky.js create mode 100755 controller/static/semantic/src/definitions/modules/sticky.less create mode 100755 controller/static/semantic/src/definitions/modules/tab.js create mode 100755 controller/static/semantic/src/definitions/modules/tab.less create mode 100755 controller/static/semantic/src/definitions/modules/transition.js create mode 100755 controller/static/semantic/src/definitions/modules/transition.less create mode 100755 controller/static/semantic/src/definitions/modules/video.js create mode 100755 controller/static/semantic/src/definitions/modules/video.less create mode 100755 controller/static/semantic/src/definitions/views/ad.less create mode 100755 controller/static/semantic/src/definitions/views/card.less create mode 100755 controller/static/semantic/src/definitions/views/comment.less create mode 100755 controller/static/semantic/src/definitions/views/feed.less create mode 100755 controller/static/semantic/src/definitions/views/item.less create mode 100755 controller/static/semantic/src/definitions/views/statistic.less create mode 100755 controller/static/semantic/src/semantic.less create mode 100755 controller/static/semantic/src/site/collections/breadcrumb.overrides create mode 100755 controller/static/semantic/src/site/collections/breadcrumb.variables create mode 100755 controller/static/semantic/src/site/collections/form.overrides create mode 100755 controller/static/semantic/src/site/collections/form.variables create mode 100755 controller/static/semantic/src/site/collections/grid.overrides create mode 100755 controller/static/semantic/src/site/collections/grid.variables create mode 100755 controller/static/semantic/src/site/collections/menu.overrides create mode 100755 controller/static/semantic/src/site/collections/menu.variables create mode 100755 controller/static/semantic/src/site/collections/message.overrides create mode 100755 controller/static/semantic/src/site/collections/message.variables create mode 100755 controller/static/semantic/src/site/collections/table.overrides create mode 100755 controller/static/semantic/src/site/collections/table.variables create mode 100755 controller/static/semantic/src/site/elements/button.overrides create mode 100755 controller/static/semantic/src/site/elements/button.variables create mode 100755 controller/static/semantic/src/site/elements/divider.overrides create mode 100755 controller/static/semantic/src/site/elements/divider.variables create mode 100755 controller/static/semantic/src/site/elements/flag.overrides create mode 100755 controller/static/semantic/src/site/elements/flag.variables create mode 100755 controller/static/semantic/src/site/elements/header.overrides create mode 100755 controller/static/semantic/src/site/elements/header.variables create mode 100755 controller/static/semantic/src/site/elements/icon.overrides create mode 100755 controller/static/semantic/src/site/elements/icon.variables create mode 100755 controller/static/semantic/src/site/elements/image.overrides create mode 100755 controller/static/semantic/src/site/elements/image.variables create mode 100755 controller/static/semantic/src/site/elements/input.overrides create mode 100755 controller/static/semantic/src/site/elements/input.variables create mode 100755 controller/static/semantic/src/site/elements/label.overrides create mode 100755 controller/static/semantic/src/site/elements/label.variables create mode 100755 controller/static/semantic/src/site/elements/list.overrides create mode 100755 controller/static/semantic/src/site/elements/list.variables create mode 100755 controller/static/semantic/src/site/elements/loader.overrides create mode 100755 controller/static/semantic/src/site/elements/loader.variables create mode 100755 controller/static/semantic/src/site/elements/rail.overrides create mode 100755 controller/static/semantic/src/site/elements/rail.variables create mode 100755 controller/static/semantic/src/site/elements/reveal.overrides create mode 100755 controller/static/semantic/src/site/elements/reveal.variables create mode 100755 controller/static/semantic/src/site/elements/segment.overrides create mode 100755 controller/static/semantic/src/site/elements/segment.variables create mode 100755 controller/static/semantic/src/site/elements/step.overrides create mode 100755 controller/static/semantic/src/site/elements/step.variables create mode 100755 controller/static/semantic/src/site/globals/reset.overrides create mode 100755 controller/static/semantic/src/site/globals/reset.variables create mode 100755 controller/static/semantic/src/site/globals/site.overrides create mode 100755 controller/static/semantic/src/site/globals/site.variables create mode 100755 controller/static/semantic/src/site/modules/accordion.overrides create mode 100755 controller/static/semantic/src/site/modules/accordion.variables create mode 100755 controller/static/semantic/src/site/modules/chatroom.overrides create mode 100755 controller/static/semantic/src/site/modules/chatroom.variables create mode 100755 controller/static/semantic/src/site/modules/checkbox.overrides create mode 100755 controller/static/semantic/src/site/modules/checkbox.variables create mode 100755 controller/static/semantic/src/site/modules/dimmer.overrides create mode 100755 controller/static/semantic/src/site/modules/dimmer.variables create mode 100755 controller/static/semantic/src/site/modules/dropdown.overrides create mode 100755 controller/static/semantic/src/site/modules/dropdown.variables create mode 100755 controller/static/semantic/src/site/modules/modal.overrides create mode 100755 controller/static/semantic/src/site/modules/modal.variables create mode 100755 controller/static/semantic/src/site/modules/nag.overrides create mode 100755 controller/static/semantic/src/site/modules/nag.variables create mode 100755 controller/static/semantic/src/site/modules/popup.overrides create mode 100755 controller/static/semantic/src/site/modules/popup.variables create mode 100755 controller/static/semantic/src/site/modules/progress.overrides create mode 100755 controller/static/semantic/src/site/modules/progress.variables create mode 100755 controller/static/semantic/src/site/modules/rating.overrides create mode 100755 controller/static/semantic/src/site/modules/rating.variables create mode 100755 controller/static/semantic/src/site/modules/search.overrides create mode 100755 controller/static/semantic/src/site/modules/search.variables create mode 100755 controller/static/semantic/src/site/modules/shape.overrides create mode 100755 controller/static/semantic/src/site/modules/shape.variables create mode 100755 controller/static/semantic/src/site/modules/sidebar.overrides create mode 100755 controller/static/semantic/src/site/modules/sidebar.variables create mode 100755 controller/static/semantic/src/site/modules/sticky.overrides create mode 100755 controller/static/semantic/src/site/modules/sticky.variables create mode 100755 controller/static/semantic/src/site/modules/tab.overrides create mode 100755 controller/static/semantic/src/site/modules/tab.variables create mode 100755 controller/static/semantic/src/site/modules/transition.overrides create mode 100755 controller/static/semantic/src/site/modules/transition.variables create mode 100755 controller/static/semantic/src/site/modules/video.overrides create mode 100755 controller/static/semantic/src/site/modules/video.variables create mode 100755 controller/static/semantic/src/site/views/ad.overrides create mode 100755 controller/static/semantic/src/site/views/ad.variables create mode 100755 controller/static/semantic/src/site/views/card.overrides create mode 100755 controller/static/semantic/src/site/views/card.variables create mode 100755 controller/static/semantic/src/site/views/comment.overrides create mode 100755 controller/static/semantic/src/site/views/comment.variables create mode 100755 controller/static/semantic/src/site/views/feed.overrides create mode 100755 controller/static/semantic/src/site/views/feed.variables create mode 100755 controller/static/semantic/src/site/views/item.overrides create mode 100755 controller/static/semantic/src/site/views/item.variables create mode 100755 controller/static/semantic/src/site/views/statistic.overrides create mode 100755 controller/static/semantic/src/site/views/statistic.variables create mode 100755 controller/static/semantic/src/theme.config create mode 100755 controller/static/semantic/src/theme.less create mode 100755 controller/static/semantic/src/themes/default/assets/fonts/icons.eot create mode 100755 controller/static/semantic/src/themes/default/assets/fonts/icons.svg create mode 100755 controller/static/semantic/src/themes/default/assets/fonts/icons.ttf create mode 100755 controller/static/semantic/src/themes/default/assets/fonts/icons.woff create mode 100755 controller/static/semantic/src/themes/default/assets/fonts/icons.woff2 create mode 100755 controller/static/semantic/src/themes/default/assets/images/flags.png create mode 100755 controller/static/semantic/src/themes/default/collections/breadcrumb.overrides create mode 100755 controller/static/semantic/src/themes/default/collections/breadcrumb.variables create mode 100755 controller/static/semantic/src/themes/default/collections/form.overrides create mode 100755 controller/static/semantic/src/themes/default/collections/form.variables create mode 100755 controller/static/semantic/src/themes/default/collections/grid.overrides create mode 100755 controller/static/semantic/src/themes/default/collections/grid.variables create mode 100755 controller/static/semantic/src/themes/default/collections/menu.overrides create mode 100755 controller/static/semantic/src/themes/default/collections/menu.variables create mode 100755 controller/static/semantic/src/themes/default/collections/message.overrides create mode 100755 controller/static/semantic/src/themes/default/collections/message.variables create mode 100755 controller/static/semantic/src/themes/default/collections/table.overrides create mode 100755 controller/static/semantic/src/themes/default/collections/table.variables create mode 100755 controller/static/semantic/src/themes/default/elements/button.overrides create mode 100755 controller/static/semantic/src/themes/default/elements/button.variables create mode 100755 controller/static/semantic/src/themes/default/elements/divider.overrides create mode 100755 controller/static/semantic/src/themes/default/elements/divider.variables create mode 100755 controller/static/semantic/src/themes/default/elements/flag.overrides create mode 100755 controller/static/semantic/src/themes/default/elements/flag.variables create mode 100755 controller/static/semantic/src/themes/default/elements/header.overrides create mode 100755 controller/static/semantic/src/themes/default/elements/header.variables create mode 100755 controller/static/semantic/src/themes/default/elements/icon.overrides create mode 100755 controller/static/semantic/src/themes/default/elements/icon.variables create mode 100755 controller/static/semantic/src/themes/default/elements/image.overrides create mode 100755 controller/static/semantic/src/themes/default/elements/image.variables create mode 100755 controller/static/semantic/src/themes/default/elements/input.overrides create mode 100755 controller/static/semantic/src/themes/default/elements/input.variables create mode 100755 controller/static/semantic/src/themes/default/elements/label.overrides create mode 100755 controller/static/semantic/src/themes/default/elements/label.variables create mode 100755 controller/static/semantic/src/themes/default/elements/list.overrides create mode 100755 controller/static/semantic/src/themes/default/elements/list.variables create mode 100755 controller/static/semantic/src/themes/default/elements/loader.overrides create mode 100755 controller/static/semantic/src/themes/default/elements/loader.variables create mode 100755 controller/static/semantic/src/themes/default/elements/rail.overrides create mode 100755 controller/static/semantic/src/themes/default/elements/rail.variables create mode 100755 controller/static/semantic/src/themes/default/elements/reveal.overrides create mode 100755 controller/static/semantic/src/themes/default/elements/reveal.variables create mode 100755 controller/static/semantic/src/themes/default/elements/segment.overrides create mode 100755 controller/static/semantic/src/themes/default/elements/segment.variables create mode 100755 controller/static/semantic/src/themes/default/elements/step.overrides create mode 100755 controller/static/semantic/src/themes/default/elements/step.variables create mode 100755 controller/static/semantic/src/themes/default/globals/reset.overrides create mode 100755 controller/static/semantic/src/themes/default/globals/reset.variables create mode 100755 controller/static/semantic/src/themes/default/globals/site.overrides create mode 100755 controller/static/semantic/src/themes/default/globals/site.variables create mode 100755 controller/static/semantic/src/themes/default/modules/accordion.overrides create mode 100755 controller/static/semantic/src/themes/default/modules/accordion.variables create mode 100755 controller/static/semantic/src/themes/default/modules/chatroom.overrides create mode 100755 controller/static/semantic/src/themes/default/modules/chatroom.variables create mode 100755 controller/static/semantic/src/themes/default/modules/checkbox.overrides create mode 100755 controller/static/semantic/src/themes/default/modules/checkbox.variables create mode 100755 controller/static/semantic/src/themes/default/modules/dimmer.overrides create mode 100755 controller/static/semantic/src/themes/default/modules/dimmer.variables create mode 100755 controller/static/semantic/src/themes/default/modules/dropdown.overrides create mode 100755 controller/static/semantic/src/themes/default/modules/dropdown.variables create mode 100755 controller/static/semantic/src/themes/default/modules/modal.overrides create mode 100755 controller/static/semantic/src/themes/default/modules/modal.variables create mode 100755 controller/static/semantic/src/themes/default/modules/nag.overrides create mode 100755 controller/static/semantic/src/themes/default/modules/nag.variables create mode 100755 controller/static/semantic/src/themes/default/modules/popup.overrides create mode 100755 controller/static/semantic/src/themes/default/modules/popup.variables create mode 100755 controller/static/semantic/src/themes/default/modules/progress.overrides create mode 100755 controller/static/semantic/src/themes/default/modules/progress.variables create mode 100755 controller/static/semantic/src/themes/default/modules/rating.overrides create mode 100755 controller/static/semantic/src/themes/default/modules/rating.variables create mode 100755 controller/static/semantic/src/themes/default/modules/search.overrides create mode 100755 controller/static/semantic/src/themes/default/modules/search.variables create mode 100755 controller/static/semantic/src/themes/default/modules/shape.overrides create mode 100755 controller/static/semantic/src/themes/default/modules/shape.variables create mode 100755 controller/static/semantic/src/themes/default/modules/sidebar.overrides create mode 100755 controller/static/semantic/src/themes/default/modules/sidebar.variables create mode 100755 controller/static/semantic/src/themes/default/modules/sticky.overrides create mode 100755 controller/static/semantic/src/themes/default/modules/sticky.variables create mode 100755 controller/static/semantic/src/themes/default/modules/tab.overrides create mode 100755 controller/static/semantic/src/themes/default/modules/tab.variables create mode 100755 controller/static/semantic/src/themes/default/modules/transition.overrides create mode 100755 controller/static/semantic/src/themes/default/modules/transition.variables create mode 100755 controller/static/semantic/src/themes/default/modules/video.overrides create mode 100755 controller/static/semantic/src/themes/default/modules/video.variables create mode 100755 controller/static/semantic/src/themes/default/views/ad.overrides create mode 100755 controller/static/semantic/src/themes/default/views/ad.variables create mode 100755 controller/static/semantic/src/themes/default/views/card.overrides create mode 100755 controller/static/semantic/src/themes/default/views/card.variables create mode 100755 controller/static/semantic/src/themes/default/views/comment.overrides create mode 100755 controller/static/semantic/src/themes/default/views/comment.variables create mode 100755 controller/static/semantic/src/themes/default/views/feed.overrides create mode 100755 controller/static/semantic/src/themes/default/views/feed.variables create mode 100755 controller/static/semantic/src/themes/default/views/item.overrides create mode 100755 controller/static/semantic/src/themes/default/views/item.variables create mode 100755 controller/static/semantic/src/themes/default/views/statistic.overrides create mode 100755 controller/static/semantic/src/themes/default/views/statistic.variables create mode 100755 controller/static/semantic/src/themes/shipyard/assets/fonts/icons.eot create mode 100755 controller/static/semantic/src/themes/shipyard/assets/fonts/icons.svg create mode 100755 controller/static/semantic/src/themes/shipyard/assets/fonts/icons.ttf create mode 100755 controller/static/semantic/src/themes/shipyard/assets/fonts/icons.woff create mode 100755 controller/static/semantic/src/themes/shipyard/assets/fonts/icons.woff2 create mode 100755 controller/static/semantic/src/themes/shipyard/assets/images/flags.png create mode 100755 controller/static/semantic/src/themes/shipyard/collections/breadcrumb.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/collections/breadcrumb.variables create mode 100755 controller/static/semantic/src/themes/shipyard/collections/form.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/collections/form.variables create mode 100755 controller/static/semantic/src/themes/shipyard/collections/grid.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/collections/grid.variables create mode 100755 controller/static/semantic/src/themes/shipyard/collections/menu.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/collections/menu.variables create mode 100755 controller/static/semantic/src/themes/shipyard/collections/message.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/collections/message.variables create mode 100755 controller/static/semantic/src/themes/shipyard/collections/table.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/collections/table.variables create mode 100755 controller/static/semantic/src/themes/shipyard/elements/button.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/elements/button.variables create mode 100755 controller/static/semantic/src/themes/shipyard/elements/divider.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/elements/divider.variables create mode 100755 controller/static/semantic/src/themes/shipyard/elements/flag.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/elements/flag.variables create mode 100755 controller/static/semantic/src/themes/shipyard/elements/header.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/elements/header.variables create mode 100755 controller/static/semantic/src/themes/shipyard/elements/icon.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/elements/icon.variables create mode 100755 controller/static/semantic/src/themes/shipyard/elements/image.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/elements/image.variables create mode 100755 controller/static/semantic/src/themes/shipyard/elements/input.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/elements/input.variables create mode 100755 controller/static/semantic/src/themes/shipyard/elements/label.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/elements/label.variables create mode 100755 controller/static/semantic/src/themes/shipyard/elements/list.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/elements/list.variables create mode 100755 controller/static/semantic/src/themes/shipyard/elements/loader.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/elements/loader.variables create mode 100755 controller/static/semantic/src/themes/shipyard/elements/rail.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/elements/rail.variables create mode 100755 controller/static/semantic/src/themes/shipyard/elements/reveal.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/elements/reveal.variables create mode 100755 controller/static/semantic/src/themes/shipyard/elements/segment.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/elements/segment.variables create mode 100755 controller/static/semantic/src/themes/shipyard/elements/step.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/elements/step.variables create mode 100755 controller/static/semantic/src/themes/shipyard/globals/reset.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/globals/reset.variables create mode 100755 controller/static/semantic/src/themes/shipyard/globals/site.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/globals/site.variables create mode 100755 controller/static/semantic/src/themes/shipyard/modules/accordion.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/modules/accordion.variables create mode 100755 controller/static/semantic/src/themes/shipyard/modules/chatroom.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/modules/chatroom.variables create mode 100755 controller/static/semantic/src/themes/shipyard/modules/checkbox.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/modules/checkbox.variables create mode 100755 controller/static/semantic/src/themes/shipyard/modules/dimmer.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/modules/dimmer.variables create mode 100755 controller/static/semantic/src/themes/shipyard/modules/dropdown.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/modules/dropdown.variables create mode 100755 controller/static/semantic/src/themes/shipyard/modules/modal.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/modules/modal.variables create mode 100755 controller/static/semantic/src/themes/shipyard/modules/nag.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/modules/nag.variables create mode 100755 controller/static/semantic/src/themes/shipyard/modules/popup.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/modules/popup.variables create mode 100755 controller/static/semantic/src/themes/shipyard/modules/progress.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/modules/progress.variables create mode 100755 controller/static/semantic/src/themes/shipyard/modules/rating.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/modules/rating.variables create mode 100755 controller/static/semantic/src/themes/shipyard/modules/search.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/modules/search.variables create mode 100755 controller/static/semantic/src/themes/shipyard/modules/shape.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/modules/shape.variables create mode 100755 controller/static/semantic/src/themes/shipyard/modules/sidebar.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/modules/sidebar.variables create mode 100755 controller/static/semantic/src/themes/shipyard/modules/sticky.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/modules/sticky.variables create mode 100755 controller/static/semantic/src/themes/shipyard/modules/tab.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/modules/tab.variables create mode 100755 controller/static/semantic/src/themes/shipyard/modules/transition.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/modules/transition.variables create mode 100755 controller/static/semantic/src/themes/shipyard/modules/video.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/modules/video.variables create mode 100755 controller/static/semantic/src/themes/shipyard/views/ad.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/views/ad.variables create mode 100755 controller/static/semantic/src/themes/shipyard/views/card.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/views/card.variables create mode 100755 controller/static/semantic/src/themes/shipyard/views/comment.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/views/comment.variables create mode 100755 controller/static/semantic/src/themes/shipyard/views/feed.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/views/feed.variables create mode 100755 controller/static/semantic/src/themes/shipyard/views/item.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/views/item.variables create mode 100755 controller/static/semantic/src/themes/shipyard/views/statistic.overrides create mode 100755 controller/static/semantic/src/themes/shipyard/views/statistic.variables create mode 100755 controller/static/semantic/tasks/README.md create mode 100755 controller/static/semantic/tasks/admin/components/create.js create mode 100755 controller/static/semantic/tasks/admin/components/init.js create mode 100755 controller/static/semantic/tasks/admin/components/update.js create mode 100755 controller/static/semantic/tasks/admin/distributions/create.js create mode 100755 controller/static/semantic/tasks/admin/distributions/init.js create mode 100755 controller/static/semantic/tasks/admin/distributions/update.js create mode 100755 controller/static/semantic/tasks/admin/publish.js create mode 100755 controller/static/semantic/tasks/admin/register.js create mode 100755 controller/static/semantic/tasks/admin/release.js create mode 100755 controller/static/semantic/tasks/build.js create mode 100755 controller/static/semantic/tasks/check-install.js create mode 100755 controller/static/semantic/tasks/clean.js create mode 100755 controller/static/semantic/tasks/collections/README.md create mode 100755 controller/static/semantic/tasks/collections/admin.js create mode 100755 controller/static/semantic/tasks/collections/internal.js create mode 100755 controller/static/semantic/tasks/config/admin/github.js create mode 100755 controller/static/semantic/tasks/config/admin/oauth.example.js create mode 100755 controller/static/semantic/tasks/config/admin/release.js create mode 100755 controller/static/semantic/tasks/config/admin/templates/README.md create mode 100755 controller/static/semantic/tasks/config/admin/templates/bower.json create mode 100755 controller/static/semantic/tasks/config/admin/templates/component-package.js create mode 100755 controller/static/semantic/tasks/config/admin/templates/composer.json create mode 100755 controller/static/semantic/tasks/config/admin/templates/css-package.js create mode 100755 controller/static/semantic/tasks/config/admin/templates/less-package.js create mode 100755 controller/static/semantic/tasks/config/admin/templates/package.json create mode 100755 controller/static/semantic/tasks/config/defaults.js create mode 100755 controller/static/semantic/tasks/config/docs.js create mode 100755 controller/static/semantic/tasks/config/npm/gulpfile.js create mode 100755 controller/static/semantic/tasks/config/project/config.js create mode 100755 controller/static/semantic/tasks/config/project/install.js create mode 100755 controller/static/semantic/tasks/config/project/release.js create mode 100755 controller/static/semantic/tasks/config/project/tasks.js create mode 100755 controller/static/semantic/tasks/config/user.js create mode 100755 controller/static/semantic/tasks/docs/build.js create mode 100755 controller/static/semantic/tasks/docs/serve.js create mode 100755 controller/static/semantic/tasks/install.js create mode 100755 controller/static/semantic/tasks/rtl/build.js create mode 100755 controller/static/semantic/tasks/rtl/watch.js create mode 100755 controller/static/semantic/tasks/version.js create mode 100755 controller/static/semantic/tasks/watch.js diff --git a/.gitignore b/.gitignore index ff5c9425b..17101c308 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ cli/cli controller/controller Godeps/_workspace +bower_components diff --git a/controller/static/app/config.routes.js b/controller/static/app/config.routes.js new file mode 100644 index 000000000..27d891e66 --- /dev/null +++ b/controller/static/app/config.routes.js @@ -0,0 +1,20 @@ +(function() { + 'use strict'; + + angular + .module('shipyard') + .config(getRoutes); + + getRoutes.$inject = ['$stateProvider', '$urlRouterProvider']; + + function getRoutes($stateProvider, $urlRouterProvider) { + $stateProvider + .state('error', { + url: '/error', + templateUrl: 'app/error/error.html', + authenticate: false + }); + + $urlRouterProvider.otherwise('/containers'); + } +})(); diff --git a/controller/static/app/containers/config.routes.js b/controller/static/app/containers/config.routes.js new file mode 100644 index 000000000..219850c7f --- /dev/null +++ b/controller/static/app/containers/config.routes.js @@ -0,0 +1,27 @@ +(function(){ + 'use strict'; + + angular + .module('shipyard.containers') + .config(getRoutes); + + getRoutes.$inject = ['$stateProvider', '$urlRouterProvider']; + + function getRoutes($stateProvider, $urlRouterProvider) { + $stateProvider + .state('containers', { + url: '/containers', + templateUrl: 'app/containers/containers.html', + controller: 'ContainersController', + controllerAs: 'vm', + authenticate: true, + resolve: { + containers: ['ContainersService', '$state', function (ContainersService, $state) { + return ContainersService.query().$promise.then(null, function(errorData) { + $state.go('error'); + }); + }] + } + }); + } +})(); diff --git a/controller/static/app/containers/containers.controller.js b/controller/static/app/containers/containers.controller.js new file mode 100644 index 000000000..c25d3bf60 --- /dev/null +++ b/controller/static/app/containers/containers.controller.js @@ -0,0 +1,13 @@ +(function(){ + 'use strict'; + + angular + .module('shipyard.containers') + .controller('ContainersController', ContainersController); + + ContainersController.$inject = ['containers']; + function ContainersController(containers) { + var vm = this; + vm.containers = containers; + } +})(); diff --git a/controller/static/app/containers/containers.html b/controller/static/app/containers/containers.html new file mode 100644 index 000000000..c3eab477f --- /dev/null +++ b/controller/static/app/containers/containers.html @@ -0,0 +1,38 @@ +
+
+ Deploy +
+
+ +
+
+ Containers +
+

There are no containers deployed.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
IdNodeNameImageStatusCommandCreated
{{c.Id | limitTo:8 }}{{c.Names[0].split('/')[1]}}{{c.Names[0].split('/')[2]}}{{c.Image}}{{c.Status}}{{c.Command}}{{c.Created * 1000 | date:'yyyy-MM-dd HH:mm:ss Z'}}
+
diff --git a/controller/static/app/containers/containers.module.js b/controller/static/app/containers/containers.module.js new file mode 100644 index 000000000..5565556d1 --- /dev/null +++ b/controller/static/app/containers/containers.module.js @@ -0,0 +1,10 @@ +(function(){ + 'use strict'; + + angular + .module('shipyard.containers', [ + 'ngResource', + 'ui.router' + ]); + +})(); \ No newline at end of file diff --git a/controller/static/app/containers/containers.service.js b/controller/static/app/containers/containers.service.js new file mode 100644 index 000000000..9557270a7 --- /dev/null +++ b/controller/static/app/containers/containers.service.js @@ -0,0 +1,13 @@ +(function(){ + 'use strict'; + + angular + .module('shipyard.containers') + .factory('ContainersService', ContainersService); + + ContainersService.$inject = ['$resource']; + function ContainersService($resource) { + return $resource('/containers/json'); + } + +})(); diff --git a/controller/static/app/error/error.html b/controller/static/app/error/error.html new file mode 100644 index 000000000..6ef1fcbf3 --- /dev/null +++ b/controller/static/app/error/error.html @@ -0,0 +1,4 @@ +
+

Error

+

An error occurred, please try again later...

+
diff --git a/controller/static/app/events/config.routes.js b/controller/static/app/events/config.routes.js new file mode 100644 index 000000000..b8c531bda --- /dev/null +++ b/controller/static/app/events/config.routes.js @@ -0,0 +1,27 @@ +(function(){ + 'use strict'; + + angular + .module('shipyard.events') + .config(getRoutes); + + getRoutes.$inject = ['$stateProvider', '$urlRouterProvider']; + + function getRoutes($stateProvider, $urlRouterProvider) { + $stateProvider + .state('events', { + url: '/events', + templateUrl: 'app/events/events.html', + controller: 'EventsController', + controllerAs: 'vm', + authenticate: true, + resolve: { + events: ['EventsService', '$state', function (EventsService, $state) { + return EventsService.query().$promise.then(null, function(errorData) { + $state.go('error'); + }); + }] + } + }); + } +})(); diff --git a/controller/static/app/events/events.controller.js b/controller/static/app/events/events.controller.js new file mode 100644 index 000000000..fd0815949 --- /dev/null +++ b/controller/static/app/events/events.controller.js @@ -0,0 +1,13 @@ +(function(){ + 'use strict'; + + angular + .module('shipyard.events') + .controller('EventsController', EventsController); + + EventsController.$inject = ['events']; + function EventsController(events) { + var vm = this; + vm.events = events; + } +})(); diff --git a/controller/static/app/events/events.html b/controller/static/app/events/events.html new file mode 100644 index 000000000..1148416eb --- /dev/null +++ b/controller/static/app/events/events.html @@ -0,0 +1,32 @@ +
+
+ +
+
+ Events +
+

There are no events recorded.

+
+
+ + + + + + + + + + + + + + + + + + + + +
TimeContainerEngineTypeTags
{{e.time}}{{e.container.id | limitTo:8}}{{e.engine.id}}{{e.type}}{{e.tags.join(', ')}}
+
diff --git a/controller/static/app/events/events.module.js b/controller/static/app/events/events.module.js new file mode 100644 index 000000000..09b4d95f0 --- /dev/null +++ b/controller/static/app/events/events.module.js @@ -0,0 +1,10 @@ +(function(){ + 'use strict'; + + angular + .module('shipyard.events', [ + 'ngResource', + 'ui.router', + ]); + +})(); diff --git a/controller/static/app/events/events.service.js b/controller/static/app/events/events.service.js new file mode 100644 index 000000000..b3eb71f04 --- /dev/null +++ b/controller/static/app/events/events.service.js @@ -0,0 +1,12 @@ +(function(){ + 'use strict'; + + angular + .module('shipyard.events') + .factory('EventsService', EventsService); + + EventsService.$inject = ['$resource']; + function EventsService($resource) { + return $resource('/api/events'); + } +})(); diff --git a/controller/static/app/layout/menu.html b/controller/static/app/layout/menu.html new file mode 100644 index 000000000..9577d0c95 --- /dev/null +++ b/controller/static/app/layout/menu.html @@ -0,0 +1,19 @@ +
\ No newline at end of file diff --git a/controller/static/app/login/config.routes.js b/controller/static/app/login/config.routes.js new file mode 100644 index 000000000..f75d5c03e --- /dev/null +++ b/controller/static/app/login/config.routes.js @@ -0,0 +1,27 @@ +(function(){ + 'use strict'; + + angular + .module('shipyard.login') + .config(getRoutes); + + getRoutes.$inject = ['$stateProvider', '$urlRouterProvider']; + + function getRoutes($stateProvider, $urlRouterProvider) { + $stateProvider + .state('login', { + url: '/login', + templateUrl: 'app/login/login.html', + controller: 'LoginController', + controllerAs: 'vm', + authenticate: false + }) + .state('logout', { + url: '/logout', + controller: 'LogoutController', + controllerAs: 'vm', + authenticate: true + }); + + } +})(); diff --git a/controller/static/app/login/login.controller.js b/controller/static/app/login/login.controller.js new file mode 100644 index 000000000..fbdbb69ae --- /dev/null +++ b/controller/static/app/login/login.controller.js @@ -0,0 +1,24 @@ +(function(){ + 'use strict'; + + angular + .module('shipyard.login') + .controller('LoginController', LoginController); + + LoginController.$inject = ['AuthService', '$state']; + function LoginController(AuthService, $state) { + var vm = this; + vm.username = ""; + vm.password = ""; + vm.login = login; + + function login() { + vm.error = ""; + AuthService.login({ + username: vm.username, + password: vm.password + }); + } + } +})(); + diff --git a/controller/static/app/login/login.html b/controller/static/app/login/login.html new file mode 100644 index 000000000..5dcda5705 --- /dev/null +++ b/controller/static/app/login/login.html @@ -0,0 +1,53 @@ +
+
+
+ +
+
+ {{vm.error}} +
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
Login
+
+
+
+ + diff --git a/controller/static/app/login/login.module.js b/controller/static/app/login/login.module.js new file mode 100644 index 000000000..9b7c80832 --- /dev/null +++ b/controller/static/app/login/login.module.js @@ -0,0 +1,12 @@ +(function(){ + 'use strict'; + + angular + .module('shipyard.login', [ + 'shipyard.services', + 'ngResource', + 'ui.router' + ]); + +})(); + diff --git a/controller/static/app/login/logout.controller.js b/controller/static/app/login/logout.controller.js new file mode 100644 index 000000000..2e3a3c942 --- /dev/null +++ b/controller/static/app/login/logout.controller.js @@ -0,0 +1,15 @@ +(function(){ + 'use strict'; + + angular + .module('shipyard.login') + .controller('LogoutController', LogoutController); + + LogoutController.$inject = ['AuthService', '$state']; + function LogoutController(AuthService, $state) { + var vm = this; + AuthService.logout(); + $state.transitionTo('login'); + } +})(); + diff --git a/controller/static/app/services/auth.service.js b/controller/static/app/services/auth.service.js new file mode 100644 index 000000000..957c575fe --- /dev/null +++ b/controller/static/app/services/auth.service.js @@ -0,0 +1,31 @@ +(function(){ + 'use strict'; + + angular + .module('shipyard.services') + .factory('AuthService', AuthService); + + AuthService.$inject = ['$http', '$state']; + function AuthService($http, $state) { + return { + login: function(credentials) { + $http + .post('/auth/login', credentials) + .success(function(data, status, headers, config) { + localStorage.setItem('X-Access-Token', credentials.username + ':' + data.auth_token); + $state.transitionTo('containers'); + }) + .error(function(data, status, headers, config) { + localStorage.removeItem('X-Access-Token'); + }); + }, + logout: function() { + localStorage.removeItem('X-Access-Token'); + }, + isLoggedIn: function() { + return localStorage.getItem('X-Access-Token') != null; + } + }; + } +})(); + diff --git a/controller/static/app/services/services.module.js b/controller/static/app/services/services.module.js new file mode 100644 index 000000000..22c03d8fb --- /dev/null +++ b/controller/static/app/services/services.module.js @@ -0,0 +1,10 @@ +(function(){ + 'use strict'; + + angular + .module('shipyard.services', [ + ]); + +})(); + + diff --git a/controller/static/app/shipyard.config.js b/controller/static/app/shipyard.config.js new file mode 100644 index 000000000..718d8be20 --- /dev/null +++ b/controller/static/app/shipyard.config.js @@ -0,0 +1,11 @@ +(function(){ + 'use strict'; + + angular + .module('shipyard') + .config(configure); + + configure.$inject = []; + + function configure() {} +})(); \ No newline at end of file diff --git a/controller/static/app/shipyard.js b/controller/static/app/shipyard.js new file mode 100644 index 000000000..99f1d6625 --- /dev/null +++ b/controller/static/app/shipyard.js @@ -0,0 +1,22 @@ +(function() { + 'use strict'; + + angular + .module('shipyard') + .run( + ['$rootScope', '$state', '$stateParams', '$window', 'AuthService', + function ($rootScope, $state, $stateParams, $window, AuthService) { + $rootScope.$state = $state; + $rootScope.$stateParams = $stateParams; + + $rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams){ + if (toState.authenticate && !AuthService.isLoggedIn()){ + $state.transitionTo('login'); + event.preventDefault(); + } + }); + + } + ] + ) +})(); diff --git a/controller/static/app/shipyard.jwt.js b/controller/static/app/shipyard.jwt.js new file mode 100644 index 000000000..b9cc9a252 --- /dev/null +++ b/controller/static/app/shipyard.jwt.js @@ -0,0 +1,22 @@ +(function() { + 'use strict'; + + angular + .module('shipyard') + .config([ + '$httpProvider', + 'jwtInterceptorProvider', + function ($httpProvider, jwtInterceptorProvider) { + jwtInterceptorProvider.tokenGetter = function() { + // Temporary solution to get angular-jwt to use our header format + jwtInterceptorProvider.authHeader = "X-Access-Token"; + jwtInterceptorProvider.authPrefix = ""; + + return localStorage.getItem('X-Access-Token'); + }; + + $httpProvider.interceptors.push('jwtInterceptor'); + } + ]); +})(); + diff --git a/controller/static/app/shipyard.module.js b/controller/static/app/shipyard.module.js new file mode 100644 index 000000000..456fb31a2 --- /dev/null +++ b/controller/static/app/shipyard.module.js @@ -0,0 +1,14 @@ +(function(){ + 'use strict'; + + angular + .module('shipyard', [ + 'shipyard.services', + 'shipyard.login', + 'shipyard.containers', + 'shipyard.events', + 'angular-jwt', + 'ui.router' + ]); + +})(); diff --git a/controller/static/bower.json b/controller/static/bower.json new file mode 100644 index 000000000..5f903b2e8 --- /dev/null +++ b/controller/static/bower.json @@ -0,0 +1,24 @@ +{ + "name": "shipyard-ui", + "version": "3.0.0", + "homepage": "https://github.com/tombee/shipyard-ui", + "authors": [ + "Evan Hazlett", + "Tom Barlow " + ], + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "angular": "~1.3.15", + "ui-router": "~0.2.13", + "jquery": "~2.1.3", + "angular-resource": "~1.3.15", + "angular-jwt": "~0.0.6" + } +} diff --git a/controller/static/gulpfile.js b/controller/static/gulpfile.js new file mode 100755 index 000000000..b4115f6ae --- /dev/null +++ b/controller/static/gulpfile.js @@ -0,0 +1,49 @@ +/******************************* + Set-up +*******************************/ + +var + gulp = require('gulp-help')(require('gulp')), + + // read user config to know what task to load + config = require('./semantic/tasks/config/user'), + + // import tasks + build = require('./semantic/tasks/build'), + clean = require('./semantic/tasks/clean'), + version = require('./semantic/tasks/version'), + watch = require('./semantic/tasks/watch'), + bower = require('gulp-bower'), + livereload = require('gulp-livereload'), + + // docs tasks + serveDocs = require('./semantic/tasks/docs/serve'), + buildDocs = require('./semantic/tasks/docs/build'), + + // rtl + buildRTL = require('./semantic/tasks/rtl/build'), + watchRTL = require('./semantic/tasks/rtl/watch') +; + +/*-------------- + Common +---------------*/ + +gulp.task('default', false, [ + 'bower', 'watch' +]); + +gulp.task('watch', 'Watch for site/theme changes', watch); +gulp.task('build', 'Builds all files from source', build); + +gulp.task('bower', function() { + return bower() +}); + +gulp.task('serve', function() { + livereload.listen({port: 8080}); +}); + +gulp.task('clean', 'Clean dist folder', clean); +gulp.task('version', 'Displays current version of Semantic', version); + diff --git a/controller/static/index.html b/controller/static/index.html new file mode 100644 index 000000000..07a04aa8e --- /dev/null +++ b/controller/static/index.html @@ -0,0 +1,105 @@ + + + + + + + + Shipyard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+
+
+
+
+
+
+

Shipyard © 2015. All rights reserved.

+ +
+
+
+ +
+
+ +
+
+ Just one second +
+

We're fetching that content for you.

+
+
+
+ + diff --git a/controller/static/main.css b/controller/static/main.css new file mode 100644 index 000000000..bc1097313 --- /dev/null +++ b/controller/static/main.css @@ -0,0 +1,25 @@ +#menu .header.item { + font-size: 20px; + font-family: 'Poiret One', sans-serif; + background-color: rgba(0, 68, 91, 1) !important; + display: inline-block; + text-transform: none; +} + +#menu .item { + font-size: 20px; + text-transform: uppercase; +} + +.ui.grid.main{ + margin-top: 40px; + padding: 20px; +} + +.centered { + position: absolute; + top: 50%; + left: 50%; + margin-right: -50%; + text-align: center; +} diff --git a/controller/static/package.json b/controller/static/package.json new file mode 100644 index 000000000..928500757 --- /dev/null +++ b/controller/static/package.json @@ -0,0 +1,51 @@ +{ + "name": "shipyard-ui", + "version": "3.0.0", + "description": "", + "main": "gulpfile.js", + "dependencies": { + "better-console": "^0.2.4", + "del": "^1.1.1", + "extend": "^2.0.0", + "gulp": "^3.8.11", + "gulp-autoprefixer": "^2.1.0", + "gulp-bower": "^0.0.10", + "gulp-chmod": "^1.2.0", + "gulp-clone": "^1.0.0", + "gulp-concat": "^2.5.2", + "gulp-concat-css": "^2.2.0", + "gulp-copy": "^0.0.2", + "gulp-flatten": "^0.0.4", + "gulp-header": "^1.2.2", + "gulp-help": "^1.3.4", + "gulp-if": "^1.2.5", + "gulp-less": "^3.0.2", + "gulp-minify-css": "^0.5.1", + "gulp-notify": "^2.2.0", + "gulp-plumber": "^0.6.6", + "gulp-print": "^1.1.0", + "gulp-rename": "^1.2.2", + "gulp-replace": "^0.5.3", + "gulp-rtlcss": "^0.1.4", + "gulp-uglify": "^1.1.0", + "gulp-util": "^3.0.4", + "gulp-watch": "^4.2.2", + "require-dot-file": "^0.4.0", + "semantic-ui": "^1.11.6" + }, + "devDependencies": { + "gulp-livereload": "^3.8.0" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com/tombee/shipyard-ui" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/tombee/shipyard-ui/issues" + } +} diff --git a/controller/static/semantic.json b/controller/static/semantic.json new file mode 100755 index 000000000..5501832f2 --- /dev/null +++ b/controller/static/semantic.json @@ -0,0 +1,21 @@ +{ + "base": "semantic/", + "paths": { + "source": { + "config": "src/theme.config", + "definitions": "src/definitions/", + "site": "src/site/", + "themes": "src/themes/" + }, + "output": { + "packaged": "dist/", + "uncompressed": "dist/components/", + "compressed": "dist/components/", + "themes": "dist/themes/" + }, + "clean": "dist/" + }, + "permission": false, + "rtl": false, + "version": "1.11.6" +} \ No newline at end of file diff --git a/controller/static/semantic/dist/components/accordion.css b/controller/static/semantic/dist/components/accordion.css new file mode 100755 index 000000000..c418c606c --- /dev/null +++ b/controller/static/semantic/dist/components/accordion.css @@ -0,0 +1,256 @@ +/*! + * # Semantic UI x.x - Accordion + * http://github.com/semantic-org/semantic-ui/ + * + * + * Copyright 2014 Contributors + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ + + +/******************************* + Accordion +*******************************/ + +.ui.accordion, +.ui.accordion .accordion { + max-width: 100%; + font-size: 1em; +} +.ui.accordion .accordion { + margin: 1em 0em 0em; + padding: 0em; +} + +/* Title */ +.ui.accordion .title, +.ui.accordion .accordion .title { + cursor: pointer; +} + +/* Default Styling */ +.ui.accordion .title:not(.ui) { + padding: 0.5em 0em; + font-family: 'Roboto', 'Helvetica Neue', Arial, Helvetica, sans-serif; + font-size: 1em; + color: rgba(0, 0, 0, 0.8); +} + +/* Content */ +.ui.accordion .title ~ .content, +.ui.accordion .accordion .title ~ .content { + display: none; +} + +/* Default Styling */ +.ui.accordion:not(.styled) .title ~ .content:not(.ui), +.ui.accordion:not(.styled) .accordion .title ~ .content:not(.ui) { + margin: 0em; + padding: 0.5em 0em 1em; +} +.ui.accordion:not(.styled) .title ~ .content:not(.ui):last-child { + padding-bottom: 0em; +} + +/* Arrow */ +.ui.accordion .title .dropdown.icon, +.ui.accordion .accordion .title .dropdown.icon { + display: inline-block; + float: none; + opacity: 1; + width: 1.25em; + height: 1em; + margin: 0em 0.25rem 0em 0rem; + padding: 0em; + font-size: 1em; + -webkit-transition: -webkit-transform 0.2s ease, opacity 0.2s ease; + transition: transform 0.2s ease, opacity 0.2s ease; + vertical-align: baseline; + -webkit-transform: none; + -ms-transform: none; + transform: none; +} + +/*-------------- + Coupling +---------------*/ + + +/* Menu */ +.ui.accordion.menu .item .title { + display: block; + padding: 0em; +} +.ui.accordion.menu .item .title > .dropdown.icon { + float: right; + margin: 0.165em 0em 0em 1em; + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} + +/* Header */ +.ui.accordion .ui.header .dropdown.icon { + font-size: 1em; + margin: 0em 0.25rem 0em 0rem; +} + + +/******************************* + States +*******************************/ + +.ui.accordion .active.title .dropdown.icon, +.ui.accordion .accordion .active.title .dropdown.icon { + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} +.ui.accordion.menu .item .active.title > .dropdown.icon { + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} + + +/******************************* + Types +*******************************/ + + +/*-------------- + Styled +---------------*/ + +.ui.styled.accordion { + width: 600px; +} +.ui.styled.accordion, +.ui.styled.accordion .accordion { + border-radius: 0.2857rem; + background: #ffffff; + box-shadow: 0px 1px 2px 0 rgba(0, 0, 0, 0.05), 0px 0px 0px 1px rgba(39, 41, 43, 0.15); +} +.ui.styled.accordion .title, +.ui.styled.accordion .accordion .title { + margin: 0em; + padding: 0.75em 1em; + color: rgba(0, 0, 0, 0.4); + font-weight: bold; + border-top: 1px solid rgba(39, 41, 43, 0.15); + -webkit-transition: background 0.2s ease, color 0.2s ease; + transition: background 0.2s ease, color 0.2s ease; +} +.ui.styled.accordion > .title:first-child, +.ui.styled.accordion .accordion .title:first-child { + border-top: none; +} + +/* Content */ +.ui.styled.accordion .content, +.ui.styled.accordion .accordion .content { + margin: 0em; + padding: 0.5em 1em 1.5em; +} +.ui.styled.accordion .accordion .content { + padding: 0em; + padding: 0.5em 1em 1.5em; +} + +/* Hover */ +.ui.styled.accordion .title:hover, +.ui.styled.accordion .active.title, +.ui.styled.accordion .accordion .title:hover, +.ui.styled.accordion .accordion .active.title { + background: transparent; + color: rgba(0, 0, 0, 0.8); +} +.ui.styled.accordion .accordion .title:hover, +.ui.styled.accordion .accordion .active.title { + background: transparent; + color: rgba(0, 0, 0, 0.8); +} + +/* Active */ +.ui.styled.accordion .active.title { + background: transparent; + color: rgba(0, 0, 0, 0.8); +} +.ui.styled.accordion .accordion .active.title { + background: transparent; + color: rgba(0, 0, 0, 0.8); +} + + +/******************************* + States +*******************************/ + + +/*-------------- + Active +---------------*/ + +.ui.accordion .active.content, +.ui.accordion .accordion .active.content { + display: block; +} + + +/******************************* + Variations +*******************************/ + + +/*-------------- + Fluid +---------------*/ + +.ui.fluid.accordion, +.ui.fluid.accordion .accordion { + width: 100%; +} + +/*-------------- + Inverted +---------------*/ + +.ui.inverted.accordion .title:not(.ui) { + color: #ffffff; +} + + +/******************************* + Theme Overrides +*******************************/ + +@font-face { + font-family: 'Accordion'; + src: url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMggjB5AAAAC8AAAAYGNtYXAPfOIKAAABHAAAAExnYXNwAAAAEAAAAWgAAAAIZ2x5Zryj6HgAAAFwAAAAyGhlYWT/0IhHAAACOAAAADZoaGVhApkB5wAAAnAAAAAkaG10eAJuABIAAAKUAAAAGGxvY2EAjABWAAACrAAAAA5tYXhwAAgAFgAAArwAAAAgbmFtZfC1n04AAALcAAABPHBvc3QAAwAAAAAEGAAAACAAAwIAAZAABQAAAUwBZgAAAEcBTAFmAAAA9QAZAIQAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADw2gHg/+D/4AHgACAAAAABAAAAAAAAAAAAAAAgAAAAAAACAAAAAwAAABQAAwABAAAAFAAEADgAAAAKAAgAAgACAAEAIPDa//3//wAAAAAAIPDZ//3//wAB/+MPKwADAAEAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQASAEkAtwFuABMAADc0PwE2FzYXFh0BFAcGJwYvASY1EgaABQgHBQYGBQcIBYAG2wcGfwcBAQcECf8IBAcBAQd/BgYAAAAAAQAAAEkApQFuABMAADcRNDc2MzIfARYVFA8BBiMiJyY1AAUGBwgFgAYGgAUIBwYFWwEACAUGBoAFCAcFgAYGBQcAAAABAAAAAQAAqWYls18PPPUACwIAAAAAAM/9o+4AAAAAz/2j7gAAAAAAtwFuAAAACAACAAAAAAAAAAEAAAHg/+AAAAIAAAAAAAC3AAEAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAAAAQAAAAC3ABIAtwAAAAAAAAAKABQAHgBCAGQAAAABAAAABgAUAAEAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAADgCuAAEAAAAAAAEADAAAAAEAAAAAAAIADgBAAAEAAAAAAAMADAAiAAEAAAAAAAQADABOAAEAAAAAAAUAFgAMAAEAAAAAAAYABgAuAAEAAAAAAAoANABaAAMAAQQJAAEADAAAAAMAAQQJAAIADgBAAAMAAQQJAAMADAAiAAMAAQQJAAQADABOAAMAAQQJAAUAFgAMAAMAAQQJAAYADAA0AAMAAQQJAAoANABaAHIAYQB0AGkAbgBnAFYAZQByAHMAaQBvAG4AIAAxAC4AMAByAGEAdABpAG4AZ3JhdGluZwByAGEAdABpAG4AZwBSAGUAZwB1AGwAYQByAHIAYQB0AGkAbgBnAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) format('truetype'), url(data:application/font-woff;charset=utf-8;base64,d09GRk9UVE8AAASwAAoAAAAABGgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRkYgAAAA9AAAAS0AAAEtFpovuE9TLzIAAAIkAAAAYAAAAGAIIweQY21hcAAAAoQAAABMAAAATA984gpnYXNwAAAC0AAAAAgAAAAIAAAAEGhlYWQAAALYAAAANgAAADb/0IhHaGhlYQAAAxAAAAAkAAAAJAKZAedobXR4AAADNAAAABgAAAAYAm4AEm1heHAAAANMAAAABgAAAAYABlAAbmFtZQAAA1QAAAE8AAABPPC1n05wb3N0AAAEkAAAACAAAAAgAAMAAAEABAQAAQEBB3JhdGluZwABAgABADr4HAL4GwP4GAQeCgAZU/+Lix4KABlT/4uLDAeLa/iU+HQFHQAAAHkPHQAAAH4RHQAAAAkdAAABJBIABwEBBw0PERQZHnJhdGluZ3JhdGluZ3UwdTF1MjB1RjBEOXVGMERBAAACAYkABAAGAQEEBwoNVp38lA78lA78lA77lA773Z33bxWLkI2Qj44I9xT3FAWOj5CNkIuQi4+JjoePiI2Gi4YIi/uUBYuGiYeHiIiHh4mGi4aLho2Ijwj7FPcUBYeOiY+LkAgO+92L5hWL95QFi5CNkI6Oj4+PjZCLkIuQiY6HCPcU+xQFj4iNhouGi4aJh4eICPsU+xQFiIeGiYaLhouHjYePiI6Jj4uQCA74lBT4lBWLDAoAAAAAAwIAAZAABQAAAUwBZgAAAEcBTAFmAAAA9QAZAIQAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADw2gHg/+D/4AHgACAAAAABAAAAAAAAAAAAAAAgAAAAAAACAAAAAwAAABQAAwABAAAAFAAEADgAAAAKAAgAAgACAAEAIPDa//3//wAAAAAAIPDZ//3//wAB/+MPKwADAAEAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAEAADfYOJZfDzz1AAsCAAAAAADP/aPuAAAAAM/9o+4AAAAAALcBbgAAAAgAAgAAAAAAAAABAAAB4P/gAAACAAAAAAAAtwABAAAAAAAAAAAAAAAAAAAABgAAAAAAAAAAAAAAAAEAAAAAtwASALcAAAAAUAAABgAAAAAADgCuAAEAAAAAAAEADAAAAAEAAAAAAAIADgBAAAEAAAAAAAMADAAiAAEAAAAAAAQADABOAAEAAAAAAAUAFgAMAAEAAAAAAAYABgAuAAEAAAAAAAoANABaAAMAAQQJAAEADAAAAAMAAQQJAAIADgBAAAMAAQQJAAMADAAiAAMAAQQJAAQADABOAAMAAQQJAAUAFgAMAAMAAQQJAAYADAA0AAMAAQQJAAoANABaAHIAYQB0AGkAbgBnAFYAZQByAHMAaQBvAG4AIAAxAC4AMAByAGEAdABpAG4AZ3JhdGluZwByAGEAdABpAG4AZwBSAGUAZwB1AGwAYQByAHIAYQB0AGkAbgBnAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) format('woff'); + font-weight: normal; + font-style: normal; +} + +/* Dropdown Icon */ +.ui.accordion .title .dropdown.icon, +.ui.accordion .accordion .title .dropdown.icon { + font-family: Accordion; + line-height: 1; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + font-weight: normal; + font-style: normal; + text-align: center; +} +.ui.accordion .title .dropdown.icon:before, +.ui.accordion .accordion .title .dropdown.icon:before { + content: '\f0da' /*rtl:'\f0d9'*/; +} + + +/******************************* + User Overrides +*******************************/ + diff --git a/controller/static/semantic/dist/components/accordion.js b/controller/static/semantic/dist/components/accordion.js new file mode 100755 index 000000000..72b16030f --- /dev/null +++ b/controller/static/semantic/dist/components/accordion.js @@ -0,0 +1,578 @@ +/*! + * # Semantic UI x.x - Accordion + * http://github.com/semantic-org/semantic-ui/ + * + * + * Copyright 2014 Contributors + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ + +;(function ($, window, document, undefined) { + +"use strict"; + +$.fn.accordion = function(parameters) { + var + $allModules = $(this), + + time = new Date().getTime(), + performance = [], + + query = arguments[0], + methodInvoked = (typeof query == 'string'), + queryArguments = [].slice.call(arguments, 1), + + requestAnimationFrame = window.requestAnimationFrame + || window.mozRequestAnimationFrame + || window.webkitRequestAnimationFrame + || window.msRequestAnimationFrame + || function(callback) { setTimeout(callback, 0); }, + + returnedValue + ; + $allModules + .each(function() { + var + settings = ( $.isPlainObject(parameters) ) + ? $.extend(true, {}, $.fn.accordion.settings, parameters) + : $.extend({}, $.fn.accordion.settings), + + className = settings.className, + namespace = settings.namespace, + selector = settings.selector, + error = settings.error, + + eventNamespace = '.' + namespace, + moduleNamespace = 'module-' + namespace, + moduleSelector = $allModules.selector || '', + + $module = $(this), + $title = $module.find(selector.title), + $content = $module.find(selector.content), + + element = this, + instance = $module.data(moduleNamespace), + observer, + module + ; + + module = { + + initialize: function() { + module.debug('Initializing', $module); + module.bind.events(); + module.observeChanges(); + module.instantiate(); + }, + + instantiate: function() { + instance = module; + $module + .data(moduleNamespace, module) + ; + }, + + destroy: function() { + module.debug('Destroying previous instance', $module); + $module + .off(eventNamespace) + .removeData(moduleNamespace) + ; + }, + + refresh: function() { + $title = $module.find(selector.title); + $content = $module.find(selector.content); + }, + + observeChanges: function() { + if('MutationObserver' in window) { + observer = new MutationObserver(function(mutations) { + module.debug('DOM tree modified, updating selector cache'); + module.refresh(); + }); + observer.observe(element, { + childList : true, + subtree : true + }); + module.debug('Setting up mutation observer', observer); + } + }, + + bind: { + events: function() { + module.debug('Binding delegated events'); + $module + .on('click' + eventNamespace, selector.trigger, module.event.click) + ; + } + }, + + event: { + click: function() { + module.toggle.call(this); + } + }, + + toggle: function(query) { + var + $activeTitle = (query !== undefined) + ? (typeof query === 'number') + ? $title.eq(query) + : $(query).closest(selector.title) + : $(this).closest(selector.title), + $activeContent = $activeTitle.next($content), + isAnimating = $activeContent.hasClass(className.animating), + isActive = $activeContent.hasClass(className.active), + isOpen = (isActive && !isAnimating), + isOpening = (!isActive && isAnimating) + ; + module.debug('Toggling visibility of content', $activeTitle); + if(isOpen || isOpening) { + if(settings.collapsible) { + module.close.call($activeTitle); + } + else { + module.debug('Cannot close accordion content collapsing is disabled'); + } + } + else { + module.open.call($activeTitle); + } + }, + + open: function(query) { + var + $activeTitle = (query !== undefined) + ? (typeof query === 'number') + ? $title.eq(query) + : $(query).closest(selector.title) + : $(this).closest(selector.title), + $activeContent = $activeTitle.next($content), + isAnimating = $activeContent.hasClass(className.animating), + isActive = $activeContent.hasClass(className.active), + isUnopen = (!isActive && !isAnimating) + ; + if(isUnopen) { + module.debug('Opening accordion content', $activeTitle); + if(settings.exclusive) { + module.closeOthers.call($activeTitle); + } + $activeTitle + .addClass(className.active) + ; + $activeContent.addClass(className.animating); + if(settings.animateChildren) { + if($.fn.transition !== undefined && $module.transition('is supported')) { + $activeContent + .children() + .transition({ + animation : 'fade in', + queue : false, + useFailSafe : true, + debug : settings.debug, + verbose : settings.verbose, + duration : settings.duration + }) + ; + } + else { + $activeContent + .children() + .stop(true) + .animate({ + opacity: 1 + }, settings.duration, module.resetOpacity) + ; + } + } + $activeContent + .stop(true) + .slideDown(settings.duration, settings.easing, function() { + $activeContent + .removeClass(className.animating) + .addClass(className.active) + ; + module.reset.display.call(this); + settings.onOpen.call(this); + settings.onChange.call(this); + }) + ; + } + }, + + close: function(query) { + var + $activeTitle = (query !== undefined) + ? (typeof query === 'number') + ? $title.eq(query) + : $(query).closest(selector.title) + : $(this).closest(selector.title), + $activeContent = $activeTitle.next($content), + isAnimating = $activeContent.hasClass(className.animating), + isActive = $activeContent.hasClass(className.active), + isOpening = (!isActive && isAnimating), + isClosing = (isActive && isAnimating) + ; + if((isActive || isOpening) && !isClosing) { + module.debug('Closing accordion content', $activeContent); + $activeTitle + .removeClass(className.active) + ; + $activeContent + .addClass(className.animating) + ; + if(settings.animateChildren) { + if($.fn.transition !== undefined && $module.transition('is supported')) { + $activeContent + .children() + .transition({ + animation : 'fade out', + queue : false, + useFailSafe : true, + debug : settings.debug, + verbose : settings.verbose, + duration : settings.duration + }) + ; + } + else { + $activeContent + .children() + .stop(true) + .animate({ + opacity: 0 + }, settings.duration, module.resetOpacity) + ; + } + } + $activeContent + .stop(true) + .slideUp(settings.duration, settings.easing, function() { + $activeContent + .removeClass(className.animating) + .removeClass(className.active) + ; + module.reset.display.call(this); + settings.onClose.call(this); + settings.onChange.call(this); + }) + ; + } + }, + + closeOthers: function(index) { + var + $activeTitle = (index !== undefined) + ? $title.eq(index) + : $(this).closest(selector.title), + $parentTitles = $activeTitle.parents(selector.content).prev(selector.title), + $activeAccordion = $activeTitle.closest(selector.accordion), + activeSelector = selector.title + '.' + className.active + ':visible', + activeContent = selector.content + '.' + className.active + ':visible', + $openTitles, + $nestedTitles, + $openContents + ; + if(settings.closeNested) { + $openTitles = $activeAccordion.find(activeSelector).not($parentTitles); + $openContents = $openTitles.next($content); + } + else { + $openTitles = $activeAccordion.find(activeSelector).not($parentTitles); + $nestedTitles = $activeAccordion.find(activeContent).find(activeSelector).not($parentTitles); + $openTitles = $openTitles.not($nestedTitles); + $openContents = $openTitles.next($content); + } + if( ($openTitles.length > 0) ) { + module.debug('Exclusive enabled, closing other content', $openTitles); + $openTitles + .removeClass(className.active) + ; + if(settings.animateChildren) { + if($.fn.transition !== undefined && $module.transition('is supported')) { + $openContents + .children() + .transition({ + animation : 'fade out', + useFailSafe : true, + debug : settings.debug, + verbose : settings.verbose, + duration : settings.duration + }) + ; + } + else { + $openContents + .children() + .stop() + .animate({ + opacity: 0 + }, settings.duration, module.resetOpacity) + ; + } + } + $openContents + .stop() + .slideUp(settings.duration , settings.easing, function() { + $(this).removeClass(className.active); + module.reset.display.call(this); + }) + ; + } + }, + + reset: { + + display: function() { + module.verbose('Removing inline display from element', this); + $(this).css('display', ''); + if( $(this).attr('style') === '') { + $(this) + .attr('style', '') + .removeAttr('style') + ; + } + }, + + opacity: function() { + module.verbose('Removing inline opacity from element', this); + $(this).css('opacity', ''); + if( $(this).attr('style') === '') { + $(this) + .attr('style', '') + .removeAttr('style') + ; + } + }, + + }, + + setting: function(name, value) { + module.debug('Changing setting', name, value); + if( $.isPlainObject(name) ) { + $.extend(true, settings, name); + } + else if(value !== undefined) { + settings[name] = value; + } + else { + return settings[name]; + } + }, + internal: function(name, value) { + module.debug('Changing internal', name, value); + if(value !== undefined) { + if( $.isPlainObject(name) ) { + $.extend(true, module, name); + } + else { + module[name] = value; + } + } + else { + return module[name]; + } + }, + debug: function() { + if(settings.debug) { + if(settings.performance) { + module.performance.log(arguments); + } + else { + module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':'); + module.debug.apply(console, arguments); + } + } + }, + verbose: function() { + if(settings.verbose && settings.debug) { + if(settings.performance) { + module.performance.log(arguments); + } + else { + module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':'); + module.verbose.apply(console, arguments); + } + } + }, + error: function() { + module.error = Function.prototype.bind.call(console.error, console, settings.name + ':'); + module.error.apply(console, arguments); + }, + performance: { + log: function(message) { + var + currentTime, + executionTime, + previousTime + ; + if(settings.performance) { + currentTime = new Date().getTime(); + previousTime = time || currentTime; + executionTime = currentTime - previousTime; + time = currentTime; + performance.push({ + 'Name' : message[0], + 'Arguments' : [].slice.call(message, 1) || '', + 'Element' : element, + 'Execution Time' : executionTime + }); + } + clearTimeout(module.performance.timer); + module.performance.timer = setTimeout(module.performance.display, 100); + }, + display: function() { + var + title = settings.name + ':', + totalTime = 0 + ; + time = false; + clearTimeout(module.performance.timer); + $.each(performance, function(index, data) { + totalTime += data['Execution Time']; + }); + title += ' ' + totalTime + 'ms'; + if(moduleSelector) { + title += ' \'' + moduleSelector + '\''; + } + if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) { + console.groupCollapsed(title); + if(console.table) { + console.table(performance); + } + else { + $.each(performance, function(index, data) { + console.log(data['Name'] + ': ' + data['Execution Time']+'ms'); + }); + } + console.groupEnd(); + } + performance = []; + } + }, + invoke: function(query, passedArguments, context) { + var + object = instance, + maxDepth, + found, + response + ; + passedArguments = passedArguments || queryArguments; + context = element || context; + if(typeof query == 'string' && object !== undefined) { + query = query.split(/[\. ]/); + maxDepth = query.length - 1; + $.each(query, function(depth, value) { + var camelCaseValue = (depth != maxDepth) + ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1) + : query + ; + if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) { + object = object[camelCaseValue]; + } + else if( object[camelCaseValue] !== undefined ) { + found = object[camelCaseValue]; + return false; + } + else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) { + object = object[value]; + } + else if( object[value] !== undefined ) { + found = object[value]; + return false; + } + else { + module.error(error.method, query); + return false; + } + }); + } + if ( $.isFunction( found ) ) { + response = found.apply(context, passedArguments); + } + else if(found !== undefined) { + response = found; + } + if($.isArray(returnedValue)) { + returnedValue.push(response); + } + else if(returnedValue !== undefined) { + returnedValue = [returnedValue, response]; + } + else if(response !== undefined) { + returnedValue = response; + } + return found; + } + }; + if(methodInvoked) { + if(instance === undefined) { + module.initialize(); + } + module.invoke(query); + } + else { + if(instance !== undefined) { + instance.invoke('destroy'); + } + module.initialize(); + } + }) + ; + return (returnedValue !== undefined) + ? returnedValue + : this + ; +}; + +$.fn.accordion.settings = { + + name : 'Accordion', + namespace : 'accordion', + + debug : false, + verbose : true, + performance : true, + + exclusive : true, + collapsible : true, + closeNested : false, + animateChildren : true, + + duration : 350, + easing : 'easeOutQuad', + + onOpen : function(){}, + onClose : function(){}, + onChange : function(){}, + + error: { + method : 'The method you called is not defined' + }, + + className : { + active : 'active', + animating : 'animating' + }, + + selector : { + accordion : '.accordion', + title : '.title', + trigger : '.title', + content : '.content' + } + +}; + +// Adds easing +$.extend( $.easing, { + easeOutQuad: function (x, t, b, c, d) { + return -c *(t/=d)*(t-2) + b; + } +}); + +})( jQuery, window , document ); + diff --git a/controller/static/semantic/dist/components/accordion.min.css b/controller/static/semantic/dist/components/accordion.min.css new file mode 100755 index 000000000..4fbc4d19a --- /dev/null +++ b/controller/static/semantic/dist/components/accordion.min.css @@ -0,0 +1,10 @@ +/*! + * # Semantic UI x.x - Accordion + * http://github.com/semantic-org/semantic-ui/ + * + * + * Copyright 2014 Contributors + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.accordion,.ui.accordion .accordion{max-width:100%;font-size:1em}.ui.accordion .accordion{margin:1em 0 0;padding:0}.ui.accordion .accordion .title,.ui.accordion .title{cursor:pointer}.ui.accordion .title:not(.ui){padding:.5em 0;font-family:Roboto,'Helvetica Neue',Arial,Helvetica,sans-serif;font-size:1em;color:rgba(0,0,0,.8)}.ui.accordion .accordion .title~.content,.ui.accordion .title~.content{display:none}.ui.accordion:not(.styled) .accordion .title~.content:not(.ui),.ui.accordion:not(.styled) .title~.content:not(.ui){margin:0;padding:.5em 0 1em}.ui.accordion:not(.styled) .title~.content:not(.ui):last-child{padding-bottom:0}.ui.accordion .accordion .title .dropdown.icon,.ui.accordion .title .dropdown.icon{display:inline-block;float:none;opacity:1;width:1.25em;height:1em;margin:0 .25rem 0 0;padding:0;font-size:1em;-webkit-transition:-webkit-transform .2s ease,opacity .2s ease;transition:transform .2s ease,opacity .2s ease;vertical-align:baseline;-webkit-transform:none;-ms-transform:none;transform:none}.ui.accordion.menu .item .title{display:block;padding:0}.ui.accordion.menu .item .title>.dropdown.icon{float:right;margin:.165em 0 0 1em;-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.ui.accordion .ui.header .dropdown.icon{font-size:1em;margin:0 .25rem 0 0}.ui.accordion .accordion .active.title .dropdown.icon,.ui.accordion .active.title .dropdown.icon,.ui.accordion.menu .item .active.title>.dropdown.icon{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.ui.styled.accordion{width:600px}.ui.styled.accordion,.ui.styled.accordion .accordion{border-radius:.2857rem;background:#fff;box-shadow:0 1px 2px 0 rgba(0,0,0,.05),0 0 0 1px rgba(39,41,43,.15)}.ui.styled.accordion .accordion .title,.ui.styled.accordion .title{margin:0;padding:.75em 1em;color:rgba(0,0,0,.4);font-weight:700;border-top:1px solid rgba(39,41,43,.15);-webkit-transition:background .2s ease,color .2s ease;transition:background .2s ease,color .2s ease}.ui.styled.accordion .accordion .title:first-child,.ui.styled.accordion>.title:first-child{border-top:none}.ui.styled.accordion .accordion .content,.ui.styled.accordion .content{margin:0;padding:.5em 1em 1.5em}.ui.styled.accordion .accordion .content{padding:.5em 1em 1.5em}.ui.styled.accordion .accordion .active.title,.ui.styled.accordion .accordion .title:hover,.ui.styled.accordion .active.title,.ui.styled.accordion .title:hover{background:0 0;color:rgba(0,0,0,.8)}.ui.accordion .accordion .active.content,.ui.accordion .active.content{display:block}.ui.fluid.accordion,.ui.fluid.accordion .accordion{width:100%}.ui.inverted.accordion .title:not(.ui){color:#fff}@font-face{font-family:Accordion;src:url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMggjB5AAAAC8AAAAYGNtYXAPfOIKAAABHAAAAExnYXNwAAAAEAAAAWgAAAAIZ2x5Zryj6HgAAAFwAAAAyGhlYWT/0IhHAAACOAAAADZoaGVhApkB5wAAAnAAAAAkaG10eAJuABIAAAKUAAAAGGxvY2EAjABWAAACrAAAAA5tYXhwAAgAFgAAArwAAAAgbmFtZfC1n04AAALcAAABPHBvc3QAAwAAAAAEGAAAACAAAwIAAZAABQAAAUwBZgAAAEcBTAFmAAAA9QAZAIQAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADw2gHg/+D/4AHgACAAAAABAAAAAAAAAAAAAAAgAAAAAAACAAAAAwAAABQAAwABAAAAFAAEADgAAAAKAAgAAgACAAEAIPDa//3//wAAAAAAIPDZ//3//wAB/+MPKwADAAEAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQASAEkAtwFuABMAADc0PwE2FzYXFh0BFAcGJwYvASY1EgaABQgHBQYGBQcIBYAG2wcGfwcBAQcECf8IBAcBAQd/BgYAAAAAAQAAAEkApQFuABMAADcRNDc2MzIfARYVFA8BBiMiJyY1AAUGBwgFgAYGgAUIBwYFWwEACAUGBoAFCAcFgAYGBQcAAAABAAAAAQAAqWYls18PPPUACwIAAAAAAM/9o+4AAAAAz/2j7gAAAAAAtwFuAAAACAACAAAAAAAAAAEAAAHg/+AAAAIAAAAAAAC3AAEAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAAAAQAAAAC3ABIAtwAAAAAAAAAKABQAHgBCAGQAAAABAAAABgAUAAEAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAADgCuAAEAAAAAAAEADAAAAAEAAAAAAAIADgBAAAEAAAAAAAMADAAiAAEAAAAAAAQADABOAAEAAAAAAAUAFgAMAAEAAAAAAAYABgAuAAEAAAAAAAoANABaAAMAAQQJAAEADAAAAAMAAQQJAAIADgBAAAMAAQQJAAMADAAiAAMAAQQJAAQADABOAAMAAQQJAAUAFgAMAAMAAQQJAAYADAA0AAMAAQQJAAoANABaAHIAYQB0AGkAbgBnAFYAZQByAHMAaQBvAG4AIAAxAC4AMAByAGEAdABpAG4AZ3JhdGluZwByAGEAdABpAG4AZwBSAGUAZwB1AGwAYQByAHIAYQB0AGkAbgBnAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA)format('truetype'),url(data:application/font-woff;charset=utf-8;base64,d09GRk9UVE8AAASwAAoAAAAABGgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRkYgAAAA9AAAAS0AAAEtFpovuE9TLzIAAAIkAAAAYAAAAGAIIweQY21hcAAAAoQAAABMAAAATA984gpnYXNwAAAC0AAAAAgAAAAIAAAAEGhlYWQAAALYAAAANgAAADb/0IhHaGhlYQAAAxAAAAAkAAAAJAKZAedobXR4AAADNAAAABgAAAAYAm4AEm1heHAAAANMAAAABgAAAAYABlAAbmFtZQAAA1QAAAE8AAABPPC1n05wb3N0AAAEkAAAACAAAAAgAAMAAAEABAQAAQEBB3JhdGluZwABAgABADr4HAL4GwP4GAQeCgAZU/+Lix4KABlT/4uLDAeLa/iU+HQFHQAAAHkPHQAAAH4RHQAAAAkdAAABJBIABwEBBw0PERQZHnJhdGluZ3JhdGluZ3UwdTF1MjB1RjBEOXVGMERBAAACAYkABAAGAQEEBwoNVp38lA78lA78lA77lA773Z33bxWLkI2Qj44I9xT3FAWOj5CNkIuQi4+JjoePiI2Gi4YIi/uUBYuGiYeHiIiHh4mGi4aLho2Ijwj7FPcUBYeOiY+LkAgO+92L5hWL95QFi5CNkI6Oj4+PjZCLkIuQiY6HCPcU+xQFj4iNhouGi4aJh4eICPsU+xQFiIeGiYaLhouHjYePiI6Jj4uQCA74lBT4lBWLDAoAAAAAAwIAAZAABQAAAUwBZgAAAEcBTAFmAAAA9QAZAIQAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADw2gHg/+D/4AHgACAAAAABAAAAAAAAAAAAAAAgAAAAAAACAAAAAwAAABQAAwABAAAAFAAEADgAAAAKAAgAAgACAAEAIPDa//3//wAAAAAAIPDZ//3//wAB/+MPKwADAAEAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAEAADfYOJZfDzz1AAsCAAAAAADP/aPuAAAAAM/9o+4AAAAAALcBbgAAAAgAAgAAAAAAAAABAAAB4P/gAAACAAAAAAAAtwABAAAAAAAAAAAAAAAAAAAABgAAAAAAAAAAAAAAAAEAAAAAtwASALcAAAAAUAAABgAAAAAADgCuAAEAAAAAAAEADAAAAAEAAAAAAAIADgBAAAEAAAAAAAMADAAiAAEAAAAAAAQADABOAAEAAAAAAAUAFgAMAAEAAAAAAAYABgAuAAEAAAAAAAoANABaAAMAAQQJAAEADAAAAAMAAQQJAAIADgBAAAMAAQQJAAMADAAiAAMAAQQJAAQADABOAAMAAQQJAAUAFgAMAAMAAQQJAAYADAA0AAMAAQQJAAoANABaAHIAYQB0AGkAbgBnAFYAZQByAHMAaQBvAG4AIAAxAC4AMAByAGEAdABpAG4AZ3JhdGluZwByAGEAdABpAG4AZwBSAGUAZwB1AGwAYQByAHIAYQB0AGkAbgBnAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA)format('woff');font-weight:400;font-style:normal}.ui.accordion .accordion .title .dropdown.icon,.ui.accordion .title .dropdown.icon{font-family:Accordion;line-height:1;-webkit-backface-visibility:hidden;backface-visibility:hidden;font-weight:400;font-style:normal;text-align:center}.ui.accordion .accordion .title .dropdown.icon:before,.ui.accordion .title .dropdown.icon:before{content:'\f0da'} \ No newline at end of file diff --git a/controller/static/semantic/dist/components/accordion.min.js b/controller/static/semantic/dist/components/accordion.min.js new file mode 100755 index 000000000..9ec9090a3 --- /dev/null +++ b/controller/static/semantic/dist/components/accordion.min.js @@ -0,0 +1,11 @@ +/*! + * # Semantic UI x.x - Accordion + * http://github.com/semantic-org/semantic-ui/ + * + * + * Copyright 2014 Contributors + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ +!function(e,n,t,i){"use strict";e.fn.accordion=function(t){{var o,a=e(this),s=(new Date).getTime(),r=[],c=arguments[0],l="string"==typeof c,u=[].slice.call(arguments,1);n.requestAnimationFrame||n.mozRequestAnimationFrame||n.webkitRequestAnimationFrame||n.msRequestAnimationFrame||function(e){setTimeout(e,0)}}return a.each(function(){var d,m,g=e.isPlainObject(t)?e.extend(!0,{},e.fn.accordion.settings,t):e.extend({},e.fn.accordion.settings),f=g.className,p=g.namespace,v=g.selector,h=g.error,b="."+p,y="module-"+p,C=a.selector||"",O=e(this),x=O.find(v.title),F=O.find(v.content),T=this,q=O.data(y);m={initialize:function(){m.debug("Initializing",O),m.bind.events(),m.observeChanges(),m.instantiate()},instantiate:function(){q=m,O.data(y,m)},destroy:function(){m.debug("Destroying previous instance",O),O.off(b).removeData(y)},refresh:function(){x=O.find(v.title),F=O.find(v.content)},observeChanges:function(){"MutationObserver"in n&&(d=new MutationObserver(function(){m.debug("DOM tree modified, updating selector cache"),m.refresh()}),d.observe(T,{childList:!0,subtree:!0}),m.debug("Setting up mutation observer",d))},bind:{events:function(){m.debug("Binding delegated events"),O.on("click"+b,v.trigger,m.event.click)}},event:{click:function(){m.toggle.call(this)}},toggle:function(n){var t=n!==i?"number"==typeof n?x.eq(n):e(n).closest(v.title):e(this).closest(v.title),o=t.next(F),a=o.hasClass(f.animating),s=o.hasClass(f.active),r=s&&!a,c=!s&&a;m.debug("Toggling visibility of content",t),r||c?g.collapsible?m.close.call(t):m.debug("Cannot close accordion content collapsing is disabled"):m.open.call(t)},open:function(n){var t=n!==i?"number"==typeof n?x.eq(n):e(n).closest(v.title):e(this).closest(v.title),o=t.next(F),a=o.hasClass(f.animating),s=o.hasClass(f.active),r=!s&&!a;r&&(m.debug("Opening accordion content",t),g.exclusive&&m.closeOthers.call(t),t.addClass(f.active),o.addClass(f.animating),g.animateChildren&&(e.fn.transition!==i&&O.transition("is supported")?o.children().transition({animation:"fade in",queue:!1,useFailSafe:!0,debug:g.debug,verbose:g.verbose,duration:g.duration}):o.children().stop(!0).animate({opacity:1},g.duration,m.resetOpacity)),o.stop(!0).slideDown(g.duration,g.easing,function(){o.removeClass(f.animating).addClass(f.active),m.reset.display.call(this),g.onOpen.call(this),g.onChange.call(this)}))},close:function(n){var t=n!==i?"number"==typeof n?x.eq(n):e(n).closest(v.title):e(this).closest(v.title),o=t.next(F),a=o.hasClass(f.animating),s=o.hasClass(f.active),r=!s&&a,c=s&&a;!s&&!r||c||(m.debug("Closing accordion content",o),t.removeClass(f.active),o.addClass(f.animating),g.animateChildren&&(e.fn.transition!==i&&O.transition("is supported")?o.children().transition({animation:"fade out",queue:!1,useFailSafe:!0,debug:g.debug,verbose:g.verbose,duration:g.duration}):o.children().stop(!0).animate({opacity:0},g.duration,m.resetOpacity)),o.stop(!0).slideUp(g.duration,g.easing,function(){o.removeClass(f.animating).removeClass(f.active),m.reset.display.call(this),g.onClose.call(this),g.onChange.call(this)}))},closeOthers:function(n){var t,o,a,s=n!==i?x.eq(n):e(this).closest(v.title),r=s.parents(v.content).prev(v.title),c=s.closest(v.accordion),l=v.title+"."+f.active+":visible",u=v.content+"."+f.active+":visible";g.closeNested?(t=c.find(l).not(r),a=t.next(F)):(t=c.find(l).not(r),o=c.find(u).find(l).not(r),t=t.not(o),a=t.next(F)),t.length>0&&(m.debug("Exclusive enabled, closing other content",t),t.removeClass(f.active),g.animateChildren&&(e.fn.transition!==i&&O.transition("is supported")?a.children().transition({animation:"fade out",useFailSafe:!0,debug:g.debug,verbose:g.verbose,duration:g.duration}):a.children().stop().animate({opacity:0},g.duration,m.resetOpacity)),a.stop().slideUp(g.duration,g.easing,function(){e(this).removeClass(f.active),m.reset.display.call(this)}))},reset:{display:function(){m.verbose("Removing inline display from element",this),e(this).css("display",""),""===e(this).attr("style")&&e(this).attr("style","").removeAttr("style")},opacity:function(){m.verbose("Removing inline opacity from element",this),e(this).css("opacity",""),""===e(this).attr("style")&&e(this).attr("style","").removeAttr("style")}},setting:function(n,t){if(m.debug("Changing setting",n,t),e.isPlainObject(n))e.extend(!0,g,n);else{if(t===i)return g[n];g[n]=t}},internal:function(n,t){return m.debug("Changing internal",n,t),t===i?m[n]:void(e.isPlainObject(n)?e.extend(!0,m,n):m[n]=t)},debug:function(){g.debug&&(g.performance?m.performance.log(arguments):(m.debug=Function.prototype.bind.call(console.info,console,g.name+":"),m.debug.apply(console,arguments)))},verbose:function(){g.verbose&&g.debug&&(g.performance?m.performance.log(arguments):(m.verbose=Function.prototype.bind.call(console.info,console,g.name+":"),m.verbose.apply(console,arguments)))},error:function(){m.error=Function.prototype.bind.call(console.error,console,g.name+":"),m.error.apply(console,arguments)},performance:{log:function(e){var n,t,i;g.performance&&(n=(new Date).getTime(),i=s||n,t=n-i,s=n,r.push({Name:e[0],Arguments:[].slice.call(e,1)||"",Element:T,"Execution Time":t})),clearTimeout(m.performance.timer),m.performance.timer=setTimeout(m.performance.display,100)},display:function(){var n=g.name+":",t=0;s=!1,clearTimeout(m.performance.timer),e.each(r,function(e,n){t+=n["Execution Time"]}),n+=" "+t+"ms",C&&(n+=" '"+C+"'"),(console.group!==i||console.table!==i)&&r.length>0&&(console.groupCollapsed(n),console.table?console.table(r):e.each(r,function(e,n){console.log(n.Name+": "+n["Execution Time"]+"ms")}),console.groupEnd()),r=[]}},invoke:function(n,t,a){var s,r,c,l=q;return t=t||u,a=T||a,"string"==typeof n&&l!==i&&(n=n.split(/[\. ]/),s=n.length-1,e.each(n,function(t,o){var a=t!=s?o+n[t+1].charAt(0).toUpperCase()+n[t+1].slice(1):n;if(e.isPlainObject(l[a])&&t!=s)l=l[a];else{if(l[a]!==i)return r=l[a],!1;if(!e.isPlainObject(l[o])||t==s)return l[o]!==i?(r=l[o],!1):(m.error(h.method,n),!1);l=l[o]}})),e.isFunction(r)?c=r.apply(a,t):r!==i&&(c=r),e.isArray(o)?o.push(c):o!==i?o=[o,c]:c!==i&&(o=c),r}},l?(q===i&&m.initialize(),m.invoke(c)):(q!==i&&q.invoke("destroy"),m.initialize())}),o!==i?o:this},e.fn.accordion.settings={name:"Accordion",namespace:"accordion",debug:!1,verbose:!0,performance:!0,exclusive:!0,collapsible:!0,closeNested:!1,animateChildren:!0,duration:350,easing:"easeOutQuad",onOpen:function(){},onClose:function(){},onChange:function(){},error:{method:"The method you called is not defined"},className:{active:"active",animating:"animating"},selector:{accordion:".accordion",title:".title",trigger:".title",content:".content"}},e.extend(e.easing,{easeOutQuad:function(e,n,t,i,o){return-i*(n/=o)*(n-2)+t}})}(jQuery,window,document); \ No newline at end of file diff --git a/controller/static/semantic/dist/components/ad.css b/controller/static/semantic/dist/components/ad.css new file mode 100755 index 000000000..7a14f0d5b --- /dev/null +++ b/controller/static/semantic/dist/components/ad.css @@ -0,0 +1,276 @@ +/*! + * # Semantic UI x.x - Ad + * http://github.com/semantic-org/semantic-ui/ + * + * + * Copyright 2013 Contributors + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ + + +/******************************* + Advertisement +*******************************/ + +.ui.ad { + display: block; + overflow: hidden; + margin: 1em 0em; +} +.ui.ad:first-child { + margin: 0em; +} +.ui.ad:last-child { + margin: 0em; +} +.ui.ad iframe { + margin: 0em; + padding: 0em; + border: none; + overflow: hidden; +} + +/*-------------- + Common +---------------*/ + + +/* Leaderboard */ +.ui.leaderboard.ad { + width: 728px; + height: 90px; +} + +/* Medium Rectangle */ +.ui[class*="medium rectangle"].ad { + width: 300px; + height: 250px; +} + +/* Large Rectangle */ +.ui[class*="large rectangle"].ad { + width: 336px; + height: 280px; +} + +/* Half Page */ +.ui[class*="half page"].ad { + width: 300px; + height: 600px; +} + +/*-------------- + Square +---------------*/ + + +/* Square */ +.ui.square.ad { + width: 250px; + height: 250px; +} + +/* Small Square */ +.ui[class*="small square"].ad { + width: 200px; + height: 200px; +} + +/*-------------- + Rectangle +---------------*/ + + +/* Small Rectangle */ +.ui[class*="small rectangle"].ad { + width: 180px; + height: 150px; +} + +/* Vertical Rectangle */ +.ui[class*="vertical rectangle"].ad { + width: 240px; + height: 400px; +} + +/*-------------- + Button +---------------*/ + +.ui.button.ad { + width: 120px; + height: 90px; +} +.ui[class*="square button"].ad { + width: 125px; + height: 125px; +} +.ui[class*="small button"].ad { + width: 120px; + height: 60px; +} + +/*-------------- + Skyscrapers +---------------*/ + + +/* Skyscraper */ +.ui.skyscraper.ad { + width: 120px; + height: 600px; +} + +/* Wide Skyscraper */ +.ui[class*="wide skyscraper"].ad { + width: 160px; +} + +/*-------------- + Banners +---------------*/ + + +/* Banner */ +.ui.banner.ad { + width: 468px; + height: 60px; +} + +/* Vertical Banner */ +.ui[class*="vertical banner"].ad { + width: 120px; + height: 240px; +} + +/* Top Banner */ +.ui[class*="top banner"].ad { + width: 930px; + height: 180px; +} + +/* Half Banner */ +.ui[class*="half banner"].ad { + width: 234px; + height: 60px; +} + +/*-------------- + Boards +---------------*/ + + +/* Leaderboard */ +.ui[class*="large leaderboard"].ad { + width: 970px; + height: 90px; +} + +/* Billboard */ +.ui.billboard.ad { + width: 970px; + height: 250px; +} + +/*-------------- + Panorama +---------------*/ + + +/* Panorama */ +.ui.panorama.ad { + width: 980px; + height: 120px; +} + +/*-------------- + Netboard +---------------*/ + + +/* Netboard */ +.ui.netboard.ad { + width: 580px; + height: 400px; +} + +/*-------------- + Mobile +---------------*/ + + +/* Large Mobile Banner */ +.ui[class*="large mobile banner"].ad { + width: 320px; + height: 100px; +} + +/* Mobile Leaderboard */ +.ui[class*="mobile leaderboard"].ad { + width: 320px; + height: 50px; +} + + +/******************************* + Types +*******************************/ + + +/* Mobile Sizes */ +.ui.mobile.ad { + display: none; +} +@media only screen and (max-width: 767px) { + .ui.mobile.ad { + display: block; + } +} + + +/******************************* + Variations +*******************************/ + +.ui.centered.ad { + margin-left: auto; + margin-right: auto; +} +.ui.test.ad { + position: relative; + background: #333333; +} +.ui.test.ad:after { + position: absolute; + top: 50%; + left: 50%; + width: 100%; + text-align: center; + -webkit-transform: translateX(-50%) translateY(-50%); + -ms-transform: translateX(-50%) translateY(-50%); + transform: translateX(-50%) translateY(-50%); + content: 'Ad'; + color: #ffffff; + font-size: 1em; + font-weight: bold; +} +.ui.mobile.test.ad:after { + font-size: 0.85714em; +} +.ui.test.ad[data-text]:after { + content: attr(data-text); +} + + +/******************************* + Theme Overrides +*******************************/ + + + +/******************************* + User Variable Overrides +*******************************/ + diff --git a/controller/static/semantic/dist/components/ad.min.css b/controller/static/semantic/dist/components/ad.min.css new file mode 100755 index 000000000..1e2b85258 --- /dev/null +++ b/controller/static/semantic/dist/components/ad.min.css @@ -0,0 +1,10 @@ +/*! + * # Semantic UI x.x - Ad + * http://github.com/semantic-org/semantic-ui/ + * + * + * Copyright 2013 Contributors + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.ad{display:block;overflow:hidden;margin:1em 0}.ui.ad:first-child,.ui.ad:last-child{margin:0}.ui.ad iframe{margin:0;padding:0;border:none;overflow:hidden}.ui.leaderboard.ad{width:728px;height:90px}.ui[class*="medium rectangle"].ad{width:300px;height:250px}.ui[class*="large rectangle"].ad{width:336px;height:280px}.ui[class*="half page"].ad{width:300px;height:600px}.ui.square.ad{width:250px;height:250px}.ui[class*="small square"].ad{width:200px;height:200px}.ui[class*="small rectangle"].ad{width:180px;height:150px}.ui[class*="vertical rectangle"].ad{width:240px;height:400px}.ui.button.ad{width:120px;height:90px}.ui[class*="square button"].ad{width:125px;height:125px}.ui[class*="small button"].ad{width:120px;height:60px}.ui.skyscraper.ad{width:120px;height:600px}.ui[class*="wide skyscraper"].ad{width:160px}.ui.banner.ad{width:468px;height:60px}.ui[class*="vertical banner"].ad{width:120px;height:240px}.ui[class*="top banner"].ad{width:930px;height:180px}.ui[class*="half banner"].ad{width:234px;height:60px}.ui[class*="large leaderboard"].ad{width:970px;height:90px}.ui.billboard.ad{width:970px;height:250px}.ui.panorama.ad{width:980px;height:120px}.ui.netboard.ad{width:580px;height:400px}.ui[class*="large mobile banner"].ad{width:320px;height:100px}.ui[class*="mobile leaderboard"].ad{width:320px;height:50px}.ui.mobile.ad{display:none}@media only screen and (max-width:767px){.ui.mobile.ad{display:block}}.ui.centered.ad{margin-left:auto;margin-right:auto}.ui.test.ad{position:relative;background:#333}.ui.test.ad:after{position:absolute;top:50%;left:50%;width:100%;text-align:center;-webkit-transform:translateX(-50%)translateY(-50%);-ms-transform:translateX(-50%)translateY(-50%);transform:translateX(-50%)translateY(-50%);content:'Ad';color:#fff;font-size:1em;font-weight:700}.ui.mobile.test.ad:after{font-size:.85714em}.ui.test.ad[data-text]:after{content:attr(data-text)} \ No newline at end of file diff --git a/controller/static/semantic/dist/components/api.js b/controller/static/semantic/dist/components/api.js new file mode 100755 index 000000000..19be01eef --- /dev/null +++ b/controller/static/semantic/dist/components/api.js @@ -0,0 +1,871 @@ +/*! + * # Semantic UI x.x - API + * http://github.com/semantic-org/semantic-ui/ + * + * + * Copyright 2014 Contributors + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ + +;(function ( $, window, document, undefined ) { + +"use strict"; + +$.api = $.fn.api = function(parameters) { + + var + // use window context if none specified + $allModules = $.isFunction(this) + ? $(window) + : $(this), + moduleSelector = $allModules.selector || '', + time = new Date().getTime(), + performance = [], + + query = arguments[0], + methodInvoked = (typeof query == 'string'), + queryArguments = [].slice.call(arguments, 1), + + returnedValue + ; + + $allModules + .each(function() { + var + settings = ( $.isPlainObject(parameters) ) + ? $.extend(true, {}, $.fn.api.settings, parameters) + : $.extend({}, $.fn.api.settings), + + // internal aliases + namespace = settings.namespace, + metadata = settings.metadata, + selector = settings.selector, + error = settings.error, + className = settings.className, + + // define namespaces for modules + eventNamespace = '.' + namespace, + moduleNamespace = 'module-' + namespace, + + // element that creates request + $module = $(this), + $form = $module.closest(selector.form), + + // context used for state + $context = (settings.stateContext) + ? $(settings.stateContext) + : $module, + + // request details + ajaxSettings, + requestSettings, + url, + data, + + // standard module + element = this, + context = $context.get(), + instance = $module.data(moduleNamespace), + module + ; + + module = { + + initialize: function() { + var + triggerEvent = module.get.event() + ; + // bind events + if(!methodInvoked) { + if( triggerEvent ) { + module.debug('Attaching API events to element', triggerEvent); + $module + .on(triggerEvent + eventNamespace, module.event.trigger) + ; + } + else if(settings.on == 'now') { + module.debug('Querying API now', triggerEvent); + module.query(); + } + } + module.instantiate(); + }, + + instantiate: function() { + module.verbose('Storing instance of module', module); + instance = module; + $module + .data(moduleNamespace, instance) + ; + }, + + destroy: function() { + module.verbose('Destroying previous module for', element); + $module + .removeData(moduleNamespace) + .off(eventNamespace) + ; + }, + + query: function() { + + if(module.is.disabled()) { + module.debug('Element is disabled API request aborted'); + return; + } + // determine if an api event already occurred + if(module.is.loading() && settings.throttle === 0 ) { + module.debug('Cancelling request, previous request is still pending'); + return; + } + + // pass element metadata to url (value, text) + if(settings.defaultData) { + $.extend(true, settings.urlData, module.get.defaultData()); + } + + // Add form content + if(settings.serializeForm !== false || $context.is('form')) { + if(settings.serializeForm == 'json') { + $.extend(true, settings.data, module.get.formData()); + } + else { + settings.data = module.get.formData(); + } + } + + // call beforesend and get any settings changes + requestSettings = module.get.settings(); + + // check if before send cancelled request + if(requestSettings === false) { + module.cancelled = true; + module.error(error.beforeSend); + return; + } + else { + module.cancelled = false; + } + + if(settings.url) { + // override with url if specified + module.debug('Using specified url', url); + url = module.add.urlData( settings.url ); + } + else { + // otherwise find url from api endpoints + url = module.add.urlData( module.get.templateURL() ); + module.debug('Added URL Data to url', url); + } + + // exit conditions reached, missing url parameters + if( !url ) { + if( module.is.form() ) { + url = $module.attr('action') || ''; + module.debug('No url or action specified, defaulting to form action', url); + } + else { + module.error(error.missingURL, settings.action); + return; + } + } + + // add loading state + module.set.loading(); + + // look for jQuery ajax parameters in settings + ajaxSettings = $.extend(true, {}, settings, { + type : settings.method || settings.type, + data : data, + url : settings.base + url, + beforeSend : settings.beforeXHR, + success : function() {}, + failure : function() {}, + complete : function() {} + }); + + module.debug('Querying URL', ajaxSettings.url); + module.debug('Sending data', data, ajaxSettings.method); + module.verbose('Using AJAX settings', ajaxSettings); + + if( module.is.loading() ) { + // throttle additional requests + module.timer = setTimeout(function() { + module.request = module.create.request(); + module.xhr = module.create.xhr(); + settings.onRequest.call(context, module.request, module.xhr); + }, settings.throttle); + } + else { + // immediately on first request + module.request = module.create.request(); + module.xhr = module.create.xhr(); + settings.onRequest.call(context, module.request, module.xhr); + } + + }, + + + is: { + disabled: function() { + return ($module.filter(settings.filter).length > 0); + }, + form: function() { + return $module.is('form'); + }, + input: function() { + return $module.is('input'); + }, + loading: function() { + return (module.request && module.request.state() == 'pending'); + } + }, + + was: { + cancelled: function() { + return (module.cancelled || false); + }, + succesful: function() { + return (module.request && module.request.state() == 'resolved'); + }, + failure: function() { + return (module.request && module.request.state() == 'rejected'); + }, + complete: function() { + return (module.request && (module.request.state() == 'resolved' || module.request.state() == 'rejected') ); + } + }, + + add: { + urlData: function(url, urlData) { + var + requiredVariables, + optionalVariables + ; + if(url) { + requiredVariables = url.match(settings.regExp.required); + optionalVariables = url.match(settings.regExp.optional); + urlData = urlData || settings.urlData; + if(requiredVariables) { + module.debug('Looking for required URL variables', requiredVariables); + $.each(requiredVariables, function(index, templatedString) { + var + // allow legacy {$var} style + variable = (templatedString.indexOf('$') !== -1) + ? templatedString.substr(2, templatedString.length - 3) + : templatedString.substr(1, templatedString.length - 2), + value = ($.isPlainObject(urlData) && urlData[variable] !== undefined) + ? urlData[variable] + : ($module.data(variable) !== undefined) + ? $module.data(variable) + : ($context.data(variable) !== undefined) + ? $context.data(variable) + : urlData[variable] + ; + // remove value + if(value === undefined) { + module.error(error.requiredParameter, variable, url); + url = false; + return false; + } + else { + module.verbose('Found required variable', variable, value); + url = url.replace(templatedString, value); + } + }); + } + if(optionalVariables) { + module.debug('Looking for optional URL variables', requiredVariables); + $.each(optionalVariables, function(index, templatedString) { + var + // allow legacy {/$var} style + variable = (templatedString.indexOf('$') !== -1) + ? templatedString.substr(3, templatedString.length - 4) + : templatedString.substr(2, templatedString.length - 3), + value = ($.isPlainObject(urlData) && urlData[variable] !== undefined) + ? urlData[variable] + : ($module.data(variable) !== undefined) + ? $module.data(variable) + : ($context.data(variable) !== undefined) + ? $context.data(variable) + : urlData[variable] + ; + // optional replacement + if(value !== undefined) { + module.verbose('Optional variable Found', variable, value); + url = url.replace(templatedString, value); + } + else { + module.verbose('Optional variable not found', variable); + // remove preceding slash if set + if(url.indexOf('/' + templatedString) !== -1) { + url = url.replace('/' + templatedString, ''); + } + else { + url = url.replace(templatedString, ''); + } + } + }); + } + } + return url; + } + }, + + event: { + trigger: function(event) { + module.query(); + if(event.type == 'submit' || event.type == 'click') { + event.preventDefault(); + } + }, + xhr: { + always: function() { + // calculate if loading time was below minimum threshold + }, + done: function(response) { + var + context = this, + elapsedTime = (new Date().getTime() - time), + timeLeft = (settings.loadingDuration - elapsedTime) + ; + timeLeft = (timeLeft > 0) + ? timeLeft + : 0 + ; + setTimeout(function() { + module.request.resolveWith(context, [response]); + }, timeLeft); + }, + fail: function(xhr, status, httpMessage) { + var + context = this, + elapsedTime = (new Date().getTime() - time), + timeLeft = (settings.loadingDuration - elapsedTime) + ; + timeLeft = (timeLeft > 0) + ? timeLeft + : 0 + ; + // page triggers abort on navigation, dont show error + setTimeout(function() { + if(status !== 'abort') { + module.request.rejectWith(context, [xhr, status, httpMessage]); + } + else { + module.reset(); + } + }, timeLeft); + } + }, + request: { + complete: function(response) { + module.remove.loading(); + settings.onComplete.call(context, response, $module); + }, + done: function(response) { + module.debug('API Response Received', response); + if(settings.dataType == 'json') { + if( $.isFunction(settings.successTest) ) { + module.debug('Checking JSON returned success', settings.successTest, response); + if( settings.successTest(response) ) { + settings.onSuccess.call(context, response, $module); + } + else { + module.debug('JSON test specified by user and response failed', response); + settings.onFailure.call(context, response, $module); + } + } + else { + settings.onSuccess.call(context, response, $module); + } + } + else { + settings.onSuccess.call(context, response, $module); + } + }, + error: function(xhr, status, httpMessage) { + var + errorMessage = (settings.error[status] !== undefined) + ? settings.error[status] + : httpMessage, + response + ; + // let em know unless request aborted + if(xhr !== undefined) { + // readyState 4 = done, anything less is not really sent + if(xhr.readyState !== undefined && xhr.readyState == 4) { + + // if http status code returned and json returned error, look for it + if( xhr.status != 200 && httpMessage !== undefined && httpMessage !== '') { + module.error(error.statusMessage + httpMessage, ajaxSettings.url); + } + else { + if(status == 'error' && settings.dataType == 'json') { + try { + response = $.parseJSON(xhr.responseText); + if(response && response.error !== undefined) { + errorMessage = response.error; + } + } + catch(e) { + module.error(error.JSONParse); + } + } + } + module.remove.loading(); + module.set.error(); + // show error state only for duration specified in settings + if(settings.errorDuration) { + setTimeout(module.remove.error, settings.errorDuration); + } + module.debug('API Request error:', errorMessage); + settings.onError.call(context, errorMessage, $module); + } + else { + settings.onAbort.call(context, errorMessage, $module); + module.debug('Request Aborted (Most likely caused by page change or CORS Policy)', status, httpMessage); + } + } + } + } + }, + + create: { + request: function() { + return $.Deferred() + .always(module.event.request.complete) + .done(module.event.request.done) + .fail(module.event.request.error) + ; + }, + xhr: function() { + return $.ajax(ajaxSettings) + .always(module.event.xhr.always) + .done(module.event.xhr.done) + .fail(module.event.xhr.fail) + ; + } + }, + + set: { + error: function() { + module.verbose('Adding error state to element', $context); + $context.addClass(className.error); + }, + loading: function() { + module.verbose('Adding loading state to element', $context); + $context.addClass(className.loading); + } + }, + + remove: { + error: function() { + module.verbose('Removing error state from element', $context); + $context.removeClass(className.error); + }, + loading: function() { + module.verbose('Removing loading state from element', $context); + $context.removeClass(className.loading); + } + }, + + get: { + request: function() { + return module.request || false; + }, + xhr: function() { + return module.xhr || false; + }, + settings: function() { + var + runSettings + ; + runSettings = settings.beforeSend.call($module, settings); + if(runSettings) { + if(runSettings.success !== undefined) { + module.debug('Legacy success callback detected', runSettings); + module.error(error.legacyParameters, runSettings.success); + runSettings.onSuccess = runSettings.success; + } + if(runSettings.failure !== undefined) { + module.debug('Legacy failure callback detected', runSettings); + module.error(error.legacyParameters, runSettings.failure); + runSettings.onFailure = runSettings.failure; + } + if(runSettings.complete !== undefined) { + module.debug('Legacy complete callback detected', runSettings); + module.error(error.legacyParameters, runSettings.complete); + runSettings.onComplete = runSettings.complete; + } + } + if(runSettings === undefined) { + module.error(error.noReturnedValue); + } + return (runSettings !== undefined) + ? runSettings + : settings + ; + }, + defaultData: function() { + var + data = {} + ; + if( !$.isWindow(element) ) { + if( module.is.input() ) { + data.value = $module.val(); + } + else if( !module.is.form() ) { + + } + else { + data.text = $module.text(); + } + } + return data; + }, + event: function() { + if( $.isWindow(element) || settings.on == 'now' ) { + module.debug('API called without element, no events attached'); + return false; + } + else if(settings.on == 'auto') { + if( $module.is('input') ) { + return (element.oninput !== undefined) + ? 'input' + : (element.onpropertychange !== undefined) + ? 'propertychange' + : 'keyup' + ; + } + else if( $module.is('form') ) { + return 'submit'; + } + else { + return 'click'; + } + } + else { + return settings.on; + } + }, + formData: function() { + var + formData + ; + if($module.serializeObject !== undefined) { + formData = $form.serializeObject(); + } + else { + module.error(error.missingSerialize); + formData = $form.serialize(); + } + module.debug('Retrieved form data', formData); + return formData; + }, + templateURL: function(action) { + var + url + ; + action = action || $module.data(metadata.action) || settings.action || false; + if(action) { + module.debug('Looking up url for action', action, settings.api); + if(settings.api[action] !== undefined) { + url = settings.api[action]; + module.debug('Found template url', url); + } + else if( !module.is.form() ) { + module.error(error.missingAction, settings.action, settings.api); + } + } + return url; + } + }, + + abort: function() { + var + xhr = module.get.xhr() + ; + if( xhr && xhr.state() !== 'resolved') { + module.debug('Cancelling API request'); + xhr.abort(); + module.request.rejectWith(settings.apiSettings); + } + }, + + // reset state + reset: function() { + module.remove.error(); + module.remove.loading(); + }, + + setting: function(name, value) { + module.debug('Changing setting', name, value); + if( $.isPlainObject(name) ) { + $.extend(true, settings, name); + } + else if(value !== undefined) { + settings[name] = value; + } + else { + return settings[name]; + } + }, + internal: function(name, value) { + if( $.isPlainObject(name) ) { + $.extend(true, module, name); + } + else if(value !== undefined) { + module[name] = value; + } + else { + return module[name]; + } + }, + debug: function() { + if(settings.debug) { + if(settings.performance) { + module.performance.log(arguments); + } + else { + module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':'); + module.debug.apply(console, arguments); + } + } + }, + verbose: function() { + if(settings.verbose && settings.debug) { + if(settings.performance) { + module.performance.log(arguments); + } + else { + module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':'); + module.verbose.apply(console, arguments); + } + } + }, + error: function() { + module.error = Function.prototype.bind.call(console.error, console, settings.name + ':'); + module.error.apply(console, arguments); + }, + performance: { + log: function(message) { + var + currentTime, + executionTime, + previousTime + ; + if(settings.performance) { + currentTime = new Date().getTime(); + previousTime = time || currentTime; + executionTime = currentTime - previousTime; + time = currentTime; + performance.push({ + 'Name' : message[0], + 'Arguments' : [].slice.call(message, 1) || '', + //'Element' : element, + 'Execution Time' : executionTime + }); + } + clearTimeout(module.performance.timer); + module.performance.timer = setTimeout(module.performance.display, 100); + }, + display: function() { + var + title = settings.name + ':', + totalTime = 0 + ; + time = false; + clearTimeout(module.performance.timer); + $.each(performance, function(index, data) { + totalTime += data['Execution Time']; + }); + title += ' ' + totalTime + 'ms'; + if(moduleSelector) { + title += ' \'' + moduleSelector + '\''; + } + if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) { + console.groupCollapsed(title); + if(console.table) { + console.table(performance); + } + else { + $.each(performance, function(index, data) { + console.log(data['Name'] + ': ' + data['Execution Time']+'ms'); + }); + } + console.groupEnd(); + } + performance = []; + } + }, + invoke: function(query, passedArguments, context) { + var + object = instance, + maxDepth, + found, + response + ; + passedArguments = passedArguments || queryArguments; + context = element || context; + if(typeof query == 'string' && object !== undefined) { + query = query.split(/[\. ]/); + maxDepth = query.length - 1; + $.each(query, function(depth, value) { + var camelCaseValue = (depth != maxDepth) + ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1) + : query + ; + if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) { + object = object[camelCaseValue]; + } + else if( object[camelCaseValue] !== undefined ) { + found = object[camelCaseValue]; + return false; + } + else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) { + object = object[value]; + } + else if( object[value] !== undefined ) { + found = object[value]; + return false; + } + else { + module.error(error.method, query); + return false; + } + }); + } + if ( $.isFunction( found ) ) { + response = found.apply(context, passedArguments); + } + else if(found !== undefined) { + response = found; + } + if($.isArray(returnedValue)) { + returnedValue.push(response); + } + else if(returnedValue !== undefined) { + returnedValue = [returnedValue, response]; + } + else if(response !== undefined) { + returnedValue = response; + } + return found; + } + }; + + if(methodInvoked) { + if(instance === undefined) { + module.initialize(); + } + module.invoke(query); + } + else { + if(instance !== undefined) { + instance.invoke('destroy'); + } + module.initialize(); + } + }) + ; + + return (returnedValue !== undefined) + ? returnedValue + : this + ; +}; + +$.api.settings = { + + name : 'API', + namespace : 'api', + + debug : true, + verbose : false, + performance : true, + + // event binding + on : 'auto', + filter : '.disabled', + stateContext : false, + + // state + loadingDuration : 0, + errorDuration : 2000, + + // templating + action : false, + url : false, + base : '', + + // data + urlData : {}, + + // ui + defaultData : true, + serializeForm : false, + throttle : 0, + + // jQ ajax + method : 'get', + data : {}, + dataType : 'json', + + // callbacks + beforeSend : function(settings) { return settings; }, + beforeXHR : function(xhr) {}, + + onRequest : function(promise, xhr) {}, + onSuccess : function(response, $module) {}, + onComplete : function(response, $module) {}, + onFailure : function(errorMessage, $module) {}, + onError : function(errorMessage, $module) {}, + onAbort : function(errorMessage, $module) {}, + + successTest : false, + + // errors + error : { + beforeSend : 'The before send function has aborted the request', + error : 'There was an error with your request', + exitConditions : 'API Request Aborted. Exit conditions met', + JSONParse : 'JSON could not be parsed during error handling', + legacyParameters : 'You are using legacy API success callback names', + method : 'The method you called is not defined', + missingAction : 'API action used but no url was defined', + missingSerialize : 'Required dependency jquery-serialize-object missing, using basic serialize', + missingURL : 'No URL specified for api event', + noReturnedValue : 'The beforeSend callback must return a settings object, beforeSend ignored.', + parseError : 'There was an error parsing your request', + requiredParameter : 'Missing a required URL parameter: ', + statusMessage : 'Server gave an error: ', + timeout : 'Your request timed out' + }, + + regExp : { + required: /\{\$*[A-z0-9]+\}/g, + optional: /\{\/\$*[A-z0-9]+\}/g, + }, + + className: { + loading : 'loading', + error : 'error' + }, + + selector: { + form: 'form' + }, + + metadata: { + action : 'action' + } +}; + + +$.api.settings.api = {}; + + +})( jQuery, window , document ); \ No newline at end of file diff --git a/controller/static/semantic/dist/components/api.min.js b/controller/static/semantic/dist/components/api.min.js new file mode 100755 index 000000000..04db4b8c4 --- /dev/null +++ b/controller/static/semantic/dist/components/api.min.js @@ -0,0 +1,11 @@ +/*! + * # Semantic UI x.x - API + * http://github.com/semantic-org/semantic-ui/ + * + * + * Copyright 2014 Contributors + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ +!function(e,t,r,n){"use strict";e.api=e.fn.api=function(r){var o,a=e(e.isFunction(this)?t:this),i=a.selector||"",s=(new Date).getTime(),u=[],c=arguments[0],l="string"==typeof c,d=[].slice.call(arguments,1);return a.each(function(){var t,a,f,g,m,p=e.isPlainObject(r)?e.extend(!0,{},e.fn.api.settings,r):e.extend({},e.fn.api.settings),b=p.namespace,v=p.metadata,h=p.selector,y=p.error,q=p.className,x="."+b,A="module-"+b,D=e(this),P=D.closest(h.form),R=p.stateContext?e(p.stateContext):D,T=this,S=R.get(),j=D.data(A);m={initialize:function(){var e=m.get.event();l||(e?(m.debug("Attaching API events to element",e),D.on(e+x,m.event.trigger)):"now"==p.on&&(m.debug("Querying API now",e),m.query())),m.instantiate()},instantiate:function(){m.verbose("Storing instance of module",m),j=m,D.data(A,j)},destroy:function(){m.verbose("Destroying previous module for",T),D.removeData(A).off(x)},query:function(){if(m.is.disabled())return void m.debug("Element is disabled API request aborted");if(m.is.loading()&&0===p.throttle)return void m.debug("Cancelling request, previous request is still pending");if(p.defaultData&&e.extend(!0,p.urlData,m.get.defaultData()),(p.serializeForm!==!1||R.is("form"))&&("json"==p.serializeForm?e.extend(!0,p.data,m.get.formData()):p.data=m.get.formData()),a=m.get.settings(),a===!1)return m.cancelled=!0,void m.error(y.beforeSend);if(m.cancelled=!1,p.url?(m.debug("Using specified url",f),f=m.add.urlData(p.url)):(f=m.add.urlData(m.get.templateURL()),m.debug("Added URL Data to url",f)),!f){if(!m.is.form())return void m.error(y.missingURL,p.action);f=D.attr("action")||"",m.debug("No url or action specified, defaulting to form action",f)}m.set.loading(),t=e.extend(!0,{},p,{type:p.method||p.type,data:g,url:p.base+f,beforeSend:p.beforeXHR,success:function(){},failure:function(){},complete:function(){}}),m.debug("Querying URL",t.url),m.debug("Sending data",g,t.method),m.verbose("Using AJAX settings",t),m.is.loading()?m.timer=setTimeout(function(){m.request=m.create.request(),m.xhr=m.create.xhr(),p.onRequest.call(S,m.request,m.xhr)},p.throttle):(m.request=m.create.request(),m.xhr=m.create.xhr(),p.onRequest.call(S,m.request,m.xhr))},is:{disabled:function(){return D.filter(p.filter).length>0},form:function(){return D.is("form")},input:function(){return D.is("input")},loading:function(){return m.request&&"pending"==m.request.state()}},was:{cancelled:function(){return m.cancelled||!1},succesful:function(){return m.request&&"resolved"==m.request.state()},failure:function(){return m.request&&"rejected"==m.request.state()},complete:function(){return m.request&&("resolved"==m.request.state()||"rejected"==m.request.state())}},add:{urlData:function(t,r){var o,a;return t&&(o=t.match(p.regExp.required),a=t.match(p.regExp.optional),r=r||p.urlData,o&&(m.debug("Looking for required URL variables",o),e.each(o,function(o,a){var i=-1!==a.indexOf("$")?a.substr(2,a.length-3):a.substr(1,a.length-2),s=e.isPlainObject(r)&&r[i]!==n?r[i]:D.data(i)!==n?D.data(i):R.data(i)!==n?R.data(i):r[i];return s===n?(m.error(y.requiredParameter,i,t),t=!1,!1):(m.verbose("Found required variable",i,s),void(t=t.replace(a,s)))})),a&&(m.debug("Looking for optional URL variables",o),e.each(a,function(o,a){var i=-1!==a.indexOf("$")?a.substr(3,a.length-4):a.substr(2,a.length-3),s=e.isPlainObject(r)&&r[i]!==n?r[i]:D.data(i)!==n?D.data(i):R.data(i)!==n?R.data(i):r[i];s!==n?(m.verbose("Optional variable Found",i,s),t=t.replace(a,s)):(m.verbose("Optional variable not found",i),t=-1!==t.indexOf("/"+a)?t.replace("/"+a,""):t.replace(a,""))}))),t}},event:{trigger:function(e){m.query(),("submit"==e.type||"click"==e.type)&&e.preventDefault()},xhr:{always:function(){},done:function(e){var t=this,r=(new Date).getTime()-s,n=p.loadingDuration-r;n=n>0?n:0,setTimeout(function(){m.request.resolveWith(t,[e])},n)},fail:function(e,t,r){var n=this,o=(new Date).getTime()-s,a=p.loadingDuration-o;a=a>0?a:0,setTimeout(function(){"abort"!==t?m.request.rejectWith(n,[e,t,r]):m.reset()},a)}},request:{complete:function(e){m.remove.loading(),p.onComplete.call(S,e,D)},done:function(t){m.debug("API Response Received",t),"json"==p.dataType&&e.isFunction(p.successTest)?(m.debug("Checking JSON returned success",p.successTest,t),p.successTest(t)?p.onSuccess.call(S,t,D):(m.debug("JSON test specified by user and response failed",t),p.onFailure.call(S,t,D))):p.onSuccess.call(S,t,D)},error:function(r,o,a){var i,s=p.error[o]!==n?p.error[o]:a;if(r!==n)if(r.readyState!==n&&4==r.readyState){if(200!=r.status&&a!==n&&""!==a)m.error(y.statusMessage+a,t.url);else if("error"==o&&"json"==p.dataType)try{i=e.parseJSON(r.responseText),i&&i.error!==n&&(s=i.error)}catch(u){m.error(y.JSONParse)}m.remove.loading(),m.set.error(),p.errorDuration&&setTimeout(m.remove.error,p.errorDuration),m.debug("API Request error:",s),p.onError.call(S,s,D)}else p.onAbort.call(S,s,D),m.debug("Request Aborted (Most likely caused by page change or CORS Policy)",o,a)}}},create:{request:function(){return e.Deferred().always(m.event.request.complete).done(m.event.request.done).fail(m.event.request.error)},xhr:function(){return e.ajax(t).always(m.event.xhr.always).done(m.event.xhr.done).fail(m.event.xhr.fail)}},set:{error:function(){m.verbose("Adding error state to element",R),R.addClass(q.error)},loading:function(){m.verbose("Adding loading state to element",R),R.addClass(q.loading)}},remove:{error:function(){m.verbose("Removing error state from element",R),R.removeClass(q.error)},loading:function(){m.verbose("Removing loading state from element",R),R.removeClass(q.loading)}},get:{request:function(){return m.request||!1},xhr:function(){return m.xhr||!1},settings:function(){var e;return e=p.beforeSend.call(D,p),e&&(e.success!==n&&(m.debug("Legacy success callback detected",e),m.error(y.legacyParameters,e.success),e.onSuccess=e.success),e.failure!==n&&(m.debug("Legacy failure callback detected",e),m.error(y.legacyParameters,e.failure),e.onFailure=e.failure),e.complete!==n&&(m.debug("Legacy complete callback detected",e),m.error(y.legacyParameters,e.complete),e.onComplete=e.complete)),e===n&&m.error(y.noReturnedValue),e!==n?e:p},defaultData:function(){var t={};return e.isWindow(T)||(m.is.input()?t.value=D.val():m.is.form()&&(t.text=D.text())),t},event:function(){return e.isWindow(T)||"now"==p.on?(m.debug("API called without element, no events attached"),!1):"auto"==p.on?D.is("input")?T.oninput!==n?"input":T.onpropertychange!==n?"propertychange":"keyup":D.is("form")?"submit":"click":p.on},formData:function(){var e;return D.serializeObject!==n?e=P.serializeObject():(m.error(y.missingSerialize),e=P.serialize()),m.debug("Retrieved form data",e),e},templateURL:function(e){var t;return e=e||D.data(v.action)||p.action||!1,e&&(m.debug("Looking up url for action",e,p.api),p.api[e]!==n?(t=p.api[e],m.debug("Found template url",t)):m.is.form()||m.error(y.missingAction,p.action,p.api)),t}},abort:function(){var e=m.get.xhr();e&&"resolved"!==e.state()&&(m.debug("Cancelling API request"),e.abort(),m.request.rejectWith(p.apiSettings))},reset:function(){m.remove.error(),m.remove.loading()},setting:function(t,r){if(m.debug("Changing setting",t,r),e.isPlainObject(t))e.extend(!0,p,t);else{if(r===n)return p[t];p[t]=r}},internal:function(t,r){if(e.isPlainObject(t))e.extend(!0,m,t);else{if(r===n)return m[t];m[t]=r}},debug:function(){p.debug&&(p.performance?m.performance.log(arguments):(m.debug=Function.prototype.bind.call(console.info,console,p.name+":"),m.debug.apply(console,arguments)))},verbose:function(){p.verbose&&p.debug&&(p.performance?m.performance.log(arguments):(m.verbose=Function.prototype.bind.call(console.info,console,p.name+":"),m.verbose.apply(console,arguments)))},error:function(){m.error=Function.prototype.bind.call(console.error,console,p.name+":"),m.error.apply(console,arguments)},performance:{log:function(e){var t,r,n;p.performance&&(t=(new Date).getTime(),n=s||t,r=t-n,s=t,u.push({Name:e[0],Arguments:[].slice.call(e,1)||"","Execution Time":r})),clearTimeout(m.performance.timer),m.performance.timer=setTimeout(m.performance.display,100)},display:function(){var t=p.name+":",r=0;s=!1,clearTimeout(m.performance.timer),e.each(u,function(e,t){r+=t["Execution Time"]}),t+=" "+r+"ms",i&&(t+=" '"+i+"'"),(console.group!==n||console.table!==n)&&u.length>0&&(console.groupCollapsed(t),console.table?console.table(u):e.each(u,function(e,t){console.log(t.Name+": "+t["Execution Time"]+"ms")}),console.groupEnd()),u=[]}},invoke:function(t,r,a){var i,s,u,c=j;return r=r||d,a=T||a,"string"==typeof t&&c!==n&&(t=t.split(/[\. ]/),i=t.length-1,e.each(t,function(r,o){var a=r!=i?o+t[r+1].charAt(0).toUpperCase()+t[r+1].slice(1):t;if(e.isPlainObject(c[a])&&r!=i)c=c[a];else{if(c[a]!==n)return s=c[a],!1;if(!e.isPlainObject(c[o])||r==i)return c[o]!==n?(s=c[o],!1):(m.error(y.method,t),!1);c=c[o]}})),e.isFunction(s)?u=s.apply(a,r):s!==n&&(u=s),e.isArray(o)?o.push(u):o!==n?o=[o,u]:u!==n&&(o=u),s}},l?(j===n&&m.initialize(),m.invoke(c)):(j!==n&&j.invoke("destroy"),m.initialize())}),o!==n?o:this},e.api.settings={name:"API",namespace:"api",debug:!0,verbose:!1,performance:!0,on:"auto",filter:".disabled",stateContext:!1,loadingDuration:0,errorDuration:2e3,action:!1,url:!1,base:"",urlData:{},defaultData:!0,serializeForm:!1,throttle:0,method:"get",data:{},dataType:"json",beforeSend:function(e){return e},beforeXHR:function(){},onRequest:function(){},onSuccess:function(){},onComplete:function(){},onFailure:function(){},onError:function(){},onAbort:function(){},successTest:!1,error:{beforeSend:"The before send function has aborted the request",error:"There was an error with your request",exitConditions:"API Request Aborted. Exit conditions met",JSONParse:"JSON could not be parsed during error handling",legacyParameters:"You are using legacy API success callback names",method:"The method you called is not defined",missingAction:"API action used but no url was defined",missingSerialize:"Required dependency jquery-serialize-object missing, using basic serialize",missingURL:"No URL specified for api event",noReturnedValue:"The beforeSend callback must return a settings object, beforeSend ignored.",parseError:"There was an error parsing your request",requiredParameter:"Missing a required URL parameter: ",statusMessage:"Server gave an error: ",timeout:"Your request timed out"},regExp:{required:/\{\$*[A-z0-9]+\}/g,optional:/\{\/\$*[A-z0-9]+\}/g},className:{loading:"loading",error:"error"},selector:{form:"form"},metadata:{action:"action"}},e.api.settings.api={}}(jQuery,window,document); \ No newline at end of file diff --git a/controller/static/semantic/dist/components/breadcrumb.css b/controller/static/semantic/dist/components/breadcrumb.css new file mode 100755 index 000000000..3c9768b4b --- /dev/null +++ b/controller/static/semantic/dist/components/breadcrumb.css @@ -0,0 +1,124 @@ +/*! + * # Semantic UI x.x - Breadcrumb + * http://github.com/semantic-org/semantic-ui/ + * + * + * Copyright 2014 Contributors + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ + + +/******************************* + Breadcrumb +*******************************/ + +.ui.breadcrumb { + margin: 1em 0em; + display: inline-block; + vertical-align: middle; +} +.ui.breadcrumb:first-child { + margin-top: 0em; +} +.ui.breadcrumb:last-child { + margin-bottom: 0em; +} + + +/******************************* + Content +*******************************/ + + +/* Divider */ +.ui.breadcrumb .divider { + display: inline-block; + opacity: 0.5; + margin: 0em 0.2rem 0em; + font-size: 0.9em; + color: rgba(0, 0, 0, 0.4); + vertical-align: baseline; +} + +/* Link */ +.ui.breadcrumb a { + color: #009fda; +} +.ui.breadcrumb a:hover { + color: #00b2f3; +} + +/* Icon Divider */ +.ui.breadcrumb .icon.divider { + font-size: 0.85714286em; + vertical-align: baseline; +} + +/* Section */ +.ui.breadcrumb a.section { + cursor: pointer; +} +.ui.breadcrumb .section { + display: inline-block; + margin: 0em; + padding: 0em; +} + +/* Loose Coupling */ +.ui.breadcrumb.segment { + display: inline-block; + padding: 0.5em 1em; +} + + +/******************************* + States +*******************************/ + +.ui.breadcrumb .active.section { + font-weight: bold; +} + + +/******************************* + Variations +*******************************/ + +.ui.mini.breadcrumb { + font-size: 0.65em; +} +.ui.tiny.breadcrumb { + font-size: 0.7em; +} +.ui.small.breadcrumb { + font-size: 0.75em; +} +.ui.breadcrumb { + font-size: 1em; +} +.ui.large.breadcrumb { + font-size: 1.1em; +} +.ui.big.breadcrumb { + font-size: 1.05em; +} +.ui.huge.breadcrumb { + font-size: 1.3em; +} +.ui.massive.breadcrumb { + font-size: 1.5em; +} + + +/******************************* + Theme Overrides +*******************************/ + + + +/******************************* + Site Overrides +*******************************/ + diff --git a/controller/static/semantic/dist/components/breadcrumb.min.css b/controller/static/semantic/dist/components/breadcrumb.min.css new file mode 100755 index 000000000..1146c2942 --- /dev/null +++ b/controller/static/semantic/dist/components/breadcrumb.min.css @@ -0,0 +1,10 @@ +/*! + * # Semantic UI x.x - Breadcrumb + * http://github.com/semantic-org/semantic-ui/ + * + * + * Copyright 2014 Contributors + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.breadcrumb{margin:1em 0;display:inline-block;vertical-align:middle}.ui.breadcrumb:first-child{margin-top:0}.ui.breadcrumb:last-child{margin-bottom:0}.ui.breadcrumb .divider{display:inline-block;opacity:.5;margin:0 .2rem;font-size:.9em;color:rgba(0,0,0,.4);vertical-align:baseline}.ui.breadcrumb a{color:#009fda}.ui.breadcrumb a:hover{color:#00b2f3}.ui.breadcrumb .icon.divider{font-size:.85714286em;vertical-align:baseline}.ui.breadcrumb a.section{cursor:pointer}.ui.breadcrumb .section{display:inline-block;margin:0;padding:0}.ui.breadcrumb.segment{display:inline-block;padding:.5em 1em}.ui.breadcrumb .active.section{font-weight:700}.ui.mini.breadcrumb{font-size:.65em}.ui.tiny.breadcrumb{font-size:.7em}.ui.small.breadcrumb{font-size:.75em}.ui.breadcrumb{font-size:1em}.ui.large.breadcrumb{font-size:1.1em}.ui.big.breadcrumb{font-size:1.05em}.ui.huge.breadcrumb{font-size:1.3em}.ui.massive.breadcrumb{font-size:1.5em} \ No newline at end of file diff --git a/controller/static/semantic/dist/components/button.css b/controller/static/semantic/dist/components/button.css new file mode 100755 index 000000000..ca01b2086 --- /dev/null +++ b/controller/static/semantic/dist/components/button.css @@ -0,0 +1,2391 @@ +/*! + * # Semantic UI x.x - Button + * http://github.com/semantic-org/semantic-ui/ + * + * + * Copyright 2014 Contributorss + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ + + +/******************************* + Button +*******************************/ + +.ui.button { + cursor: pointer; + display: inline-block; + min-height: 1em; + outline: none; + border: none; + vertical-align: baseline; + background-color: #e0e0e0; + color: rgba(0, 0, 0, 0.6); + font-family: 'Roboto', 'Helvetica Neue', Arial, Helvetica, sans-serif; + margin: 0em 0.25em 0em 0em; + padding: 0.78571em 1.5em 0.78571em; + text-transform: none; + text-shadow: none; + font-weight: bold; + line-height: 1; + font-style: normal; + text-align: center; + text-decoration: none; + background-image: none; + border-radius: 0.2857rem; + box-shadow: 0px 0px 0px 1px transparent inset, 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-transition: opacity 0.1s ease, background-color 0.1s ease, color 0.1s ease, box-shadow 0.1s ease, background 0.1s ease; + transition: opacity 0.1s ease, background-color 0.1s ease, color 0.1s ease, box-shadow 0.1s ease, background 0.1s ease; + will-change: ''; + -webkit-tap-highlight-color: transparent; +} + + +/******************************* + States +*******************************/ + + +/*-------------- + Hover +---------------*/ + +.ui.button:hover { + background-color: #e8e8e8; + background-image: none; + box-shadow: ''; + color: rgba(0, 0, 0, 0.8); +} +.ui.button:hover .icon { + opacity: 0.85; +} + +/*-------------- + Focus +---------------*/ + +.ui.button:focus { + background-color: ''; + color: rgba(0, 0, 0, 0.8); + background-image: '' !important; + box-shadow: 0px 0px 0px 1px transparent inset, 0px 0px 1px rgba(81, 167, 232, 0.8) inset, 0px 0px 3px 2px rgba(81, 167, 232, 0.8) !important; +} +.ui.button:focus .icon { + opacity: 0.85; +} + +/*-------------- + Down +---------------*/ + +.ui.button:active, +.ui.active.button:active { + background-color: #cccccc; + background-image: ''; + color: rgba(0, 0, 0, 0.8); + box-shadow: 0px 0px 0px 1px transparent inset, 0px 1px 4px 0px rgba(39, 41, 43, 0.15) inset !important; +} + +/*-------------- + Active +---------------*/ + +.ui.active.button { + background-color: #d0d0d0; + background-image: none; + box-shadow: 0px 0px 0px 1px transparent inset; + color: rgba(0, 0, 0, 0.8); +} +.ui.active.button:hover { + background-color: #d0d0d0; + background-image: none; + color: rgba(0, 0, 0, 0.8); +} +.ui.active.button:active { + background-color: #d0d0d0; + background-image: none; +} + +/*-------------- + Loading +---------------*/ + + +/* Specificity hack */ +.ui.loading.loading.loading.loading.loading.loading.button { + position: relative; + cursor: default; + text-shadow: none !important; + color: transparent !important; + opacity: 1; + pointer-events: none; + -webkit-transition: all 0s linear, opacity 0.2s ease; + transition: all 0s linear, opacity 0.2s ease; +} +.ui.loading.button:before { + position: absolute; + content: ''; + top: 50%; + left: 50%; + margin: -0.64285em 0em 0em -0.64285em; + width: 1.2857em; + height: 1.2857em; + border-radius: 500rem; + border: 0.2em solid rgba(0, 0, 0, 0.15); +} +.ui.loading.button:after { + position: absolute; + content: ''; + top: 50%; + left: 50%; + margin: -0.64285em 0em 0em -0.64285em; + width: 1.2857em; + height: 1.2857em; + -webkit-animation: button-spin 0.6s linear; + animation: button-spin 0.6s linear; + -webkit-animation-iteration-count: infinite; + animation-iteration-count: infinite; + border-radius: 500rem; + border-color: #ffffff transparent transparent; + border-style: solid; + border-width: 0.2em; + box-shadow: 0px 0px 0px 1px transparent; +} +.ui.labeled.icon.loading.button .icon { + background-color: transparent; + box-shadow: none; +} +@-webkit-keyframes button-spin { + from { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@keyframes button-spin { + from { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +.ui.basic.loading.button:not(.inverted):before { + border-color: rgba(0, 0, 0, 0.1); +} +.ui.basic.loading.button:not(.inverted):after { + border-top-color: #aaaaaa; +} + +/*------------------- + Disabled +--------------------*/ + +.ui.buttons .disabled.button, +.ui.disabled.button, +.ui.button:disabled, +.ui.disabled.button:hover, +.ui.disabled.active.button { + cursor: default; + background-color: #dcddde !important; + color: rgba(0, 0, 0, 0.4) !important; + opacity: 0.3 !important; + background-image: none !important; + box-shadow: none !important; + pointer-events: none; +} + +/* Basic Group With Disabled */ +.ui.basic.buttons .ui.disabled.button { + border-color: rgba(39, 41, 43, 0.5); +} + + +/******************************* + Types +*******************************/ + + +/*------------------- + Animated +--------------------*/ + +.ui.animated.button { + position: relative; + overflow: hidden; + vertical-align: middle; + padding-right: 0em !important; +} +.ui.animated.button .content { + will-change: transform, opacity; +} +.ui.animated.button .visible.content { + position: relative; + margin-right: 1.5em; +} +.ui.animated.button .hidden.content { + position: absolute; + width: 100%; +} + +/* Horizontal */ +.ui.animated.button .visible.content, +.ui.animated.button .hidden.content { + -webkit-transition: right 0.3s ease 0s; + transition: right 0.3s ease 0s; +} +.ui.animated.button .visible.content { + left: auto; + right: 0%; +} +.ui.animated.button .hidden.content { + top: 50%; + left: auto; + right: -100%; + margin-top: -0.5em; +} +.ui.animated.button:hover .visible.content { + left: auto; + right: 200%; +} +.ui.animated.button:hover .hidden.content { + left: auto; + right: 0%; +} + +/* Vertical */ +.ui.vertical.animated.button .visible.content, +.ui.vertical.animated.button .hidden.content { + -webkit-transition: top 0.3s ease, -webkit-transform 0.3s ease; + transition: top 0.3s ease, transform 0.3s ease; +} +.ui.vertical.animated.button .visible.content { + -webkit-transform: translateY(0%); + -ms-transform: translateY(0%); + transform: translateY(0%); + right: auto; +} +.ui.vertical.animated.button .hidden.content { + top: -50%; + left: 0%; + right: auto; +} +.ui.vertical.animated.button:hover .visible.content { + -webkit-transform: translateY(200%); + -ms-transform: translateY(200%); + transform: translateY(200%); + right: auto; +} +.ui.vertical.animated.button:hover .hidden.content { + top: 50%; + right: auto; +} + +/* Fade */ +.ui.fade.animated.button .visible.content, +.ui.fade.animated.button .hidden.content { + -webkit-transition: opacity 0.3s ease, -webkit-transform 0.3s ease; + transition: opacity 0.3s ease, transform 0.3s ease; +} +.ui.fade.animated.button .visible.content { + left: auto; + right: auto; + opacity: 1; + -webkit-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); +} +.ui.fade.animated.button .hidden.content { + opacity: 0; + left: 0%; + right: auto; + -webkit-transform: scale(1.5); + -ms-transform: scale(1.5); + transform: scale(1.5); +} +.ui.fade.animated.button:hover .visible.content { + left: auto; + right: auto; + opacity: 0; + -webkit-transform: scale(0.75); + -ms-transform: scale(0.75); + transform: scale(0.75); +} +.ui.fade.animated.button:hover .hidden.content { + left: 0%; + right: auto; + opacity: 1; + -webkit-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); +} + +/*------------------- + Inverted +--------------------*/ + +.ui.inverted.button { + box-shadow: 0px 0px 0px 2px #ffffff inset !important; + background: transparent none; + color: #ffffff; + text-shadow: none !important; +} +.ui.inverted.buttons .button { + margin: 0px 0px 0px -2px; +} +.ui.inverted.buttons .button:first-child { + margin-left: 0em; +} +.ui.inverted.vertical.buttons .button { + margin: 0px 0px -2px 0px; +} +.ui.inverted.vertical.buttons .button:first-child { + margin-top: 0em; +} +.ui.inverted.buttons .button:hover { + position: relative; +} +.ui.inverted.button:hover { + background: #ffffff; + box-shadow: 0px 0px 0px 2px #ffffff inset !important; + color: rgba(0, 0, 0, 0.8); +} + +/*------------------- + Social +--------------------*/ + + +/* Facebook */ +.ui.facebook.button { + background-color: #3b579d; + color: #ffffff; + text-shadow: none; + background-image: none; + box-shadow: 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.facebook.button:hover { + background-color: #3f5da8; + color: #ffffff; + text-shadow: none; +} +.ui.facebook.button:active { + background-color: #314983; + color: #ffffff; + text-shadow: none; +} + +/* Twitter */ +.ui.twitter.button { + background-color: #4092cc; + color: #ffffff; + text-shadow: none; + background-image: none; + box-shadow: 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.twitter.button:hover { + background-color: #4c99cf; + color: #ffffff; + text-shadow: none; +} +.ui.twitter.button:active { + background-color: #3180b7; + color: #ffffff; + text-shadow: none; +} + +/* Google Plus */ +.ui.google.plus.button { + background-color: #d34836; + color: #ffffff; + text-shadow: none; + background-image: none; + box-shadow: 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.google.plus.button:hover { + background-color: #d65343; + color: #ffffff; + text-shadow: none; +} +.ui.google.plus.button:active { + background-color: #bc3a29; + color: #ffffff; + text-shadow: none; +} + +/* Linked In */ +.ui.linkedin.button { + background-color: #1f88be; + color: #ffffff; + text-shadow: none; +} +.ui.linkedin.button:hover { + background-color: #2191cb; + color: #ffffff; + text-shadow: none; +} +.ui.linkedin.button:active { + background-color: #1a729f; + color: #ffffff; + text-shadow: none; +} + +/* YouTube */ +.ui.youtube.button { + background-color: #cc181e; + color: #ffffff; + text-shadow: none; + background-image: none; + box-shadow: 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.youtube.button:hover { + background-color: #da1a20; + color: #ffffff; + text-shadow: none; +} +.ui.youtube.button:active { + background-color: #ac1419; + color: #ffffff; + text-shadow: none; +} + +/* Instagram */ +.ui.instagram.button { + background-color: #49769c; + color: #ffffff; + text-shadow: none; + background-image: none; + box-shadow: 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.instagram.button:hover { + background-color: #4e7ea6; + color: #ffffff; + text-shadow: none; +} +.ui.instagram.button:active { + background-color: #3e6484; + color: #ffffff; + text-shadow: none; +} + +/* Pinterest */ +.ui.pinterest.button { + background-color: #00aced; + color: #ffffff; + text-shadow: none; + background-image: none; + box-shadow: 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.pinterest.button:hover { + background-color: #00b7fc; + color: #ffffff; + text-shadow: none; +} +.ui.pinterest.button:active { + background-color: #0092c9; + color: #ffffff; + text-shadow: none; +} + +/* VK */ +.ui.vk.button { + background-color: #4D7198; + color: #ffffff; + background-image: none; + box-shadow: 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.vk.button:hover { + background-color: #5279a2; + color: #ffffff; +} +.ui.vk.button:active { + background-color: #415f80; + color: #ffffff; +} + +/*-------------- + Icon +---------------*/ + +.ui.button > .icon { + opacity: 0.8; + margin: 0em 0.4em 0em -0.2em; + -webkit-transition: opacity 0.2s ease; + transition: opacity 0.2s ease; + vertical-align: baseline; + color: ''; +} +.ui.button > .right.icon { + margin: 0em -0.2em 0em 0.4em; +} + + +/******************************* + Variations +*******************************/ + + +/*------------------- + Floated +--------------------*/ + +.ui[class*="left floated"].buttons, +.ui[class*="left floated"].button { + float: left; + margin-left: 0em; + margin-right: 0.25em; +} +.ui[class*="right floated"].buttons, +.ui[class*="right floated"].button { + float: right; + margin-right: 0em; + margin-left: 0.25em; +} + +/*------------------- + Compact +--------------------*/ + +.ui.compact.buttons .button, +.ui.compact.button { + padding: 0.5892825em 1.125em 0.5892825em; +} +.ui.compact.icon.buttons .button, +.ui.compact.icon.button { + padding: 0.5892825em 0.5892825em 0.5892825em; +} +.ui.compact.labeled.icon.buttons .button, +.ui.compact.labeled.icon.button { + padding: 0.5892825em 3.69642em 0.5892825em; +} + +/*------------------- + Sizes +--------------------*/ + +.ui.mini.buttons .button, +.ui.mini.buttons .or, +.ui.mini.button { + font-size: 0.71428571rem; +} +.ui.tiny.buttons .button, +.ui.tiny.buttons .or, +.ui.tiny.button { + font-size: 0.85714286rem; +} +.ui.small.buttons .button, +.ui.small.buttons .or, +.ui.small.button { + font-size: 0.92857143rem; +} +.ui.buttons .button, +.ui.buttons .or, +.ui.button { + font-size: 1rem; +} +.ui.large.buttons .button, +.ui.large.buttons .or, +.ui.large.button { + font-size: 1.14285714rem; +} +.ui.big.buttons .button, +.ui.big.buttons .or, +.ui.big.button { + font-size: 1.28571429rem; +} +.ui.huge.buttons .button, +.ui.huge.buttons .or, +.ui.huge.button { + font-size: 1.42857143rem; +} +.ui.massive.buttons .button, +.ui.massive.buttons .or, +.ui.massive.button { + font-size: 1.71428571rem; +} + +/*-------------- + Icon Only +---------------*/ + +.ui.icon.buttons .button, +.ui.icon.button { + padding: 0.78571em 0.78571em 0.78571em; +} +.ui.icon.buttons .button > .icon, +.ui.icon.button > .icon { + opacity: 0.9; + margin: 0em; + vertical-align: top; +} + +/*------------------- + Basic +--------------------*/ + +.ui.basic.buttons .button, +.ui.basic.button { + background: transparent !important; + background-image: none; + color: rgba(0, 0, 0, 0.6) !important; + font-weight: normal; + border-radius: 0.2857rem; + text-transform: none; + text-shadow: none !important; + box-shadow: 0px 0px 0px 1px rgba(39, 41, 43, 0.15) inset; +} +.ui.basic.buttons { + box-shadow: 0px 0px 0px 1px rgba(39, 41, 43, 0.15); + border-radius: 0.2857rem; +} +.ui.basic.buttons .button { + border-radius: 0em; +} +.ui.basic.buttons .button:hover, +.ui.basic.button:hover { + background: #fafafa !important; + color: rgba(0, 0, 0, 0.8) !important; + box-shadow: 0px 0px 0px 1px rgba(39, 41, 43, 0.15) inset, 0px 0px 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.basic.buttons .button:active, +.ui.basic.button:active { + background: #f8f8f8 !important; + color: rgba(0, 0, 0, 0.8) !important; + box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.15) inset, 0px 1px 4px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.basic.buttons .active.button, +.ui.basic.active.button { + background: rgba(0, 0, 0, 0.05) !important; + box-shadow: '' !important; + color: rgba(0, 0, 0, 0.8); + box-shadow: rgba(39, 41, 43, 0.3); +} +.ui.basic.buttons .active.button:hover, +.ui.basic.active.button:hover { + background-color: rgba(0, 0, 0, 0.05); +} + +/* Vertical */ +.ui.basic.buttons .button:hover { + box-shadow: 0px 0px 0px 1px rgba(39, 41, 43, 0.15) inset, 0px 0px 0px 0px rgba(39, 41, 43, 0.15) inset inset; +} +.ui.basic.buttons .button:active { + box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.15) inset, 0px 1px 4px 0px rgba(39, 41, 43, 0.15) inset inset; +} +.ui.basic.buttons .active.button { + box-shadow: rgba(39, 41, 43, 0.3) inset; +} + +/* Standard Basic Inverted */ +.ui.basic.inverted.buttons .button, +.ui.basic.inverted.button { + background-color: transparent !important; + color: #fafafa !important; + box-shadow: 0px 0px 0px 2px rgba(255, 255, 255, 0.5) inset !important; +} +.ui.basic.inverted.buttons .button:hover, +.ui.basic.inverted.button:hover { + color: #ffffff !important; + box-shadow: 0px 0px 0px 2px #ffffff inset !important; +} +.ui.basic.inverted.buttons .button:active, +.ui.basic.inverted.button:active { + background-color: rgba(255, 255, 255, 0.05) !important; + color: #ffffff !important; + box-shadow: 0px 0px 0px 2px rgba(255, 255, 255, 0.9) inset !important; +} +.ui.basic.inverted.buttons .active.button, +.ui.basic.inverted.active.button { + background-color: rgba(255, 255, 255, 0.05); + color: #ffffff; + text-shadow: none; + box-shadow: 0px 0px 0px 2px rgba(255, 255, 255, 0.7) inset; +} +.ui.basic.inverted.buttons .active.button:hover, +.ui.basic.inverted.active.button:hover { + background-color: rgba(255, 255, 255, 0.07); + box-shadow: 0px 0px 0px 2px #ffffff inset !important; +} + +/* Basic Group */ +.ui.basic.buttons .button { + border-left: 1px solid rgba(39, 41, 43, 0.15); + box-shadow: none; +} +.ui.basic.vertical.buttons .button { + border-left: none; +} +.ui.basic.vertical.buttons .button { + border-left-width: 0px; + border-top: 1px solid rgba(39, 41, 43, 0.15); +} +.ui.basic.vertical.buttons .button:first-child { + border-top-width: 0px; +} + +/*-------------- + Labeled Icon +---------------*/ + +.ui.labeled.icon.buttons .button, +.ui.labeled.icon.button { + position: relative; + padding-left: 4.07142em !important; + padding-right: 1.5em !important; +} + +/* Left Labeled */ +.ui.labeled.icon.buttons > .button > .icon, +.ui.labeled.icon.button > .icon { + position: absolute; + width: 2.57142em; + height: 100%; + background-color: rgba(0, 0, 0, 0.05); + text-align: center; + color: ''; + border-radius: 0.2857rem 0px 0px 0.2857rem; + line-height: 1; + box-shadow: -1px 0px 0px 0px transparent inset; +} + +/* Left Labeled */ +.ui.labeled.icon.buttons > .button > .icon, +.ui.labeled.icon.button > .icon { + top: 0em; + left: 0em; +} + +/* Right Labeled */ +.ui[class*="right labeled"].icon.button { + padding-right: 4.07142em !important; + padding-left: 1.5em !important; +} +.ui[class*="right labeled"].icon.button > .icon { + left: auto; + right: 0em; + border-radius: 0em 0.2857rem 0.2857rem 0em; + box-shadow: 1px 0px 0px 0px transparent inset; +} +.ui.labeled.icon.buttons > .button > .icon:before, +.ui.labeled.icon.button > .icon:before, +.ui.labeled.icon.buttons > .button > .icon:after, +.ui.labeled.icon.button > .icon:after { + display: block; + position: absolute; + width: 100%; + top: 50%; + text-align: center; + margin-top: -0.5em; +} +.ui.labeled.icon.buttons .button > .icon { + border-radius: 0em; +} +.ui.labeled.icon.buttons .button:first-child > .icon { + border-top-left-radius: 0.2857rem; + border-bottom-left-radius: 0.2857rem; +} +.ui.labeled.icon.buttons .button:last-child > .icon { + border-top-right-radius: 0.2857rem; + border-bottom-right-radius: 0.2857rem; +} +.ui.vertical.labeled.icon.buttons .button:first-child > .icon { + border-radius: 0em; + border-top-left-radius: 0.2857rem; +} +.ui.vertical.labeled.icon.buttons .button:last-child > .icon { + border-radius: 0em; + border-bottom-left-radius: 0.2857rem; +} + +/* Fluid Labeled */ +.ui.fluid[class*="left labeled"].icon.button, +.ui.fluid[class*="right labeled"].icon.button { + padding-left: 1.5em !important; + padding-right: 1.5em !important; +} + +/*-------------- + Toggle +---------------*/ + + +/* Toggle (Modifies active state to give affordances) */ +.ui.toggle.buttons .active.button, +.ui.buttons .button.toggle.active, +.ui.button.toggle.active { + background-color: #5bbd72 !important; + box-shadow: none !important; + text-shadow: none; + color: #ffffff !important; +} +.ui.button.toggle.active:hover { + background-color: #66c17b !important; + text-shadow: none; + color: #ffffff !important; +} + +/*-------------- + Circular +---------------*/ + +.ui.circular.button { + border-radius: 10em; +} +.ui.circular.button > .icon { + width: 1em; + vertical-align: baseline; +} + +/*-------------- + Attached +---------------*/ + +.ui.attached.button { + display: block; + margin: 0em; + box-shadow: 0px 0px 0px 1px rgba(39, 41, 43, 0.15) !important; + border-radius: 0em; +} +.ui.attached.top.button { + border-radius: 0.2857rem 0.2857rem 0em 0em; +} +.ui.attached.bottom.button { + border-radius: 0em 0em 0.2857rem 0.2857rem; +} +.ui.attached.left.button { + display: inline-block; + border-left: none; + padding-right: 0.75em; + text-align: right; + border-radius: 0.2857rem 0em 0em 0.2857rem; +} +.ui.attached.right.button { + display: inline-block; + padding-left: 0.75em; + text-align: left; + border-radius: 0em 0.2857rem 0.2857rem 0em; +} + +/*------------------- + Or Buttons +--------------------*/ + +.ui.buttons .or { + position: relative; + float: left; + width: 0.3em; + height: 2.57142em; + z-index: 3; +} +.ui.buttons .or:before { + position: absolute; + text-align: center; + border-radius: 500rem; + content: 'or'; + top: 50%; + left: 50%; + background-color: #ffffff; + text-shadow: none; + margin-top: -0.892855em; + margin-left: -0.892855em; + width: 1.78571em; + height: 1.78571em; + line-height: 1.78571em; + color: rgba(0, 0, 0, 0.4); + font-style: normal; + font-weight: bold; + box-shadow: 0px 0px 0px 1px transparent inset; +} +.ui.buttons .or[data-text]:before { + content: attr(data-text); +} + +/* Fluid Or */ +.ui.fluid.buttons .or { + width: 0em !important; +} +.ui.fluid.buttons .or:after { + display: none; +} + +/*------------------- + Attached +--------------------*/ + + +/* Plural Attached */ +.attached.ui.buttons { + margin: 0px; + border-radius: 0em 0em 0em 0em; +} +.attached.ui.buttons .button { + margin: 0em; +} +.attached.ui.buttons .button:first-child { + border-radius: 0em 0em 0em 0em; +} +.attached.ui.buttons .button:last-child { + border-radius: 0em 0em 0em 0em; +} + +/* Top Side */ +[class*="top attached"].ui.buttons { + margin-bottom: -1px; + border-radius: 0.2857rem 0.2857rem 0em 0em; +} +[class*="top attached"].ui.buttons .button:first-child { + border-radius: 0.2857rem 0em 0em 0em; +} +[class*="top attached"].ui.buttons .button:last-child { + border-radius: 0em 0.2857rem 0em 0em; +} + +/* Bottom Side */ +[class*="bottom attached"].ui.buttons { + margin-top: -1px; + border-radius: 0em 0em 0.2857rem 0.2857rem; +} +[class*="bottom attached"].ui.buttons .button:first-child { + border-radius: 0em 0em 0em 0.2857rem; +} +[class*="bottom attached"].ui.buttons .button:last-child { + border-radius: 0em 0em 0.2857rem 0em; +} + +/* Left Side */ +[class*="left attached"].ui.buttons { + margin-left: -1px; + border-radius: 0em 0.2857rem 0.2857rem 0em; +} +[class*="left attached"].ui.buttons .button:first-child { + margin-left: -1px; + border-radius: 0em 0.2857rem 0em 0em; +} +[class*="left attached"].ui.buttons .button:last-child { + margin-left: -1px; + border-radius: 0em 0em 0.2857rem 0em; +} + +/* Right Side */ +[class*="right attached"].ui.buttons, +[class*="right attached"].ui.buttons .button { + margin-right: -1px; + border-radius: 0.2857rem 0em 0em 0.2857rem; +} +[class*="right attached"].ui.buttons .button:first-child { + margin-left: -1px; + border-radius: 0.2857rem 0em 0em 0em; +} +[class*="right attached"].ui.buttons .button:last-child { + margin-left: -1px; + border-radius: 0em 0em 0em 0.2857rem; +} + +/* Fluid */ +.ui.fluid.buttons, +.ui.button.fluid, +.ui.fluid.buttons > .button { + display: block; + width: 100%; +} +.ui.\32.buttons, +.ui.two.buttons { + width: 100%; +} +.ui.\32.buttons > .button, +.ui.two.buttons > .button { + width: 50%; +} +.ui.\33.buttons, +.ui.three.buttons { + width: 100%; +} +.ui.\33.buttons > .button, +.ui.three.buttons > .button { + width: 33.333%; +} +.ui.\34.buttons, +.ui.four.buttons { + width: 100%; +} +.ui.\34.buttons > .button, +.ui.four.buttons > .button { + width: 25%; +} +.ui.\35.buttons, +.ui.five.buttons { + width: 100%; +} +.ui.\35.buttons > .button, +.ui.five.buttons > .button { + width: 20%; +} +.ui.\36.buttons, +.ui.six.buttons { + width: 100%; +} +.ui.\36.buttons > .button, +.ui.six.buttons > .button { + width: 16.666%; +} +.ui.\37.buttons, +.ui.seven.buttons { + width: 100%; +} +.ui.\37.buttons > .button, +.ui.seven.buttons > .button { + width: 14.285%; +} +.ui.\38.buttons, +.ui.eight.buttons { + width: 100%; +} +.ui.\38.buttons > .button, +.ui.eight.buttons > .button { + width: 12.500%; +} +.ui.\39.buttons, +.ui.nine.buttons { + width: 100%; +} +.ui.\39.buttons > .button, +.ui.nine.buttons > .button { + width: 11.11%; +} +.ui.\31\30.buttons, +.ui.ten.buttons { + width: 100%; +} +.ui.\31\30.buttons > .button, +.ui.ten.buttons > .button { + width: 10%; +} +.ui.\31\31.buttons, +.ui.eleven.buttons { + width: 100%; +} +.ui.\31\31.buttons > .button, +.ui.eleven.buttons > .button { + width: 9.09%; +} +.ui.\31\32.buttons, +.ui.twelve.buttons { + width: 100%; +} +.ui.\31\32.buttons > .button, +.ui.twelve.buttons > .button { + width: 8.3333%; +} + +/* Fluid Vertical Buttons */ +.ui.fluid.vertical.buttons, +.ui.fluid.vertical.buttons > .button { + display: block; + width: auto; +} +.ui.\32.vertical.buttons > .button, +.ui.two.vertical.buttons > .button { + height: 50%; +} +.ui.\33.vertical.buttons > .button, +.ui.three.vertical.buttons > .button { + height: 33.333%; +} +.ui.\34.vertical.buttons > .button, +.ui.four.vertical.buttons > .button { + height: 25%; +} +.ui.\35.vertical.buttons > .button, +.ui.five.vertical.buttons > .button { + height: 20%; +} +.ui.\36.vertical.buttons > .button, +.ui.six.vertical.buttons > .button { + height: 16.666%; +} +.ui.\37.vertical.buttons > .button, +.ui.seven.vertical.buttons > .button { + height: 14.285%; +} +.ui.\38.vertical.buttons > .button, +.ui.eight.vertical.buttons > .button { + height: 12.500%; +} +.ui.\39.vertical.buttons > .button, +.ui.nine.vertical.buttons > .button { + height: 11.11%; +} +.ui.\31\30.vertical.buttons > .button, +.ui.ten.vertical.buttons > .button { + height: 10%; +} +.ui.\31\31.vertical.buttons > .button, +.ui.eleven.vertical.buttons > .button { + height: 9.09%; +} +.ui.\31\32.vertical.buttons > .button, +.ui.twelve.vertical.buttons > .button { + height: 8.3333%; +} + +/*------------------- + Colors +--------------------*/ + + +/*--- Black ---*/ + +.ui.black.buttons .button, +.ui.black.button { + background-color: #1b1c1d; + color: #ffffff; + text-shadow: none; + background-image: none; +} +.ui.black.button { + box-shadow: 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.black.buttons .button:hover, +.ui.black.button:hover { + background-color: #1b1c1d; + color: #ffffff; + text-shadow: none; +} +.ui.black.buttons .button:active, +.ui.black.button:active { + background-color: #0a0a0b; + color: #ffffff; + text-shadow: none; +} +.ui.black.buttons .active.button, +.ui.black.buttons .active.button:active, +.ui.black.active.button, +.ui.black.button .active.button:active { + background-color: #0f0f10; + color: #ffffff; + text-shadow: none; +} + +/* Basic */ +.ui.basic.black.buttons .button, +.ui.basic.black.button { + box-shadow: 0px 0px 0px 2px rgba(39, 41, 43, 0.15) inset !important; + color: rgba(0, 0, 0, 0.6) !important; +} +.ui.basic.black.buttons .button:hover, +.ui.basic.black.button:hover { + background: transparent !important; + box-shadow: 0px 0px 0px 2px #1b1c1d inset !important; + color: #1b1c1d !important; +} +.ui.basic.black.buttons .button:active, +.ui.basic.black.button:active { + box-shadow: 0px 0px 0px 2px #0a0a0b inset !important; + color: #0a0a0b !important; +} +.ui.basic.black.buttons .active.button, +.ui.basic.black.active.button { + background: transparent !important; + box-shadow: 0px 0px 0px 2px #0a0a0b inset !important; + color: #0a0a0b !important; +} +.ui.buttons > .basic.black.button:not(:first-child) { + margin-left: -2px; +} + +/* Inverted */ +.ui.inverted.black.buttons .button, +.ui.inverted.black.button { + background-color: transparent; + box-shadow: 0px 0px 0px 2px #d4d4d5 inset !important; + color: #ffffff; +} +.ui.inverted.black.buttons .button:hover, +.ui.inverted.black.button:hover { + box-shadow: 0px 0px 0px 2px #333333 inset !important; + background-color: #333333; + color: #ffffff; +} +.ui.inverted.black.buttons .active.button, +.ui.inverted.black.active.button { + box-shadow: 0px 0px 0px 2px #333333 inset !important; + background-color: #333333; + color: #ffffff; +} +.ui.inverted.black.buttons .button:active, +.ui.inverted.black.button:active { + box-shadow: 0px 0px 0px 2px #212121 inset !important; + background-color: #212121; + color: #ffffff; +} + +/* Inverted Basic */ +.ui.inverted.black.basic.buttons .button, +.ui.inverted.black.buttons .basic.button, +.ui.inverted.black.basic.button { + background-color: transparent; + box-shadow: 0px 0px 0px 2px rgba(255, 255, 255, 0.5) inset !important; + color: #ffffff !important; +} +.ui.inverted.black.basic.buttons .button:hover, +.ui.inverted.black.buttons .basic.button:hover, +.ui.inverted.black.basic.button:hover { + box-shadow: 0px 0px 0px 2px #333333 inset !important; + color: #ffffff !important; +} +.ui.inverted.black.basic.buttons .active.button, +.ui.inverted.black.buttons .basic.active.button, +.ui.inverted.black.basic.active.button { + box-shadow: 0px 0px 0px 2px #333333 inset !important; + color: #ffffff !important; +} +.ui.inverted.black.basic.buttons .button:active, +.ui.inverted.black.buttons .basic.button:active, +.ui.inverted.black.basic.button:active { + box-shadow: 0px 0px 0px 2px #212121 inset !important; + color: #ffffff !important; +} + +/*--- Blue ---*/ + +.ui.blue.buttons .button, +.ui.blue.button { + background-color: #0097c9; + color: #ffffff; + text-shadow: none; + background-image: none; +} +.ui.blue.button { + box-shadow: 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.blue.buttons .button:hover, +.ui.blue.button:hover { + background-color: #0079a2; + color: #ffffff; + text-shadow: none; +} +.ui.blue.buttons .button:active, +.ui.blue.button:active { + background-color: #00536f; + color: #ffffff; + text-shadow: none; +} +.ui.blue.buttons .active.button, +.ui.blue.buttons .active.button:active, +.ui.blue.active.button, +.ui.blue.button .active.button:active { + background-color: #005b7a; + color: #ffffff; + text-shadow: none; +} + +/* Basic */ +.ui.basic.blue.buttons .button, +.ui.basic.blue.button { + box-shadow: 0px 0px 0px 2px rgba(39, 41, 43, 0.15) inset !important; + color: rgba(0, 0, 0, 0.6) !important; +} +.ui.basic.blue.buttons .button:hover, +.ui.basic.blue.button:hover { + background: transparent !important; + box-shadow: 0px 0px 0px 2px #0079a2 inset !important; + color: #0079a2 !important; +} +.ui.basic.blue.buttons .button:active, +.ui.basic.blue.button:active { + box-shadow: 0px 0px 0px 2px #00536f inset !important; + color: #00536f !important; +} +.ui.basic.blue.buttons .active.button, +.ui.basic.blue.active.button { + background: transparent !important; + box-shadow: 0px 0px 0px 2px #00536f inset !important; + color: #00536f !important; +} +.ui.buttons > .basic.blue.button:not(:first-child) { + margin-left: -2px; +} + +/* Inverted */ +.ui.inverted.blue.buttons .button, +.ui.inverted.blue.button { + background-color: transparent; + box-shadow: 0px 0px 0px 2px #0097c9 inset !important; + color: #0097c9; +} +.ui.inverted.blue.buttons .button:hover, +.ui.inverted.blue.button:hover { + box-shadow: 0px 0px 0px 2px #0097c9 inset !important; + background-color: #0097c9; + color: #ffffff; +} +.ui.inverted.blue.buttons .active.button, +.ui.inverted.blue.active.button { + box-shadow: 0px 0px 0px 2px #0097c9 inset !important; + background-color: #0097c9; + color: #ffffff; +} +.ui.inverted.blue.buttons .button:active, +.ui.inverted.blue.button:active { + box-shadow: 0px 0px 0px 2px #007ca5 inset !important; + background-color: #007ca5; + color: #ffffff; +} + +/* Inverted Basic */ +.ui.inverted.blue.basic.buttons .button, +.ui.inverted.blue.buttons .basic.button, +.ui.inverted.blue.basic.button { + background-color: transparent; + box-shadow: 0px 0px 0px 2px rgba(255, 255, 255, 0.5) inset !important; + color: #ffffff !important; +} +.ui.inverted.blue.basic.buttons .button:hover, +.ui.inverted.blue.buttons .basic.button:hover, +.ui.inverted.blue.basic.button:hover { + box-shadow: 0px 0px 0px 2px #0097c9 inset !important; + color: #0097c9 !important; +} +.ui.inverted.blue.basic.buttons .active.button, +.ui.inverted.blue.buttons .basic.active.button, +.ui.inverted.blue.basic.active.button { + box-shadow: 0px 0px 0px 2px #0097c9 inset !important; + color: #0097c9 !important; +} +.ui.inverted.blue.basic.buttons .button:active, +.ui.inverted.blue.buttons .basic.button:active, +.ui.inverted.blue.basic.button:active { + box-shadow: 0px 0px 0px 2px #007ca5 inset !important; + color: #0097c9 !important; +} + +/*--- Green ---*/ + +.ui.green.buttons .button, +.ui.green.button { + background-color: #5bbd72; + color: #ffffff; + text-shadow: none; + background-image: none; +} +.ui.green.button { + box-shadow: 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.green.buttons .button:hover, +.ui.green.button:hover { + background-color: #66c17b; + color: #ffffff; + text-shadow: none; +} +.ui.green.buttons .button:active, +.ui.green.button:active { + background-color: #46ae5f; + color: #ffffff; + text-shadow: none; +} +.ui.green.buttons .active.button, +.ui.green.buttons .active.button:active, +.ui.green.active.button, +.ui.green.button .active.button:active { + background-color: #49b562; + color: #ffffff; + text-shadow: none; +} + +/* Basic */ +.ui.basic.green.buttons .button, +.ui.basic.green.button { + box-shadow: 0px 0px 0px 2px rgba(39, 41, 43, 0.15) inset !important; + color: rgba(0, 0, 0, 0.6) !important; +} +.ui.basic.green.buttons .button:hover, +.ui.basic.green.button:hover { + background: transparent !important; + box-shadow: 0px 0px 0px 2px #66c17b inset !important; + color: #66c17b !important; +} +.ui.basic.green.buttons .button:active, +.ui.basic.green.button:active { + box-shadow: 0px 0px 0px 2px #46ae5f inset !important; + color: #46ae5f !important; +} +.ui.basic.green.buttons .active.button, +.ui.basic.green.active.button { + background: transparent !important; + box-shadow: 0px 0px 0px 2px #46ae5f inset !important; + color: #46ae5f !important; +} +.ui.buttons > .basic.green.button:not(:first-child) { + margin-left: -2px; +} + +/* Inverted */ +.ui.inverted.green.buttons .button, +.ui.inverted.green.button { + background-color: transparent; + box-shadow: 0px 0px 0px 2px #2ecc40 inset !important; + color: #2ecc40; +} +.ui.inverted.green.buttons .button:hover, +.ui.inverted.green.button:hover { + box-shadow: 0px 0px 0px 2px #2ecc40 inset !important; + background-color: #2ecc40; + color: #ffffff; +} +.ui.inverted.green.buttons .active.button, +.ui.inverted.green.active.button { + box-shadow: 0px 0px 0px 2px #2ecc40 inset !important; + background-color: #2ecc40; + color: #ffffff; +} +.ui.inverted.green.buttons .button:active, +.ui.inverted.green.button:active { + box-shadow: 0px 0px 0px 2px #27af37 inset !important; + background-color: #27af37; + color: #ffffff; +} + +/* Inverted Basic */ +.ui.inverted.green.basic.buttons .button, +.ui.inverted.green.buttons .basic.button, +.ui.inverted.green.basic.button { + background-color: transparent; + box-shadow: 0px 0px 0px 2px rgba(255, 255, 255, 0.5) inset !important; + color: #ffffff !important; +} +.ui.inverted.green.basic.buttons .button:hover, +.ui.inverted.green.buttons .basic.button:hover, +.ui.inverted.green.basic.button:hover { + box-shadow: 0px 0px 0px 2px #2ecc40 inset !important; + color: #2ecc40 !important; +} +.ui.inverted.green.basic.buttons .active.button, +.ui.inverted.green.buttons .basic.active.button, +.ui.inverted.green.basic.active.button { + box-shadow: 0px 0px 0px 2px #2ecc40 inset !important; + color: #2ecc40 !important; +} +.ui.inverted.green.basic.buttons .button:active, +.ui.inverted.green.buttons .basic.button:active, +.ui.inverted.green.basic.button:active { + box-shadow: 0px 0px 0px 2px #27af37 inset !important; + color: #2ecc40 !important; +} + +/*--- Orange ---*/ + +.ui.orange.buttons .button, +.ui.orange.button { + background-color: #e07b53; + color: #ffffff; + text-shadow: none; + background-image: none; +} +.ui.orange.button { + box-shadow: 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.orange.buttons .button:hover, +.ui.orange.button:hover { + background-color: #e28560; + color: #ffffff; + text-shadow: none; +} +.ui.orange.buttons .button:active, +.ui.orange.button:active { + background-color: #db6435; + color: #ffffff; + text-shadow: none; +} +.ui.orange.buttons .active.button, +.ui.orange.buttons .active.button:active, +.ui.orange.active.button, +.ui.orange.button .active.button:active { + background-color: #dc6a3d; + color: #ffffff; + text-shadow: none; +} + +/* Basic */ +.ui.basic.orange.buttons .button, +.ui.basic.orange.button { + box-shadow: 0px 0px 0px 2px rgba(39, 41, 43, 0.15) inset !important; + color: rgba(0, 0, 0, 0.6) !important; +} +.ui.basic.orange.buttons .button:hover, +.ui.basic.orange.button:hover { + background: transparent !important; + box-shadow: 0px 0px 0px 2px #e28560 inset !important; + color: #e28560 !important; +} +.ui.basic.orange.buttons .button:active, +.ui.basic.orange.button:active { + box-shadow: 0px 0px 0px 2px #db6435 inset !important; + color: #db6435 !important; +} +.ui.basic.orange.buttons .active.button, +.ui.basic.orange.active.button { + background: transparent !important; + box-shadow: 0px 0px 0px 2px #db6435 inset !important; + color: #db6435 !important; +} +.ui.buttons > .basic.orange.button:not(:first-child) { + margin-left: -2px; +} + +/* Inverted */ +.ui.inverted.orange.buttons .button, +.ui.inverted.orange.button { + background-color: transparent; + box-shadow: 0px 0px 0px 2px #ff851b inset !important; + color: #ff851b; +} +.ui.inverted.orange.buttons .button:hover, +.ui.inverted.orange.button:hover { + box-shadow: 0px 0px 0px 2px #ff851b inset !important; + background-color: #ff851b; + color: #ffffff; +} +.ui.inverted.orange.buttons .active.button, +.ui.inverted.orange.active.button { + box-shadow: 0px 0px 0px 2px #ff851b inset !important; + background-color: #ff851b; + color: #ffffff; +} +.ui.inverted.orange.buttons .button:active, +.ui.inverted.orange.button:active { + box-shadow: 0px 0px 0px 2px #f67300 inset !important; + background-color: #f67300; + color: #ffffff; +} + +/* Inverted Basic */ +.ui.inverted.orange.basic.buttons .button, +.ui.inverted.orange.buttons .basic.button, +.ui.inverted.orange.basic.button { + background-color: transparent; + box-shadow: 0px 0px 0px 2px rgba(255, 255, 255, 0.5) inset !important; + color: #ffffff !important; +} +.ui.inverted.orange.basic.buttons .button:hover, +.ui.inverted.orange.buttons .basic.button:hover, +.ui.inverted.orange.basic.button:hover { + box-shadow: 0px 0px 0px 2px #ff851b inset !important; + color: #ff851b !important; +} +.ui.inverted.orange.basic.buttons .active.button, +.ui.inverted.orange.buttons .basic.active.button, +.ui.inverted.orange.basic.active.button { + box-shadow: 0px 0px 0px 2px #ff851b inset !important; + color: #ff851b !important; +} +.ui.inverted.orange.basic.buttons .button:active, +.ui.inverted.orange.buttons .basic.button:active, +.ui.inverted.orange.basic.button:active { + box-shadow: 0px 0px 0px 2px #f67300 inset !important; + color: #ff851b !important; +} + +/*--- Pink ---*/ + +.ui.pink.buttons .button, +.ui.pink.button { + background-color: #d9499a; + color: #ffffff; + text-shadow: none; + background-image: none; +} +.ui.pink.button { + box-shadow: 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.pink.buttons .button:hover, +.ui.pink.button:hover { + background-color: #dc56a1; + color: #ffffff; + text-shadow: none; +} +.ui.pink.buttons .button:active, +.ui.pink.button:active { + background-color: #d22c8a; + color: #ffffff; + text-shadow: none; +} +.ui.pink.buttons .active.button, +.ui.pink.buttons .active.button:active, +.ui.pink.active.button, +.ui.pink.button .active.button:active { + background-color: #d5348e; + color: #ffffff; + text-shadow: none; +} + +/* Basic */ +.ui.basic.pink.buttons .button, +.ui.basic.pink.button { + box-shadow: 0px 0px 0px 2px rgba(39, 41, 43, 0.15) inset !important; + color: rgba(0, 0, 0, 0.6) !important; +} +.ui.basic.pink.buttons .button:hover, +.ui.basic.pink.button:hover { + background: transparent !important; + box-shadow: 0px 0px 0px 2px #dc56a1 inset !important; + color: #dc56a1 !important; +} +.ui.basic.pink.buttons .button:active, +.ui.basic.pink.button:active { + box-shadow: 0px 0px 0px 2px #d22c8a inset !important; + color: #d22c8a !important; +} +.ui.basic.pink.buttons .active.button, +.ui.basic.pink.active.button { + background: transparent !important; + box-shadow: 0px 0px 0px 2px #d22c8a inset !important; + color: #d22c8a !important; +} +.ui.buttons > .basic.pink.button:not(:first-child) { + margin-left: -2px; +} + +/* Inverted */ +.ui.inverted.pink.buttons .button, +.ui.inverted.pink.button { + background-color: transparent; + box-shadow: 0px 0px 0px 2px #ff8edf inset !important; + color: #ff8edf; +} +.ui.inverted.pink.buttons .button:hover, +.ui.inverted.pink.button:hover { + box-shadow: 0px 0px 0px 2px #ff8edf inset !important; + background-color: #ff8edf; + color: #ffffff; +} +.ui.inverted.pink.buttons .active.button, +.ui.inverted.pink.active.button { + box-shadow: 0px 0px 0px 2px #ff8edf inset !important; + background-color: #ff8edf; + color: #ffffff; +} +.ui.inverted.pink.buttons .button:active, +.ui.inverted.pink.button:active { + box-shadow: 0px 0px 0px 2px #ff6ad5 inset !important; + background-color: #ff6ad5; + color: #ffffff; +} + +/* Inverted Basic */ +.ui.inverted.pink.basic.buttons .button, +.ui.inverted.pink.buttons .basic.button, +.ui.inverted.pink.basic.button { + background-color: transparent; + box-shadow: 0px 0px 0px 2px rgba(255, 255, 255, 0.5) inset !important; + color: #ffffff !important; +} +.ui.inverted.pink.basic.buttons .button:hover, +.ui.inverted.pink.buttons .basic.button:hover, +.ui.inverted.pink.basic.button:hover { + box-shadow: 0px 0px 0px 2px #ff8edf inset !important; + color: #ff8edf !important; +} +.ui.inverted.pink.basic.buttons .active.button, +.ui.inverted.pink.buttons .basic.active.button, +.ui.inverted.pink.basic.active.button { + box-shadow: 0px 0px 0px 2px #ff8edf inset !important; + color: #ff8edf !important; +} +.ui.inverted.pink.basic.buttons .button:active, +.ui.inverted.pink.buttons .basic.button:active, +.ui.inverted.pink.basic.button:active { + box-shadow: 0px 0px 0px 2px #ff6ad5 inset !important; + color: #ff8edf !important; +} + +/*--- Purple ---*/ + +.ui.purple.buttons .button, +.ui.purple.button { + background-color: #564f8a; + color: #ffffff; + text-shadow: none; + background-image: none; +} +.ui.purple.button { + box-shadow: 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.purple.buttons .button:hover, +.ui.purple.button:hover { + background-color: #5c5594; + color: #ffffff; + text-shadow: none; +} +.ui.purple.buttons .button:active, +.ui.purple.button:active { + background-color: #484273; + color: #ffffff; + text-shadow: none; +} +.ui.purple.buttons .active.button, +.ui.purple.buttons .active.button:active, +.ui.purple.active.button, +.ui.purple.button .active.button:active { + background-color: #4c467a; + color: #ffffff; + text-shadow: none; +} + +/* Basic */ +.ui.basic.purple.buttons .button, +.ui.basic.purple.button { + box-shadow: 0px 0px 0px 2px rgba(39, 41, 43, 0.15) inset !important; + color: rgba(0, 0, 0, 0.6) !important; +} +.ui.basic.purple.buttons .button:hover, +.ui.basic.purple.button:hover { + background: transparent !important; + box-shadow: 0px 0px 0px 2px #5c5594 inset !important; + color: #5c5594 !important; +} +.ui.basic.purple.buttons .button:active, +.ui.basic.purple.button:active { + box-shadow: 0px 0px 0px 2px #484273 inset !important; + color: #484273 !important; +} +.ui.basic.purple.buttons .active.button, +.ui.basic.purple.active.button { + background: transparent !important; + box-shadow: 0px 0px 0px 2px #484273 inset !important; + color: #484273 !important; +} +.ui.buttons > .basic.purple.button:not(:first-child) { + margin-left: -2px; +} + +/* Inverted */ +.ui.inverted.purple.buttons .button, +.ui.inverted.purple.button { + background-color: transparent; + box-shadow: 0px 0px 0px 2px #cdc6ff inset !important; + color: #cdc6ff; +} +.ui.inverted.purple.buttons .button:hover, +.ui.inverted.purple.button:hover { + box-shadow: 0px 0px 0px 2px #cdc6ff inset !important; + background-color: #cdc6ff; + color: #1b1c1d; +} +.ui.inverted.purple.buttons .active.button, +.ui.inverted.purple.active.button { + box-shadow: 0px 0px 0px 2px #cdc6ff inset !important; + background-color: #cdc6ff; + color: #1b1c1d; +} +.ui.inverted.purple.buttons .button:active, +.ui.inverted.purple.button:active { + box-shadow: 0px 0px 0px 2px #aea2ff inset !important; + background-color: #aea2ff; + color: #1b1c1d; +} + +/* Inverted Basic */ +.ui.inverted.purple.basic.buttons .button, +.ui.inverted.purple.buttons .basic.button, +.ui.inverted.purple.basic.button { + background-color: transparent; + box-shadow: 0px 0px 0px 2px rgba(255, 255, 255, 0.5) inset !important; + color: #ffffff !important; +} +.ui.inverted.purple.basic.buttons .button:hover, +.ui.inverted.purple.buttons .basic.button:hover, +.ui.inverted.purple.basic.button:hover { + box-shadow: 0px 0px 0px 2px #cdc6ff inset !important; + color: #cdc6ff !important; +} +.ui.inverted.purple.basic.buttons .active.button, +.ui.inverted.purple.buttons .basic.active.button, +.ui.inverted.purple.basic.active.button { + box-shadow: 0px 0px 0px 2px #cdc6ff inset !important; + color: #cdc6ff !important; +} +.ui.inverted.purple.basic.buttons .button:active, +.ui.inverted.purple.buttons .basic.button:active, +.ui.inverted.purple.basic.button:active { + box-shadow: 0px 0px 0px 2px #aea2ff inset !important; + color: #cdc6ff !important; +} + +/*--- Red ---*/ + +.ui.red.buttons .button, +.ui.red.button { + background-color: #d95c5c; + color: #ffffff; + text-shadow: none; + background-image: none; +} +.ui.red.button { + box-shadow: 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.red.buttons .button:hover, +.ui.red.button:hover { + background-color: #dc6868; + color: #ffffff; + text-shadow: none; +} +.ui.red.buttons .button:active, +.ui.red.button:active { + background-color: #d23f3f; + color: #ffffff; + text-shadow: none; +} +.ui.red.buttons .active.button, +.ui.red.buttons .active.button:active, +.ui.red.active.button, +.ui.red.button .active.button:active { + background-color: #d44747; + color: #ffffff; + text-shadow: none; +} + +/* Basic */ +.ui.basic.red.buttons .button, +.ui.basic.red.button { + box-shadow: 0px 0px 0px 2px rgba(39, 41, 43, 0.15) inset !important; + color: rgba(0, 0, 0, 0.6) !important; +} +.ui.basic.red.buttons .button:hover, +.ui.basic.red.button:hover { + background: transparent !important; + box-shadow: 0px 0px 0px 2px #dc6868 inset !important; + color: #dc6868 !important; +} +.ui.basic.red.buttons .button:active, +.ui.basic.red.button:active { + box-shadow: 0px 0px 0px 2px #d23f3f inset !important; + color: #d23f3f !important; +} +.ui.basic.red.buttons .active.button, +.ui.basic.red.active.button { + background: transparent !important; + box-shadow: 0px 0px 0px 2px #d23f3f inset !important; + color: #d23f3f !important; +} +.ui.buttons > .basic.red.button:not(:first-child) { + margin-left: -2px; +} + +/* Inverted */ +.ui.inverted.red.buttons .button, +.ui.inverted.red.button { + background-color: transparent; + box-shadow: 0px 0px 0px 2px #ff695e inset !important; + color: #ff695e; +} +.ui.inverted.red.buttons .button:hover, +.ui.inverted.red.button:hover { + box-shadow: 0px 0px 0px 2px #ff695e inset !important; + background-color: #ff695e; + color: #ffffff; +} +.ui.inverted.red.buttons .active.button, +.ui.inverted.red.active.button { + box-shadow: 0px 0px 0px 2px #ff695e inset !important; + background-color: #ff695e; + color: #ffffff; +} +.ui.inverted.red.buttons .button:active, +.ui.inverted.red.button:active { + box-shadow: 0px 0px 0px 2px #ff483a inset !important; + background-color: #ff483a; + color: #ffffff; +} + +/* Inverted Basic */ +.ui.inverted.red.basic.buttons .button, +.ui.inverted.red.buttons .basic.button, +.ui.inverted.red.basic.button { + background-color: transparent; + box-shadow: 0px 0px 0px 2px rgba(255, 255, 255, 0.5) inset !important; + color: #ffffff !important; +} +.ui.inverted.red.basic.buttons .button:hover, +.ui.inverted.red.buttons .basic.button:hover, +.ui.inverted.red.basic.button:hover { + box-shadow: 0px 0px 0px 2px #ff695e inset !important; + color: #ff695e !important; +} +.ui.inverted.red.basic.buttons .active.button, +.ui.inverted.red.buttons .basic.active.button, +.ui.inverted.red.basic.active.button { + box-shadow: 0px 0px 0px 2px #ff695e inset !important; + color: #ff695e !important; +} +.ui.inverted.red.basic.buttons .button:active, +.ui.inverted.red.buttons .basic.button:active, +.ui.inverted.red.basic.button:active { + box-shadow: 0px 0px 0px 2px #ff483a inset !important; + color: #ff695e !important; +} + +/*--- Teal ---*/ + +.ui.teal.buttons .button, +.ui.teal.button { + background-color: #00b5ad; + color: #ffffff; + text-shadow: none; + background-image: none; +} +.ui.teal.button { + box-shadow: 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.teal.buttons .button:hover, +.ui.teal.button:hover { + background-color: #00c4bc; + color: #ffffff; + text-shadow: none; +} +.ui.teal.buttons .button:active, +.ui.teal.button:active { + background-color: #00918b; + color: #ffffff; + text-shadow: none; +} +.ui.teal.buttons .active.button, +.ui.teal.buttons .active.button:active, +.ui.teal.active.button, +.ui.teal.button .active.button:active { + background-color: #009c95; + color: #ffffff; + text-shadow: none; +} + +/* Basic */ +.ui.basic.teal.buttons .button, +.ui.basic.teal.button { + box-shadow: 0px 0px 0px 2px rgba(39, 41, 43, 0.15) inset !important; + color: rgba(0, 0, 0, 0.6) !important; +} +.ui.basic.teal.buttons .button:hover, +.ui.basic.teal.button:hover { + background: transparent !important; + box-shadow: 0px 0px 0px 2px #00c4bc inset !important; + color: #00c4bc !important; +} +.ui.basic.teal.buttons .button:active, +.ui.basic.teal.button:active { + box-shadow: 0px 0px 0px 2px #00918b inset !important; + color: #00918b !important; +} +.ui.basic.teal.buttons .active.button, +.ui.basic.teal.active.button { + background: transparent !important; + box-shadow: 0px 0px 0px 2px #00918b inset !important; + color: #00918b !important; +} +.ui.buttons > .basic.teal.button:not(:first-child) { + margin-left: -2px; +} + +/* Inverted */ +.ui.inverted.teal.buttons .button, +.ui.inverted.teal.button { + background-color: transparent; + box-shadow: 0px 0px 0px 2px #6dffff inset !important; + color: #6dffff; +} +.ui.inverted.teal.buttons .button:hover, +.ui.inverted.teal.button:hover { + box-shadow: 0px 0px 0px 2px #6dffff inset !important; + background-color: #6dffff; + color: #1b1c1d; +} +.ui.inverted.teal.buttons .active.button, +.ui.inverted.teal.active.button { + box-shadow: 0px 0px 0px 2px #6dffff inset !important; + background-color: #6dffff; + color: #1b1c1d; +} +.ui.inverted.teal.buttons .button:active, +.ui.inverted.teal.button:active { + box-shadow: 0px 0px 0px 2px #49ffff inset !important; + background-color: #49ffff; + color: #1b1c1d; +} + +/* Inverted Basic */ +.ui.inverted.teal.basic.buttons .button, +.ui.inverted.teal.buttons .basic.button, +.ui.inverted.teal.basic.button { + background-color: transparent; + box-shadow: 0px 0px 0px 2px rgba(255, 255, 255, 0.5) inset !important; + color: #ffffff !important; +} +.ui.inverted.teal.basic.buttons .button:hover, +.ui.inverted.teal.buttons .basic.button:hover, +.ui.inverted.teal.basic.button:hover { + box-shadow: 0px 0px 0px 2px #6dffff inset !important; + color: #6dffff !important; +} +.ui.inverted.teal.basic.buttons .active.button, +.ui.inverted.teal.buttons .basic.active.button, +.ui.inverted.teal.basic.active.button { + box-shadow: 0px 0px 0px 2px #6dffff inset !important; + color: #6dffff !important; +} +.ui.inverted.teal.basic.buttons .button:active, +.ui.inverted.teal.buttons .basic.button:active, +.ui.inverted.teal.basic.button:active { + box-shadow: 0px 0px 0px 2px #49ffff inset !important; + color: #6dffff !important; +} + +/*--- Yellow ---*/ + +.ui.yellow.buttons .button, +.ui.yellow.button { + background-color: #f2c61f; + color: #ffffff; + text-shadow: none; + background-image: none; +} +.ui.yellow.button { + box-shadow: 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.yellow.buttons .button:hover, +.ui.yellow.button:hover { + background-color: #f3ca2d; + color: #ffffff; + text-shadow: none; +} +.ui.yellow.buttons .button:active, +.ui.yellow.button:active { + background-color: #e0b40d; + color: #ffffff; + text-shadow: none; +} +.ui.yellow.buttons .active.button, +.ui.yellow.buttons .active.button:active, +.ui.yellow.active.button, +.ui.yellow.button .active.button:active { + background-color: #eabc0e; + color: #ffffff; + text-shadow: none; +} + +/* Basic */ +.ui.basic.yellow.buttons .button, +.ui.basic.yellow.button { + box-shadow: 0px 0px 0px 2px rgba(39, 41, 43, 0.15) inset !important; + color: rgba(0, 0, 0, 0.6) !important; +} +.ui.basic.yellow.buttons .button:hover, +.ui.basic.yellow.button:hover { + background: transparent !important; + box-shadow: 0px 0px 0px 2px #f3ca2d inset !important; + color: #f3ca2d !important; +} +.ui.basic.yellow.buttons .button:active, +.ui.basic.yellow.button:active { + box-shadow: 0px 0px 0px 2px #e0b40d inset !important; + color: #e0b40d !important; +} +.ui.basic.yellow.buttons .active.button, +.ui.basic.yellow.active.button { + background: transparent !important; + box-shadow: 0px 0px 0px 2px #e0b40d inset !important; + color: #e0b40d !important; +} +.ui.buttons > .basic.yellow.button:not(:first-child) { + margin-left: -2px; +} + +/* Inverted */ +.ui.inverted.yellow.buttons .button, +.ui.inverted.yellow.button { + background-color: transparent; + box-shadow: 0px 0px 0px 2px #ffe21f inset !important; + color: #ffe21f; +} +.ui.inverted.yellow.buttons .button:hover, +.ui.inverted.yellow.button:hover { + box-shadow: 0px 0px 0px 2px #ffe21f inset !important; + background-color: #ffe21f; + color: #1b1c1d; +} +.ui.inverted.yellow.buttons .active.button, +.ui.inverted.yellow.active.button { + box-shadow: 0px 0px 0px 2px #ffe21f inset !important; + background-color: #ffe21f; + color: #1b1c1d; +} +.ui.inverted.yellow.buttons .button:active, +.ui.inverted.yellow.button:active { + box-shadow: 0px 0px 0px 2px #fada00 inset !important; + background-color: #fada00; + color: #1b1c1d; +} + +/* Inverted Basic */ +.ui.inverted.yellow.basic.buttons .button, +.ui.inverted.yellow.buttons .basic.button, +.ui.inverted.yellow.basic.button { + background-color: transparent; + box-shadow: 0px 0px 0px 2px rgba(255, 255, 255, 0.5) inset !important; + color: #ffffff !important; +} +.ui.inverted.yellow.basic.buttons .button:hover, +.ui.inverted.yellow.buttons .basic.button:hover, +.ui.inverted.yellow.basic.button:hover { + box-shadow: 0px 0px 0px 2px #ffe21f inset !important; + color: #ffe21f !important; +} +.ui.inverted.yellow.basic.buttons .active.button, +.ui.inverted.yellow.buttons .basic.active.button, +.ui.inverted.yellow.basic.active.button { + box-shadow: 0px 0px 0px 2px #ffe21f inset !important; + color: #ffe21f !important; +} +.ui.inverted.yellow.basic.buttons .button:active, +.ui.inverted.yellow.buttons .basic.button:active, +.ui.inverted.yellow.basic.button:active { + box-shadow: 0px 0px 0px 2px #fada00 inset !important; + color: #ffe21f !important; +} + +/*------------------- + Primary +--------------------*/ + +.ui.primary.buttons .button, +.ui.primary.button { + background-color: #006e93; + color: #ffffff; + text-shadow: none; + background-image: none; +} +.ui.primary.button { + box-shadow: 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.primary.buttons .button:hover, +.ui.primary.button:hover { + background-color: #0079a2; + color: #ffffff; + text-shadow: none; +} +.ui.primary.buttons .button:active, +.ui.primary.button:active { + background-color: #00536f; + color: #ffffff; + text-shadow: none; +} +.ui.primary.buttons .active.button, +.ui.primary.active.button { + background-color: #005b7a; + color: #ffffff; + text-shadow: none; +} + +/*------------------- + Secondary +--------------------*/ + +.ui.secondary.buttons .button, +.ui.secondary.button { + background-color: #1b1c1d; + color: #ffffff; + text-shadow: none; + background-image: none; +} +.ui.secondary.button { + box-shadow: 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.secondary.buttons .button:hover, +.ui.secondary.button:hover { + background-color: #222425; + color: #ffffff; + text-shadow: none; +} +.ui.secondary.buttons .button:active, +.ui.secondary.button:active { + background-color: #0a0a0b; + color: #ffffff; + text-shadow: none; +} +.ui.secondary.buttons .active.button, +.ui.secondary.active.button { + background-color: #0f0f10; + color: #ffffff; + text-shadow: none; +} + +/*--------------- + Positive +----------------*/ + +.ui.positive.buttons .button, +.ui.positive.button { + background-color: #5bbd72 !important; + color: #ffffff; + text-shadow: none; + background-image: none; +} +.ui.positive.button { + box-shadow: 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.positive.buttons .button:hover, +.ui.positive.button:hover, +.ui.positive.buttons .active.button, +.ui.positive.active.button { + background-color: #66c17b !important; + color: #ffffff; + text-shadow: none; +} +.ui.positive.buttons .button:active, +.ui.positive.button:active { + background-color: #46ae5f !important; + color: #ffffff; + text-shadow: none; +} +.ui.positive.buttons .active.button, +.ui.positive.buttons .active.button:active, +.ui.positive.active.button, +.ui.positive.button .active.button:active { + background-color: #49b562; + color: #ffffff; + text-shadow: none; +} + +/*--------------- + Negative +----------------*/ + +.ui.negative.buttons .button, +.ui.negative.button { + background-color: #d95c5c !important; + color: #ffffff; + text-shadow: none; + background-image: none; +} +.ui.negative.button { + box-shadow: 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.negative.buttons .button:hover, +.ui.negative.button:hover, +.ui.negative.buttons .active.button, +.ui.negative.active.button { + background-color: #dc6868 !important; + color: #ffffff; + text-shadow: none; +} +.ui.negative.buttons .button:active, +.ui.negative.button:active { + background-color: #d23f3f !important; + color: #ffffff; + text-shadow: none; +} +.ui.negative.buttons .active.button, +.ui.negative.buttons .active.button:active, +.ui.negative.active.button, +.ui.negative.button .active.button:active { + background-color: #d44747; + color: #ffffff; + text-shadow: none; +} + + +/******************************* + Groups +*******************************/ + +.ui.buttons { + display: inline-block; + vertical-align: middle; + margin: 0em 0.25em 0em 0em; +} +.ui.buttons > .button:hover, +.ui.buttons > .active.button { + position: relative; +} +.ui.buttons:after { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; +} +.ui.buttons .button:first-child { + border-left: none; +} +.ui.buttons:not(.basic):not(.inverted) { + box-shadow: none; +} +.ui.buttons > .ui.button:not(.basic):not(.inverted), +.ui.buttons:not(.basic):not(.inverted) > .button { + box-shadow: 0px 0px 0px 1px transparent inset, 0px 0em 0px 0px rgba(39, 41, 43, 0.15) inset; +} +.ui.buttons .button { + margin: 0em; + float: left; + border-radius: 0em; + margin: 0px 0px 0px 0px; +} +.ui.buttons .button:first-child { + margin-left: 0em; + border-top-left-radius: 0.2857rem; + border-bottom-left-radius: 0.2857rem; +} +.ui.buttons .button:last-child { + border-top-right-radius: 0.2857rem; + border-bottom-right-radius: 0.2857rem; +} + +/* Vertical Style */ +.ui.vertical.buttons { + display: inline-block; +} +.ui.vertical.buttons .button { + display: block; + float: none; + width: 100%; + margin: 0px 0px 0px 0px; + box-shadow: none; +} +.ui.vertical.buttons .button:first-child, +.ui.vertical.buttons .mini.button:first-child, +.ui.vertical.buttons .tiny.button:first-child, +.ui.vertical.buttons .small.button:first-child, +.ui.vertical.buttons .massive.button:first-child, +.ui.vertical.buttons .huge.button:first-child { + border-radius: 0.2857rem 0.2857rem 0px 0px; +} +.ui.vertical.buttons .button:last-child, +.ui.vertical.buttons .mini.button:last-child, +.ui.vertical.buttons .tiny.button:last-child, +.ui.vertical.buttons .small.button:last-child, +.ui.vertical.buttons .massive.button:last-child, +.ui.vertical.buttons .huge.button:last-child, +.ui.vertical.buttons .gigantic.button:last-child { + margin-bottom: 0px; + border-radius: 0px 0px 0.2857rem 0.2857rem; +} + + +/******************************* + Theme Overrides +*******************************/ + + + +/******************************* + Site Overrides +*******************************/ + diff --git a/controller/static/semantic/dist/components/button.min.css b/controller/static/semantic/dist/components/button.min.css new file mode 100755 index 000000000..c0a705b34 --- /dev/null +++ b/controller/static/semantic/dist/components/button.min.css @@ -0,0 +1,10 @@ +/*! + * # Semantic UI x.x - Button + * http://github.com/semantic-org/semantic-ui/ + * + * + * Copyright 2014 Contributorss + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.button{cursor:pointer;display:inline-block;min-height:1em;outline:0;border:none;vertical-align:baseline;background-color:#e0e0e0;color:rgba(0,0,0,.6);font-family:Roboto,'Helvetica Neue',Arial,Helvetica,sans-serif;margin:0 .25em 0 0;padding:.78571em 1.5em;text-transform:none;text-shadow:none;font-weight:700;line-height:1;font-style:normal;text-align:center;text-decoration:none;background-image:none;border-radius:.2857rem;box-shadow:0 0 0 1px transparent inset,0 0 0 0 rgba(39,41,43,.15)inset;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:opacity .1s ease,background-color .1s ease,color .1s ease,box-shadow .1s ease,background .1s ease;transition:opacity .1s ease,background-color .1s ease,color .1s ease,box-shadow .1s ease,background .1s ease;will-change:'';-webkit-tap-highlight-color:transparent}.ui.button:hover{background-color:#e8e8e8;background-image:none;box-shadow:'';color:rgba(0,0,0,.8)}.ui.button:hover .icon{opacity:.85}.ui.button:focus{background-color:'';color:rgba(0,0,0,.8);background-image:''!important;box-shadow:0 0 0 1px transparent inset,0 0 1px rgba(81,167,232,.8)inset,0 0 3px 2px rgba(81,167,232,.8)!important}.ui.button:focus .icon{opacity:.85}.ui.active.button:active,.ui.button:active{background-color:#ccc;background-image:'';color:rgba(0,0,0,.8);box-shadow:0 0 0 1px transparent inset,0 1px 4px 0 rgba(39,41,43,.15)inset!important}.ui.active.button{background-color:#d0d0d0;background-image:none;box-shadow:0 0 0 1px transparent inset;color:rgba(0,0,0,.8)}.ui.active.button:hover{background-color:#d0d0d0;background-image:none;color:rgba(0,0,0,.8)}.ui.active.button:active{background-color:#d0d0d0;background-image:none}.ui.loading.loading.loading.loading.loading.loading.button{position:relative;cursor:default;text-shadow:none!important;color:transparent!important;opacity:1;pointer-events:none;-webkit-transition:all 0s linear,opacity .2s ease;transition:all 0s linear,opacity .2s ease}.ui.loading.button:before{position:absolute;content:'';top:50%;left:50%;margin:-.64285em 0 0 -.64285em;width:1.2857em;height:1.2857em;border-radius:500rem;border:.2em solid rgba(0,0,0,.15)}.ui.loading.button:after{position:absolute;content:'';top:50%;left:50%;margin:-.64285em 0 0 -.64285em;width:1.2857em;height:1.2857em;-webkit-animation:button-spin .6s linear;animation:button-spin .6s linear;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;border-radius:500rem;border-color:#fff transparent transparent;border-style:solid;border-width:.2em;box-shadow:0 0 0 1px transparent}.ui.labeled.icon.loading.button .icon{background-color:transparent;box-shadow:none}@-webkit-keyframes button-spin{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes button-spin{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.ui.basic.loading.button:not(.inverted):before{border-color:rgba(0,0,0,.1)}.ui.basic.loading.button:not(.inverted):after{border-top-color:#aaa}.ui.button:disabled,.ui.buttons .disabled.button,.ui.disabled.active.button,.ui.disabled.button,.ui.disabled.button:hover{cursor:default;background-color:#dcddde!important;color:rgba(0,0,0,.4)!important;opacity:.3!important;background-image:none!important;box-shadow:none!important;pointer-events:none}.ui.basic.buttons .ui.disabled.button{border-color:rgba(39,41,43,.5)}.ui.animated.button{position:relative;overflow:hidden;vertical-align:middle;padding-right:0!important}.ui.animated.button .content{will-change:transform,opacity}.ui.animated.button .visible.content{position:relative;margin-right:1.5em;left:auto;right:0}.ui.animated.button .hidden.content{position:absolute;width:100%;top:50%;left:auto;right:-100%;margin-top:-.5em}.ui.animated.button .hidden.content,.ui.animated.button .visible.content{-webkit-transition:right .3s ease 0s;transition:right .3s ease 0s}.ui.animated.button:hover .visible.content{left:auto;right:200%}.ui.animated.button:hover .hidden.content{left:auto;right:0}.ui.vertical.animated.button .hidden.content,.ui.vertical.animated.button .visible.content{-webkit-transition:top .3s ease,-webkit-transform .3s ease;transition:top .3s ease,transform .3s ease}.ui.vertical.animated.button .visible.content{-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0);right:auto}.ui.vertical.animated.button .hidden.content{top:-50%;left:0;right:auto}.ui.vertical.animated.button:hover .visible.content{-webkit-transform:translateY(200%);-ms-transform:translateY(200%);transform:translateY(200%);right:auto}.ui.vertical.animated.button:hover .hidden.content{top:50%;right:auto}.ui.fade.animated.button .hidden.content,.ui.fade.animated.button .visible.content{-webkit-transition:opacity .3s ease,-webkit-transform .3s ease;transition:opacity .3s ease,transform .3s ease}.ui.fade.animated.button .visible.content{left:auto;right:auto;opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}.ui.fade.animated.button .hidden.content{opacity:0;left:0;right:auto;-webkit-transform:scale(1.5);-ms-transform:scale(1.5);transform:scale(1.5)}.ui.fade.animated.button:hover .visible.content{left:auto;right:auto;opacity:0;-webkit-transform:scale(.75);-ms-transform:scale(.75);transform:scale(.75)}.ui.fade.animated.button:hover .hidden.content{left:0;right:auto;opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}.ui.inverted.button{box-shadow:0 0 0 2px #fff inset!important;background:0 0;color:#fff;text-shadow:none!important}.ui.inverted.buttons .button{margin:0 0 0 -2px}.ui.inverted.buttons .button:first-child{margin-left:0}.ui.inverted.vertical.buttons .button{margin:0 0 -2px}.ui.inverted.vertical.buttons .button:first-child{margin-top:0}.ui.inverted.buttons .button:hover{position:relative}.ui.inverted.button:hover{background:#fff;box-shadow:0 0 0 2px #fff inset!important;color:rgba(0,0,0,.8)}.ui.facebook.button{background-color:#3b579d;color:#fff;text-shadow:none;background-image:none;box-shadow:0 0 0 0 rgba(39,41,43,.15)inset}.ui.facebook.button:hover{background-color:#3f5da8;color:#fff;text-shadow:none}.ui.facebook.button:active{background-color:#314983;color:#fff;text-shadow:none}.ui.twitter.button{background-color:#4092cc;color:#fff;text-shadow:none;background-image:none;box-shadow:0 0 0 0 rgba(39,41,43,.15)inset}.ui.twitter.button:hover{background-color:#4c99cf;color:#fff;text-shadow:none}.ui.twitter.button:active{background-color:#3180b7;color:#fff;text-shadow:none}.ui.google.plus.button{background-color:#d34836;color:#fff;text-shadow:none;background-image:none;box-shadow:0 0 0 0 rgba(39,41,43,.15)inset}.ui.google.plus.button:hover{background-color:#d65343;color:#fff;text-shadow:none}.ui.google.plus.button:active{background-color:#bc3a29;color:#fff;text-shadow:none}.ui.linkedin.button{background-color:#1f88be;color:#fff;text-shadow:none}.ui.linkedin.button:hover{background-color:#2191cb;color:#fff;text-shadow:none}.ui.linkedin.button:active{background-color:#1a729f;color:#fff;text-shadow:none}.ui.youtube.button{background-color:#cc181e;color:#fff;text-shadow:none;background-image:none;box-shadow:0 0 0 0 rgba(39,41,43,.15)inset}.ui.youtube.button:hover{background-color:#da1a20;color:#fff;text-shadow:none}.ui.youtube.button:active{background-color:#ac1419;color:#fff;text-shadow:none}.ui.instagram.button{background-color:#49769c;color:#fff;text-shadow:none;background-image:none;box-shadow:0 0 0 0 rgba(39,41,43,.15)inset}.ui.instagram.button:hover{background-color:#4e7ea6;color:#fff;text-shadow:none}.ui.instagram.button:active{background-color:#3e6484;color:#fff;text-shadow:none}.ui.pinterest.button{background-color:#00aced;color:#fff;text-shadow:none;background-image:none;box-shadow:0 0 0 0 rgba(39,41,43,.15)inset}.ui.pinterest.button:hover{background-color:#00b7fc;color:#fff;text-shadow:none}.ui.pinterest.button:active{background-color:#0092c9;color:#fff;text-shadow:none}.ui.vk.button{background-color:#4D7198;color:#fff;background-image:none;box-shadow:0 0 0 0 rgba(39,41,43,.15)inset}.ui.vk.button:hover{background-color:#5279a2;color:#fff}.ui.vk.button:active{background-color:#415f80;color:#fff}.ui.button>.icon{opacity:.8;margin:0 .4em 0 -.2em;-webkit-transition:opacity .2s ease;transition:opacity .2s ease;vertical-align:baseline;color:''}.ui.button>.right.icon{margin:0 -.2em 0 .4em}.ui[class*="left floated"].button,.ui[class*="left floated"].buttons{float:left;margin-left:0;margin-right:.25em}.ui[class*="right floated"].button,.ui[class*="right floated"].buttons{float:right;margin-right:0;margin-left:.25em}.ui.compact.button,.ui.compact.buttons .button{padding:.5892825em 1.125em}.ui.compact.icon.button,.ui.compact.icon.buttons .button{padding:.5892825em}.ui.compact.labeled.icon.button,.ui.compact.labeled.icon.buttons .button{padding:.5892825em 3.69642em}.ui.mini.button,.ui.mini.buttons .button,.ui.mini.buttons .or{font-size:.71428571rem}.ui.tiny.button,.ui.tiny.buttons .button,.ui.tiny.buttons .or{font-size:.85714286rem}.ui.small.button,.ui.small.buttons .button,.ui.small.buttons .or{font-size:.92857143rem}.ui.button,.ui.buttons .button,.ui.buttons .or{font-size:1rem}.ui.large.button,.ui.large.buttons .button,.ui.large.buttons .or{font-size:1.14285714rem}.ui.big.button,.ui.big.buttons .button,.ui.big.buttons .or{font-size:1.28571429rem}.ui.huge.button,.ui.huge.buttons .button,.ui.huge.buttons .or{font-size:1.42857143rem}.ui.massive.button,.ui.massive.buttons .button,.ui.massive.buttons .or{font-size:1.71428571rem}.ui.icon.button,.ui.icon.buttons .button{padding:.78571em}.ui.icon.button>.icon,.ui.icon.buttons .button>.icon{opacity:.9;margin:0;vertical-align:top}.ui.basic.button,.ui.basic.buttons .button{background:0 0!important;color:rgba(0,0,0,.6)!important;font-weight:400;border-radius:.2857rem;text-transform:none;text-shadow:none!important;box-shadow:0 0 0 1px rgba(39,41,43,.15)inset}.ui.basic.buttons{box-shadow:0 0 0 1px rgba(39,41,43,.15);border-radius:.2857rem}.ui.basic.button:hover,.ui.basic.buttons .button:hover{background:#fafafa!important;color:rgba(0,0,0,.8)!important;box-shadow:0 0 0 1px rgba(39,41,43,.15)inset,0 0 0 0 rgba(39,41,43,.15)inset}.ui.basic.button:active,.ui.basic.buttons .button:active{background:#f8f8f8!important;color:rgba(0,0,0,.8)!important;box-shadow:0 0 0 1px rgba(0,0,0,.15)inset,0 1px 4px 0 rgba(39,41,43,.15)inset}.ui.basic.active.button,.ui.basic.buttons .active.button{background:rgba(0,0,0,.05)!important;box-shadow:''!important;color:rgba(0,0,0,.8)}.ui.basic.active.button:hover,.ui.basic.buttons .active.button:hover{background-color:rgba(0,0,0,.05)}.ui.basic.buttons .button:hover{box-shadow:0 0 0 1px rgba(39,41,43,.15)inset,0 0 0 0 rgba(39,41,43,.15)inset inset}.ui.basic.buttons .button:active{box-shadow:0 0 0 1px rgba(0,0,0,.15)inset,0 1px 4px 0 rgba(39,41,43,.15)inset inset}.ui.basic.buttons .active.button{box-shadow:rgba(39,41,43,.3)inset}.ui.basic.inverted.button,.ui.basic.inverted.buttons .button{background-color:transparent!important;color:#fafafa!important;box-shadow:0 0 0 2px rgba(255,255,255,.5)inset!important}.ui.basic.inverted.button:hover,.ui.basic.inverted.buttons .button:hover{color:#fff!important;box-shadow:0 0 0 2px #fff inset!important}.ui.basic.inverted.button:active,.ui.basic.inverted.buttons .button:active{background-color:rgba(255,255,255,.05)!important;color:#fff!important;box-shadow:0 0 0 2px rgba(255,255,255,.9)inset!important}.ui.basic.inverted.active.button,.ui.basic.inverted.buttons .active.button{background-color:rgba(255,255,255,.05);color:#fff;text-shadow:none;box-shadow:0 0 0 2px rgba(255,255,255,.7)inset}.ui.basic.inverted.active.button:hover,.ui.basic.inverted.buttons .active.button:hover{background-color:rgba(255,255,255,.07);box-shadow:0 0 0 2px #fff inset!important}.ui.basic.buttons .button{border-radius:0;border-left:1px solid rgba(39,41,43,.15);box-shadow:none}.ui.basic.vertical.buttons .button{border-left:none;border-left-width:0;border-top:1px solid rgba(39,41,43,.15)}.ui.basic.vertical.buttons .button:first-child{border-top-width:0}.ui.labeled.icon.button,.ui.labeled.icon.buttons .button{position:relative;padding-left:4.07142em!important;padding-right:1.5em!important}.ui.labeled.icon.button>.icon,.ui.labeled.icon.buttons>.button>.icon{position:absolute;width:2.57142em;height:100%;background-color:rgba(0,0,0,.05);text-align:center;color:'';border-radius:.2857rem 0 0 .2857rem;line-height:1;box-shadow:-1px 0 0 0 transparent inset;top:0;left:0}.ui[class*="right labeled"].icon.button{padding-right:4.07142em!important;padding-left:1.5em!important}.ui[class*="right labeled"].icon.button>.icon{left:auto;right:0;border-radius:0 .2857rem .2857rem 0;box-shadow:1px 0 0 0 transparent inset}.ui.labeled.icon.button>.icon:after,.ui.labeled.icon.button>.icon:before,.ui.labeled.icon.buttons>.button>.icon:after,.ui.labeled.icon.buttons>.button>.icon:before{display:block;position:absolute;width:100%;top:50%;text-align:center;margin-top:-.5em}.ui.labeled.icon.buttons .button>.icon{border-radius:0}.ui.labeled.icon.buttons .button:first-child>.icon{border-top-left-radius:.2857rem;border-bottom-left-radius:.2857rem}.ui.labeled.icon.buttons .button:last-child>.icon{border-top-right-radius:.2857rem;border-bottom-right-radius:.2857rem}.ui.vertical.labeled.icon.buttons .button:first-child>.icon{border-radius:.2857rem 0 0}.ui.vertical.labeled.icon.buttons .button:last-child>.icon{border-radius:0 0 0 .2857rem}.ui.fluid[class*="right labeled"].icon.button,.ui.fluid[class*="left labeled"].icon.button{padding-left:1.5em!important;padding-right:1.5em!important}.ui.button.toggle.active,.ui.buttons .button.toggle.active,.ui.toggle.buttons .active.button{background-color:#5bbd72!important;box-shadow:none!important;text-shadow:none;color:#fff!important}.ui.button.toggle.active:hover{background-color:#66c17b!important;text-shadow:none;color:#fff!important}.ui.circular.button{border-radius:10em}.ui.circular.button>.icon{width:1em;vertical-align:baseline}.ui.attached.button{display:block;margin:0;box-shadow:0 0 0 1px rgba(39,41,43,.15)!important;border-radius:0}.ui.attached.top.button{border-radius:.2857rem .2857rem 0 0}.ui.attached.bottom.button{border-radius:0 0 .2857rem .2857rem}.ui.attached.left.button{display:inline-block;border-left:none;padding-right:.75em;text-align:right;border-radius:.2857rem 0 0 .2857rem}.ui.attached.right.button{display:inline-block;padding-left:.75em;text-align:left;border-radius:0 .2857rem .2857rem 0}.ui.buttons .or{position:relative;float:left;width:.3em;height:2.57142em;z-index:3}.ui.buttons .or:before{position:absolute;text-align:center;border-radius:500rem;content:'or';top:50%;left:50%;background-color:#fff;text-shadow:none;margin-top:-.892855em;margin-left:-.892855em;width:1.78571em;height:1.78571em;line-height:1.78571em;color:rgba(0,0,0,.4);font-style:normal;font-weight:700;box-shadow:0 0 0 1px transparent inset}.ui.buttons .or[data-text]:before{content:attr(data-text)}.ui.fluid.buttons .or{width:0!important}.ui.fluid.buttons .or:after{display:none}.attached.ui.buttons{margin:0;border-radius:0}.attached.ui.buttons .button{margin:0}.attached.ui.buttons .button:first-child,.attached.ui.buttons .button:last-child{border-radius:0}[class*="top attached"].ui.buttons{margin-bottom:-1px;border-radius:.2857rem .2857rem 0 0}[class*="top attached"].ui.buttons .button:first-child{border-radius:.2857rem 0 0}[class*="top attached"].ui.buttons .button:last-child{border-radius:0 .2857rem 0 0}[class*="bottom attached"].ui.buttons{margin-top:-1px;border-radius:0 0 .2857rem .2857rem}[class*="bottom attached"].ui.buttons .button:first-child{border-radius:0 0 0 .2857rem}[class*="bottom attached"].ui.buttons .button:last-child{border-radius:0 0 .2857rem}[class*="left attached"].ui.buttons{margin-left:-1px;border-radius:0 .2857rem .2857rem 0}[class*="left attached"].ui.buttons .button:first-child{margin-left:-1px;border-radius:0 .2857rem 0 0}[class*="left attached"].ui.buttons .button:last-child{margin-left:-1px;border-radius:0 0 .2857rem}[class*="right attached"].ui.buttons,[class*="right attached"].ui.buttons .button{margin-right:-1px;border-radius:.2857rem 0 0 .2857rem}[class*="right attached"].ui.buttons .button:first-child{margin-left:-1px;border-radius:.2857rem 0 0}[class*="right attached"].ui.buttons .button:last-child{margin-left:-1px;border-radius:0 0 0 .2857rem}.ui.button.fluid,.ui.fluid.buttons,.ui.fluid.buttons>.button{display:block;width:100%}.ui.\32.buttons,.ui.two.buttons{width:100%}.ui.\32.buttons>.button,.ui.two.buttons>.button{width:50%}.ui.\33.buttons,.ui.three.buttons{width:100%}.ui.\33.buttons>.button,.ui.three.buttons>.button{width:33.333%}.ui.\34.buttons,.ui.four.buttons{width:100%}.ui.\34.buttons>.button,.ui.four.buttons>.button{width:25%}.ui.\35.buttons,.ui.five.buttons{width:100%}.ui.\35.buttons>.button,.ui.five.buttons>.button{width:20%}.ui.\36.buttons,.ui.six.buttons{width:100%}.ui.\36.buttons>.button,.ui.six.buttons>.button{width:16.666%}.ui.\37.buttons,.ui.seven.buttons{width:100%}.ui.\37.buttons>.button,.ui.seven.buttons>.button{width:14.285%}.ui.\38.buttons,.ui.eight.buttons{width:100%}.ui.\38.buttons>.button,.ui.eight.buttons>.button{width:12.5%}.ui.\39.buttons,.ui.nine.buttons{width:100%}.ui.\39.buttons>.button,.ui.nine.buttons>.button{width:11.11%}.ui.\31\30.buttons,.ui.ten.buttons{width:100%}.ui.\31\30.buttons>.button,.ui.ten.buttons>.button{width:10%}.ui.\31\31.buttons,.ui.eleven.buttons{width:100%}.ui.\31\31.buttons>.button,.ui.eleven.buttons>.button{width:9.09%}.ui.\31\32.buttons,.ui.twelve.buttons{width:100%}.ui.\31\32.buttons>.button,.ui.twelve.buttons>.button{width:8.3333%}.ui.fluid.vertical.buttons,.ui.fluid.vertical.buttons>.button{display:block;width:auto}.ui.\32.vertical.buttons>.button,.ui.two.vertical.buttons>.button{height:50%}.ui.\33.vertical.buttons>.button,.ui.three.vertical.buttons>.button{height:33.333%}.ui.\34.vertical.buttons>.button,.ui.four.vertical.buttons>.button{height:25%}.ui.\35.vertical.buttons>.button,.ui.five.vertical.buttons>.button{height:20%}.ui.\36.vertical.buttons>.button,.ui.six.vertical.buttons>.button{height:16.666%}.ui.\37.vertical.buttons>.button,.ui.seven.vertical.buttons>.button{height:14.285%}.ui.\38.vertical.buttons>.button,.ui.eight.vertical.buttons>.button{height:12.5%}.ui.\39.vertical.buttons>.button,.ui.nine.vertical.buttons>.button{height:11.11%}.ui.\31\30.vertical.buttons>.button,.ui.ten.vertical.buttons>.button{height:10%}.ui.\31\31.vertical.buttons>.button,.ui.eleven.vertical.buttons>.button{height:9.09%}.ui.\31\32.vertical.buttons>.button,.ui.twelve.vertical.buttons>.button{height:8.3333%}.ui.black.button,.ui.black.buttons .button{background-color:#1b1c1d;color:#fff;text-shadow:none;background-image:none}.ui.black.button{box-shadow:0 0 0 0 rgba(39,41,43,.15)inset}.ui.black.button:hover,.ui.black.buttons .button:hover{background-color:#1b1c1d;color:#fff;text-shadow:none}.ui.black.button:active,.ui.black.buttons .button:active{background-color:#0a0a0b;color:#fff;text-shadow:none}.ui.black.active.button,.ui.black.button .active.button:active,.ui.black.buttons .active.button,.ui.black.buttons .active.button:active{background-color:#0f0f10;color:#fff;text-shadow:none}.ui.basic.black.button,.ui.basic.black.buttons .button{box-shadow:0 0 0 2px rgba(39,41,43,.15)inset!important;color:rgba(0,0,0,.6)!important}.ui.basic.black.button:hover,.ui.basic.black.buttons .button:hover{background:0 0!important;box-shadow:0 0 0 2px #1b1c1d inset!important;color:#1b1c1d!important}.ui.basic.black.button:active,.ui.basic.black.buttons .button:active{box-shadow:0 0 0 2px #0a0a0b inset!important;color:#0a0a0b!important}.ui.basic.black.active.button,.ui.basic.black.buttons .active.button{background:0 0!important;box-shadow:0 0 0 2px #0a0a0b inset!important;color:#0a0a0b!important}.ui.buttons>.basic.black.button:not(:first-child){margin-left:-2px}.ui.inverted.black.button,.ui.inverted.black.buttons .button{background-color:transparent;box-shadow:0 0 0 2px #d4d4d5 inset!important;color:#fff}.ui.inverted.black.active.button,.ui.inverted.black.button:hover,.ui.inverted.black.buttons .active.button,.ui.inverted.black.buttons .button:hover{box-shadow:0 0 0 2px #333 inset!important;background-color:#333;color:#fff}.ui.inverted.black.button:active,.ui.inverted.black.buttons .button:active{box-shadow:0 0 0 2px #212121 inset!important;background-color:#212121;color:#fff}.ui.inverted.black.basic.button,.ui.inverted.black.basic.buttons .button,.ui.inverted.black.buttons .basic.button{background-color:transparent;box-shadow:0 0 0 2px rgba(255,255,255,.5)inset!important;color:#fff!important}.ui.inverted.black.basic.active.button,.ui.inverted.black.basic.button:hover,.ui.inverted.black.basic.buttons .active.button,.ui.inverted.black.basic.buttons .button:hover,.ui.inverted.black.buttons .basic.active.button,.ui.inverted.black.buttons .basic.button:hover{box-shadow:0 0 0 2px #333 inset!important;color:#fff!important}.ui.inverted.black.basic.button:active,.ui.inverted.black.basic.buttons .button:active,.ui.inverted.black.buttons .basic.button:active{box-shadow:0 0 0 2px #212121 inset!important;color:#fff!important}.ui.blue.button,.ui.blue.buttons .button{background-color:#0097c9;color:#fff;text-shadow:none;background-image:none}.ui.blue.button{box-shadow:0 0 0 0 rgba(39,41,43,.15)inset}.ui.blue.button:hover,.ui.blue.buttons .button:hover{background-color:#0079a2;color:#fff;text-shadow:none}.ui.blue.button:active,.ui.blue.buttons .button:active{background-color:#00536f;color:#fff;text-shadow:none}.ui.blue.active.button,.ui.blue.button .active.button:active,.ui.blue.buttons .active.button,.ui.blue.buttons .active.button:active{background-color:#005b7a;color:#fff;text-shadow:none}.ui.basic.blue.button,.ui.basic.blue.buttons .button{box-shadow:0 0 0 2px rgba(39,41,43,.15)inset!important;color:rgba(0,0,0,.6)!important}.ui.basic.blue.button:hover,.ui.basic.blue.buttons .button:hover{background:0 0!important;box-shadow:0 0 0 2px #0079a2 inset!important;color:#0079a2!important}.ui.basic.blue.button:active,.ui.basic.blue.buttons .button:active{box-shadow:0 0 0 2px #00536f inset!important;color:#00536f!important}.ui.basic.blue.active.button,.ui.basic.blue.buttons .active.button{background:0 0!important;box-shadow:0 0 0 2px #00536f inset!important;color:#00536f!important}.ui.buttons>.basic.blue.button:not(:first-child){margin-left:-2px}.ui.inverted.blue.button,.ui.inverted.blue.buttons .button{background-color:transparent;box-shadow:0 0 0 2px #0097c9 inset!important;color:#0097c9}.ui.inverted.blue.active.button,.ui.inverted.blue.button:hover,.ui.inverted.blue.buttons .active.button,.ui.inverted.blue.buttons .button:hover{box-shadow:0 0 0 2px #0097c9 inset!important;background-color:#0097c9;color:#fff}.ui.inverted.blue.button:active,.ui.inverted.blue.buttons .button:active{box-shadow:0 0 0 2px #007ca5 inset!important;background-color:#007ca5;color:#fff}.ui.inverted.blue.basic.button,.ui.inverted.blue.basic.buttons .button,.ui.inverted.blue.buttons .basic.button{background-color:transparent;box-shadow:0 0 0 2px rgba(255,255,255,.5)inset!important;color:#fff!important}.ui.inverted.blue.basic.active.button,.ui.inverted.blue.basic.button:hover,.ui.inverted.blue.basic.buttons .active.button,.ui.inverted.blue.basic.buttons .button:hover,.ui.inverted.blue.buttons .basic.active.button,.ui.inverted.blue.buttons .basic.button:hover{box-shadow:0 0 0 2px #0097c9 inset!important;color:#0097c9!important}.ui.inverted.blue.basic.button:active,.ui.inverted.blue.basic.buttons .button:active,.ui.inverted.blue.buttons .basic.button:active{box-shadow:0 0 0 2px #007ca5 inset!important;color:#0097c9!important}.ui.green.button,.ui.green.buttons .button{background-color:#5bbd72;color:#fff;text-shadow:none;background-image:none}.ui.green.button{box-shadow:0 0 0 0 rgba(39,41,43,.15)inset}.ui.green.button:hover,.ui.green.buttons .button:hover{background-color:#66c17b;color:#fff;text-shadow:none}.ui.green.button:active,.ui.green.buttons .button:active{background-color:#46ae5f;color:#fff;text-shadow:none}.ui.green.active.button,.ui.green.button .active.button:active,.ui.green.buttons .active.button,.ui.green.buttons .active.button:active{background-color:#49b562;color:#fff;text-shadow:none}.ui.basic.green.button,.ui.basic.green.buttons .button{box-shadow:0 0 0 2px rgba(39,41,43,.15)inset!important;color:rgba(0,0,0,.6)!important}.ui.basic.green.button:hover,.ui.basic.green.buttons .button:hover{background:0 0!important;box-shadow:0 0 0 2px #66c17b inset!important;color:#66c17b!important}.ui.basic.green.button:active,.ui.basic.green.buttons .button:active{box-shadow:0 0 0 2px #46ae5f inset!important;color:#46ae5f!important}.ui.basic.green.active.button,.ui.basic.green.buttons .active.button{background:0 0!important;box-shadow:0 0 0 2px #46ae5f inset!important;color:#46ae5f!important}.ui.buttons>.basic.green.button:not(:first-child){margin-left:-2px}.ui.inverted.green.button,.ui.inverted.green.buttons .button{background-color:transparent;box-shadow:0 0 0 2px #2ecc40 inset!important;color:#2ecc40}.ui.inverted.green.active.button,.ui.inverted.green.button:hover,.ui.inverted.green.buttons .active.button,.ui.inverted.green.buttons .button:hover{box-shadow:0 0 0 2px #2ecc40 inset!important;background-color:#2ecc40;color:#fff}.ui.inverted.green.button:active,.ui.inverted.green.buttons .button:active{box-shadow:0 0 0 2px #27af37 inset!important;background-color:#27af37;color:#fff}.ui.inverted.green.basic.button,.ui.inverted.green.basic.buttons .button,.ui.inverted.green.buttons .basic.button{background-color:transparent;box-shadow:0 0 0 2px rgba(255,255,255,.5)inset!important;color:#fff!important}.ui.inverted.green.basic.active.button,.ui.inverted.green.basic.button:hover,.ui.inverted.green.basic.buttons .active.button,.ui.inverted.green.basic.buttons .button:hover,.ui.inverted.green.buttons .basic.active.button,.ui.inverted.green.buttons .basic.button:hover{box-shadow:0 0 0 2px #2ecc40 inset!important;color:#2ecc40!important}.ui.inverted.green.basic.button:active,.ui.inverted.green.basic.buttons .button:active,.ui.inverted.green.buttons .basic.button:active{box-shadow:0 0 0 2px #27af37 inset!important;color:#2ecc40!important}.ui.orange.button,.ui.orange.buttons .button{background-color:#e07b53;color:#fff;text-shadow:none;background-image:none}.ui.orange.button{box-shadow:0 0 0 0 rgba(39,41,43,.15)inset}.ui.orange.button:hover,.ui.orange.buttons .button:hover{background-color:#e28560;color:#fff;text-shadow:none}.ui.orange.button:active,.ui.orange.buttons .button:active{background-color:#db6435;color:#fff;text-shadow:none}.ui.orange.active.button,.ui.orange.button .active.button:active,.ui.orange.buttons .active.button,.ui.orange.buttons .active.button:active{background-color:#dc6a3d;color:#fff;text-shadow:none}.ui.basic.orange.button,.ui.basic.orange.buttons .button{box-shadow:0 0 0 2px rgba(39,41,43,.15)inset!important;color:rgba(0,0,0,.6)!important}.ui.basic.orange.button:hover,.ui.basic.orange.buttons .button:hover{background:0 0!important;box-shadow:0 0 0 2px #e28560 inset!important;color:#e28560!important}.ui.basic.orange.button:active,.ui.basic.orange.buttons .button:active{box-shadow:0 0 0 2px #db6435 inset!important;color:#db6435!important}.ui.basic.orange.active.button,.ui.basic.orange.buttons .active.button{background:0 0!important;box-shadow:0 0 0 2px #db6435 inset!important;color:#db6435!important}.ui.buttons>.basic.orange.button:not(:first-child){margin-left:-2px}.ui.inverted.orange.button,.ui.inverted.orange.buttons .button{background-color:transparent;box-shadow:0 0 0 2px #ff851b inset!important;color:#ff851b}.ui.inverted.orange.active.button,.ui.inverted.orange.button:hover,.ui.inverted.orange.buttons .active.button,.ui.inverted.orange.buttons .button:hover{box-shadow:0 0 0 2px #ff851b inset!important;background-color:#ff851b;color:#fff}.ui.inverted.orange.button:active,.ui.inverted.orange.buttons .button:active{box-shadow:0 0 0 2px #f67300 inset!important;background-color:#f67300;color:#fff}.ui.inverted.orange.basic.button,.ui.inverted.orange.basic.buttons .button,.ui.inverted.orange.buttons .basic.button{background-color:transparent;box-shadow:0 0 0 2px rgba(255,255,255,.5)inset!important;color:#fff!important}.ui.inverted.orange.basic.active.button,.ui.inverted.orange.basic.button:hover,.ui.inverted.orange.basic.buttons .active.button,.ui.inverted.orange.basic.buttons .button:hover,.ui.inverted.orange.buttons .basic.active.button,.ui.inverted.orange.buttons .basic.button:hover{box-shadow:0 0 0 2px #ff851b inset!important;color:#ff851b!important}.ui.inverted.orange.basic.button:active,.ui.inverted.orange.basic.buttons .button:active,.ui.inverted.orange.buttons .basic.button:active{box-shadow:0 0 0 2px #f67300 inset!important;color:#ff851b!important}.ui.pink.button,.ui.pink.buttons .button{background-color:#d9499a;color:#fff;text-shadow:none;background-image:none}.ui.pink.button{box-shadow:0 0 0 0 rgba(39,41,43,.15)inset}.ui.pink.button:hover,.ui.pink.buttons .button:hover{background-color:#dc56a1;color:#fff;text-shadow:none}.ui.pink.button:active,.ui.pink.buttons .button:active{background-color:#d22c8a;color:#fff;text-shadow:none}.ui.pink.active.button,.ui.pink.button .active.button:active,.ui.pink.buttons .active.button,.ui.pink.buttons .active.button:active{background-color:#d5348e;color:#fff;text-shadow:none}.ui.basic.pink.button,.ui.basic.pink.buttons .button{box-shadow:0 0 0 2px rgba(39,41,43,.15)inset!important;color:rgba(0,0,0,.6)!important}.ui.basic.pink.button:hover,.ui.basic.pink.buttons .button:hover{background:0 0!important;box-shadow:0 0 0 2px #dc56a1 inset!important;color:#dc56a1!important}.ui.basic.pink.button:active,.ui.basic.pink.buttons .button:active{box-shadow:0 0 0 2px #d22c8a inset!important;color:#d22c8a!important}.ui.basic.pink.active.button,.ui.basic.pink.buttons .active.button{background:0 0!important;box-shadow:0 0 0 2px #d22c8a inset!important;color:#d22c8a!important}.ui.buttons>.basic.pink.button:not(:first-child){margin-left:-2px}.ui.inverted.pink.button,.ui.inverted.pink.buttons .button{background-color:transparent;box-shadow:0 0 0 2px #ff8edf inset!important;color:#ff8edf}.ui.inverted.pink.active.button,.ui.inverted.pink.button:hover,.ui.inverted.pink.buttons .active.button,.ui.inverted.pink.buttons .button:hover{box-shadow:0 0 0 2px #ff8edf inset!important;background-color:#ff8edf;color:#fff}.ui.inverted.pink.button:active,.ui.inverted.pink.buttons .button:active{box-shadow:0 0 0 2px #ff6ad5 inset!important;background-color:#ff6ad5;color:#fff}.ui.inverted.pink.basic.button,.ui.inverted.pink.basic.buttons .button,.ui.inverted.pink.buttons .basic.button{background-color:transparent;box-shadow:0 0 0 2px rgba(255,255,255,.5)inset!important;color:#fff!important}.ui.inverted.pink.basic.active.button,.ui.inverted.pink.basic.button:hover,.ui.inverted.pink.basic.buttons .active.button,.ui.inverted.pink.basic.buttons .button:hover,.ui.inverted.pink.buttons .basic.active.button,.ui.inverted.pink.buttons .basic.button:hover{box-shadow:0 0 0 2px #ff8edf inset!important;color:#ff8edf!important}.ui.inverted.pink.basic.button:active,.ui.inverted.pink.basic.buttons .button:active,.ui.inverted.pink.buttons .basic.button:active{box-shadow:0 0 0 2px #ff6ad5 inset!important;color:#ff8edf!important}.ui.purple.button,.ui.purple.buttons .button{background-color:#564f8a;color:#fff;text-shadow:none;background-image:none}.ui.purple.button{box-shadow:0 0 0 0 rgba(39,41,43,.15)inset}.ui.purple.button:hover,.ui.purple.buttons .button:hover{background-color:#5c5594;color:#fff;text-shadow:none}.ui.purple.button:active,.ui.purple.buttons .button:active{background-color:#484273;color:#fff;text-shadow:none}.ui.purple.active.button,.ui.purple.button .active.button:active,.ui.purple.buttons .active.button,.ui.purple.buttons .active.button:active{background-color:#4c467a;color:#fff;text-shadow:none}.ui.basic.purple.button,.ui.basic.purple.buttons .button{box-shadow:0 0 0 2px rgba(39,41,43,.15)inset!important;color:rgba(0,0,0,.6)!important}.ui.basic.purple.button:hover,.ui.basic.purple.buttons .button:hover{background:0 0!important;box-shadow:0 0 0 2px #5c5594 inset!important;color:#5c5594!important}.ui.basic.purple.button:active,.ui.basic.purple.buttons .button:active{box-shadow:0 0 0 2px #484273 inset!important;color:#484273!important}.ui.basic.purple.active.button,.ui.basic.purple.buttons .active.button{background:0 0!important;box-shadow:0 0 0 2px #484273 inset!important;color:#484273!important}.ui.buttons>.basic.purple.button:not(:first-child){margin-left:-2px}.ui.inverted.purple.button,.ui.inverted.purple.buttons .button{background-color:transparent;box-shadow:0 0 0 2px #cdc6ff inset!important;color:#cdc6ff}.ui.inverted.purple.active.button,.ui.inverted.purple.button:hover,.ui.inverted.purple.buttons .active.button,.ui.inverted.purple.buttons .button:hover{box-shadow:0 0 0 2px #cdc6ff inset!important;background-color:#cdc6ff;color:#1b1c1d}.ui.inverted.purple.button:active,.ui.inverted.purple.buttons .button:active{box-shadow:0 0 0 2px #aea2ff inset!important;background-color:#aea2ff;color:#1b1c1d}.ui.inverted.purple.basic.button,.ui.inverted.purple.basic.buttons .button,.ui.inverted.purple.buttons .basic.button{background-color:transparent;box-shadow:0 0 0 2px rgba(255,255,255,.5)inset!important;color:#fff!important}.ui.inverted.purple.basic.active.button,.ui.inverted.purple.basic.button:hover,.ui.inverted.purple.basic.buttons .active.button,.ui.inverted.purple.basic.buttons .button:hover,.ui.inverted.purple.buttons .basic.active.button,.ui.inverted.purple.buttons .basic.button:hover{box-shadow:0 0 0 2px #cdc6ff inset!important;color:#cdc6ff!important}.ui.inverted.purple.basic.button:active,.ui.inverted.purple.basic.buttons .button:active,.ui.inverted.purple.buttons .basic.button:active{box-shadow:0 0 0 2px #aea2ff inset!important;color:#cdc6ff!important}.ui.red.button,.ui.red.buttons .button{background-color:#d95c5c;color:#fff;text-shadow:none;background-image:none}.ui.red.button{box-shadow:0 0 0 0 rgba(39,41,43,.15)inset}.ui.red.button:hover,.ui.red.buttons .button:hover{background-color:#dc6868;color:#fff;text-shadow:none}.ui.red.button:active,.ui.red.buttons .button:active{background-color:#d23f3f;color:#fff;text-shadow:none}.ui.red.active.button,.ui.red.button .active.button:active,.ui.red.buttons .active.button,.ui.red.buttons .active.button:active{background-color:#d44747;color:#fff;text-shadow:none}.ui.basic.red.button,.ui.basic.red.buttons .button{box-shadow:0 0 0 2px rgba(39,41,43,.15)inset!important;color:rgba(0,0,0,.6)!important}.ui.basic.red.button:hover,.ui.basic.red.buttons .button:hover{background:0 0!important;box-shadow:0 0 0 2px #dc6868 inset!important;color:#dc6868!important}.ui.basic.red.button:active,.ui.basic.red.buttons .button:active{box-shadow:0 0 0 2px #d23f3f inset!important;color:#d23f3f!important}.ui.basic.red.active.button,.ui.basic.red.buttons .active.button{background:0 0!important;box-shadow:0 0 0 2px #d23f3f inset!important;color:#d23f3f!important}.ui.buttons>.basic.red.button:not(:first-child){margin-left:-2px}.ui.inverted.red.button,.ui.inverted.red.buttons .button{background-color:transparent;box-shadow:0 0 0 2px #ff695e inset!important;color:#ff695e}.ui.inverted.red.active.button,.ui.inverted.red.button:hover,.ui.inverted.red.buttons .active.button,.ui.inverted.red.buttons .button:hover{box-shadow:0 0 0 2px #ff695e inset!important;background-color:#ff695e;color:#fff}.ui.inverted.red.button:active,.ui.inverted.red.buttons .button:active{box-shadow:0 0 0 2px #ff483a inset!important;background-color:#ff483a;color:#fff}.ui.inverted.red.basic.button,.ui.inverted.red.basic.buttons .button,.ui.inverted.red.buttons .basic.button{background-color:transparent;box-shadow:0 0 0 2px rgba(255,255,255,.5)inset!important;color:#fff!important}.ui.inverted.red.basic.active.button,.ui.inverted.red.basic.button:hover,.ui.inverted.red.basic.buttons .active.button,.ui.inverted.red.basic.buttons .button:hover,.ui.inverted.red.buttons .basic.active.button,.ui.inverted.red.buttons .basic.button:hover{box-shadow:0 0 0 2px #ff695e inset!important;color:#ff695e!important}.ui.inverted.red.basic.button:active,.ui.inverted.red.basic.buttons .button:active,.ui.inverted.red.buttons .basic.button:active{box-shadow:0 0 0 2px #ff483a inset!important;color:#ff695e!important}.ui.teal.button,.ui.teal.buttons .button{background-color:#00b5ad;color:#fff;text-shadow:none;background-image:none}.ui.teal.button{box-shadow:0 0 0 0 rgba(39,41,43,.15)inset}.ui.teal.button:hover,.ui.teal.buttons .button:hover{background-color:#00c4bc;color:#fff;text-shadow:none}.ui.teal.button:active,.ui.teal.buttons .button:active{background-color:#00918b;color:#fff;text-shadow:none}.ui.teal.active.button,.ui.teal.button .active.button:active,.ui.teal.buttons .active.button,.ui.teal.buttons .active.button:active{background-color:#009c95;color:#fff;text-shadow:none}.ui.basic.teal.button,.ui.basic.teal.buttons .button{box-shadow:0 0 0 2px rgba(39,41,43,.15)inset!important;color:rgba(0,0,0,.6)!important}.ui.basic.teal.button:hover,.ui.basic.teal.buttons .button:hover{background:0 0!important;box-shadow:0 0 0 2px #00c4bc inset!important;color:#00c4bc!important}.ui.basic.teal.button:active,.ui.basic.teal.buttons .button:active{box-shadow:0 0 0 2px #00918b inset!important;color:#00918b!important}.ui.basic.teal.active.button,.ui.basic.teal.buttons .active.button{background:0 0!important;box-shadow:0 0 0 2px #00918b inset!important;color:#00918b!important}.ui.buttons>.basic.teal.button:not(:first-child){margin-left:-2px}.ui.inverted.teal.button,.ui.inverted.teal.buttons .button{background-color:transparent;box-shadow:0 0 0 2px #6dffff inset!important;color:#6dffff}.ui.inverted.teal.active.button,.ui.inverted.teal.button:hover,.ui.inverted.teal.buttons .active.button,.ui.inverted.teal.buttons .button:hover{box-shadow:0 0 0 2px #6dffff inset!important;background-color:#6dffff;color:#1b1c1d}.ui.inverted.teal.button:active,.ui.inverted.teal.buttons .button:active{box-shadow:0 0 0 2px #49ffff inset!important;background-color:#49ffff;color:#1b1c1d}.ui.inverted.teal.basic.button,.ui.inverted.teal.basic.buttons .button,.ui.inverted.teal.buttons .basic.button{background-color:transparent;box-shadow:0 0 0 2px rgba(255,255,255,.5)inset!important;color:#fff!important}.ui.inverted.teal.basic.active.button,.ui.inverted.teal.basic.button:hover,.ui.inverted.teal.basic.buttons .active.button,.ui.inverted.teal.basic.buttons .button:hover,.ui.inverted.teal.buttons .basic.active.button,.ui.inverted.teal.buttons .basic.button:hover{box-shadow:0 0 0 2px #6dffff inset!important;color:#6dffff!important}.ui.inverted.teal.basic.button:active,.ui.inverted.teal.basic.buttons .button:active,.ui.inverted.teal.buttons .basic.button:active{box-shadow:0 0 0 2px #49ffff inset!important;color:#6dffff!important}.ui.yellow.button,.ui.yellow.buttons .button{background-color:#f2c61f;color:#fff;text-shadow:none;background-image:none}.ui.yellow.button{box-shadow:0 0 0 0 rgba(39,41,43,.15)inset}.ui.yellow.button:hover,.ui.yellow.buttons .button:hover{background-color:#f3ca2d;color:#fff;text-shadow:none}.ui.yellow.button:active,.ui.yellow.buttons .button:active{background-color:#e0b40d;color:#fff;text-shadow:none}.ui.yellow.active.button,.ui.yellow.button .active.button:active,.ui.yellow.buttons .active.button,.ui.yellow.buttons .active.button:active{background-color:#eabc0e;color:#fff;text-shadow:none}.ui.basic.yellow.button,.ui.basic.yellow.buttons .button{box-shadow:0 0 0 2px rgba(39,41,43,.15)inset!important;color:rgba(0,0,0,.6)!important}.ui.basic.yellow.button:hover,.ui.basic.yellow.buttons .button:hover{background:0 0!important;box-shadow:0 0 0 2px #f3ca2d inset!important;color:#f3ca2d!important}.ui.basic.yellow.button:active,.ui.basic.yellow.buttons .button:active{box-shadow:0 0 0 2px #e0b40d inset!important;color:#e0b40d!important}.ui.basic.yellow.active.button,.ui.basic.yellow.buttons .active.button{background:0 0!important;box-shadow:0 0 0 2px #e0b40d inset!important;color:#e0b40d!important}.ui.buttons>.basic.yellow.button:not(:first-child){margin-left:-2px}.ui.inverted.yellow.button,.ui.inverted.yellow.buttons .button{background-color:transparent;box-shadow:0 0 0 2px #ffe21f inset!important;color:#ffe21f}.ui.inverted.yellow.active.button,.ui.inverted.yellow.button:hover,.ui.inverted.yellow.buttons .active.button,.ui.inverted.yellow.buttons .button:hover{box-shadow:0 0 0 2px #ffe21f inset!important;background-color:#ffe21f;color:#1b1c1d}.ui.inverted.yellow.button:active,.ui.inverted.yellow.buttons .button:active{box-shadow:0 0 0 2px #fada00 inset!important;background-color:#fada00;color:#1b1c1d}.ui.inverted.yellow.basic.button,.ui.inverted.yellow.basic.buttons .button,.ui.inverted.yellow.buttons .basic.button{background-color:transparent;box-shadow:0 0 0 2px rgba(255,255,255,.5)inset!important;color:#fff!important}.ui.inverted.yellow.basic.active.button,.ui.inverted.yellow.basic.button:hover,.ui.inverted.yellow.basic.buttons .active.button,.ui.inverted.yellow.basic.buttons .button:hover,.ui.inverted.yellow.buttons .basic.active.button,.ui.inverted.yellow.buttons .basic.button:hover{box-shadow:0 0 0 2px #ffe21f inset!important;color:#ffe21f!important}.ui.inverted.yellow.basic.button:active,.ui.inverted.yellow.basic.buttons .button:active,.ui.inverted.yellow.buttons .basic.button:active{box-shadow:0 0 0 2px #fada00 inset!important;color:#ffe21f!important}.ui.primary.button,.ui.primary.buttons .button{background-color:#006e93;color:#fff;text-shadow:none;background-image:none}.ui.primary.button{box-shadow:0 0 0 0 rgba(39,41,43,.15)inset}.ui.primary.button:hover,.ui.primary.buttons .button:hover{background-color:#0079a2;color:#fff;text-shadow:none}.ui.primary.button:active,.ui.primary.buttons .button:active{background-color:#00536f;color:#fff;text-shadow:none}.ui.primary.active.button,.ui.primary.buttons .active.button{background-color:#005b7a;color:#fff;text-shadow:none}.ui.secondary.button,.ui.secondary.buttons .button{background-color:#1b1c1d;color:#fff;text-shadow:none;background-image:none}.ui.secondary.button{box-shadow:0 0 0 0 rgba(39,41,43,.15)inset}.ui.secondary.button:hover,.ui.secondary.buttons .button:hover{background-color:#222425;color:#fff;text-shadow:none}.ui.secondary.button:active,.ui.secondary.buttons .button:active{background-color:#0a0a0b;color:#fff;text-shadow:none}.ui.secondary.active.button,.ui.secondary.buttons .active.button{background-color:#0f0f10;color:#fff;text-shadow:none}.ui.positive.button,.ui.positive.buttons .button{background-color:#5bbd72!important;color:#fff;text-shadow:none;background-image:none}.ui.positive.button{box-shadow:0 0 0 0 rgba(39,41,43,.15)inset}.ui.positive.active.button,.ui.positive.button:hover,.ui.positive.buttons .active.button,.ui.positive.buttons .button:hover{background-color:#66c17b!important;color:#fff;text-shadow:none}.ui.positive.button:active,.ui.positive.buttons .button:active{background-color:#46ae5f!important;color:#fff;text-shadow:none}.ui.positive.active.button,.ui.positive.button .active.button:active,.ui.positive.buttons .active.button,.ui.positive.buttons .active.button:active{background-color:#49b562;color:#fff;text-shadow:none}.ui.negative.button,.ui.negative.buttons .button{background-color:#d95c5c!important;color:#fff;text-shadow:none;background-image:none}.ui.negative.button{box-shadow:0 0 0 0 rgba(39,41,43,.15)inset}.ui.negative.active.button,.ui.negative.button:hover,.ui.negative.buttons .active.button,.ui.negative.buttons .button:hover{background-color:#dc6868!important;color:#fff;text-shadow:none}.ui.negative.button:active,.ui.negative.buttons .button:active{background-color:#d23f3f!important;color:#fff;text-shadow:none}.ui.negative.active.button,.ui.negative.button .active.button:active,.ui.negative.buttons .active.button,.ui.negative.buttons .active.button:active{background-color:#d44747;color:#fff;text-shadow:none}.ui.buttons{display:inline-block;vertical-align:middle;margin:0 .25em 0 0}.ui.buttons>.active.button,.ui.buttons>.button:hover{position:relative}.ui.buttons:after{content:".";display:block;height:0;clear:both;visibility:hidden}.ui.buttons:not(.basic):not(.inverted){box-shadow:none}.ui.buttons:not(.basic):not(.inverted)>.button,.ui.buttons>.ui.button:not(.basic):not(.inverted){box-shadow:0 0 0 1px transparent inset,0 0 0 0 rgba(39,41,43,.15)inset}.ui.buttons .button{float:left;border-radius:0;margin:0}.ui.buttons .button:first-child{border-left:none;margin-left:0;border-top-left-radius:.2857rem;border-bottom-left-radius:.2857rem}.ui.buttons .button:last-child{border-top-right-radius:.2857rem;border-bottom-right-radius:.2857rem}.ui.vertical.buttons{display:inline-block}.ui.vertical.buttons .button{display:block;float:none;width:100%;margin:0;box-shadow:none}.ui.vertical.buttons .button:first-child,.ui.vertical.buttons .huge.button:first-child,.ui.vertical.buttons .massive.button:first-child,.ui.vertical.buttons .mini.button:first-child,.ui.vertical.buttons .small.button:first-child,.ui.vertical.buttons .tiny.button:first-child{border-radius:.2857rem .2857rem 0 0}.ui.vertical.buttons .button:last-child,.ui.vertical.buttons .gigantic.button:last-child,.ui.vertical.buttons .huge.button:last-child,.ui.vertical.buttons .massive.button:last-child,.ui.vertical.buttons .mini.button:last-child,.ui.vertical.buttons .small.button:last-child,.ui.vertical.buttons .tiny.button:last-child{margin-bottom:0;border-radius:0 0 .2857rem .2857rem} \ No newline at end of file diff --git a/controller/static/semantic/dist/components/card.css b/controller/static/semantic/dist/components/card.css new file mode 100755 index 000000000..eb909b13e --- /dev/null +++ b/controller/static/semantic/dist/components/card.css @@ -0,0 +1,909 @@ +/*! + * # Semantic UI x.x - Item + * http://github.com/semantic-org/semantic-ui/ + * + * + * Copyright 2014 Contributorss + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ + + +/******************************* + Standard +*******************************/ + + +/*-------------- + Card +---------------*/ + +.ui.cards > .card, +.ui.card { + max-width: 100%; + position: relative; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + width: 290px; + min-height: 0px; + background: #ffffff; + padding: 0em; + border: none; + border-radius: 0.2857rem; + box-shadow: 0px 3px 0px 0px #d4d4d5, 0px 0px 0px 1px #d4d4d5; + -webkit-transition: box-shadow 0.2s ease; + transition: box-shadow 0.2s ease; + z-index: ''; +} +.ui.card { + margin: 1em 0em; +} +.ui.cards > .card a, +.ui.card a { + cursor: pointer; +} +.ui.card:first-child { + margin-top: 0em; +} +.ui.card:last-child { + margin-bottom: 0em; +} + +/*-------------- + Cards +---------------*/ + +.ui.cards { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + margin: -0.875em -0.5em; + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; +} +.ui.cards > .card { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + margin: 0.875em 0.5em; + float: none; +} + +/* Clearing */ +.ui.cards:after, +.ui.card:after { + display: block; + content: ' '; + height: 0px; + clear: both; + overflow: hidden; + visibility: hidden; +} + +/* Consecutive Card Groups Preserve Row Spacing */ +.ui.cards ~ .ui.cards { + margin-top: 0.875em; +} + +/*-------------- + Rounded Edges +---------------*/ + +.ui.cards > .card > :first-child, +.ui.card > :first-child { + border-radius: 0.2857rem 0.2857rem 0em 0em !important; +} +.ui.cards > .card > :last-child, +.ui.card > :last-child { + border-radius: 0em 0em 0.2857rem 0.2857rem !important; +} + +/*-------------- + Images +---------------*/ + +.ui.cards > .card > .image, +.ui.card > .image { + display: block; + position: relative; + padding: 0em; + background: rgba(0, 0, 0, 0.05); +} +.ui.cards > .card > .image > img, +.ui.card > .image > img { + display: block; + width: 100%; + height: auto; + border-radius: 0.2857rem 0.2857rem 0em 0em; + border: none; +} +.ui.cards > .card > .image:only-child > img, +.ui.card > .image:only-child > img { + border-radius: 0.2857rem; +} + +/*-------------- + Content +---------------*/ + +.ui.cards > .card > .content, +.ui.card > .content { + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + background: none; + margin: 0em; + padding: 1em 1em; + box-shadow: none; + font-size: 1em; + border: none; + border-radius: 0em; +} +.ui.cards > .card > .content:after, +.ui.card > .content:after { + display: block; + content: ' '; + height: 0px; + clear: both; + overflow: hidden; + visibility: hidden; +} +.ui.cards > .card > .content > .header, +.ui.card > .content > .header { + display: block; + margin: 0em; + font-family: 'Roboto', 'Helvetica Neue', Arial, Helvetica, sans-serif; + color: rgba(0, 0, 0, 0.85); +} + +/* Default Header Size */ +.ui.cards > .card > .content > .header:not(.ui), +.ui.card > .content > .header:not(.ui) { + font-weight: bold; + font-size: 1.2em; + margin-top: -0.165em; + line-height: 1.33em; +} +.ui.cards > .card > .content > .meta + .description, +.ui.cards > .card > .content > .header + .description, +.ui.card > .content > .meta + .description, +.ui.card > .content > .header + .description { + margin-top: 0.5em; +} + +/*-------------- + Floated +---------------*/ + +.ui.cards > .card [class*="left floated"], +.ui.card [class*="left floated"] { + float: left; +} +.ui.cards > .card [class*="right floated"], +.ui.card [class*="right floated"] { + float: right; +} + +/*-------------- + Aligned +---------------*/ + +.ui.cards > .card [class*="left aligned"], +.ui.card [class*="left aligned"] { + text-align: left; +} +.ui.cards > .card [class*="center aligned"], +.ui.card [class*="center aligned"] { + text-align: center; +} +.ui.cards > .card [class*="right aligned"], +.ui.card [class*="right aligned"] { + text-align: right; +} + +/*-------------- + Content Image +---------------*/ + +.ui.cards > .card .content img, +.ui.card .content img { + display: inline-block; + vertical-align: middle; + width: auto; +} +.ui.cards > .card img.avatar, +.ui.cards > .card .avatar img, +.ui.card img.avatar, +.ui.card .avatar img { + width: 2.5em; + height: 2.5em; + border-radius: 500rem; +} + +/*-------------- + Description +---------------*/ + +.ui.cards > .card > .content > .description, +.ui.card > .content > .description { + clear: both; + color: rgba(0, 0, 0, 0.5); +} + +/*-------------- + Paragraph +---------------*/ + +.ui.cards > .card > .content p, +.ui.card > .content p { + margin: 0em 0em 0.5em; +} +.ui.cards > .card > .content p:last-child, +.ui.card > .content p:last-child { + margin-bottom: 0em; +} + +/*-------------- + Meta +---------------*/ + +.ui.cards > .card .meta, +.ui.card .meta { + font-size: 0.9em; + color: rgba(0, 0, 0, 0.4); +} +.ui.cards > .card .meta *, +.ui.card .meta * { + margin-right: 0.3em; +} +.ui.cards > .card .meta :last-child, +.ui.card .meta :last-child { + margin-right: 0em; +} +.ui.cards > .card .meta [class*="right floated"], +.ui.card .meta [class*="right floated"] { + margin-right: 0em; + margin-left: 0.3em; +} + +/*-------------- + Links +---------------*/ + + +/* Generic */ +.ui.cards > .card > .content a:not(.ui), +.ui.card > .content a:not(.ui) { + color: ''; + -webkit-transition: color 0.2s ease; + transition: color 0.2s ease; +} +.ui.cards > .card > .content a:not(.ui):hover, +.ui.card > .content a:not(.ui):hover { + color: ''; +} + +/* Header */ +.ui.cards > .card > .content > a.header, +.ui.card > .content > a.header { + color: rgba(0, 0, 0, 0.85); +} +.ui.cards > .card > .content > a.header:hover, +.ui.card > .content > a.header:hover { + color: #00b2f3; +} + +/* Meta */ +.ui.cards > .card .meta > a:not(.ui), +.ui.card .meta > a:not(.ui) { + color: rgba(0, 0, 0, 0.4); +} +.ui.cards > .card .meta > a:not(.ui):hover, +.ui.card .meta > a:not(.ui):hover { + color: rgba(0, 0, 0, 0.8); +} + +/*-------------- + Buttons +---------------*/ + +.ui.cards > .card > .buttons:last-child, +.ui.card > .buttons:last-child, +.ui.cards > .card > .button:last-child, +.ui.card > .button:last-child { + margin: 0em 0em -1px; + width: 100%; +} + +/*-------------- + Dimmer +---------------*/ + +.ui.cards > .card .dimmer, +.ui.card .dimmer { + background-color: ''; + z-index: 10; +} + +/*-------------- + Labels +---------------*/ + + +/*-----Star----- */ + + +/* Icon */ +.ui.cards > .card > .content .star.icon, +.ui.card > .content .star.icon { + cursor: pointer; + opacity: 0.75; + -webkit-transition: color 0.2s ease; + transition: color 0.2s ease; +} +.ui.cards > .card > .content .star.icon:hover, +.ui.card > .content .star.icon:hover { + opacity: 1; + color: #ffb70a; +} +.ui.cards > .card > .content .active.star.icon, +.ui.card > .content .active.star.icon { + color: #ffe623; +} + +/*-----Like----- */ + + +/* Icon */ +.ui.cards > .card > .content .like.icon, +.ui.card > .content .like.icon { + cursor: pointer; + opacity: 0.75; + -webkit-transition: color 0.2s ease; + transition: color 0.2s ease; +} +.ui.cards > .card > .content .like.icon:hover, +.ui.card > .content .like.icon:hover { + opacity: 1; + color: #ff2733; +} +.ui.cards > .card > .content .active.like.icon, +.ui.card > .content .active.like.icon { + color: #ff2733; +} + +/*---------------- + Extra Content +-----------------*/ + +.ui.cards > .card > .extra, +.ui.card > .extra { + max-width: 100%; + min-height: 0em !important; + -webkit-box-flex: 0; + -webkit-flex-grow: 0; + -ms-flex-positive: 0; + flex-grow: 0; + position: static; + background: none; + width: auto; + margin: 0em 0em; + padding: 0.75em 1em; + top: 0em; + left: 0em; + color: rgba(0, 0, 0, 0.4); + box-shadow: none; + -webkit-transition: color 0.2s ease; + transition: color 0.2s ease; + border-top: 1px solid rgba(0, 0, 0, 0.05); +} +.ui.cards > .card > .extra a:not(.ui), +.ui.card > .extra a:not(.ui) { + color: rgba(0, 0, 0, 0.4); +} +.ui.cards > .card > .extra a:not(.ui):hover, +.ui.card > .extra a:not(.ui):hover { + color: #00b2f3; +} + + +/******************************* + Variations +*******************************/ + + +/*------------------- + Fluid +--------------------*/ + +.ui.fluid.card { + width: 100%; + max-width: 9999px; +} + +/*------------------- + Link +--------------------*/ + +.ui.cards a.card:hover, +.ui.link.cards .card:hover, +a.ui.card:hover, +.ui.link.card:hover { + cursor: pointer; + z-index: 5; + background: ''; + border: none; + box-shadow: 0px 3px 0px 0px #bebebf, 0px 0px 0px 1px rgba(39, 41, 43, 0.3); +} + +/*------------------- + Colors +--------------------*/ + +.ui.black.cards > .card, +.ui.cards > .black.card, +.ui.black.card { + box-shadow: 0px 3px 0px 0px #1b1c1d, 0px 0px 0px 1px #d4d4d5; +} +.ui.blue.cards > .card, +.ui.cards > .blue.card, +.ui.blue.card { + box-shadow: 0px 3px 0px 0px #006e93, 0px 0px 0px 1px #d4d4d5; +} +.ui.green.cards > .card, +.ui.cards > .green.card, +.ui.green.card { + box-shadow: 0px 3px 0px 0px #5bbd72, 0px 0px 0px 1px #d4d4d5; +} +.ui.orange.cards > .card, +.ui.cards > .orange.card, +.ui.orange.card { + box-shadow: 0px 3px 0px 0px #e07b53, 0px 0px 0px 1px #d4d4d5; +} +.ui.pink.cards > .card, +.ui.cards > .pink.card, +.ui.pink.card { + box-shadow: 0px 3px 0px 0px #d9499a, 0px 0px 0px 1px #d4d4d5; +} +.ui.purple.cards > .card, +.ui.cards > .purple.card, +.ui.purple.card { + box-shadow: 0px 3px 0px 0px #564f8a, 0px 0px 0px 1px #d4d4d5; +} +.ui.red.cards > .card, +.ui.cards > .red.card, +.ui.red.card { + box-shadow: 0px 3px 0px 0px #d95c5c, 0px 0px 0px 1px #d4d4d5; +} +.ui.teal.cards > .card, +.ui.cards > .teal.card, +.ui.teal.card { + box-shadow: 0px 3px 0px 0px #00b5ad, 0px 0px 0px 1px #d4d4d5; +} +.ui.yellow.cards > .card, +.ui.cards > .yellow.card, +.ui.yellow.card { + box-shadow: 0px 3px 0px 0px #f2c61f, 0px 0px 0px 1px #d4d4d5; +} + +/* Hover */ +.ui.black.cards > .card:hover, +.ui.cards > .black.card:hover, +.ui.black.card:hover { + box-shadow: 0px 3px 0px 0px #1b1c1d, 0px 0px 0px 1px #d4d4d5; +} +.ui.blue.cards > .card:hover, +.ui.cards > .blue.card:hover, +.ui.blue.card:hover { + box-shadow: 0px 3px 0px 0px #0079a2, 0px 0px 0px 1px #d4d4d5; +} +.ui.green.cards > .card:hover, +.ui.cards > .green.card:hover, +.ui.green.card:hover { + box-shadow: 0px 3px 0px 0px #66c17b, 0px 0px 0px 1px #d4d4d5; +} +.ui.orange.cards > .card:hover, +.ui.cards > .orange.card:hover, +.ui.orange.card:hover { + box-shadow: 0px 3px 0px 0px #e28560, 0px 0px 0px 1px #d4d4d5; +} +.ui.pink.cards > .card:hover, +.ui.cards > .pink.card:hover, +.ui.pink.card:hover { + box-shadow: 0px 3px 0px 0px #dc56a1, 0px 0px 0px 1px #d4d4d5; +} +.ui.purple.cards > .card:hover, +.ui.cards > .purple.card:hover, +.ui.purple.card:hover { + box-shadow: 0px 3px 0px 0px #5c5594, 0px 0px 0px 1px #d4d4d5; +} +.ui.red.cards > .card:hover, +.ui.cards > .red.card:hover, +.ui.red.card:hover { + box-shadow: 0px 3px 0px 0px #dc6868, 0px 0px 0px 1px #d4d4d5; +} +.ui.teal.cards > .card:hover, +.ui.cards > .teal.card:hover, +.ui.teal.card:hover { + box-shadow: 0px 3px 0px 0px #00c4bc, 0px 0px 0px 1px #d4d4d5; +} +.ui.yellow.cards > .card:hover, +.ui.cards > .yellow.card:hover, +.ui.yellow.card:hover { + box-shadow: 0px 3px 0px 0px #f3ca2d, 0px 0px 0px 1px #d4d4d5; +} + +/*-------------- + Card Count +---------------*/ + +.ui.one.cards { + margin-left: 0em; + margin-right: 0em; +} +.ui.one.cards > .card { + width: 100%; +} +.ui.two.cards { + margin-left: -1em; + margin-right: -1em; +} +.ui.two.cards > .card { + width: -webkit-calc( 50% - 2em ); + width: calc( 50% - 2em ); + margin-left: 1em; + margin-right: 1em; +} +.ui.two.cards > .card:nth-child(2n+1) { + clear: left; +} +.ui.three.cards { + margin-left: -1em; + margin-right: -1em; +} +.ui.three.cards > .card { + width: -webkit-calc( 33.33333333% - 2em ); + width: calc( 33.33333333% - 2em ); + margin-left: 1em; + margin-right: 1em; +} +.ui.three.cards > .card:nth-child(3n+1) { + clear: left; +} +.ui.four.cards { + margin-left: -0.75em; + margin-right: -0.75em; +} +.ui.four.cards > .card { + width: -webkit-calc( 25% - 1.5em ); + width: calc( 25% - 1.5em ); + margin-left: 0.75em; + margin-right: 0.75em; +} +.ui.four.cards > .card:nth-child(4n+1) { + clear: left; +} +.ui.five.cards { + margin-left: -0.75em; + margin-right: -0.75em; +} +.ui.five.cards > .card { + width: -webkit-calc( 20% - 1.5em ); + width: calc( 20% - 1.5em ); + margin-left: 0.75em; + margin-right: 0.75em; +} +.ui.five.cards > .card:nth-child(5n+1) { + clear: left; +} +.ui.six.cards { + margin-left: -0.75em; + margin-right: -0.75em; +} +.ui.six.cards > .card { + width: -webkit-calc( 16.66666667% - 1.5em ); + width: calc( 16.66666667% - 1.5em ); + margin-left: 0.75em; + margin-right: 0.75em; +} +.ui.six.cards > .card:nth-child(6n+1) { + clear: left; +} +.ui.seven.cards { + margin-left: -0.5em; + margin-right: -0.5em; +} +.ui.seven.cards > .card { + width: -webkit-calc( 14.28571429% - 1em ); + width: calc( 14.28571429% - 1em ); + margin-left: 0.5em; + margin-right: 0.5em; +} +.ui.seven.cards > .card:nth-child(7n+1) { + clear: left; +} +.ui.eight.cards { + margin-left: -0.5em; + margin-right: -0.5em; +} +.ui.eight.cards > .card { + width: -webkit-calc( 12.5% - 1em ); + width: calc( 12.5% - 1em ); + margin-left: 0.5em; + margin-right: 0.5em; + font-size: 11px; +} +.ui.eight.cards > .card:nth-child(8n+1) { + clear: left; +} +.ui.nine.cards { + margin-left: -0.5em; + margin-right: -0.5em; +} +.ui.nine.cards > .card { + width: -webkit-calc( 11.11111111% - 1em ); + width: calc( 11.11111111% - 1em ); + margin-left: 0.5em; + margin-right: 0.5em; + font-size: 10px; +} +.ui.nine.cards > .card:nth-child(9n+1) { + clear: left; +} +.ui.ten.cards { + margin-left: -0.5em; + margin-right: -0.5em; +} +.ui.ten.cards > .card { + width: -webkit-calc( 10% - 1em ); + width: calc( 10% - 1em ); + margin-left: 0.5em; + margin-right: 0.5em; +} +.ui.ten.cards > .card:nth-child(10n+1) { + clear: left; +} + +/*------------------- + Doubling +--------------------*/ + + +/* Mobily Only */ +@media only screen and (max-width: 767px) { + .ui.two.doubling.cards { + margin-left: 0em; + margin-right: 0em; + } + .ui.two.doubling.cards .card { + width: 100%; + margin-left: 0em; + margin-right: 0em; + } + .ui.three.doubling.cards { + margin-left: -1em; + margin-right: -1em; + } + .ui.three.doubling.cards .card { + width: -webkit-calc( 50% - 2em ); + width: calc( 50% - 2em ); + margin-left: 1em; + margin-right: 1em; + } + .ui.four.doubling.cards { + margin-left: -1em; + margin-right: -1em; + } + .ui.four.doubling.cards .card { + width: -webkit-calc( 50% - 2em ); + width: calc( 50% - 2em ); + margin-left: 1em; + margin-right: 1em; + } + .ui.five.doubling.cards { + margin-left: -1em; + margin-right: -1em; + } + .ui.five.doubling.cards .card { + width: -webkit-calc( 50% - 2em ); + width: calc( 50% - 2em ); + margin-left: 1em; + margin-right: 1em; + } + .ui.six.doubling.cards { + margin-left: -1em; + margin-right: -1em; + } + .ui.six.doubling.cards .card { + width: -webkit-calc( 50% - 2em ); + width: calc( 50% - 2em ); + margin-left: 1em; + margin-right: 1em; + } + .ui.seven.doubling.cards { + margin-left: -1em; + margin-right: -1em; + } + .ui.seven.doubling.cards .card { + width: -webkit-calc( 33.33333333% - 2em ); + width: calc( 33.33333333% - 2em ); + margin-left: 1em; + margin-right: 1em; + } + .ui.eight.doubling.cards { + margin-left: -1em; + margin-right: -1em; + } + .ui.eight.doubling.cards .card { + width: -webkit-calc( 33.33333333% - 2em ); + width: calc( 33.33333333% - 2em ); + margin-left: 1em; + margin-right: 1em; + } + .ui.nine.doubling.cards { + margin-left: -1em; + margin-right: -1em; + } + .ui.nine.doubling.cards .card { + width: -webkit-calc( 33.33333333% - 2em ); + width: calc( 33.33333333% - 2em ); + margin-left: 1em; + margin-right: 1em; + } + .ui.ten.doubling.cards { + margin-left: -1em; + margin-right: -1em; + } + .ui.ten.doubling.cards .card { + width: -webkit-calc( 33.33333333% - 2em ); + width: calc( 33.33333333% - 2em ); + margin-left: 1em; + margin-right: 1em; + } +} + +/* Tablet Only */ +@media only screen and (min-width: 768px) and (max-width: 991px) { + .ui.two.doubling.cards { + margin-left: 0em; + margin-right: 0em; + } + .ui.two.doubling.cards .card { + width: 100%; + margin-left: 0em; + margin-right: 0em; + } + .ui.three.doubling.cards { + margin-left: -1em; + margin-right: -1em; + } + .ui.three.doubling.cards .card { + width: -webkit-calc( 50% - 2em ); + width: calc( 50% - 2em ); + margin-left: 1em; + margin-right: 1em; + } + .ui.four.doubling.cards { + margin-left: -1em; + margin-right: -1em; + } + .ui.four.doubling.cards .card { + width: -webkit-calc( 50% - 2em ); + width: calc( 50% - 2em ); + margin-left: 1em; + margin-right: 1em; + } + .ui.five.doubling.cards { + margin-left: -1em; + margin-right: -1em; + } + .ui.five.doubling.cards .card { + width: -webkit-calc( 33.33333333% - 2em ); + width: calc( 33.33333333% - 2em ); + margin-left: 1em; + margin-right: 1em; + } + .ui.six.doubling.cards { + margin-left: -1em; + margin-right: -1em; + } + .ui.six.doubling.cards .card { + width: -webkit-calc( 33.33333333% - 2em ); + width: calc( 33.33333333% - 2em ); + margin-left: 1em; + margin-right: 1em; + } + .ui.eight.doubling.cards { + margin-left: -1em; + margin-right: -1em; + } + .ui.eight.doubling.cards .card { + width: -webkit-calc( 33.33333333% - 2em ); + width: calc( 33.33333333% - 2em ); + margin-left: 1em; + margin-right: 1em; + } + .ui.eight.doubling.cards { + margin-left: -0.75em; + margin-right: -0.75em; + } + .ui.eight.doubling.cards .card { + width: -webkit-calc( 25% - 1.5em ); + width: calc( 25% - 1.5em ); + margin-left: 0.75em; + margin-right: 0.75em; + } + .ui.nine.doubling.cards { + margin-left: -0.75em; + margin-right: -0.75em; + } + .ui.nine.doubling.cards .card { + width: -webkit-calc( 25% - 1.5em ); + width: calc( 25% - 1.5em ); + margin-left: 0.75em; + margin-right: 0.75em; + } + .ui.ten.doubling.cards { + margin-left: -0.75em; + margin-right: -0.75em; + } + .ui.ten.doubling.cards .card { + width: -webkit-calc( 20% - 1.5em ); + width: calc( 20% - 1.5em ); + margin-left: 0.75em; + margin-right: 0.75em; + } +} + +/*------------------- + Stackable +--------------------*/ + +@media only screen and (max-width: 767px) { + .ui.stackable.cards { + display: block !important; + } + .ui.stackable.cards .card:first-child { + margin-top: 0em !important; + } + .ui.stackable.cards > .card { + display: block !important; + height: auto !important; + margin: 1em 1em; + padding: 0 !important; + width: -webkit-calc( 100% - 2em ) !important; + width: calc( 100% - 2em ) !important; + } +} + +/*-------------- + Size +---------------*/ + +.ui.cards > .card { + font-size: 1em; +} + + +/******************************* + Theme Overrides +*******************************/ + + + +/******************************* + User Variable Overrides +*******************************/ + diff --git a/controller/static/semantic/dist/components/card.min.css b/controller/static/semantic/dist/components/card.min.css new file mode 100755 index 000000000..ab86e435e --- /dev/null +++ b/controller/static/semantic/dist/components/card.min.css @@ -0,0 +1,10 @@ +/*! + * # Semantic UI x.x - Item + * http://github.com/semantic-org/semantic-ui/ + * + * + * Copyright 2014 Contributorss + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.card,.ui.cards>.card{max-width:100%;position:relative;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;width:290px;min-height:0;background:#fff;padding:0;border:none;border-radius:.2857rem;box-shadow:0 3px 0 0 #d4d4d5,0 0 0 1px #d4d4d5;-webkit-transition:box-shadow .2s ease;transition:box-shadow .2s ease;z-index:''}.ui.card{margin:1em 0}.ui.card a,.ui.cards>.card a{cursor:pointer}.ui.card:first-child{margin-top:0}.ui.card:last-child{margin-bottom:0}.ui.cards{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;margin:-.875em -.5em;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.ui.cards>.card{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;margin:.875em .5em;float:none}.ui.card:after,.ui.cards:after{display:block;content:' ';height:0;clear:both;overflow:hidden;visibility:hidden}.ui.cards~.ui.cards{margin-top:.875em}.ui.card>:first-child,.ui.cards>.card>:first-child{border-radius:.2857rem .2857rem 0 0!important}.ui.card>:last-child,.ui.cards>.card>:last-child{border-radius:0 0 .2857rem .2857rem!important}.ui.card>.image,.ui.cards>.card>.image{display:block;position:relative;padding:0;background:rgba(0,0,0,.05)}.ui.card>.image>img,.ui.cards>.card>.image>img{display:block;width:100%;height:auto;border-radius:.2857rem .2857rem 0 0;border:none}.ui.card>.image:only-child>img,.ui.cards>.card>.image:only-child>img{border-radius:.2857rem}.ui.card>.content,.ui.cards>.card>.content{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;background:0 0;margin:0;padding:1em;box-shadow:none;font-size:1em;border:none;border-radius:0}.ui.card>.content:after,.ui.cards>.card>.content:after{display:block;content:' ';height:0;clear:both;overflow:hidden;visibility:hidden}.ui.card>.content>.header,.ui.cards>.card>.content>.header{display:block;margin:0;font-family:Roboto,'Helvetica Neue',Arial,Helvetica,sans-serif;color:rgba(0,0,0,.85)}.ui.card>.content>.header:not(.ui),.ui.cards>.card>.content>.header:not(.ui){font-weight:700;font-size:1.2em;margin-top:-.165em;line-height:1.33em}.ui.card>.content>.header+.description,.ui.card>.content>.meta+.description,.ui.cards>.card>.content>.header+.description,.ui.cards>.card>.content>.meta+.description{margin-top:.5em}.ui.card [class*="left floated"],.ui.cards>.card [class*="left floated"]{float:left}.ui.card [class*="right floated"],.ui.cards>.card [class*="right floated"]{float:right}.ui.card [class*="left aligned"],.ui.cards>.card [class*="left aligned"]{text-align:left}.ui.card [class*="center aligned"],.ui.cards>.card [class*="center aligned"]{text-align:center}.ui.card [class*="right aligned"],.ui.cards>.card [class*="right aligned"]{text-align:right}.ui.card .content img,.ui.cards>.card .content img{display:inline-block;vertical-align:middle;width:auto}.ui.card .avatar img,.ui.card img.avatar,.ui.cards>.card .avatar img,.ui.cards>.card img.avatar{width:2.5em;height:2.5em;border-radius:500rem}.ui.card>.content>.description,.ui.cards>.card>.content>.description{clear:both;color:rgba(0,0,0,.5)}.ui.card>.content p,.ui.cards>.card>.content p{margin:0 0 .5em}.ui.card>.content p:last-child,.ui.cards>.card>.content p:last-child{margin-bottom:0}.ui.card .meta,.ui.cards>.card .meta{font-size:.9em;color:rgba(0,0,0,.4)}.ui.card .meta *,.ui.cards>.card .meta *{margin-right:.3em}.ui.card .meta :last-child,.ui.cards>.card .meta :last-child{margin-right:0}.ui.card .meta [class*="right floated"],.ui.cards>.card .meta [class*="right floated"]{margin-right:0;margin-left:.3em}.ui.card>.content a:not(.ui),.ui.cards>.card>.content a:not(.ui){color:'';-webkit-transition:color .2s ease;transition:color .2s ease}.ui.card>.content a:not(.ui):hover,.ui.cards>.card>.content a:not(.ui):hover{color:''}.ui.card>.content>a.header,.ui.cards>.card>.content>a.header{color:rgba(0,0,0,.85)}.ui.card>.content>a.header:hover,.ui.cards>.card>.content>a.header:hover{color:#00b2f3}.ui.card .meta>a:not(.ui),.ui.cards>.card .meta>a:not(.ui){color:rgba(0,0,0,.4)}.ui.card .meta>a:not(.ui):hover,.ui.cards>.card .meta>a:not(.ui):hover{color:rgba(0,0,0,.8)}.ui.card>.button:last-child,.ui.card>.buttons:last-child,.ui.cards>.card>.button:last-child,.ui.cards>.card>.buttons:last-child{margin:0 0 -1px;width:100%}.ui.card .dimmer,.ui.cards>.card .dimmer{background-color:'';z-index:10}.ui.card>.content .star.icon,.ui.cards>.card>.content .star.icon{cursor:pointer;opacity:.75;-webkit-transition:color .2s ease;transition:color .2s ease}.ui.card>.content .star.icon:hover,.ui.cards>.card>.content .star.icon:hover{opacity:1;color:#ffb70a}.ui.card>.content .active.star.icon,.ui.cards>.card>.content .active.star.icon{color:#ffe623}.ui.card>.content .like.icon,.ui.cards>.card>.content .like.icon{cursor:pointer;opacity:.75;-webkit-transition:color .2s ease;transition:color .2s ease}.ui.card>.content .like.icon:hover,.ui.cards>.card>.content .like.icon:hover{opacity:1;color:#ff2733}.ui.card>.content .active.like.icon,.ui.cards>.card>.content .active.like.icon{color:#ff2733}.ui.card>.extra,.ui.cards>.card>.extra{max-width:100%;min-height:0!important;-webkit-box-flex:0;-webkit-flex-grow:0;-ms-flex-positive:0;flex-grow:0;position:static;background:0 0;width:auto;margin:0;padding:.75em 1em;top:0;left:0;color:rgba(0,0,0,.4);box-shadow:none;-webkit-transition:color .2s ease;transition:color .2s ease;border-top:1px solid rgba(0,0,0,.05)}.ui.card>.extra a:not(.ui),.ui.cards>.card>.extra a:not(.ui){color:rgba(0,0,0,.4)}.ui.card>.extra a:not(.ui):hover,.ui.cards>.card>.extra a:not(.ui):hover{color:#00b2f3}.ui.fluid.card{width:100%;max-width:9999px}.ui.cards a.card:hover,.ui.link.card:hover,.ui.link.cards .card:hover,a.ui.card:hover{cursor:pointer;z-index:5;background:0 0;border:none;box-shadow:0 3px 0 0 #bebebf,0 0 0 1px rgba(39,41,43,.3)}.ui.black.card,.ui.black.cards>.card,.ui.cards>.black.card{box-shadow:0 3px 0 0 #1b1c1d,0 0 0 1px #d4d4d5}.ui.blue.card,.ui.blue.cards>.card,.ui.cards>.blue.card{box-shadow:0 3px 0 0 #006e93,0 0 0 1px #d4d4d5}.ui.cards>.green.card,.ui.green.card,.ui.green.cards>.card{box-shadow:0 3px 0 0 #5bbd72,0 0 0 1px #d4d4d5}.ui.cards>.orange.card,.ui.orange.card,.ui.orange.cards>.card{box-shadow:0 3px 0 0 #e07b53,0 0 0 1px #d4d4d5}.ui.cards>.pink.card,.ui.pink.card,.ui.pink.cards>.card{box-shadow:0 3px 0 0 #d9499a,0 0 0 1px #d4d4d5}.ui.cards>.purple.card,.ui.purple.card,.ui.purple.cards>.card{box-shadow:0 3px 0 0 #564f8a,0 0 0 1px #d4d4d5}.ui.cards>.red.card,.ui.red.card,.ui.red.cards>.card{box-shadow:0 3px 0 0 #d95c5c,0 0 0 1px #d4d4d5}.ui.cards>.teal.card,.ui.teal.card,.ui.teal.cards>.card{box-shadow:0 3px 0 0 #00b5ad,0 0 0 1px #d4d4d5}.ui.cards>.yellow.card,.ui.yellow.card,.ui.yellow.cards>.card{box-shadow:0 3px 0 0 #f2c61f,0 0 0 1px #d4d4d5}.ui.black.card:hover,.ui.black.cards>.card:hover,.ui.cards>.black.card:hover{box-shadow:0 3px 0 0 #1b1c1d,0 0 0 1px #d4d4d5}.ui.blue.card:hover,.ui.blue.cards>.card:hover,.ui.cards>.blue.card:hover{box-shadow:0 3px 0 0 #0079a2,0 0 0 1px #d4d4d5}.ui.cards>.green.card:hover,.ui.green.card:hover,.ui.green.cards>.card:hover{box-shadow:0 3px 0 0 #66c17b,0 0 0 1px #d4d4d5}.ui.cards>.orange.card:hover,.ui.orange.card:hover,.ui.orange.cards>.card:hover{box-shadow:0 3px 0 0 #e28560,0 0 0 1px #d4d4d5}.ui.cards>.pink.card:hover,.ui.pink.card:hover,.ui.pink.cards>.card:hover{box-shadow:0 3px 0 0 #dc56a1,0 0 0 1px #d4d4d5}.ui.cards>.purple.card:hover,.ui.purple.card:hover,.ui.purple.cards>.card:hover{box-shadow:0 3px 0 0 #5c5594,0 0 0 1px #d4d4d5}.ui.cards>.red.card:hover,.ui.red.card:hover,.ui.red.cards>.card:hover{box-shadow:0 3px 0 0 #dc6868,0 0 0 1px #d4d4d5}.ui.cards>.teal.card:hover,.ui.teal.card:hover,.ui.teal.cards>.card:hover{box-shadow:0 3px 0 0 #00c4bc,0 0 0 1px #d4d4d5}.ui.cards>.yellow.card:hover,.ui.yellow.card:hover,.ui.yellow.cards>.card:hover{box-shadow:0 3px 0 0 #f3ca2d,0 0 0 1px #d4d4d5}.ui.one.cards{margin-left:0;margin-right:0}.ui.one.cards>.card{width:100%}.ui.two.cards{margin-left:-1em;margin-right:-1em}.ui.two.cards>.card{width:-webkit-calc(50% - 2em);width:calc(50% - 2em);margin-left:1em;margin-right:1em}.ui.two.cards>.card:nth-child(2n+1){clear:left}.ui.three.cards{margin-left:-1em;margin-right:-1em}.ui.three.cards>.card{width:-webkit-calc(33.33333333% - 2em);width:calc(33.33333333% - 2em);margin-left:1em;margin-right:1em}.ui.three.cards>.card:nth-child(3n+1){clear:left}.ui.four.cards{margin-left:-.75em;margin-right:-.75em}.ui.four.cards>.card{width:-webkit-calc(25% - 1.5em);width:calc(25% - 1.5em);margin-left:.75em;margin-right:.75em}.ui.four.cards>.card:nth-child(4n+1){clear:left}.ui.five.cards{margin-left:-.75em;margin-right:-.75em}.ui.five.cards>.card{width:-webkit-calc(20% - 1.5em);width:calc(20% - 1.5em);margin-left:.75em;margin-right:.75em}.ui.five.cards>.card:nth-child(5n+1){clear:left}.ui.six.cards{margin-left:-.75em;margin-right:-.75em}.ui.six.cards>.card{width:-webkit-calc(16.66666667% - 1.5em);width:calc(16.66666667% - 1.5em);margin-left:.75em;margin-right:.75em}.ui.six.cards>.card:nth-child(6n+1){clear:left}.ui.seven.cards{margin-left:-.5em;margin-right:-.5em}.ui.seven.cards>.card{width:-webkit-calc(14.28571429% - 1em);width:calc(14.28571429% - 1em);margin-left:.5em;margin-right:.5em}.ui.seven.cards>.card:nth-child(7n+1){clear:left}.ui.eight.cards{margin-left:-.5em;margin-right:-.5em}.ui.eight.cards>.card{width:-webkit-calc(12.5% - 1em);width:calc(12.5% - 1em);margin-left:.5em;margin-right:.5em;font-size:11px}.ui.eight.cards>.card:nth-child(8n+1){clear:left}.ui.nine.cards{margin-left:-.5em;margin-right:-.5em}.ui.nine.cards>.card{width:-webkit-calc(11.11111111% - 1em);width:calc(11.11111111% - 1em);margin-left:.5em;margin-right:.5em;font-size:10px}.ui.nine.cards>.card:nth-child(9n+1){clear:left}.ui.ten.cards{margin-left:-.5em;margin-right:-.5em}.ui.ten.cards>.card{width:-webkit-calc(10% - 1em);width:calc(10% - 1em);margin-left:.5em;margin-right:.5em}.ui.ten.cards>.card:nth-child(10n+1){clear:left}@media only screen and (max-width:767px){.ui.two.doubling.cards{margin-left:0;margin-right:0}.ui.two.doubling.cards .card{width:100%;margin-left:0;margin-right:0}.ui.three.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.three.doubling.cards .card{width:-webkit-calc(50% - 2em);width:calc(50% - 2em);margin-left:1em;margin-right:1em}.ui.four.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.four.doubling.cards .card{width:-webkit-calc(50% - 2em);width:calc(50% - 2em);margin-left:1em;margin-right:1em}.ui.five.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.five.doubling.cards .card{width:-webkit-calc(50% - 2em);width:calc(50% - 2em);margin-left:1em;margin-right:1em}.ui.six.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.six.doubling.cards .card{width:-webkit-calc(50% - 2em);width:calc(50% - 2em);margin-left:1em;margin-right:1em}.ui.seven.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.seven.doubling.cards .card{width:-webkit-calc(33.33333333% - 2em);width:calc(33.33333333% - 2em);margin-left:1em;margin-right:1em}.ui.eight.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.eight.doubling.cards .card{width:-webkit-calc(33.33333333% - 2em);width:calc(33.33333333% - 2em);margin-left:1em;margin-right:1em}.ui.nine.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.nine.doubling.cards .card{width:-webkit-calc(33.33333333% - 2em);width:calc(33.33333333% - 2em);margin-left:1em;margin-right:1em}.ui.ten.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.ten.doubling.cards .card{width:-webkit-calc(33.33333333% - 2em);width:calc(33.33333333% - 2em);margin-left:1em;margin-right:1em}}@media only screen and (min-width:768px)and (max-width:991px){.ui.two.doubling.cards{margin-left:0;margin-right:0}.ui.two.doubling.cards .card{width:100%;margin-left:0;margin-right:0}.ui.three.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.three.doubling.cards .card{width:-webkit-calc(50% - 2em);width:calc(50% - 2em);margin-left:1em;margin-right:1em}.ui.four.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.four.doubling.cards .card{width:-webkit-calc(50% - 2em);width:calc(50% - 2em);margin-left:1em;margin-right:1em}.ui.five.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.five.doubling.cards .card{width:-webkit-calc(33.33333333% - 2em);width:calc(33.33333333% - 2em);margin-left:1em;margin-right:1em}.ui.six.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.six.doubling.cards .card{width:-webkit-calc(33.33333333% - 2em);width:calc(33.33333333% - 2em);margin-left:1em;margin-right:1em}.ui.eight.doubling.cards{margin-left:-.75em;margin-right:-.75em}.ui.eight.doubling.cards .card{width:-webkit-calc(25% - 1.5em);width:calc(25% - 1.5em);margin-left:.75em;margin-right:.75em}.ui.nine.doubling.cards{margin-left:-.75em;margin-right:-.75em}.ui.nine.doubling.cards .card{width:-webkit-calc(25% - 1.5em);width:calc(25% - 1.5em);margin-left:.75em;margin-right:.75em}.ui.ten.doubling.cards{margin-left:-.75em;margin-right:-.75em}.ui.ten.doubling.cards .card{width:-webkit-calc(20% - 1.5em);width:calc(20% - 1.5em);margin-left:.75em;margin-right:.75em}}@media only screen and (max-width:767px){.ui.stackable.cards{display:block!important}.ui.stackable.cards .card:first-child{margin-top:0!important}.ui.stackable.cards>.card{display:block!important;height:auto!important;margin:1em;padding:0!important;width:-webkit-calc(100% - 2em)!important;width:calc(100% - 2em)!important}}.ui.cards>.card{font-size:1em} \ No newline at end of file diff --git a/controller/static/semantic/dist/components/checkbox.css b/controller/static/semantic/dist/components/checkbox.css new file mode 100755 index 000000000..c1dc90b9f --- /dev/null +++ b/controller/static/semantic/dist/components/checkbox.css @@ -0,0 +1,513 @@ +/*! + * # Semantic UI x.x - Checkbox + * http://github.com/semantic-org/semantic-ui/ + * + * + * Copyright 2014 Contributors + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ + + +/******************************* + Checkbox +*******************************/ + + +/*-------------- + Content +---------------*/ + +.ui.checkbox { + position: relative; + display: inline-block; + min-height: 17px; + font-size: 1rem; + line-height: 15px; + min-width: 17px; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + outline: none; + vertical-align: middle; +} +.ui.checkbox input[type="checkbox"], +.ui.checkbox input[type="radio"] { + position: absolute; + top: 0px; + left: 0px; + opacity: 0 !important; + outline: none; + z-index: -1; +} + +/*-------------- + Box +---------------*/ + +.ui.checkbox .box, +.ui.checkbox label { + display: block; + cursor: pointer; + padding-left: 1.75em; + outline: none; +} +.ui.checkbox label { + font-size: 1em; +} +.ui.checkbox .box:before, +.ui.checkbox label:before { + position: absolute; + line-height: 1; + width: 17px; + height: 17px; + top: 0em; + left: 0em; + content: ''; + background: #ffffff; + border-radius: 0.25em; + -webkit-transition: background-color 0.3s ease, border 0.3s ease, box-shadow 0.3s ease; + transition: background-color 0.3s ease, border 0.3s ease, box-shadow 0.3s ease; + border: 1px solid #d4d4d5; +} + +/*-------------- + Checkmark +---------------*/ + +.ui.checkbox .box:after, +.ui.checkbox label:after { + position: absolute; + top: 0px; + left: 0px; + line-height: 17px; + width: 17px; + height: 17px; + text-align: center; + opacity: 0; + color: rgba(0, 0, 0, 0.8); + -webkit-transition: all 0.1s ease; + transition: all 0.1s ease; +} + +/*-------------- + Label +---------------*/ + + +/* Inside */ +.ui.checkbox label, +.ui.checkbox + label { + cursor: pointer; + color: rgba(0, 0, 0, 0.8); + -webkit-transition: color 0.2s ease; + transition: color 0.2s ease; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +/* Outside */ +.ui.checkbox + label { + vertical-align: middle; +} + + +/******************************* + States +*******************************/ + + +/*-------------- + Hover +---------------*/ + +.ui.checkbox .box:hover::before, +.ui.checkbox label:hover::before { + background: #ffffff; + border: 1px solid rgba(39, 41, 43, 0.3); +} +.ui.checkbox label:hover, +.ui.checkbox + label:hover { + color: rgba(0, 0, 0, 0.8); +} + +/*-------------- + Down +---------------*/ + +.ui.checkbox .box:active::before, +.ui.checkbox label:active::before { + background: #f5f5f5; + border: 1px solid 1px solid rgba(39, 41, 43, 0.3); +} +.ui.checkbox input[type="checkbox"]:active ~ label, +.ui.checkbox input[type="radio"]:active ~ label { + color: rgba(0, 0, 0, 0.8); +} + +/*-------------- + Focus +---------------*/ + +.ui.checkbox input[type="checkbox"]:focus ~ .box:before, +.ui.checkbox input[type="checkbox"]:focus ~ label:before, +.ui.checkbox input[type="radio"]:focus ~ .box:before, +.ui.checkbox input[type="radio"]:focus ~ label:before { + background: #f5f5f5; + border: 1px solid 1px solid rgba(39, 41, 43, 0.3); +} +.ui.checkbox input[type="checkbox"]:focus ~ label, +.ui.checkbox input[type="radio"]:focus ~ label { + color: rgba(0, 0, 0, 0.8); +} + +/*-------------- + Active +---------------*/ + +.ui.checkbox input[type="checkbox"]:checked ~ .box:after, +.ui.checkbox input[type="checkbox"]:checked ~ label:after, +.ui.checkbox input[type="radio"]:checked ~ .box:after, +.ui.checkbox input[type="radio"]:checked ~ label:after { + opacity: 1; +} + +/*-------------- + Read-Only +---------------*/ + +.ui.read-only.checkbox, +.ui.read-only.checkbox label { + cursor: default; +} + +/*-------------- + Disabled +---------------*/ + +.ui.disabled.checkbox .box:after, +.ui.disabled.checkbox label, +.ui.checkbox input[type="checkbox"][disabled] ~ .box:after, +.ui.checkbox input[type="checkbox"][disabled] ~ label, +.ui.checkbox input[type="radio"][disabled] ~ .box:after, +.ui.checkbox input[type="radio"][disabled] ~ label { + cursor: default; + opacity: 0.5; + color: #000000; +} + + +/******************************* + Types +*******************************/ + + +/*-------------- + Radio +---------------*/ + +.ui.radio.checkbox { + min-height: 14px; +} + +/* Box */ +.ui.radio.checkbox .box:before, +.ui.radio.checkbox label:before { + width: 14px; + height: 14px; + border-radius: 500rem; + top: 1px; + left: 0px; + -webkit-transform: none; + -ms-transform: none; + transform: none; +} + +/* Circle */ +.ui.radio.checkbox .box:after, +.ui.radio.checkbox label:after { + border: none; + width: 14px; + height: 14px; + line-height: 14px; + top: 1px; + left: 0px; + font-size: 9px; +} + +/* Radio Checkbox */ +.ui.radio.checkbox .box:after, +.ui.radio.checkbox label:after { + width: 14px; + height: 14px; + border-radius: 500rem; + -webkit-transform: scale(0.42857143); + -ms-transform: scale(0.42857143); + transform: scale(0.42857143); + background-color: rgba(0, 0, 0, 0.8); +} + +/*-------------- + Slider +---------------*/ + +.ui.slider.checkbox { + cursor: pointer; + min-height: 1.25rem; +} +.ui.slider.checkbox .box, +.ui.slider.checkbox label { + padding-left: 4.5rem; + line-height: 1rem; + color: rgba(0, 0, 0, 0.4); +} + +/* Line */ +.ui.slider.checkbox .box:before, +.ui.slider.checkbox label:before { + cursor: pointer; + display: block; + position: absolute; + content: ''; + top: 0.4rem; + left: 0em; + z-index: 1; + border: none !important; + background-color: rgba(0, 0, 0, 0.05); + width: 3.5rem; + height: 0.25rem; + -webkit-transform: none; + -ms-transform: none; + transform: none; + border-radius: 500rem; + -webkit-transition: background 0.3s ease + ; + transition: background 0.3s ease + ; +} + +/* Handle */ +.ui.slider.checkbox .box:after, +.ui.slider.checkbox label:after { + background: #ffffff -webkit-linear-gradient(transparent, rgba(0, 0, 0, 0.05)); + background: #ffffff linear-gradient(transparent, rgba(0, 0, 0, 0.05)); + position: absolute; + content: ''; + opacity: 1; + z-index: 2; + border: none; + box-shadow: 0px 1px 2px 0 rgba(0, 0, 0, 0.05), 0px 0px 0px 1px rgba(39, 41, 43, 0.15) inset; + width: 1.5rem; + height: 1.5rem; + top: -0.25rem; + left: 0em; + -webkit-transform: none; + -ms-transform: none; + transform: none; + border-radius: 500rem; + -webkit-transition: left 0.3s ease 0s + ; + transition: left 0.3s ease 0s + ; +} + +/* Focus */ +.ui.slider.checkbox input[type="checkbox"]:focus ~ .box:before, +.ui.slider.checkbox input[type="checkbox"]:focus ~ label:before, +.ui.slider.checkbox input[type="radio"]:focus ~ .box:before, +.ui.slider.checkbox input[type="radio"]:focus ~ label:before { + background-color: rgba(0, 0, 0, 0.1); + border: none; +} + +/* Hover */ +.ui.slider.checkbox .box:hover, +.ui.slider.checkbox label:hover { + color: rgba(0, 0, 0, 0.8); +} +.ui.slider.checkbox .box:hover::before, +.ui.slider.checkbox label:hover::before { + background: rgba(0, 0, 0, 0.1); +} + +/* Active */ +.ui.slider.checkbox input[type="checkbox"]:checked ~ .box, +.ui.slider.checkbox input[type="checkbox"]:checked ~ label, +.ui.slider.checkbox input[type="radio"]:checked ~ .box, +.ui.slider.checkbox input[type="radio"]:checked ~ label { + color: rgba(0, 0, 0, 0.8); +} +.ui.slider.checkbox input[type="checkbox"]:checked ~ .box:before, +.ui.slider.checkbox input[type="checkbox"]:checked ~ label:before, +.ui.slider.checkbox input[type="radio"]:checked ~ .box:before, +.ui.slider.checkbox input[type="radio"]:checked ~ label:before { + background-color: rgba(0, 0, 0, 0.1); +} +.ui.slider.checkbox input[type="checkbox"]:checked ~ .box:after, +.ui.slider.checkbox input[type="checkbox"]:checked ~ label:after, +.ui.slider.checkbox input[type="radio"]:checked ~ .box:after, +.ui.slider.checkbox input[type="radio"]:checked ~ label:after { + left: 2rem; +} + +/*-------------- + Toggle +---------------*/ + +.ui.toggle.checkbox { + cursor: pointer; + min-height: 1.5rem; +} +.ui.toggle.checkbox .box, +.ui.toggle.checkbox label { + min-height: 1.5rem; + padding-left: 4.5rem; + color: rgba(0, 0, 0, 0.8); +} +.ui.toggle.checkbox label { + padding-top: 0.15em; +} + +/* Switch */ +.ui.toggle.checkbox .box:before, +.ui.toggle.checkbox label:before { + cursor: pointer; + display: block; + position: absolute; + content: ''; + top: 0rem; + z-index: 1; + border: none; + background-color: rgba(0, 0, 0, 0.05); + width: 3.5rem; + height: 1.5rem; + border-radius: 500rem; +} + +/* Handle */ +.ui.toggle.checkbox .box:after, +.ui.toggle.checkbox label:after { + background: #ffffff -webkit-linear-gradient(transparent, rgba(0, 0, 0, 0.05)); + background: #ffffff linear-gradient(transparent, rgba(0, 0, 0, 0.05)); + position: absolute; + content: ''; + opacity: 1; + z-index: 2; + border: none; + box-shadow: 0px 1px 2px 0 rgba(0, 0, 0, 0.05), 0px 0px 0px 1px rgba(39, 41, 43, 0.15) inset; + width: 1.5rem; + height: 1.5rem; + top: 0rem; + left: 0em; + border-radius: 500rem; + -webkit-transition: background 0.3s ease 0s, + left 0.3s ease 0s + ; + transition: background 0.3s ease 0s, + left 0.3s ease 0s + ; +} +.ui.toggle.checkbox input[type="checkbox"] ~ .box:after, +.ui.toggle.checkbox input[type="checkbox"] ~ label:after, +.ui.toggle.checkbox input[type="radio"] ~ .box:after, +.ui.toggle.checkbox input[type="radio"] ~ label:after { + left: -0.05rem; +} + +/* Focus */ +.ui.toggle.checkbox input[type="checkbox"]:focus ~ .box:before, +.ui.toggle.checkbox input[type="checkbox"]:focus ~ label:before, +.ui.toggle.checkbox input[type="radio"]:focus ~ .box:before, +.ui.toggle.checkbox input[type="radio"]:focus ~ label:before { + background-color: rgba(0, 0, 0, 0.1); + border: none; +} + +/* Hover */ +.ui.toggle.checkbox .box:hover::before, +.ui.toggle.checkbox label:hover::before { + background-color: rgba(0, 0, 0, 0.1); + border: none; +} + +/* Active */ +.ui.toggle.checkbox input[type="checkbox"]:checked ~ .box, +.ui.toggle.checkbox input[type="checkbox"]:checked ~ label, +.ui.toggle.checkbox input[type="radio"]:checked ~ .box, +.ui.toggle.checkbox input[type="radio"]:checked ~ label { + color: #5bbd72; +} +.ui.toggle.checkbox input[type="checkbox"]:checked ~ .box:before, +.ui.toggle.checkbox input[type="checkbox"]:checked ~ label:before, +.ui.toggle.checkbox input[type="radio"]:checked ~ .box:before, +.ui.toggle.checkbox input[type="radio"]:checked ~ label:before { + background-color: #5bbd72; +} +.ui.toggle.checkbox input[type="checkbox"]:checked ~ .box:after, +.ui.toggle.checkbox input[type="checkbox"]:checked ~ label:after, +.ui.toggle.checkbox input[type="radio"]:checked ~ .box:after, +.ui.toggle.checkbox input[type="radio"]:checked ~ label:after { + left: 2.05rem; +} + + +/******************************* + Variations +*******************************/ + + +/*-------------- + Fitted +---------------*/ + +.ui.fitted.checkbox .box, +.ui.fitted.checkbox label { + padding-left: 0em !important; +} +.ui.fitted.toggle.checkbox, +.ui.fitted.toggle.checkbox { + width: 3.5rem; +} +.ui.fitted.slider.checkbox, +.ui.fitted.slider.checkbox { + width: 3.5rem; +} + + +/******************************* + Theme Overrides +*******************************/ + +@font-face { + font-family: 'Checkbox'; + src: url(data:application/x-font-ttf;charset=utf-8;base64,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) format('truetype'), url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAAoUAA4AAAAAEPQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAABRAAAAEQAAABWPeFJAWNtYXAAAAGIAAAAOgAAAUrQEhm3Y3Z0IAAAAcQAAAAUAAAAHAZJ/5RmcGdtAAAB2AAABPkAAAmRigp4O2dhc3AAAAbUAAAACAAAAAgAAAAQZ2x5ZgAABtwAAACuAAAAtt9nBHZoZWFkAAAHjAAAADUAAAA2ASs8e2hoZWEAAAfEAAAAIAAAACQHUwNNaG10eAAAB+QAAAAMAAAADAspAABsb2NhAAAH8AAAAAgAAAAIADgAW21heHAAAAf4AAAAIAAAACAApgm8bmFtZQAACBgAAAF3AAACzcydGhxwb3N0AAAJkAAAACoAAAA7rr1AmHByZXAAAAm8AAAAVgAAAFaSoZr/eJxjYGTewTiBgZWBg6mKaQ8DA0MPhGZ8wGDIyMTAwMTAysyAFQSkuaYwOLxgeMHIHPQ/iyGKmZvBHyjMCJIDAPe9C2B4nGNgYGBmgGAZBkYGEHAB8hjBfBYGDSDNBqQZGZgYGF4w/v8PUvCCAURLMELVAwEjG8OIBwBk5AavAAB4nGNgQANGDEbM3P83gjAAELQD4XicnVXZdtNWFJU8ZHASOmSgoA7X3DhQ68qEKRgwaSrFdiEdHAitBB2kDHTkncc+62uOQrtWH/m07n09JLR0rbYsls++R1tn2DrnRhwjKn0aiGvUoZKXA6msPZZK90lc13Uvj5UMBnFdthJPSZuonSRKat3sUC7xWOsqWSdYJ+PlIFZPVZ5noAziFB5lSUQbRBuplyZJ4onjJ4kWZxAfJUkgJaMQp9LIUEI1GsRS1aFM6dCr1xNx00DKRqMedVhU90PFJ8c1p9SsA0YqVznCFevVRr4bpwMve5DEOsGzrYcxHnisfpQqkIqR6cg/dkpOlIaBVHHUoVbi6DCTX/eRTCrNQKaMYkWl7oG43f102xYxPXQ6vi5KlUaqurnOKJrt0fGogygP2cbppNzQ2fbw5RlTVKtdcbPtQGYNXErJbHSfRAAdJlLj6QFONZwCqRn1R8XZ588BEslclKo8VTKHegOZMzt7cTHtbiersnCknwcyb3Z2452HQ6dXh3/R+hdM4cxHj+Jifj5C+lBqfiJOJKVGWMzyp4YfcVcgQrkxiAsXyuBThDl0RdrZZl3jtTH2hs/5SqlhPQna6KP4fgr9TiQrHGdRo/VInM1j13Wt3GdQS7W7Fzsyr0OVIu7vCwuuM+eEYZ4WC1VfnvneBTT/Bohn/EDeNIVL+5YpSrRvm6JMu2iKCu0SVKVdNsUU7YoppmnPmmKG9h1TzNKeMzLj/8vc55H7HN7xkJv2XeSmfQ+5ad9HbtoPkJtWITdtHblpLyA3rUZu2lWjOnYEGgZpF1IVQdA0svph3Fab9UDWjDR8aWDyLmLI+upER521tcofxX914gsHcmmip7siF5viLq/bFj483e6rj5pG3bDV+MaR8jAeRnocmtBZ+c3hv+1N3S6a7jKqMugBFUwKwABl7UAC0zrbCaT1mqf48gdgXIZ4zkpDtVSfO4am7+V5X/exOfG+x+3GLrdcd3kJWdYNcmP28N9SZKrrH+UtrVQnR6wrJ49VaxhDKrwour6SlHu0tRu/KKmy8l6U1srnk5CbPYMbQlu27mGwI0xpyiUeXlOlKD3UUo6yQyxvKco84JSLC1qGxLgOdQ9qa8TpoXoYGwshhqG0vRBwSCldFd+0ynfxHqtr2Oj4xRXh6XpyEhGf4ir7UfBU10b96A7avGbdMoMpVaqn+4xPsa/b9lFZaaSOsxe3VAfXNOsaORXTT+Rr4HRvOGjdAz1UfDRBI1U1x+jGKGM0ljXl3wR0MVZ+w2jVYvs93E+dpFWsuUuY7JsT9+C0u/0q+7WcW0bW/dcGvW3kip8jMb8tCvw7B2K3ZA3UO5OBGAvIWdAYxhYmdxiug23EbfY/Jqf/34aFRXJXOxq7eerD1ZNRJXfZ8rjLTXZZ16M2R9VOGvsIjS0PN+bY4XIstsRgQbb+wf8x7gF3aVEC4NDIZZiI2nShnurh6h6rsW04VxIBds2x43QAegAuQd8cu9bzCYD13CPnLsB9cgh2yCH4lByCz8i5BfA5OQRfkEMwIIdgl5w7AA/IIXhIDsEeOQSPyNkE+JIcgq/IIYjJIUjIuQ3wmByCJ+QQfE0OwTdGrk5k/pYH2QD6zqKbQKmdGhzaOGRGrk3Y+zxY9oFFZB9aROqRkesT6lMeLPV7i0j9wSJSfzRyY0L9iQdL/dkiUn+xiNRnxpeZIymvDp7zjg7+BJfqrV4AAAAAAQAB//8AD3icY2BkAALmJUwzGEQZZBwk+RkZGBmdGJgYmbIYgMwsoGSiiLgIs5A2owg7I5uSOqOaiT2jmZE8I5gQY17C/09BQEfg3yt+fh8gvYQxD0j68DOJiQn8U+DnZxQDcQUEljLmCwBpBgbG/3//b2SOZ+Zm4GEQcuAH2sblDLSEm8FFVJhJEGgLH6OSHpMdo5EcI3Nk0bEXJ/LYqvZ82VXHGFd6pKTkyCsQwQAAq+QkqAAAeJxjYGRgYADiw5VSsfH8Nl8ZuJlfAEUYzpvO6IXQCb7///7fyLyEmRvI5WBgAokCAFb/DJAAAAB4nGNgZGBgDvqfxRDF/IKB4f935iUMQBEUwAwAi5YFpgPoAAAD6AAAA1kAAAAAAAAAOABbAAEAAAADABYAAQAAAAAAAgAGABMAbgAAAC0JkQAAAAB4nHWQy2rCQBSG//HSi0JbWui2sypKabxgN4IgWHTTbqS4LTHGJBIzMhkFX6Pv0IfpS/RZ+puMpShNmMx3vjlz5mQAXOMbAvnzxJGzwBmjnAs4Rc9ykf7Zcon8YrmMKt4sn9C/W67gAYHlKm7wwQqidM5ogU/LAlfi0nIBF+LOcpH+0XKJ3LNcxq14tXxC71muYCJSy1Xci6+BWm11FIRG1gZ12W62OnK6lYoqStxYumsTKp3KvpyrxPhxrBxPLfc89oN17Op9uJ8nvk4jlciW09yrkZ/42jX+bFc93QRtY+ZyrtVSDm2GXGm18D3jhMasuo3G3/MwgMIKW2hEvKoQBhI12jrnNppooUOaMkMyM8+KkMBFTONizR1htpIy7nPMGSW0PjNisgOP3+WRH5MC7o9ZRR+tHsYT0u6MKPOSfTns7jBrREqyTDezs9/eU2x4WpvWcNeuS511JTE8qCF5H7u1BY1H72S3Ymi7aPD95/9+AN1fhEsAeJxjYGKAAC4G7ICZgYGRiZGZMzkjNTk7N7Eomy05syg5J5WBAQBE1QZBAABLuADIUlixAQGOWbkIAAgAYyCwASNEsAMjcLIEKAlFUkSyCgIHKrEGAUSxJAGIUViwQIhYsQYDRLEmAYhRWLgEAIhYsQYBRFlZWVm4Af+FsASNsQUARAAA) format('woff'); +} +.ui.checkbox label:before, +.ui.checkbox .box:before, +.ui.checkbox label:after, +.ui.checkbox .box:after { + font-family: 'Checkbox'; +} +.ui.checkbox label:after, +.ui.checkbox .box:after { + content: '\e800'; +} +/* UTF Reference +.check:before { content: '\e800'; } '' +.circle:before { content: '\e801'; } +.ok-circled:before { content: '\e806'; } +.ok-circle:before { content: '\e805'; } +.cancel-circle:before { content: '\e807'; } +.cancel-circle-1:before { content: '\e804'; } +.empty-circle:before { content: '\e802'; } +.radio:before { content: '\e803'; } + +*/ + + +/******************************* + Site Overrides +*******************************/ + diff --git a/controller/static/semantic/dist/components/checkbox.js b/controller/static/semantic/dist/components/checkbox.js new file mode 100755 index 000000000..90a4a02dd --- /dev/null +++ b/controller/static/semantic/dist/components/checkbox.js @@ -0,0 +1,509 @@ +/*! + * # Semantic UI x.x - Checkbox + * http://github.com/semantic-org/semantic-ui/ + * + * + * Copyright 2014 Contributors + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ + +;(function ( $, window, document, undefined ) { + +"use strict"; + +$.fn.checkbox = function(parameters) { + var + $allModules = $(this), + moduleSelector = $allModules.selector || '', + + time = new Date().getTime(), + performance = [], + + query = arguments[0], + methodInvoked = (typeof query == 'string'), + queryArguments = [].slice.call(arguments, 1), + returnedValue + ; + + $allModules + .each(function() { + var + settings = $.extend(true, {}, $.fn.checkbox.settings, parameters), + + className = settings.className, + namespace = settings.namespace, + selector = settings.selector, + error = settings.error, + + eventNamespace = '.' + namespace, + moduleNamespace = 'module-' + namespace, + + $module = $(this), + $label = $(this).find(selector.label).first(), + $input = $(this).find(selector.input), + + instance = $module.data(moduleNamespace), + + observer, + element = this, + module + ; + + module = { + + initialize: function() { + module.verbose('Initializing checkbox', settings); + + module.create.label(); + module.add.events(); + + if( module.is.checked() ) { + module.set.checked(); + if(settings.fireOnInit) { + settings.onChecked.call($input.get()); + } + } + else { + module.remove.checked(); + if(settings.fireOnInit) { + settings.onUnchecked.call($input.get()); + } + } + module.observeChanges(); + + module.instantiate(); + }, + + instantiate: function() { + module.verbose('Storing instance of module', module); + instance = module; + $module + .data(moduleNamespace, module) + ; + }, + + destroy: function() { + module.verbose('Destroying module'); + module.remove.events(); + $module + .removeData(moduleNamespace) + ; + }, + + refresh: function() { + $module = $(this); + $label = $(this).find(selector.label).first(); + $input = $(this).find(selector.input); + }, + + observeChanges: function() { + if('MutationObserver' in window) { + observer = new MutationObserver(function(mutations) { + module.debug('DOM tree modified, updating selector cache'); + module.refresh(); + }); + observer.observe(element, { + childList : true, + subtree : true + }); + module.debug('Setting up mutation observer', observer); + } + }, + + attachEvents: function(selector, event) { + var + $element = $(selector) + ; + event = $.isFunction(module[event]) + ? module[event] + : module.toggle + ; + if($element.length > 0) { + module.debug('Attaching checkbox events to element', selector, event); + $element + .on('click' + eventNamespace, event) + ; + } + else { + module.error(error.notFound); + } + }, + + event: { + keydown: function(event) { + var + key = event.which, + keyCode = { + enter : 13, + space : 32, + escape : 27 + } + ; + if( key == keyCode.escape) { + module.verbose('Escape key pressed blurring field'); + $module + .blur() + ; + } + if(!event.ctrlKey && (key == keyCode.enter || key == keyCode.space)) { + module.verbose('Enter key pressed, toggling checkbox'); + module.toggle.call(this); + event.preventDefault(); + } + } + }, + + is: { + radio: function() { + return $module.hasClass(className.radio); + }, + checked: function() { + return $input.prop('checked') !== undefined && $input.prop('checked'); + }, + unchecked: function() { + return !module.is.checked(); + } + }, + + can: { + change: function() { + return !( $module.hasClass(className.disabled) || $module.hasClass(className.readOnly) || $input.prop('disabled') ); + }, + uncheck: function() { + return (typeof settings.uncheckable === 'boolean') + ? settings.uncheckable + : !module.is.radio() + ; + } + }, + + set: { + checked: function() { + $module.addClass(className.checked); + }, + tab: function() { + if( $input.attr('tabindex') === undefined) { + $input + .attr('tabindex', 0) + ; + } + } + }, + + create: { + label: function() { + if($input.prevAll(selector.label).length > 0) { + $input.prev(selector.label).detach().insertAfter($input); + module.debug('Moving existing label', $label); + } + else if( !module.has.label() ) { + $label = $('