diff --git a/bridge/bridge.go b/bridge/bridge.go deleted file mode 100755 index 41568df..0000000 --- a/bridge/bridge.go +++ /dev/null @@ -1,536 +0,0 @@ -package bridge - -import ( - "ehang.io/nps-mux" - "encoding/binary" - "errors" - "fmt" - "net" - "os" - "strconv" - "strings" - "sync" - "time" - - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/conn" - "ehang.io/nps/lib/crypt" - "ehang.io/nps/lib/file" - "ehang.io/nps/lib/version" - "ehang.io/nps/server/connection" - "ehang.io/nps/server/tool" - "github.com/astaxie/beego" - "github.com/astaxie/beego/logs" -) - -type Client struct { - tunnel *nps_mux.Mux - signal *conn.Conn - file *nps_mux.Mux - Version string - retryTime int // it will be add 1 when ping not ok until to 3 will close the client -} - -func NewClient(t, f *nps_mux.Mux, s *conn.Conn, vs string) *Client { - return &Client{ - signal: s, - tunnel: t, - file: f, - Version: vs, - } -} - -type Bridge struct { - TunnelPort int //通信隧道端口 - Client sync.Map - Register sync.Map - tunnelType string //bridge type kcp or tcp - OpenTask chan *file.Tunnel - CloseTask chan *file.Tunnel - CloseClient chan int - SecretChan chan *conn.Secret - ipVerify bool - runList sync.Map //map[int]interface{} - disconnectTime int -} - -func NewTunnel(tunnelPort int, tunnelType string, ipVerify bool, runList sync.Map, disconnectTime int) *Bridge { - return &Bridge{ - TunnelPort: tunnelPort, - tunnelType: tunnelType, - OpenTask: make(chan *file.Tunnel), - CloseTask: make(chan *file.Tunnel), - CloseClient: make(chan int), - SecretChan: make(chan *conn.Secret), - ipVerify: ipVerify, - runList: runList, - disconnectTime: disconnectTime, - } -} - -func (s *Bridge) StartTunnel() error { - go s.ping() - if s.tunnelType == "kcp" { - logs.Info("server start, the bridge type is %s, the bridge port is %d", s.tunnelType, s.TunnelPort) - return conn.NewKcpListenerAndProcess(beego.AppConfig.String("bridge_ip")+":"+beego.AppConfig.String("bridge_port"), func(c net.Conn) { - s.cliProcess(conn.NewConn(c)) - }) - } else { - listener, err := connection.GetBridgeListener(s.tunnelType) - if err != nil { - logs.Error(err) - os.Exit(0) - return err - } - conn.Accept(listener, func(c net.Conn) { - s.cliProcess(conn.NewConn(c)) - }) - } - return nil -} - -//get health information form client -func (s *Bridge) GetHealthFromClient(id int, c *conn.Conn) { - for { - if info, status, err := c.GetHealthInfo(); err != nil { - break - } else if !status { //the status is true , return target to the targetArr - file.GetDb().JsonDb.Tasks.Range(func(key, value interface{}) bool { - v := value.(*file.Tunnel) - if v.Client.Id == id && v.Mode == "tcp" && strings.Contains(v.Target.TargetStr, info) { - v.Lock() - if v.Target.TargetArr == nil || (len(v.Target.TargetArr) == 0 && len(v.HealthRemoveArr) == 0) { - v.Target.TargetArr = common.TrimArr(strings.Split(v.Target.TargetStr, "\n")) - } - v.Target.TargetArr = common.RemoveArrVal(v.Target.TargetArr, info) - if v.HealthRemoveArr == nil { - v.HealthRemoveArr = make([]string, 0) - } - v.HealthRemoveArr = append(v.HealthRemoveArr, info) - v.Unlock() - } - return true - }) - file.GetDb().JsonDb.Hosts.Range(func(key, value interface{}) bool { - v := value.(*file.Host) - if v.Client.Id == id && strings.Contains(v.Target.TargetStr, info) { - v.Lock() - if v.Target.TargetArr == nil || (len(v.Target.TargetArr) == 0 && len(v.HealthRemoveArr) == 0) { - v.Target.TargetArr = common.TrimArr(strings.Split(v.Target.TargetStr, "\n")) - } - v.Target.TargetArr = common.RemoveArrVal(v.Target.TargetArr, info) - if v.HealthRemoveArr == nil { - v.HealthRemoveArr = make([]string, 0) - } - v.HealthRemoveArr = append(v.HealthRemoveArr, info) - v.Unlock() - } - return true - }) - } else { //the status is false,remove target from the targetArr - file.GetDb().JsonDb.Tasks.Range(func(key, value interface{}) bool { - v := value.(*file.Tunnel) - if v.Client.Id == id && v.Mode == "tcp" && common.IsArrContains(v.HealthRemoveArr, info) && !common.IsArrContains(v.Target.TargetArr, info) { - v.Lock() - v.Target.TargetArr = append(v.Target.TargetArr, info) - v.HealthRemoveArr = common.RemoveArrVal(v.HealthRemoveArr, info) - v.Unlock() - } - return true - }) - - file.GetDb().JsonDb.Hosts.Range(func(key, value interface{}) bool { - v := value.(*file.Host) - if v.Client.Id == id && common.IsArrContains(v.HealthRemoveArr, info) && !common.IsArrContains(v.Target.TargetArr, info) { - v.Lock() - v.Target.TargetArr = append(v.Target.TargetArr, info) - v.HealthRemoveArr = common.RemoveArrVal(v.HealthRemoveArr, info) - v.Unlock() - } - return true - }) - } - } - s.DelClient(id) -} - -//验证失败,返回错误验证flag,并且关闭连接 -func (s *Bridge) verifyError(c *conn.Conn) { - c.Write([]byte(common.VERIFY_EER)) -} - -func (s *Bridge) verifySuccess(c *conn.Conn) { - c.Write([]byte(common.VERIFY_SUCCESS)) -} - -func (s *Bridge) cliProcess(c *conn.Conn) { - //read test flag - if _, err := c.GetShortContent(3); err != nil { - logs.Info("The client %s connect error", c.Conn.RemoteAddr(), err.Error()) - return - } - //version check - if b, err := c.GetShortLenContent(); err != nil || string(b) != version.GetVersion() { - logs.Info("The client %s version does not match", c.Conn.RemoteAddr()) - c.Close() - return - } - //version get - var vs []byte - var err error - if vs, err = c.GetShortLenContent(); err != nil { - logs.Info("get client %s version error", err.Error()) - c.Close() - return - } - //write server version to client - c.Write([]byte(crypt.Md5(version.GetVersion()))) - c.SetReadDeadlineBySecond(5) - var buf []byte - //get vKey from client - if buf, err = c.GetShortContent(32); err != nil { - c.Close() - return - } - //verify - id, err := file.GetDb().GetIdByVerifyKey(string(buf), c.Conn.RemoteAddr().String()) - if err != nil { - logs.Info("Current client connection validation error, close this client:", c.Conn.RemoteAddr()) - s.verifyError(c) - return - } else { - s.verifySuccess(c) - } - if flag, err := c.ReadFlag(); err == nil { - s.typeDeal(flag, c, id, string(vs)) - } else { - logs.Warn(err, flag) - } - return -} - -func (s *Bridge) DelClient(id int) { - if v, ok := s.Client.Load(id); ok { - if v.(*Client).signal != nil { - v.(*Client).signal.Close() - } - s.Client.Delete(id) - if file.GetDb().IsPubClient(id) { - return - } - if c, err := file.GetDb().GetClient(id); err == nil { - s.CloseClient <- c.Id - } - } -} - -//use different -func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int, vs string) { - isPub := file.GetDb().IsPubClient(id) - switch typeVal { - case common.WORK_MAIN: - if isPub { - c.Close() - return - } - tcpConn, ok := c.Conn.(*net.TCPConn) - if ok { - // add tcp keep alive option for signal connection - _ = tcpConn.SetKeepAlive(true) - _ = tcpConn.SetKeepAlivePeriod(5 * time.Second) - } - //the vKey connect by another ,close the client of before - if v, ok := s.Client.LoadOrStore(id, NewClient(nil, nil, c, vs)); ok { - if v.(*Client).signal != nil { - v.(*Client).signal.WriteClose() - } - v.(*Client).signal = c - v.(*Client).Version = vs - } - go s.GetHealthFromClient(id, c) - logs.Info("clientId %d connection succeeded, address:%s ", id, c.Conn.RemoteAddr()) - case common.WORK_CHAN: - muxConn := nps_mux.NewMux(c.Conn, s.tunnelType, s.disconnectTime) - if v, ok := s.Client.LoadOrStore(id, NewClient(muxConn, nil, nil, vs)); ok { - v.(*Client).tunnel = muxConn - } - case common.WORK_CONFIG: - client, err := file.GetDb().GetClient(id) - if err != nil || (!isPub && !client.ConfigConnAllow) { - c.Close() - return - } - binary.Write(c, binary.LittleEndian, isPub) - go s.getConfig(c, isPub, client) - case common.WORK_REGISTER: - go s.register(c) - case common.WORK_SECRET: - if b, err := c.GetShortContent(32); err == nil { - s.SecretChan <- conn.NewSecret(string(b), c) - } else { - logs.Error("secret error, failed to match the key successfully") - } - case common.WORK_FILE: - muxConn := nps_mux.NewMux(c.Conn, s.tunnelType, s.disconnectTime) - if v, ok := s.Client.LoadOrStore(id, NewClient(nil, muxConn, nil, vs)); ok { - v.(*Client).file = muxConn - } - case common.WORK_P2P: - //read md5 secret - if b, err := c.GetShortContent(32); err != nil { - logs.Error("p2p error,", err.Error()) - } else if t := file.GetDb().GetTaskByMd5Password(string(b)); t == nil { - logs.Error("p2p error, failed to match the key successfully") - } else { - if v, ok := s.Client.Load(t.Client.Id); !ok { - return - } else { - //向密钥对应的客户端发送与服务端udp建立连接信息,地址,密钥 - v.(*Client).signal.Write([]byte(common.NEW_UDP_CONN)) - svrAddr := beego.AppConfig.String("p2p_ip") + ":" + beego.AppConfig.String("p2p_port") - if err != nil { - logs.Warn("get local udp addr error") - return - } - v.(*Client).signal.WriteLenContent([]byte(svrAddr)) - v.(*Client).signal.WriteLenContent(b) - //向该请求者发送建立连接请求,服务器地址 - c.WriteLenContent([]byte(svrAddr)) - } - } - } - c.SetAlive(s.tunnelType) - return -} - -//register ip -func (s *Bridge) register(c *conn.Conn) { - var hour int32 - if err := binary.Read(c, binary.LittleEndian, &hour); err == nil { - s.Register.Store(common.GetIpByAddr(c.Conn.RemoteAddr().String()), time.Now().Add(time.Hour*time.Duration(hour))) - } -} - -func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link, t *file.Tunnel) (target net.Conn, err error) { - //if the proxy type is local - if link.LocalProxy { - target, err = net.Dial("tcp", link.Host) - return - } - if v, ok := s.Client.Load(clientId); ok { - //If ip is restricted to do ip verification - if s.ipVerify { - ip := common.GetIpByAddr(link.RemoteAddr) - if v, ok := s.Register.Load(ip); !ok { - return nil, errors.New(fmt.Sprintf("The ip %s is not in the validation list", ip)) - } else { - if !v.(time.Time).After(time.Now()) { - return nil, errors.New(fmt.Sprintf("The validity of the ip %s has expired", ip)) - } - } - } - var tunnel *nps_mux.Mux - if t != nil && t.Mode == "file" { - tunnel = v.(*Client).file - } else { - tunnel = v.(*Client).tunnel - } - if tunnel == nil { - err = errors.New("the client connect error") - return - } - if target, err = tunnel.NewConn(); err != nil { - return - } - if t != nil && t.Mode == "file" { - //TODO if t.mode is file ,not use crypt or compress - link.Crypt = false - link.Compress = false - return - } - if _, err = conn.NewConn(target).SendInfo(link, ""); err != nil { - logs.Info("new connect error ,the target %s refuse to connect", link.Host) - return - } - } else { - err = errors.New(fmt.Sprintf("the client %d is not connect", clientId)) - } - return -} - -func (s *Bridge) ping() { - ticker := time.NewTicker(time.Second * 5) - defer ticker.Stop() - for { - select { - case <-ticker.C: - arr := make([]int, 0) - s.Client.Range(func(key, value interface{}) bool { - v := value.(*Client) - if v.tunnel == nil || v.signal == nil { - v.retryTime += 1 - if v.retryTime >= 3 { - arr = append(arr, key.(int)) - } - return true - } - if v.tunnel.IsClose { - arr = append(arr, key.(int)) - } - return true - }) - for _, v := range arr { - logs.Info("the client %d closed", v) - s.DelClient(v) - } - } - } -} - -//get config and add task from client config -func (s *Bridge) getConfig(c *conn.Conn, isPub bool, client *file.Client) { - var fail bool -loop: - for { - flag, err := c.ReadFlag() - if err != nil { - break - } - switch flag { - case common.WORK_STATUS: - if b, err := c.GetShortContent(32); err != nil { - break loop - } else { - var str string - id, err := file.GetDb().GetClientIdByVkey(string(b)) - if err != nil { - break loop - } - file.GetDb().JsonDb.Hosts.Range(func(key, value interface{}) bool { - v := value.(*file.Host) - if v.Client.Id == id { - str += v.Remark + common.CONN_DATA_SEQ - } - return true - }) - file.GetDb().JsonDb.Tasks.Range(func(key, value interface{}) bool { - v := value.(*file.Tunnel) - //if _, ok := s.runList[v.Id]; ok && v.Client.Id == id { - if _, ok := s.runList.Load(v.Id); ok && v.Client.Id == id { - str += v.Remark + common.CONN_DATA_SEQ - } - return true - }) - binary.Write(c, binary.LittleEndian, int32(len([]byte(str)))) - binary.Write(c, binary.LittleEndian, []byte(str)) - } - case common.NEW_CONF: - var err error - if client, err = c.GetConfigInfo(); err != nil { - fail = true - c.WriteAddFail() - break loop - } else { - if err = file.GetDb().NewClient(client); err != nil { - fail = true - c.WriteAddFail() - break loop - } - c.WriteAddOk() - c.Write([]byte(client.VerifyKey)) - s.Client.Store(client.Id, NewClient(nil, nil, nil, "")) - } - case common.NEW_HOST: - h, err := c.GetHostInfo() - if err != nil { - fail = true - c.WriteAddFail() - break loop - } - h.Client = client - if h.Location == "" { - h.Location = "/" - } - if !client.HasHost(h) { - if file.GetDb().IsHostExist(h) { - fail = true - c.WriteAddFail() - break loop - } else { - file.GetDb().NewHost(h) - c.WriteAddOk() - } - } else { - c.WriteAddOk() - } - case common.NEW_TASK: - if t, err := c.GetTaskInfo(); err != nil { - fail = true - c.WriteAddFail() - break loop - } else { - ports := common.GetPorts(t.Ports) - targets := common.GetPorts(t.Target.TargetStr) - if len(ports) > 1 && (t.Mode == "tcp" || t.Mode == "udp") && (len(ports) != len(targets)) { - fail = true - c.WriteAddFail() - break loop - } else if t.Mode == "secret" || t.Mode == "p2p" { - ports = append(ports, 0) - } - if len(ports) == 0 { - fail = true - c.WriteAddFail() - break loop - } - for i := 0; i < len(ports); i++ { - tl := new(file.Tunnel) - tl.Mode = t.Mode - tl.Port = ports[i] - tl.ServerIp = t.ServerIp - if len(ports) == 1 { - tl.Target = t.Target - tl.Remark = t.Remark - } else { - tl.Remark = t.Remark + "_" + strconv.Itoa(tl.Port) - tl.Target = new(file.Target) - if t.TargetAddr != "" { - tl.Target.TargetStr = t.TargetAddr + ":" + strconv.Itoa(targets[i]) - } else { - tl.Target.TargetStr = strconv.Itoa(targets[i]) - } - } - tl.Id = int(file.GetDb().JsonDb.GetTaskId()) - tl.Status = true - tl.Flow = new(file.Flow) - tl.NoStore = true - tl.Client = client - tl.Password = t.Password - tl.LocalPath = t.LocalPath - tl.StripPre = t.StripPre - tl.MultiAccount = t.MultiAccount - if !client.HasTunnel(tl) { - if err := file.GetDb().NewTask(tl); err != nil { - logs.Notice("Add task error ", err.Error()) - fail = true - c.WriteAddFail() - break loop - } - if b := tool.TestServerPort(tl.Port, tl.Mode); !b && t.Mode != "secret" && t.Mode != "p2p" { - fail = true - c.WriteAddFail() - break loop - } else { - s.OpenTask <- tl - } - } - c.WriteAddOk() - } - } - } - } - if fail && client != nil { - s.DelClient(client.Id) - } - c.Close() -} diff --git a/client/client.go b/client/client.go deleted file mode 100755 index e1e02f6..0000000 --- a/client/client.go +++ /dev/null @@ -1,311 +0,0 @@ -package client - -import ( - "bufio" - "bytes" - "ehang.io/nps-mux" - "net" - "net/http" - "strconv" - "sync" - "time" - - "github.com/astaxie/beego/logs" - "github.com/xtaci/kcp-go" - - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/config" - "ehang.io/nps/lib/conn" - "ehang.io/nps/lib/crypt" -) - -type TRPClient struct { - svrAddr string - bridgeConnType string - proxyUrl string - vKey string - p2pAddr map[string]string - tunnel *nps_mux.Mux - signal *conn.Conn - ticker *time.Ticker - cnf *config.Config - disconnectTime int - once sync.Once -} - -//new client -func NewRPClient(svraddr string, vKey string, bridgeConnType string, proxyUrl string, cnf *config.Config, disconnectTime int) *TRPClient { - return &TRPClient{ - svrAddr: svraddr, - p2pAddr: make(map[string]string, 0), - vKey: vKey, - bridgeConnType: bridgeConnType, - proxyUrl: proxyUrl, - cnf: cnf, - disconnectTime: disconnectTime, - once: sync.Once{}, - } -} - -var NowStatus int -var CloseClient bool - -//start -func (s *TRPClient) Start() { - CloseClient = false -retry: - if CloseClient { - return - } - NowStatus = 0 - c, err := NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_MAIN, s.proxyUrl) - if err != nil { - logs.Error("The connection server failed and will be reconnected in five seconds, error", err.Error()) - time.Sleep(time.Second * 5) - goto retry - } - if c == nil { - logs.Error("Error data from server, and will be reconnected in five seconds") - time.Sleep(time.Second * 5) - goto retry - } - logs.Info("Successful connection with server %s", s.svrAddr) - //monitor the connection - go s.ping() - s.signal = c - //start a channel connection - go s.newChan() - //start health check if the it's open - if s.cnf != nil && len(s.cnf.Healths) > 0 { - go heathCheck(s.cnf.Healths, s.signal) - } - NowStatus = 1 - //msg connection, eg udp - s.handleMain() -} - -//handle main connection -func (s *TRPClient) handleMain() { - for { - flags, err := s.signal.ReadFlag() - if err != nil { - logs.Error("Accept server data error %s, end this service", err.Error()) - break - } - switch flags { - case common.NEW_UDP_CONN: - //read server udp addr and password - if lAddr, err := s.signal.GetShortLenContent(); err != nil { - logs.Warn(err) - return - } else if pwd, err := s.signal.GetShortLenContent(); err == nil { - var localAddr string - //The local port remains unchanged for a certain period of time - if v, ok := s.p2pAddr[crypt.Md5(string(pwd)+strconv.Itoa(int(time.Now().Unix()/100)))]; !ok { - tmpConn, err := common.GetLocalUdpAddr() - if err != nil { - logs.Error(err) - return - } - localAddr = tmpConn.LocalAddr().String() - } else { - localAddr = v - } - go s.newUdpConn(localAddr, string(lAddr), string(pwd)) - } - } - } - s.Close() -} - -func (s *TRPClient) newUdpConn(localAddr, rAddr string, md5Password string) { - var localConn net.PacketConn - var err error - var remoteAddress string - if remoteAddress, localConn, err = handleP2PUdp(localAddr, rAddr, md5Password, common.WORK_P2P_PROVIDER); err != nil { - logs.Error(err) - return - } - l, err := kcp.ServeConn(nil, 150, 3, localConn) - if err != nil { - logs.Error(err) - return - } - logs.Trace("start local p2p udp listen, local address", localConn.LocalAddr().String()) - for { - udpTunnel, err := l.AcceptKCP() - if err != nil { - logs.Error(err) - l.Close() - return - } - if udpTunnel.RemoteAddr().String() == string(remoteAddress) { - conn.SetUdpSession(udpTunnel) - logs.Trace("successful connection with client ,address %s", udpTunnel.RemoteAddr().String()) - //read link info from remote - conn.Accept(nps_mux.NewMux(udpTunnel, s.bridgeConnType, s.disconnectTime), func(c net.Conn) { - go s.handleChan(c) - }) - break - } - } -} - -//pmux tunnel -func (s *TRPClient) newChan() { - tunnel, err := NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_CHAN, s.proxyUrl) - if err != nil { - logs.Error("connect to ", s.svrAddr, "error:", err) - return - } - s.tunnel = nps_mux.NewMux(tunnel.Conn, s.bridgeConnType, s.disconnectTime) - for { - src, err := s.tunnel.Accept() - if err != nil { - logs.Warn(err) - s.Close() - break - } - go s.handleChan(src) - } -} - -func (s *TRPClient) handleChan(src net.Conn) { - lk, err := conn.NewConn(src).GetLinkInfo() - if err != nil || lk == nil { - src.Close() - logs.Error("get connection info from server error ", err) - return - } - //host for target processing - lk.Host = common.FormatAddress(lk.Host) - //if Conn type is http, read the request and log - if lk.ConnType == "http" { - if targetConn, err := net.DialTimeout(common.CONN_TCP, lk.Host, lk.Option.Timeout); err != nil { - logs.Warn("connect to %s error %s", lk.Host, err.Error()) - src.Close() - } else { - srcConn := conn.GetConn(src, lk.Crypt, lk.Compress, nil, false) - go func() { - common.CopyBuffer(srcConn, targetConn) - srcConn.Close() - targetConn.Close() - }() - for { - if r, err := http.ReadRequest(bufio.NewReader(srcConn)); err != nil { - srcConn.Close() - targetConn.Close() - break - } else { - logs.Trace("http request, method %s, host %s, url %s, remote address %s", r.Method, r.Host, r.URL.Path, r.RemoteAddr) - r.Write(targetConn) - } - } - } - return - } - if lk.ConnType == "udp5" { - logs.Trace("new %s connection with the goal of %s, remote address:%s", lk.ConnType, lk.Host, lk.RemoteAddr) - s.handleUdp(src) - } - //connect to target if conn type is tcp or udp - if targetConn, err := net.DialTimeout(lk.ConnType, lk.Host, lk.Option.Timeout); err != nil { - logs.Warn("connect to %s error %s", lk.Host, err.Error()) - src.Close() - } else { - logs.Trace("new %s connection with the goal of %s, remote address:%s", lk.ConnType, lk.Host, lk.RemoteAddr) - conn.CopyWaitGroup(src, targetConn, lk.Crypt, lk.Compress, nil, nil, false, nil) - } -} - -func (s *TRPClient) handleUdp(serverConn net.Conn) { - // bind a local udp port - local, err := net.ListenUDP("udp", nil) - defer serverConn.Close() - if err != nil { - logs.Error("bind local udp port error ", err.Error()) - return - } - defer local.Close() - go func() { - defer serverConn.Close() - b := common.BufPoolUdp.Get().([]byte) - defer common.BufPoolUdp.Put(b) - for { - n, raddr, err := local.ReadFrom(b) - if err != nil { - logs.Error("read data from remote server error", err.Error()) - } - buf := bytes.Buffer{} - dgram := common.NewUDPDatagram(common.NewUDPHeader(0, 0, common.ToSocksAddr(raddr)), b[:n]) - dgram.Write(&buf) - b, err := conn.GetLenBytes(buf.Bytes()) - if err != nil { - logs.Warn("get len bytes error", err.Error()) - continue - } - if _, err := serverConn.Write(b); err != nil { - logs.Error("write data to remote error", err.Error()) - return - } - } - }() - b := common.BufPoolUdp.Get().([]byte) - defer common.BufPoolUdp.Put(b) - for { - n, err := serverConn.Read(b) - if err != nil { - logs.Error("read udp data from server error ", err.Error()) - return - } - - udpData, err := common.ReadUDPDatagram(bytes.NewReader(b[:n])) - if err != nil { - logs.Error("unpack data error", err.Error()) - return - } - raddr, err := net.ResolveUDPAddr("udp", udpData.Header.Addr.String()) - if err != nil { - logs.Error("build remote addr err", err.Error()) - continue // drop silently - } - _, err = local.WriteTo(udpData.Data, raddr) - if err != nil { - logs.Error("write data to remote ", raddr.String(), "error", err.Error()) - return - } - } -} - -// Whether the monitor channel is closed -func (s *TRPClient) ping() { - s.ticker = time.NewTicker(time.Second * 5) -loop: - for { - select { - case <-s.ticker.C: - if s.tunnel != nil && s.tunnel.IsClose { - s.Close() - break loop - } - } - } -} - -func (s *TRPClient) Close() { - s.once.Do(s.closing) -} - -func (s *TRPClient) closing() { - CloseClient = true - NowStatus = 0 - if s.tunnel != nil { - _ = s.tunnel.Close() - } - if s.signal != nil { - _ = s.signal.Close() - } - if s.ticker != nil { - s.ticker.Stop() - } -} diff --git a/client/control.go b/client/control.go deleted file mode 100644 index 5aaff94..0000000 --- a/client/control.go +++ /dev/null @@ -1,526 +0,0 @@ -package client - -import ( - "bufio" - "encoding/base64" - "encoding/binary" - "errors" - "fmt" - "io/ioutil" - "log" - "math" - "math/rand" - "net" - "net/http" - "net/url" - "os" - "path/filepath" - "strconv" - "strings" - "time" - - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/config" - "ehang.io/nps/lib/conn" - "ehang.io/nps/lib/crypt" - "ehang.io/nps/lib/version" - "github.com/astaxie/beego/logs" - "github.com/xtaci/kcp-go" - "golang.org/x/net/proxy" -) - -func GetTaskStatus(path string) { - cnf, err := config.NewConfig(path) - if err != nil { - log.Fatalln(err) - } - c, err := NewConn(cnf.CommonConfig.Tp, cnf.CommonConfig.VKey, cnf.CommonConfig.Server, common.WORK_CONFIG, cnf.CommonConfig.ProxyUrl) - if err != nil { - log.Fatalln(err) - } - if _, err := c.Write([]byte(common.WORK_STATUS)); err != nil { - log.Fatalln(err) - } - //read now vKey and write to server - if f, err := common.ReadAllFromFile(filepath.Join(common.GetTmpPath(), "npc_vkey.txt")); err != nil { - log.Fatalln(err) - } else if _, err := c.Write([]byte(crypt.Md5(string(f)))); err != nil { - log.Fatalln(err) - } - var isPub bool - binary.Read(c, binary.LittleEndian, &isPub) - if l, err := c.GetLen(); err != nil { - log.Fatalln(err) - } else if b, err := c.GetShortContent(l); err != nil { - log.Fatalln(err) - } else { - arr := strings.Split(string(b), common.CONN_DATA_SEQ) - for _, v := range cnf.Hosts { - if common.InStrArr(arr, v.Remark) { - log.Println(v.Remark, "ok") - } else { - log.Println(v.Remark, "not running") - } - } - for _, v := range cnf.Tasks { - ports := common.GetPorts(v.Ports) - if v.Mode == "secret" { - ports = append(ports, 0) - } - for _, vv := range ports { - var remark string - if len(ports) > 1 { - remark = v.Remark + "_" + strconv.Itoa(vv) - } else { - remark = v.Remark - } - if common.InStrArr(arr, remark) { - log.Println(remark, "ok") - } else { - log.Println(remark, "not running") - } - } - } - } - os.Exit(0) -} - -var errAdd = errors.New("The server returned an error, which port or host may have been occupied or not allowed to open.") - -func StartFromFile(path string) { - first := true - cnf, err := config.NewConfig(path) - if err != nil || cnf.CommonConfig == nil { - logs.Error("Config file %s loading error %s", path, err.Error()) - os.Exit(0) - } - logs.Info("Loading configuration file %s successfully", path) - -re: - if first || cnf.CommonConfig.AutoReconnection { - if !first { - logs.Info("Reconnecting...") - time.Sleep(time.Second * 5) - } - } else { - return - } - first = false - c, err := NewConn(cnf.CommonConfig.Tp, cnf.CommonConfig.VKey, cnf.CommonConfig.Server, common.WORK_CONFIG, cnf.CommonConfig.ProxyUrl) - if err != nil { - logs.Error(err) - goto re - } - var isPub bool - binary.Read(c, binary.LittleEndian, &isPub) - - // get tmp password - var b []byte - vkey := cnf.CommonConfig.VKey - if isPub { - // send global configuration to server and get status of config setting - if _, err := c.SendInfo(cnf.CommonConfig.Client, common.NEW_CONF); err != nil { - logs.Error(err) - goto re - } - if !c.GetAddStatus() { - logs.Error("the web_user may have been occupied!") - goto re - } - - if b, err = c.GetShortContent(16); err != nil { - logs.Error(err) - goto re - } - vkey = string(b) - } - ioutil.WriteFile(filepath.Join(common.GetTmpPath(), "npc_vkey.txt"), []byte(vkey), 0600) - - //send hosts to server - for _, v := range cnf.Hosts { - if _, err := c.SendInfo(v, common.NEW_HOST); err != nil { - logs.Error(err) - goto re - } - if !c.GetAddStatus() { - logs.Error(errAdd, v.Host) - goto re - } - } - - //send task to server - for _, v := range cnf.Tasks { - if _, err := c.SendInfo(v, common.NEW_TASK); err != nil { - logs.Error(err) - goto re - } - if !c.GetAddStatus() { - logs.Error(errAdd, v.Ports, v.Remark) - goto re - } - if v.Mode == "file" { - //start local file server - go startLocalFileServer(cnf.CommonConfig, v, vkey) - } - } - - //create local server secret or p2p - for _, v := range cnf.LocalServer { - go StartLocalServer(v, cnf.CommonConfig) - } - - c.Close() - if cnf.CommonConfig.Client.WebUserName == "" || cnf.CommonConfig.Client.WebPassword == "" { - logs.Notice("web access login username:user password:%s", vkey) - } else { - logs.Notice("web access login username:%s password:%s", cnf.CommonConfig.Client.WebUserName, cnf.CommonConfig.Client.WebPassword) - } - NewRPClient(cnf.CommonConfig.Server, vkey, cnf.CommonConfig.Tp, cnf.CommonConfig.ProxyUrl, cnf, cnf.CommonConfig.DisconnectTime).Start() - CloseLocalServer() - goto re -} - -// Create a new connection with the server and verify it -func NewConn(tp string, vkey string, server string, connType string, proxyUrl string) (*conn.Conn, error) { - var err error - var connection net.Conn - var sess *kcp.UDPSession - if tp == "tcp" { - if proxyUrl != "" { - u, er := url.Parse(proxyUrl) - if er != nil { - return nil, er - } - switch u.Scheme { - case "socks5": - n, er := proxy.FromURL(u, nil) - if er != nil { - return nil, er - } - connection, err = n.Dial("tcp", server) - default: - connection, err = NewHttpProxyConn(u, server) - } - } else { - connection, err = net.Dial("tcp", server) - } - } else { - sess, err = kcp.DialWithOptions(server, nil, 10, 3) - if err == nil { - conn.SetUdpSession(sess) - connection = sess - } - } - if err != nil { - return nil, err - } - connection.SetDeadline(time.Now().Add(time.Second * 10)) - defer connection.SetDeadline(time.Time{}) - c := conn.NewConn(connection) - if _, err := c.Write([]byte(common.CONN_TEST)); err != nil { - return nil, err - } - if err := c.WriteLenContent([]byte(version.GetVersion())); err != nil { - return nil, err - } - if err := c.WriteLenContent([]byte(version.VERSION)); err != nil { - return nil, err - } - b, err := c.GetShortContent(32) - if err != nil { - logs.Error(err) - return nil, err - } - if crypt.Md5(version.GetVersion()) != string(b) { - logs.Error("The client does not match the server version. The current core version of the client is", version.GetVersion()) - return nil, err - } - if _, err := c.Write([]byte(common.Getverifyval(vkey))); err != nil { - return nil, err - } - if s, err := c.ReadFlag(); err != nil { - return nil, err - } else if s == common.VERIFY_EER { - return nil, errors.New(fmt.Sprintf("Validation key %s incorrect", vkey)) - } - if _, err := c.Write([]byte(connType)); err != nil { - return nil, err - } - c.SetAlive(tp) - - return c, nil -} - -//http proxy connection -func NewHttpProxyConn(url *url.URL, remoteAddr string) (net.Conn, error) { - req, err := http.NewRequest("CONNECT", "http://"+remoteAddr, nil) - if err != nil { - return nil, err - } - password, _ := url.User.Password() - req.Header.Set("Authorization", "Basic "+basicAuth(strings.Trim(url.User.Username(), " "), password)) - // we make a http proxy request - proxyConn, err := net.Dial("tcp", url.Host) - if err != nil { - return nil, err - } - if err := req.Write(proxyConn); err != nil { - return nil, err - } - res, err := http.ReadResponse(bufio.NewReader(proxyConn), req) - if err != nil { - return nil, err - } - _ = res.Body.Close() - if res.StatusCode != 200 { - return nil, errors.New("Proxy error " + res.Status) - } - return proxyConn, nil -} - -//get a basic auth string -func basicAuth(username, password string) string { - auth := username + ":" + password - return base64.StdEncoding.EncodeToString([]byte(auth)) -} - -func getRemoteAddressFromServer(rAddr string, localConn *net.UDPConn, md5Password, role string, add int) error { - rAddr, err := getNextAddr(rAddr, add) - if err != nil { - logs.Error(err) - return err - } - addr, err := net.ResolveUDPAddr("udp", rAddr) - if err != nil { - return err - } - if _, err := localConn.WriteTo(common.GetWriteStr(md5Password, role), addr); err != nil { - return err - } - return nil -} - -func handleP2PUdp(localAddr, rAddr, md5Password, role string) (remoteAddress string, c net.PacketConn, err error) { - localConn, err := newUdpConnByAddr(localAddr) - if err != nil { - return - } - err = getRemoteAddressFromServer(rAddr, localConn, md5Password, role, 0) - if err != nil { - logs.Error(err) - return - } - err = getRemoteAddressFromServer(rAddr, localConn, md5Password, role, 1) - if err != nil { - logs.Error(err) - return - } - err = getRemoteAddressFromServer(rAddr, localConn, md5Password, role, 2) - if err != nil { - logs.Error(err) - return - } - var remoteAddr1, remoteAddr2, remoteAddr3 string - for { - buf := make([]byte, 1024) - if n, addr, er := localConn.ReadFromUDP(buf); er != nil { - err = er - return - } else { - rAddr2, _ := getNextAddr(rAddr, 1) - rAddr3, _ := getNextAddr(rAddr, 2) - switch addr.String() { - case rAddr: - remoteAddr1 = string(buf[:n]) - case rAddr2: - remoteAddr2 = string(buf[:n]) - case rAddr3: - remoteAddr3 = string(buf[:n]) - } - } - if remoteAddr1 != "" && remoteAddr2 != "" && remoteAddr3 != "" { - break - } - } - if remoteAddress, err = sendP2PTestMsg(localConn, remoteAddr1, remoteAddr2, remoteAddr3); err != nil { - return - } - c, err = newUdpConnByAddr(localAddr) - return -} - -func sendP2PTestMsg(localConn *net.UDPConn, remoteAddr1, remoteAddr2, remoteAddr3 string) (string, error) { - logs.Trace(remoteAddr3, remoteAddr2, remoteAddr1) - defer localConn.Close() - isClose := false - defer func() { isClose = true }() - interval, err := getAddrInterval(remoteAddr1, remoteAddr2, remoteAddr3) - if err != nil { - return "", err - } - go func() { - addr, err := getNextAddr(remoteAddr3, interval) - if err != nil { - return - } - remoteUdpAddr, err := net.ResolveUDPAddr("udp", addr) - if err != nil { - return - } - logs.Trace("try send test packet to target %s", addr) - ticker := time.NewTicker(time.Millisecond * 500) - defer ticker.Stop() - for { - select { - case <-ticker.C: - if isClose { - return - } - if _, err := localConn.WriteTo([]byte(common.WORK_P2P_CONNECT), remoteUdpAddr); err != nil { - return - } - } - } - }() - if interval != 0 { - ip := common.GetIpByAddr(remoteAddr2) - go func() { - ports := getRandomPortArr(common.GetPortByAddr(remoteAddr3), common.GetPortByAddr(remoteAddr3)+interval*50) - for i := 0; i <= 50; i++ { - go func(port int) { - trueAddress := ip + ":" + strconv.Itoa(port) - logs.Trace("try send test packet to target %s", trueAddress) - remoteUdpAddr, err := net.ResolveUDPAddr("udp", trueAddress) - if err != nil { - return - } - ticker := time.NewTicker(time.Second * 2) - defer ticker.Stop() - for { - select { - case <-ticker.C: - if isClose { - return - } - if _, err := localConn.WriteTo([]byte(common.WORK_P2P_CONNECT), remoteUdpAddr); err != nil { - return - } - } - } - }(ports[i]) - time.Sleep(time.Millisecond * 10) - } - }() - - } - - buf := make([]byte, 10) - for { - localConn.SetReadDeadline(time.Now().Add(time.Second * 10)) - n, addr, err := localConn.ReadFromUDP(buf) - localConn.SetReadDeadline(time.Time{}) - if err != nil { - break - } - switch string(buf[:n]) { - case common.WORK_P2P_SUCCESS: - for i := 20; i > 0; i-- { - if _, err = localConn.WriteTo([]byte(common.WORK_P2P_END), addr); err != nil { - return "", err - } - } - return addr.String(), nil - case common.WORK_P2P_END: - logs.Trace("Remotely Address %s Reply Packet Successfully Received", addr.String()) - return addr.String(), nil - case common.WORK_P2P_CONNECT: - go func() { - for i := 20; i > 0; i-- { - logs.Trace("try send receive success packet to target %s", addr.String()) - if _, err = localConn.WriteTo([]byte(common.WORK_P2P_SUCCESS), addr); err != nil { - return - } - time.Sleep(time.Second) - } - }() - default: - continue - } - } - return "", errors.New("connect to the target failed, maybe the nat type is not support p2p") -} - -func newUdpConnByAddr(addr string) (*net.UDPConn, error) { - udpAddr, err := net.ResolveUDPAddr("udp", addr) - if err != nil { - return nil, err - } - udpConn, err := net.ListenUDP("udp", udpAddr) - if err != nil { - return nil, err - } - return udpConn, nil -} - -func getNextAddr(addr string, n int) (string, error) { - arr := strings.Split(addr, ":") - if len(arr) != 2 { - return "", errors.New(fmt.Sprintf("the format of %s incorrect", addr)) - } - if p, err := strconv.Atoi(arr[1]); err != nil { - return "", err - } else { - return arr[0] + ":" + strconv.Itoa(p+n), nil - } -} - -func getAddrInterval(addr1, addr2, addr3 string) (int, error) { - arr1 := strings.Split(addr1, ":") - if len(arr1) != 2 { - return 0, errors.New(fmt.Sprintf("the format of %s incorrect", addr1)) - } - arr2 := strings.Split(addr2, ":") - if len(arr2) != 2 { - return 0, errors.New(fmt.Sprintf("the format of %s incorrect", addr2)) - } - arr3 := strings.Split(addr3, ":") - if len(arr3) != 2 { - return 0, errors.New(fmt.Sprintf("the format of %s incorrect", addr3)) - } - p1, err := strconv.Atoi(arr1[1]) - if err != nil { - return 0, err - } - p2, err := strconv.Atoi(arr2[1]) - if err != nil { - return 0, err - } - p3, err := strconv.Atoi(arr3[1]) - if err != nil { - return 0, err - } - interVal := int(math.Floor(math.Min(math.Abs(float64(p3-p2)), math.Abs(float64(p2-p1))))) - if p3-p1 < 0 { - return -interVal, nil - } - return interVal, nil -} - -func getRandomPortArr(min, max int) []int { - if min > max { - min, max = max, min - } - addrAddr := make([]int, max-min+1) - for i := min; i <= max; i++ { - addrAddr[max-i] = i - } - rand.Seed(time.Now().UnixNano()) - var r, temp int - for i := max - min; i > 0; i-- { - r = rand.Int() % i - temp = addrAddr[i] - addrAddr[i] = addrAddr[r] - addrAddr[r] = temp - } - return addrAddr -} diff --git a/client/health.go b/client/health.go deleted file mode 100644 index 2726c5b..0000000 --- a/client/health.go +++ /dev/null @@ -1,102 +0,0 @@ -package client - -import ( - "container/heap" - "net" - "net/http" - "strings" - "time" - - "ehang.io/nps/lib/conn" - "ehang.io/nps/lib/file" - "ehang.io/nps/lib/sheap" - "github.com/astaxie/beego/logs" - "github.com/pkg/errors" -) - -var isStart bool -var serverConn *conn.Conn - -func heathCheck(healths []*file.Health, c *conn.Conn) bool { - serverConn = c - if isStart { - for _, v := range healths { - v.HealthMap = make(map[string]int) - } - return true - } - isStart = true - h := &sheap.IntHeap{} - for _, v := range healths { - if v.HealthMaxFail > 0 && v.HealthCheckTimeout > 0 && v.HealthCheckInterval > 0 { - v.HealthNextTime = time.Now().Add(time.Duration(v.HealthCheckInterval) * time.Second) - heap.Push(h, v.HealthNextTime.Unix()) - v.HealthMap = make(map[string]int) - } - } - go session(healths, h) - return true -} - -func session(healths []*file.Health, h *sheap.IntHeap) { - for { - if h.Len() == 0 { - logs.Error("health check error") - break - } - rs := heap.Pop(h).(int64) - time.Now().Unix() - if rs <= 0 { - continue - } - timer := time.NewTimer(time.Duration(rs) * time.Second) - select { - case <-timer.C: - for _, v := range healths { - if v.HealthNextTime.Before(time.Now()) { - v.HealthNextTime = time.Now().Add(time.Duration(v.HealthCheckInterval) * time.Second) - //check - go check(v) - //reset time - heap.Push(h, v.HealthNextTime.Unix()) - } - } - } - } -} - -// work when just one port and many target -func check(t *file.Health) { - arr := strings.Split(t.HealthCheckTarget, ",") - var err error - var rs *http.Response - for _, v := range arr { - if t.HealthCheckType == "tcp" { - var c net.Conn - c, err = net.DialTimeout("tcp", v, time.Duration(t.HealthCheckTimeout)*time.Second) - if err == nil { - c.Close() - } - } else { - client := &http.Client{} - client.Timeout = time.Duration(t.HealthCheckTimeout) * time.Second - rs, err = client.Get("http://" + v + t.HttpHealthUrl) - if err == nil && rs.StatusCode != 200 { - err = errors.New("status code is not match") - } - } - t.Lock() - if err != nil { - t.HealthMap[v] += 1 - } else if t.HealthMap[v] >= t.HealthMaxFail { - //send recovery add - serverConn.SendHealthInfo(v, "1") - t.HealthMap[v] = 0 - } - - if t.HealthMap[v] > 0 && t.HealthMap[v]%t.HealthMaxFail == 0 { - //send fail remove - serverConn.SendHealthInfo(v, "0") - } - t.Unlock() - } -} diff --git a/client/local.go b/client/local.go deleted file mode 100644 index 21b5bd4..0000000 --- a/client/local.go +++ /dev/null @@ -1,219 +0,0 @@ -package client - -import ( - "ehang.io/nps-mux" - "errors" - "net" - "net/http" - "runtime" - "sync" - "time" - - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/config" - "ehang.io/nps/lib/conn" - "ehang.io/nps/lib/crypt" - "ehang.io/nps/lib/file" - "ehang.io/nps/server/proxy" - "github.com/astaxie/beego/logs" - "github.com/xtaci/kcp-go" -) - -var ( - LocalServer []*net.TCPListener - udpConn net.Conn - muxSession *nps_mux.Mux - fileServer []*http.Server - p2pNetBridge *p2pBridge - lock sync.RWMutex - udpConnStatus bool -) - -type p2pBridge struct { -} - -func (p2pBridge *p2pBridge) SendLinkInfo(clientId int, link *conn.Link, t *file.Tunnel) (target net.Conn, err error) { - for i := 0; muxSession == nil; i++ { - if i >= 20 { - err = errors.New("p2pBridge:too many times to get muxSession") - logs.Error(err) - return - } - runtime.Gosched() // waiting for another goroutine establish the mux connection - } - nowConn, err := muxSession.NewConn() - if err != nil { - udpConn = nil - return nil, err - } - if _, err := conn.NewConn(nowConn).SendInfo(link, ""); err != nil { - udpConnStatus = false - return nil, err - } - return nowConn, nil -} - -func CloseLocalServer() { - for _, v := range LocalServer { - v.Close() - } - for _, v := range fileServer { - v.Close() - } -} - -func startLocalFileServer(config *config.CommonConfig, t *file.Tunnel, vkey string) { - remoteConn, err := NewConn(config.Tp, vkey, config.Server, common.WORK_FILE, config.ProxyUrl) - if err != nil { - logs.Error("Local connection server failed ", err.Error()) - return - } - srv := &http.Server{ - Handler: http.StripPrefix(t.StripPre, http.FileServer(http.Dir(t.LocalPath))), - } - logs.Info("start local file system, local path %s, strip prefix %s ,remote port %s ", t.LocalPath, t.StripPre, t.Ports) - fileServer = append(fileServer, srv) - listener := nps_mux.NewMux(remoteConn.Conn, common.CONN_TCP, config.DisconnectTime) - logs.Error(srv.Serve(listener)) -} - -func StartLocalServer(l *config.LocalServer, config *config.CommonConfig) error { - if l.Type != "secret" { - go handleUdpMonitor(config, l) - } - task := &file.Tunnel{ - Port: l.Port, - ServerIp: "0.0.0.0", - Status: true, - Client: &file.Client{ - Cnf: &file.Config{ - U: "", - P: "", - Compress: config.Client.Cnf.Compress, - }, - Status: true, - RateLimit: 0, - Flow: &file.Flow{}, - }, - Flow: &file.Flow{}, - Target: &file.Target{}, - } - switch l.Type { - case "p2ps": - logs.Info("successful start-up of local socks5 monitoring, port", l.Port) - return proxy.NewSock5ModeServer(p2pNetBridge, task).Start() - case "p2pt": - logs.Info("successful start-up of local tcp trans monitoring, port", l.Port) - return proxy.NewTunnelModeServer(proxy.HandleTrans, p2pNetBridge, task).Start() - case "p2p", "secret": - listener, err := net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), l.Port, ""}) - if err != nil { - logs.Error("local listener startup failed port %d, error %s", l.Port, err.Error()) - return err - } - LocalServer = append(LocalServer, listener) - logs.Info("successful start-up of local tcp monitoring, port", l.Port) - conn.Accept(listener, func(c net.Conn) { - logs.Trace("new %s connection", l.Type) - if l.Type == "secret" { - handleSecret(c, config, l) - } else if l.Type == "p2p" { - handleP2PVisitor(c, config, l) - } - }) - } - return nil -} - -func handleUdpMonitor(config *config.CommonConfig, l *config.LocalServer) { - ticker := time.NewTicker(time.Second * 1) - defer ticker.Stop() - for { - select { - case <-ticker.C: - if !udpConnStatus { - udpConn = nil - tmpConn, err := common.GetLocalUdpAddr() - if err != nil { - logs.Error(err) - return - } - for i := 0; i < 10; i++ { - logs.Notice("try to connect to the server", i+1) - newUdpConn(tmpConn.LocalAddr().String(), config, l) - if udpConn != nil { - udpConnStatus = true - break - } - } - } - } - } -} - -func handleSecret(localTcpConn net.Conn, config *config.CommonConfig, l *config.LocalServer) { - remoteConn, err := NewConn(config.Tp, config.VKey, config.Server, common.WORK_SECRET, config.ProxyUrl) - if err != nil { - logs.Error("Local connection server failed ", err.Error()) - return - } - if _, err := remoteConn.Write([]byte(crypt.Md5(l.Password))); err != nil { - logs.Error("Local connection server failed ", err.Error()) - return - } - conn.CopyWaitGroup(remoteConn.Conn, localTcpConn, false, false, nil, nil, false, nil) -} - -func handleP2PVisitor(localTcpConn net.Conn, config *config.CommonConfig, l *config.LocalServer) { - if udpConn == nil { - logs.Notice("new conn, P2P can not penetrate successfully, traffic will be transferred through the server") - handleSecret(localTcpConn, config, l) - return - } - logs.Trace("start trying to connect with the server") - //TODO just support compress now because there is not tls file in client packages - link := conn.NewLink(common.CONN_TCP, l.Target, false, config.Client.Cnf.Compress, localTcpConn.LocalAddr().String(), false) - if target, err := p2pNetBridge.SendLinkInfo(0, link, nil); err != nil { - logs.Error(err) - udpConnStatus = false - return - } else { - conn.CopyWaitGroup(target, localTcpConn, false, config.Client.Cnf.Compress, nil, nil, false, nil) - } -} - -func newUdpConn(localAddr string, config *config.CommonConfig, l *config.LocalServer) { - lock.Lock() - defer lock.Unlock() - remoteConn, err := NewConn(config.Tp, config.VKey, config.Server, common.WORK_P2P, config.ProxyUrl) - if err != nil { - logs.Error("Local connection server failed ", err.Error()) - return - } - if _, err := remoteConn.Write([]byte(crypt.Md5(l.Password))); err != nil { - logs.Error("Local connection server failed ", err.Error()) - return - } - var rAddr []byte - //读取服务端地址、密钥 继续做处理 - if rAddr, err = remoteConn.GetShortLenContent(); err != nil { - logs.Error(err) - return - } - var localConn net.PacketConn - var remoteAddress string - if remoteAddress, localConn, err = handleP2PUdp(localAddr, string(rAddr), crypt.Md5(l.Password), common.WORK_P2P_VISITOR); err != nil { - logs.Error(err) - return - } - udpTunnel, err := kcp.NewConn(remoteAddress, nil, 150, 3, localConn) - if err != nil || udpTunnel == nil { - logs.Warn(err) - return - } - logs.Trace("successful create a connection with server", remoteAddress) - conn.SetUdpSession(udpTunnel) - udpConn = udpTunnel - muxSession = nps_mux.NewMux(udpConn, "kcp", config.DisconnectTime) - p2pNetBridge = &p2pBridge{} -} diff --git a/client/register.go b/client/register.go deleted file mode 100644 index dda4445..0000000 --- a/client/register.go +++ /dev/null @@ -1,21 +0,0 @@ -package client - -import ( - "encoding/binary" - "log" - "os" - - "ehang.io/nps/lib/common" -) - -func RegisterLocalIp(server string, vKey string, tp string, proxyUrl string, hour int) { - c, err := NewConn(tp, vKey, server, common.WORK_REGISTER, proxyUrl) - if err != nil { - log.Fatalln(err) - } - if err := binary.Write(c, binary.LittleEndian, int32(hour)); err != nil { - log.Fatalln(err) - } - log.Printf("Successful ip registration for local public network, the validity period is %d hours.", hour) - os.Exit(0) -} diff --git a/cmd/npc/npc.go b/cmd/npc/npc.go deleted file mode 100644 index 05a4e92..0000000 --- a/cmd/npc/npc.go +++ /dev/null @@ -1,246 +0,0 @@ -package main - -import ( - "ehang.io/nps/client" - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/config" - "ehang.io/nps/lib/file" - "ehang.io/nps/lib/install" - "ehang.io/nps/lib/version" - "flag" - "fmt" - "github.com/astaxie/beego/logs" - "github.com/ccding/go-stun/stun" - "github.com/kardianos/service" - "os" - "os/exec" - "runtime" - "strings" - "sync" - "time" -) - -var ( - serverAddr = flag.String("server", "", "Server addr (ip:port)") - configPath = flag.String("config", "", "Configuration file path") - verifyKey = flag.String("vkey", "", "Authentication key") - logType = flag.String("log", "stdout", "Log output mode(stdout|file)") - connType = flag.String("type", "tcp", "Connection type with the server(kcp|tcp)") - proxyUrl = flag.String("proxy", "", "proxy socks5 url(eg:socks5://111:222@127.0.0.1:9007)") - logLevel = flag.String("log_level", "7", "log level 0~7") - registerTime = flag.Int("time", 2, "register time long /h") - localPort = flag.Int("local_port", 2000, "p2p local port") - password = flag.String("password", "", "p2p password flag") - target = flag.String("target", "", "p2p target") - localType = flag.String("local_type", "p2p", "p2p target") - logPath = flag.String("log_path", "", "npc log path") - debug = flag.Bool("debug", true, "npc debug") - pprofAddr = flag.String("pprof", "", "PProf debug addr (ip:port)") - stunAddr = flag.String("stun_addr", "stun.stunprotocol.org:3478", "stun server address (eg:stun.stunprotocol.org:3478)") - ver = flag.Bool("version", false, "show current version") - disconnectTime = flag.Int("disconnect_timeout", 60, "not receiving check packet times, until timeout will disconnect the client") -) - -func main() { - flag.Parse() - logs.Reset() - logs.EnableFuncCallDepth(true) - logs.SetLogFuncCallDepth(3) - if *ver { - common.PrintVersion() - return - } - if *logPath == "" { - *logPath = common.GetNpcLogPath() - } - if common.IsWindows() { - *logPath = strings.Replace(*logPath, "\\", "\\\\", -1) - } - if *debug { - logs.SetLogger(logs.AdapterConsole, `{"level":`+*logLevel+`,"color":true}`) - } else { - logs.SetLogger(logs.AdapterFile, `{"level":`+*logLevel+`,"filename":"`+*logPath+`","daily":false,"maxlines":100000,"color":true}`) - } - - // init service - options := make(service.KeyValue) - svcConfig := &service.Config{ - Name: "Npc", - DisplayName: "nps内网穿透客户端", - Description: "一款轻量级、功能强大的内网穿透代理服务器。支持tcp、udp流量转发,支持内网http代理、内网socks5代理,同时支持snappy压缩、站点保护、加密传输、多路复用、header修改等。支持web图形化管理,集成多用户模式。", - Option: options, - } - if !common.IsWindows() { - svcConfig.Dependencies = []string{ - "Requires=network.target", - "After=network-online.target syslog.target"} - svcConfig.Option["SystemdScript"] = install.SystemdScript - svcConfig.Option["SysvScript"] = install.SysvScript - } - for _, v := range os.Args[1:] { - switch v { - case "install", "start", "stop", "uninstall", "restart": - continue - } - if !strings.Contains(v, "-service=") && !strings.Contains(v, "-debug=") { - svcConfig.Arguments = append(svcConfig.Arguments, v) - } - } - svcConfig.Arguments = append(svcConfig.Arguments, "-debug=false") - prg := &npc{ - exit: make(chan struct{}), - } - s, err := service.New(prg, svcConfig) - if err != nil { - logs.Error(err, "service function disabled") - run() - // run without service - wg := sync.WaitGroup{} - wg.Add(1) - wg.Wait() - return - } - if len(os.Args) >= 2 { - switch os.Args[1] { - case "status": - if len(os.Args) > 2 { - path := strings.Replace(os.Args[2], "-config=", "", -1) - client.GetTaskStatus(path) - } - case "register": - flag.CommandLine.Parse(os.Args[2:]) - client.RegisterLocalIp(*serverAddr, *verifyKey, *connType, *proxyUrl, *registerTime) - case "update": - install.UpdateNpc() - return - case "nat": - c := stun.NewClient() - c.SetServerAddr(*stunAddr) - nat, host, err := c.Discover() - if err != nil || host == nil { - logs.Error("get nat type error", err) - return - } - fmt.Printf("nat type: %s \npublic address: %s\n", nat.String(), host.String()) - os.Exit(0) - case "start", "stop", "restart": - // support busyBox and sysV, for openWrt - if service.Platform() == "unix-systemv" { - logs.Info("unix-systemv service") - cmd := exec.Command("/etc/init.d/"+svcConfig.Name, os.Args[1]) - err := cmd.Run() - if err != nil { - logs.Error(err) - } - return - } - err := service.Control(s, os.Args[1]) - if err != nil { - logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error()) - } - return - case "install": - service.Control(s, "stop") - service.Control(s, "uninstall") - install.InstallNpc() - err := service.Control(s, os.Args[1]) - if err != nil { - logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error()) - } - if service.Platform() == "unix-systemv" { - logs.Info("unix-systemv service") - confPath := "/etc/init.d/" + svcConfig.Name - os.Symlink(confPath, "/etc/rc.d/S90"+svcConfig.Name) - os.Symlink(confPath, "/etc/rc.d/K02"+svcConfig.Name) - } - return - case "uninstall": - err := service.Control(s, os.Args[1]) - if err != nil { - logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error()) - } - if service.Platform() == "unix-systemv" { - logs.Info("unix-systemv service") - os.Remove("/etc/rc.d/S90" + svcConfig.Name) - os.Remove("/etc/rc.d/K02" + svcConfig.Name) - } - return - } - } - s.Run() -} - -type npc struct { - exit chan struct{} -} - -func (p *npc) Start(s service.Service) error { - go p.run() - return nil -} -func (p *npc) Stop(s service.Service) error { - close(p.exit) - if service.Interactive() { - os.Exit(0) - } - return nil -} - -func (p *npc) run() error { - defer func() { - if err := recover(); err != nil { - const size = 64 << 10 - buf := make([]byte, size) - buf = buf[:runtime.Stack(buf, false)] - logs.Warning("npc: panic serving %v: %v\n%s", err, string(buf)) - } - }() - run() - select { - case <-p.exit: - logs.Warning("stop...") - } - return nil -} - -func run() { - common.InitPProfFromArg(*pprofAddr) - //p2p or secret command - if *password != "" { - commonConfig := new(config.CommonConfig) - commonConfig.Server = *serverAddr - commonConfig.VKey = *verifyKey - commonConfig.Tp = *connType - localServer := new(config.LocalServer) - localServer.Type = *localType - localServer.Password = *password - localServer.Target = *target - localServer.Port = *localPort - commonConfig.Client = new(file.Client) - commonConfig.Client.Cnf = new(file.Config) - go client.StartLocalServer(localServer, commonConfig) - return - } - env := common.GetEnvMap() - if *serverAddr == "" { - *serverAddr, _ = env["NPC_SERVER_ADDR"] - } - if *verifyKey == "" { - *verifyKey, _ = env["NPC_SERVER_VKEY"] - } - logs.Info("the version of client is %s, the core version of client is %s", version.VERSION, version.GetVersion()) - if *verifyKey != "" && *serverAddr != "" && *configPath == "" { - go func() { - for { - client.NewRPClient(*serverAddr, *verifyKey, *connType, *proxyUrl, nil, *disconnectTime).Start() - logs.Info("Client closed! It will be reconnected in five seconds") - time.Sleep(time.Second * 5) - } - }() - } else { - if *configPath == "" { - *configPath = common.GetConfigPath() - } - go client.StartFromFile(*configPath) - } -} diff --git a/cmd/npc/sdk.go b/cmd/npc/sdk.go deleted file mode 100644 index 51f60d0..0000000 --- a/cmd/npc/sdk.go +++ /dev/null @@ -1,48 +0,0 @@ -package main - -import ( - "C" - "ehang.io/nps/client" - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/version" - "github.com/astaxie/beego/logs" -) - -var cl *client.TRPClient - -//export StartClientByVerifyKey -func StartClientByVerifyKey(serverAddr, verifyKey, connType, proxyUrl *C.char) int { - _ = logs.SetLogger("store") - if cl != nil { - cl.Close() - } - cl = client.NewRPClient(C.GoString(serverAddr), C.GoString(verifyKey), C.GoString(connType), C.GoString(proxyUrl), nil, 60) - cl.Start() - return 1 -} - -//export GetClientStatus -func GetClientStatus() int { - return client.NowStatus -} - -//export CloseClient -func CloseClient() { - if cl != nil { - cl.Close() - } -} - -//export Version -func Version() *C.char { - return C.CString(version.VERSION) -} - -//export Logs -func Logs() *C.char { - return C.CString(common.GetLogMsg()) -} - -func main() { - // Need a main function to make CGO compile package as C shared library -} diff --git a/cmd/nps/nps.go b/cmd/nps/nps.go deleted file mode 100644 index 334a4ed..0000000 --- a/cmd/nps/nps.go +++ /dev/null @@ -1,213 +0,0 @@ -package main - -import ( - "flag" - "log" - "os" - "os/exec" - "path/filepath" - "runtime" - "strings" - "sync" - - "ehang.io/nps/lib/file" - "ehang.io/nps/lib/install" - "ehang.io/nps/lib/version" - "ehang.io/nps/server" - "ehang.io/nps/server/connection" - "ehang.io/nps/server/tool" - "ehang.io/nps/web/routers" - - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/crypt" - "ehang.io/nps/lib/daemon" - "github.com/astaxie/beego" - "github.com/astaxie/beego/logs" - - "github.com/kardianos/service" -) - -var ( - level string - ver = flag.Bool("version", false, "show current version") -) - -func main() { - flag.Parse() - // init log - if *ver { - common.PrintVersion() - return - } - if err := beego.LoadAppConfig("ini", filepath.Join(common.GetRunPath(), "conf", "nps.conf")); err != nil { - log.Fatalln("load config file error", err.Error()) - } - common.InitPProfFromFile() - if level = beego.AppConfig.String("log_level"); level == "" { - level = "7" - } - logs.Reset() - logs.EnableFuncCallDepth(true) - logs.SetLogFuncCallDepth(3) - logPath := beego.AppConfig.String("log_path") - if logPath == "" { - logPath = common.GetLogPath() - } - if common.IsWindows() { - logPath = strings.Replace(logPath, "\\", "\\\\", -1) - } - // init service - options := make(service.KeyValue) - svcConfig := &service.Config{ - Name: "Nps", - DisplayName: "nps内网穿透代理服务器", - Description: "一款轻量级、功能强大的内网穿透代理服务器。支持tcp、udp流量转发,支持内网http代理、内网socks5代理,同时支持snappy压缩、站点保护、加密传输、多路复用、header修改等。支持web图形化管理,集成多用户模式。", - Option: options, - } - svcConfig.Arguments = append(svcConfig.Arguments, "service") - if len(os.Args) > 1 && os.Args[1] == "service" { - _ = logs.SetLogger(logs.AdapterFile, `{"level":`+level+`,"filename":"`+logPath+`","daily":false,"maxlines":100000,"color":true}`) - } else { - _ = logs.SetLogger(logs.AdapterConsole, `{"level":`+level+`,"color":true}`) - } - if !common.IsWindows() { - svcConfig.Dependencies = []string{ - "Requires=network.target", - "After=network-online.target syslog.target"} - svcConfig.Option["SystemdScript"] = install.SystemdScript - svcConfig.Option["SysvScript"] = install.SysvScript - } - prg := &nps{} - prg.exit = make(chan struct{}) - s, err := service.New(prg, svcConfig) - if err != nil { - logs.Error(err, "service function disabled") - run() - // run without service - wg := sync.WaitGroup{} - wg.Add(1) - wg.Wait() - return - } - if len(os.Args) > 1 && os.Args[1] != "service" { - switch os.Args[1] { - case "reload": - daemon.InitDaemon("nps", common.GetRunPath(), common.GetTmpPath()) - return - case "install": - // uninstall before - _ = service.Control(s, "stop") - _ = service.Control(s, "uninstall") - - binPath := install.InstallNps() - svcConfig.Executable = binPath - s, err := service.New(prg, svcConfig) - if err != nil { - logs.Error(err) - return - } - err = service.Control(s, os.Args[1]) - if err != nil { - logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error()) - } - if service.Platform() == "unix-systemv" { - logs.Info("unix-systemv service") - confPath := "/etc/init.d/" + svcConfig.Name - os.Symlink(confPath, "/etc/rc.d/S90"+svcConfig.Name) - os.Symlink(confPath, "/etc/rc.d/K02"+svcConfig.Name) - } - return - case "start", "restart", "stop": - if service.Platform() == "unix-systemv" { - logs.Info("unix-systemv service") - cmd := exec.Command("/etc/init.d/"+svcConfig.Name, os.Args[1]) - err := cmd.Run() - if err != nil { - logs.Error(err) - } - return - } - err := service.Control(s, os.Args[1]) - if err != nil { - logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error()) - } - return - case "uninstall": - err := service.Control(s, os.Args[1]) - if err != nil { - logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error()) - } - if service.Platform() == "unix-systemv" { - logs.Info("unix-systemv service") - os.Remove("/etc/rc.d/S90" + svcConfig.Name) - os.Remove("/etc/rc.d/K02" + svcConfig.Name) - } - return - case "update": - install.UpdateNps() - return - default: - logs.Error("command is not support") - return - } - } - _ = s.Run() -} - -type nps struct { - exit chan struct{} -} - -func (p *nps) Start(s service.Service) error { - _, _ = s.Status() - go p.run() - return nil -} -func (p *nps) Stop(s service.Service) error { - _, _ = s.Status() - close(p.exit) - if service.Interactive() { - os.Exit(0) - } - return nil -} - -func (p *nps) run() error { - defer func() { - if err := recover(); err != nil { - const size = 64 << 10 - buf := make([]byte, size) - buf = buf[:runtime.Stack(buf, false)] - logs.Warning("nps: panic serving %v: %v\n%s", err, string(buf)) - } - }() - run() - select { - case <-p.exit: - logs.Warning("stop...") - } - return nil -} - -func run() { - routers.Init() - task := &file.Tunnel{ - Mode: "webServer", - } - bridgePort, err := beego.AppConfig.Int("bridge_port") - if err != nil { - logs.Error("Getting bridge_port error", err) - os.Exit(0) - } - logs.Info("the version of server is %s ,allow client core version to be %s", version.VERSION, version.GetVersion()) - connection.InitConnectionService() - //crypt.InitTls(filepath.Join(common.GetRunPath(), "conf", "server.pem"), filepath.Join(common.GetRunPath(), "conf", "server.key")) - crypt.InitTls() - tool.InitAllowPort() - tool.StartSystemInfo() - timeout, err := beego.AppConfig.Int("disconnect_timeout") - if err != nil { - timeout = 60 - } - go server.StartNewServer(bridgePort, task, beego.AppConfig.String("bridge_type"), timeout) -} diff --git a/conf/clients.json b/conf/clients.json deleted file mode 100644 index e69de29..0000000 diff --git a/conf/hosts.json b/conf/hosts.json deleted file mode 100644 index e69de29..0000000 diff --git a/conf/multi_account.conf b/conf/multi_account.conf deleted file mode 100644 index e3cd792..0000000 --- a/conf/multi_account.conf +++ /dev/null @@ -1,2 +0,0 @@ -# key -> user | value -> pwd -npc=npc.pwd \ No newline at end of file diff --git a/conf/npc.conf b/conf/npc.conf deleted file mode 100644 index 86b1479..0000000 --- a/conf/npc.conf +++ /dev/null @@ -1,78 +0,0 @@ -[common] -server_addr=127.0.0.1:8024 -conn_type=tcp -vkey=123 -auto_reconnection=true -max_conn=1000 -flow_limit=1000 -rate_limit=1000 -basic_username=11 -basic_password=3 -web_username=user -web_password=1234 -crypt=true -compress=true -#pprof_addr=0.0.0.0:9999 -disconnect_timeout=60 - -[health_check_test1] -health_check_timeout=1 -health_check_max_failed=3 -health_check_interval=1 -health_http_url=/ -health_check_type=http -health_check_target=127.0.0.1:8083,127.0.0.1:8082 - -[health_check_test2] -health_check_timeout=1 -health_check_max_failed=3 -health_check_interval=1 -health_check_type=tcp -health_check_target=127.0.0.1:8083,127.0.0.1:8082 - -[web] -host=c.o.com -target_addr=127.0.0.1:8083,127.0.0.1:8082 - -[tcp] -mode=tcp -target_addr=127.0.0.1:8080 -server_port=10000 - -[socks5] -mode=socks5 -server_port=19009 -multi_account=multi_account.conf - -[file] -mode=file -server_port=19008 -local_path=/Users/liuhe/Downloads -strip_pre=/web/ - -[http] -mode=httpProxy -server_port=19004 - -[udp] -mode=udp -server_port=12253 -target_addr=114.114.114.114:53 - -[ssh_secret] -mode=secret -password=ssh2 -target_addr=123.206.77.88:22 - -[ssh_p2p] -mode=p2p -password=ssh3 - -[secret_ssh] -local_port=2001 -password=ssh2 - -[p2p_ssh] -local_port=2002 -password=ssh3 -target_addr=123.206.77.88:22 \ No newline at end of file diff --git a/conf/nps.conf b/conf/nps.conf deleted file mode 100755 index 2b5cf31..0000000 --- a/conf/nps.conf +++ /dev/null @@ -1,85 +0,0 @@ -appname = nps -#Boot mode(dev|pro) -runmode = dev - -#HTTP(S) proxy port, no startup if empty -http_proxy_ip=0.0.0.0 -http_proxy_port=80 -https_proxy_port=443 -https_just_proxy=true -#default https certificate setting -https_default_cert_file=conf/server.pem -https_default_key_file=conf/server.key - -##bridge -bridge_type=tcp -bridge_port=8024 -bridge_ip=0.0.0.0 - -# Public password, which clients can use to connect to the server -# After the connection, the server will be able to open relevant ports and parse related domain names according to its own configuration file. -public_vkey=123 - -#Traffic data persistence interval(minute) -#Ignorance means no persistence -#flow_store_interval=1 - -# log level LevelEmergency->0 LevelAlert->1 LevelCritical->2 LevelError->3 LevelWarning->4 LevelNotice->5 LevelInformational->6 LevelDebug->7 -log_level=7 -#log_path=nps.log - -#Whether to restrict IP access, true or false or ignore -#ip_limit=true - -#p2p -#p2p_ip=127.0.0.1 -#p2p_port=6000 - -#web -web_host=a.o.com -web_username=admin -web_password=123 -web_port = 8080 -web_ip=0.0.0.0 -web_base_url= -web_open_ssl=false -web_cert_file=conf/server.pem -web_key_file=conf/server.key -# if web under proxy use sub path. like http://host/nps need this. -#web_base_url=/nps - -#Web API unauthenticated IP address(the len of auth_crypt_key must be 16) -#Remove comments if needed -#auth_key=test -auth_crypt_key =1234567812345678 - -#allow_ports=9001-9009,10001,11000-12000 - -#Web management multi-user login -allow_user_login=false -allow_user_register=false -allow_user_change_username=false - - -#extension -allow_flow_limit=false -allow_rate_limit=false -allow_tunnel_num_limit=false -allow_local_proxy=false -allow_connection_num_limit=false -allow_multi_ip=false -system_info_display=false - -#cache -http_cache=false -http_cache_length=100 - -#get origin ip -http_add_origin_header=false - -#pprof debug options -#pprof_ip=0.0.0.0 -#pprof_port=9999 - -#client disconnect timeout -disconnect_timeout=60 diff --git a/conf/server.key b/conf/server.key deleted file mode 100644 index 570054a..0000000 --- a/conf/server.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEA2MVLOHvgU8FCp6LgQrPfaWcGygrsRk7TL9hbT8MxbCRUSLV7 -Lbt3q5Knz8eTN4NWmwE6L5glOcH2x3Hnn+hPjbvgq35XBBIccAm0cYYKqoKkikeK -FZM0Gp/WhSrhJ4laTyQqyleIFKpwD9kHDiC/sxjGDhSFmHKhhAnsQIRm2tppFXX0 -aAMqJEm88jzk1BN2QtKjEAn1u8v1+QW1KP3WuzdXH4L7hhMll66/KIm6Hfs2FRHQ -pRUWqZeJY4q79NW5p5f+siGwOsGpxb/p11pM+0xnCH3UIFbm3zCTzP4sLvkfFGAe -yAHsAwmaP8dJxh40ej3NN8uNiNvt8nw2Vb/1LwIDAQABAoIBAD40x/RKoEKIyE8B -D6g0pB1EQo+CePFoN3SYewO1uR4WgtVmtxWVoa7r5BpdZGLe3uCWhpMX7z7W6bGs -f1LFQOckjkHIfMIfTGfecRjO5Yqu+Pbxtq+gUah+S/plJr3IzdC+SUVNvzBnBMeX -eU3Vmg2UQ2nQ+9GWu8D/c/vDwxx0X8oQ2G8QaxX0tUurlSMNA3M7xySwEvhx54fO -UrDF3Q4yF48eA4butxVLFWf3cnlY+nR8uYd2vKfmp689/8C6kkfoM9igB78e93sm -uDM2eRLm4kU5WLl301T42n6AF7w8J0MhLLVOIeLs4l5gZPa3uKvYFmuHQao7e/5R -U/jHKrECgYEA8alPXuxFSVOvdhIsSN//Frj9CdExVdYmaLkt/2LO4FMnOaWh1xh7 -5iCY1bJT8D9dhfbqRg3qW2oguZD8gu04R8fTRegQ89qmAIwsEYqVf9salR41lZU4 -Rc+5yc7O11WIe9Lzu+ONFBFkAh3UFMR4zVZ/JhKIG/P5Srm7SUdKW2cCgYEA5aHo -x2LR+yKhjkrBzHG3Qrfy1PtlYHjOpYYAKHQcBFuiG08W3CK/vkYl+mhv0uyhT7mn -q6NDqrpZPRnDlOoEqgRS1X/QWKN6Pgd4HNLIawvp0vK9jYXDPcAXFzVthXCIwFcn -3a3m4cHiuLdRNOHkydiHQyTOF6eEneN07TDvwvkCgYEApzOd1u9igPmFzQuF2GYi -+HXFnaU/nUQuDwcQ7EJRIKRn31raPxiRoQesty5LJU6yRp4wOYgnPliPi9Tk4TGA -XynC4/tMv2vorzhMxVY9Wdke602bhYNZC/RNd3O/aP2lEQdD3Bv04I2nxE8fDb9i -VbAjCRSJV83WDf2zt1+78sECgYEAzezjRiKdcZu9y0/I+WEk2cUCE/MaF2he0FsZ -uy1cjp/qAJltQ5452xUnK6cKWNlxU4CHF0mC/hC8xCldliZCZoEYE3PaUBLSJdwm -35o6tpxpZI3gZJCG5NJlIp/8BkVDrVC7ZHV17hAkFEf4n/bPaB8wNYtE8jt8luaK -TcarzGkCgYBn2alN0RLN2PHDurraFZB6GuCvh/arEjSCY3SDFQPF10CVjTDV7sx3 -eqJkwJ81syTmfJwZIceWbOFGgsuSx37UrQAVlHZSvzeqEg9dA5HqSoOACyidJI7j -RG2+HB+KpsIZjGgLrEM4i7VOpYUDRdaouIXngFq/t9HNT+MDck5/Lw== ------END RSA PRIVATE KEY----- diff --git a/conf/server.pem b/conf/server.pem deleted file mode 100644 index 7908eca..0000000 --- a/conf/server.pem +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDtTCCAp2gAwIBAgIJAPXRSiP0Fs7sMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQwHhcNMTcxMTA3MDg1MzQ2WhcNMjcxMTA1MDg1MzQ2WjBF -MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50 -ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEA2MVLOHvgU8FCp6LgQrPfaWcGygrsRk7TL9hbT8MxbCRUSLV7Lbt3q5Kn -z8eTN4NWmwE6L5glOcH2x3Hnn+hPjbvgq35XBBIccAm0cYYKqoKkikeKFZM0Gp/W -hSrhJ4laTyQqyleIFKpwD9kHDiC/sxjGDhSFmHKhhAnsQIRm2tppFXX0aAMqJEm8 -8jzk1BN2QtKjEAn1u8v1+QW1KP3WuzdXH4L7hhMll66/KIm6Hfs2FRHQpRUWqZeJ -Y4q79NW5p5f+siGwOsGpxb/p11pM+0xnCH3UIFbm3zCTzP4sLvkfFGAeyAHsAwma -P8dJxh40ej3NN8uNiNvt8nw2Vb/1LwIDAQABo4GnMIGkMB0GA1UdDgQWBBQdPc0R -a8alY6Ab7voidkTGaH4PxzB1BgNVHSMEbjBsgBQdPc0Ra8alY6Ab7voidkTGaH4P -x6FJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNV -BAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAPXRSiP0Fs7sMAwGA1UdEwQF -MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAH1IZNkjuvt2nZPzXsuiVNyCE1vm346z -naE0Uzt3aseAN9m/iiB8mLz+ryvWc2aFMX5lTdsHdm2rqmqBCBXeRwTLf4OeHIju -ZQW6makWt6PxANEo6gbdPbQXbS420ssUhnR2irIH1SdI31iikVFPdiS0baRRE/gS -+440M1jOOOnKm0Qin92ejsshmji/0qaD2+6D5TNw4HmIZaFTBw+kfjxCL6trfeBn -4fT0RJ121V3G3+AtG5sWQ93B3pCg+jtD+fGKkNSLhphq84bD1Zv7l73QGOoylkEn -Sc0ajTLOXFBb83yRdlgV3Da95jH9rDZ4jSod48m+KemoZTDQw0vSwAU= ------END CERTIFICATE----- diff --git a/conf/tasks.json b/conf/tasks.json deleted file mode 100644 index e69de29..0000000 diff --git a/docs/.nojekyll b/docs/.nojekyll deleted file mode 100644 index e69de29..0000000 diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 361ed2a..0000000 --- a/docs/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# nps -![](https://img.shields.io/github/stars/cnlh/nps.svg) ![](https://img.shields.io/github/forks/cnlh/nps.svg) -[![Gitter](https://badges.gitter.im/cnlh-nps/community.svg)](https://gitter.im/cnlh-nps/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) -[![Build Status](https://travis-ci.org/ehang-io/nps.svg?branch=master)](https://travis-ci.org/cnlh/nps) - -nps是一款轻量级、高性能、功能强大的**内网穿透**代理服务器。目前支持**tcp、udp流量转发**,可支持任何**tcp、udp**上层协议(访问内网网站、本地支付接口调试、ssh访问、远程桌面,内网dns解析等等……),此外还**支持内网http代理、内网socks5代理**、**p2p等**,并带有功能强大的web管理端。 - - -## 背景 -![image](https://github.com/ehang-io/nps/blob/master/image/web.png?raw=true) - -1. 做微信公众号开发、小程序开发等----> 域名代理模式 - - -2. 想在外网通过ssh连接内网的机器,做云服务器到内网服务器端口的映射,----> tcp代理模式 - -3. 在非内网环境下使用内网dns,或者需要通过udp访问内网机器等----> udp代理模式 - -4. 在外网使用HTTP代理访问内网站点----> http代理模式 - -5. 搭建一个内网穿透ss,在外网如同使用内网vpn一样访问内网资源或者设备----> socks5代理模式 diff --git a/docs/_coverpage.md b/docs/_coverpage.md deleted file mode 100644 index 8b0aaa6..0000000 --- a/docs/_coverpage.md +++ /dev/null @@ -1,16 +0,0 @@ -![logo](logo.svg) - -# NPS 0.26.10 - -> 一款轻量级、高性能、功能强大的内网穿透代理服务器 - -- 几乎支持所有协议 -- 支持内网http代理、内网socks5代理、p2p等 -- 简洁但功能强大的WEB管理界面 -- 支持服务端、客户端同时控制 -- 扩展功能强大 -- 全平台兼容,一键注册为服务 - - -[GitHub](https://github.com/ehang-io/nps/) -[开始使用](#nps) diff --git a/docs/_navbar.md b/docs/_navbar.md deleted file mode 100644 index d7b2c23..0000000 --- a/docs/_navbar.md +++ /dev/null @@ -1,3 +0,0 @@ -* [![GitHub stars](https://img.shields.io/github/stars/ehang-io/nps?style=social)](https://github.com/ehang-io/nps/stargazers) - -* [![GitHub forks](https://img.shields.io/github/forks/ehang-io/nps?style=social)](https://github.com/ehang-io/nps/network) \ No newline at end of file diff --git a/docs/_sidebar.md b/docs/_sidebar.md deleted file mode 100644 index cc88eb1..0000000 --- a/docs/_sidebar.md +++ /dev/null @@ -1,29 +0,0 @@ -* 入门 - * [安装](install.md) - * [启动](run.md) - * [使用示例](example.md) -* 服务端 - * [介绍](introduction.md) - * [使用](nps_use.md) - * [配置文件](server_config.md) - * [增强功能](nps_extend.md) - -* 客户端 - - * [基本使用](use.md) - * [增强功能](npc_extend.md) - -* 扩展 - - * [功能](feature.md) - * [说明](description.md) - * [web api](api.md) - * [sdk](npc_sdk.md) - -* 其他 - - * [FAQ](faq.md) - * [贡献](contribute.md) - * [捐助](donate.md) - * [致谢](thanks.md) - * [交流](discuss.md) diff --git a/docs/api.md b/docs/api.md deleted file mode 100644 index 445d8f5..0000000 --- a/docs/api.md +++ /dev/null @@ -1,45 +0,0 @@ -# web api - -需要开启请先去掉`nps.conf`中`auth_key`的注释并配置一个合适的密钥 -## webAPI验证说明 -- 采用auth_key的验证方式 -- 在提交的每个请求后面附带两个参数,`auth_key` 和`timestamp` - -``` -auth_key的生成方式为:md5(配置文件中的auth_key+当前时间戳) -``` - -``` -timestamp为当前时间戳 -``` -``` -curl --request POST \ - --url http://127.0.0.1:8080/client/list \ - --data 'auth_key=2a0000d9229e7dbcf79dd0f5e04bb084×tamp=1553045344&start=0&limit=10' -``` -**注意:** 为保证安全,时间戳的有效范围为20秒内,所以每次提交请求必须重新生成。 - -## 获取服务端时间 -由于服务端与api请求的客户端时间差异不能太大,所以提供了一个可以获取服务端时间的接口 - -``` -POST /auth/gettime -``` - -## 获取服务端authKey - -如果想获取authKey,服务端提供获取authKey的接口 - -``` -POST /auth/getauthkey -``` -将返回加密后的authKey,采用aes cbc加密,请使用与服务端配置文件中cryptKey相同的密钥进行解密 - -**注意:** nps配置文件中`auth_crypt_key`需为16位 -- 解密密钥长度128 -- 偏移量与密钥相同 -- 补码方式pkcs5padding -- 解密串编码方式 十六进制 - -## 详细文档 -- **[详见](webapi.md)** (感谢@avengexyz) diff --git a/docs/contribute.md b/docs/contribute.md deleted file mode 100644 index fc6e3d3..0000000 --- a/docs/contribute.md +++ /dev/null @@ -1,6 +0,0 @@ -# 贡献 - -- 如果遇到bug可以直接提交至dev分支 -- 使用遇到问题可以通过issues反馈 -- 项目处于开发阶段,还有很多待完善的地方,如果可以贡献代码,请提交 PR 至 dev 分支 -- 如果有新的功能特性反馈,可以通过issues或者qq群反馈 diff --git a/docs/description.md b/docs/description.md deleted file mode 100644 index 6b44fa1..0000000 --- a/docs/description.md +++ /dev/null @@ -1,30 +0,0 @@ -# 说明 -## 获取用户真实ip -如需使用需要在`nps.conf`中设置`http_add_origin_header=true` - -在域名代理模式中,可以通过request请求 header 中的 X-Forwarded-For 和 X-Real-IP 来获取用户真实 IP。 - -**本代理前会在每一个http(s)请求中添加了这两个 header。** - -## 热更新支持 -对于绝大多数配置,在web管理中的修改将实时使用,无需重启客户端或者服务端 - -## 客户端地址显示 -在web管理中将显示客户端的连接地址 - -## 流量统计 -可统计显示每个代理使用的流量,由于压缩和加密等原因,会和实际环境中的略有差异 - -## 当前客户端带宽 -可统计每个客户端当前的带宽,可能和实际有一定差异,仅供参考。 - -## 客户端与服务端版本对比 -为了程序正常运行,客户端与服务端的核心版本必须一致,否则将导致客户端无法成功连接致服务端。 - -## Linux系统限制 -默认情况下linux对连接数量有限制,对于性能好的机器完全可以调整内核参数以处理更多的连接。 -`tcp_max_syn_backlog` `somaxconn` -酌情调整参数,增强网络性能 - -## web管理保护 -当一个ip连续登陆失败次数超过10次,将在一分钟内禁止该ip再次尝试。 diff --git a/docs/discuss.md b/docs/discuss.md deleted file mode 100644 index a23c291..0000000 --- a/docs/discuss.md +++ /dev/null @@ -1,3 +0,0 @@ -# 交流群 - -![二维码.jpeg](https://i.loli.net/2019/02/15/5c66c32a42074.jpeg) diff --git a/docs/donate.md b/docs/donate.md deleted file mode 100644 index 5331e91..0000000 --- a/docs/donate.md +++ /dev/null @@ -1,7 +0,0 @@ -# 捐助 -如果您觉得nps对你有帮助,欢迎给予我们一定捐助,也是帮助nps更好的发展。 - -## 支付宝 -![image](https://github.com/ehang-io/nps/blob/master/image/donation_zfb.png?raw=true) -## 微信 -![image](https://github.com/ehang-io/nps/blob/master/image/donation_wx.png?raw=true) diff --git a/docs/example.md b/docs/example.md deleted file mode 100644 index 653d20a..0000000 --- a/docs/example.md +++ /dev/null @@ -1,126 +0,0 @@ -# 使用示例 -## 统一准备工作(必做) -- 开启服务端,假设公网服务器ip为1.1.1.1,配置文件中`bridge_port`为8024,配置文件中`web_port`为8080 -- 访问1.1.1.1:8080 -- 在客户端管理中创建一个客户端,记录下验证密钥 -- 内网客户端运行(windows使用cmd运行加.exe) - -```shell -./npc -server=1.1.1.1:8024 -vkey=客户端的密钥 -``` -**注意:运行服务端后,请确保能从客户端设备上正常访问配置文件中所配置的`bridge_port`端口,telnet,netcat这类的来检查** - -## 域名解析 - -**适用范围:** 小程序开发、微信公众号开发、产品演示 - -**注意:域名解析模式为http反向代理,不是dns服务器,在web上能够轻松灵活配置** - -**假设场景:** -- 有一个域名proxy.com,有一台公网机器ip为1.1.1.1 -- 两个内网开发站点127.0.0.1:81,127.0.0.1:82 -- 想通过(http|https://)a.proxy.com访问127.0.0.1:81,通过(http|https://)b.proxy.com访问127.0.0.1:82 - -**使用步骤** -- 将*.proxy.com解析到公网服务器1.1.1.1 -- 点击刚才创建的客户端的域名管理,添加两条规则规则:1、域名:`a.proxy.com`,内网目标:`127.0.0.1:81`,2、域名:`b.proxy.com`,内网目标:`127.0.0.1:82` - -现在访问(http|https://)`a.proxy.com`,`b.proxy.com`即可成功 - -**https:** 如需使用https请进行相关配置,详见 [使用https](/nps_extend?id=使用https) - -## tcp隧道 - - -**适用范围:** ssh、远程桌面等tcp连接场景 - -**假设场景:** - 想通过访问公网服务器1.1.1.1的8001端口,连接内网机器10.1.50.101的22端口,实现ssh连接 - -**使用步骤** -- 在刚才创建的客户端隧道管理中添加一条tcp隧道,填写监听的端口(8001)、内网目标ip和目标端口(10.1.50.101:22),保存。 -- 访问公网服务器ip(1.1.1.1),填写的监听端口(8001),相当于访问内网ip(10.1.50.101):目标端口(22),例如:`ssh -p 8001 root@1.1.1.1` - -## udp隧道 - -**适用范围:** 内网dns解析等udp连接场景 - -**假设场景:** -内网有一台dns(10.1.50.102:53),在非内网环境下想使用该dns,公网服务器为1.1.1.1 - -**使用步骤** -- 在刚才创建的客户端的隧道管理中添加一条udp隧道,填写监听的端口(53)、内网目标ip和目标端口(10.1.50.102:53),保存。 -- 修改需要使用的dns地址为1.1.1.1,则相当于使用10.1.50.102作为dns服务器 - -## socks5代理 - - -**适用范围:** 在外网环境下如同使用vpn一样访问内网设备或者资源 - -**假设场景:** -想将公网服务器1.1.1.1的8003端口作为socks5代理,达到访问内网任意设备或者资源的效果 - -**使用步骤** -- 在刚才创建的客户端隧道管理中添加一条socks5代理,填写监听的端口(8003),保存。 -- 在外网环境的本机配置socks5代理(例如使用proxifier进行全局代理),ip为公网服务器ip(1.1.1.1),端口为填写的监听端口(8003),即可畅享内网了 - -**注意** -经过socks5代理,当收到socks5数据包时socket已经是accept状态。表现是扫描端口全open,建立连接后短时间关闭。若想同内网表现一致,建议远程连接一台设备。 - -## http正向代理 - -**适用范围:** 在外网环境下使用http正向代理访问内网站点 - -**假设场景:** -想将公网服务器1.1.1.1的8004端口作为http代理,访问内网网站 - -**使用步骤** - -- 在刚才创建的客户端隧道管理中添加一条http代理,填写监听的端口(8004),保存。 -- 在外网环境的本机配置http代理,ip为公网服务器ip(1.1.1.1),端口为填写的监听端口(8004),即可访问了 - -**注意:对于私密代理与p2p,除了统一配置的客户端和服务端,还需要一个客户端作为访问端提供一个端口来访问** - -## 私密代理 - -**适用范围:** 无需占用多余的端口、安全性要求较高可以防止其他人连接的tcp服务,例如ssh。 - -**假设场景:** -无需新增多的端口实现访问内网服务器10.1.50.2的22端口 - -**使用步骤** -- 在刚才创建的客户端中添加一条私密代理,并设置唯一密钥secrettest和内网目标10.1.50.2:22 -- 在需要连接ssh的机器上以执行命令 - -``` -./npc -server=1.1.1.1:8024 -vkey=vkey -type=tcp -password=secrettest -local_type=secret -``` -如需指定本地端口可加参数`-local_port=xx`,默认为2000 - -**注意:** password为web管理上添加的唯一密钥,具体命令可查看web管理上的命令提示 - -假设10.1.50.2用户名为root,现在执行`ssh -p 2000 root@127.0.0.1`即可访问ssh - - -## p2p服务 - -**适用范围:** 大流量传输场景,流量不经过公网服务器,但是由于p2p穿透和nat类型关系较大,不保证100%成功,支持大部分nat类型。[nat类型检测](/npc_extend?id=nat类型检测) - -**假设场景:** - -想通过访问使用端机器(访问端,也就是本机)的2000端口---->访问到内网机器 10.2.50.2的22端口 - -**使用步骤** -- 在`nps.conf`中设置`p2p_ip`(nps服务器ip)和`p2p_port`(nps服务器udp端口) -> 注:若 `p2p_port` 设置为6000,请在防火墙开放6000~6002(额外添加2个端口)udp端口 -- 在刚才刚才创建的客户端中添加一条p2p代理,并设置唯一密钥p2pssh -- 在使用端机器(本机)执行命令 - -``` -./npc -server=1.1.1.1:8024 -vkey=123 -password=p2pssh -target=10.2.50.2:22 -``` -如需指定本地端口可加参数`-local_port=xx`,默认为2000 - -**注意:** password为web管理上添加的唯一密钥,具体命令可查看web管理上的命令提示 - -假设内网机器为10.2.50.2的ssh用户名为root,现在在本机上执行`ssh -p 2000 root@127.0.0.1`即可访问机器2的ssh,如果是网站在浏览器访问127.0.0.1:2000端口即可。 diff --git a/docs/faq.md b/docs/faq.md deleted file mode 100644 index 424e6db..0000000 --- a/docs/faq.md +++ /dev/null @@ -1,20 +0,0 @@ -# FAQ - -- 服务端无法启动 -``` -服务端默认配置启用了8024,8080,80,443端口,端口冲突无法启动,请修改配置 -``` -- 客户端无法连接服务端 -``` -请检查配置文件中的所有端口是否在安全组,防火墙放行 -请检查vkey是否对应 -请检查版本是否对应 -``` -- 服务端配置文件修改无效 -``` -install 之后,Linux 配置文件在 /etc/nps -``` -- p2p穿透失败 [p2p服务](https://ehang-io.github.io/nps/#/example?id=p2p%e6%9c%8d%e5%8a%a1) -``` -双方nat类型都是Symmetric Nat一定不成功,建议先查看nat类型。请按照文档操作(标题上有超链接) -``` diff --git a/docs/feature.md b/docs/feature.md deleted file mode 100644 index 91ca416..0000000 --- a/docs/feature.md +++ /dev/null @@ -1,254 +0,0 @@ -# 扩展功能 -## 缓存支持 -对于web站点来说,一些静态文件往往消耗更大的流量,且在内网穿透中,静态文件还需到客户端获取一次,这将导致更大的流量消耗。nps在域名解析代理中支持对静态文件进行缓存。 - -即假设一个站点有a.css,nps将只需从npc客户端读取一次该文件,然后把该文件的内容放在内存中,下一次将不再对npc客户端进行请求而直接返回内存中的对应内容。该功能默认是关闭的,如需开启请在`nps.conf`中设置`http_cache=true`,并设置`http_cache_length`(缓存文件的个数,消耗内存,不宜过大,0表示不限制个数) - -## 数据压缩支持 - -由于是内网穿透,内网客户端与服务端之间的隧道存在大量的数据交换,为节省流量,加快传输速度,由此本程序支持SNNAPY形式的压缩。 - - -- 所有模式均支持数据压缩 -- 在web管理或客户端配置文件中设置 - - -## 加密传输 - -如果公司内网防火墙对外网访问进行了流量识别与屏蔽,例如禁止了ssh协议等,通过设置 配置文件,将服务端与客户端之间的通信内容加密传输,将会有效防止流量被拦截。 -- nps现在默认每次启动时随机生成tls证书,用于加密传输 - - - -## 站点保护 -域名代理模式所有客户端共用一个http服务端口,在知道域名后任何人都可访问,一些开发或者测试环境需要保密,所以可以设置用户名和密码,nps将通过 Http Basic Auth 来保护,访问时需要输入正确的用户名和密码。 - - -- 在web管理或客户端配置文件中设置 - -## host修改 - -由于内网站点需要的host可能与公网域名不一致,域名代理支持host修改功能,即修改request的header中的host字段。 - -**使用方法:在web管理中设置** - -## 自定义header - -支持对header进行新增或者修改,以配合服务的需要 - -## 404页面配置 -支持域名解析模式的自定义404页面,修改/web/static/page/error.html中内容即可,暂不支持静态文件等内容 - -## 流量限制 - -支持客户端级流量限制,当该客户端入口流量与出口流量达到设定的总量后会拒绝服务 -,域名代理会返回404页面,其他代理会拒绝连接,使用该功能需要在`nps.conf`中设置`allow_flow_limit`,默认是关闭的。 - -## 带宽限制 - -支持客户端级带宽限制,带宽计算方式为入口和出口总和,权重均衡,使用该功能需要在`nps.conf`中设置`allow_rate_limit`,默认是关闭的。 - -## 负载均衡 -本代理支持域名解析模式和tcp代理的负载均衡,在web域名添加或者编辑中内网目标分行填写多个目标即可实现轮训级别的负载均衡 - -## 端口白名单 -为了防止服务端上的端口被滥用,可在nps.conf中配置allow_ports限制可开启的端口,忽略或者不填表示端口不受限制,格式: - -```ini -allow_ports=9001-9009,10001,11000-12000 -``` - -## 端口范围映射 -当客户端以配置文件的方式启动时,可以将本地的端口进行范围映射,仅支持tcp和udp模式,例如: - -```ini -[tcp] -mode=tcp -server_port=9001-9009,10001,11000-12000 -target_port=8001-8009,10002,13000-14000 -``` - -逗号分隔,可单个或者范围,注意上下端口的对应关系,无法一一对应将不能成功 -## 端口范围映射到其他机器 -```ini -[tcp] -mode=tcp -server_port=9001-9009,10001,11000-12000 -target_port=8001-8009,10002,13000-14000 -target_ip=10.1.50.2 -``` -填写target_ip后则表示映射的该地址机器的端口,忽略则便是映射本地127.0.0.1,仅范围映射时有效 - -## KCP协议支持 - -在网络质量非常好的情况下,例如专线,内网,可以开启略微降低延迟。如需使用可在nps.conf中修改`bridge_type`为kcp -,设置后本代理将开启udp端口(`bridge_port`) - -注意:当服务端为kcp时,客户端连接时也需要使用相同配置,无配置文件模式加上参数type=kcp,配置文件模式在配置文件中设置tp=kcp - -## 域名泛解析 -支持域名泛解析,例如将host设置为*.proxy.com,a.proxy.com、b.proxy.com等都将解析到同一目标,在web管理中或客户端配置文件中将host设置为此格式即可。 - -## URL路由 -本代理支持根据URL将同一域名转发到不同的内网服务器,可在web中或客户端配置文件中设置,此参数也可忽略,例如在客户端配置文件中 - -```ini -[web1] -host=a.proxy.com -target_addr=127.0.0.1:7001 -location=/test -[web2] -host=a.proxy.com -target_addr=127.0.0.1:7002 -location=/static -``` -对于`a.proxy.com/test`将转发到`web1`,对于`a.proxy.com/static`将转发到`web2` - -## 限制ip访问 -如果将一些危险性高的端口例如ssh端口暴露在公网上,可能会带来一些风险,本代理支持限制ip访问。 - -**使用方法:** 在配置文件nps.conf中设置`ip_limit`=true,设置后仅通过注册的ip方可访问。 - -**ip注册**: - -**方式一:** -在需要访问的机器上,运行客户端 - -``` -./npc register -server=ip:port -vkey=公钥或客户端密钥 time=2 -``` - -time为有效小时数,例如time=2,在当前时间后的两小时内,本机公网ip都可以访问nps代理. - -**方式二:** -此外nps的web登陆也可提供验证的功能,成功登陆nps web admin后将自动为登陆的ip注册两小时的允许访问权限。 - - -**注意:** 本机公网ip并不是一成不变的,请自行注意有效期的设置,同时同一网络下,多人也可能是在公用同一个公网ip。 -## 客户端最大连接数 -为防止恶意大量长连接,影响服务端程序的稳定性,可以在web或客户端配置文件中为每个客户端设置最大连接数。该功能针对`socks5`、`http正向代理`、`域名代理`、`tcp代理`、`udp代理`、`私密代理`生效,使用该功能需要在`nps.conf`中设置`allow_connection_num_limit=true`,默认是关闭的。 - -## 客户端最大隧道数限制 -nps支持对客户端的隧道数量进行限制,该功能默认是关闭的,如需开启,请在`nps.conf`中设置`allow_tunnel_num_limit=true`。 -## 端口复用 -在一些严格的网络环境中,对端口的个数等限制较大,nps支持强大端口复用功能。将`bridge_port`、 `http_proxy_port`、 `https_proxy_port` 、`web_port`都设置为同一端口,也能正常使用。 - -- 使用时将需要复用的端口设置为与`bridge_port`一致即可,将自动识别。 -- 如需将web管理的端口也复用,需要配置`web_host`也就是一个二级域名以便区分 - -## 多路复用 - -nps主要通信默认基于多路复用,无需开启。 - -多路复用基于TCP滑动窗口原理设计,动态计算延迟以及带宽来算出应该往网络管道中打入的流量。 -由于主要通信大多采用TCP协议,并无法探测其实时丢包情况,对于产生丢包重传的情况,采用较大的宽容度, -5分钟的等待时间,超时将会关闭当前隧道连接并重新建立,这将会抛弃当前所有的连接。 -在Linux上,可以通过调节内核参数来适应不同应用场景。 - -对于需求大带宽又有一定的丢包的场景,可以保持默认参数不变,尽可能少抛弃连接 -高并发下可根据[Linux系统限制](## Linux系统限制) 调整 - -对于延迟敏感而又有一定丢包的场景,可以适当调整TCP重传次数 -`tcp_syn_retries`, `tcp_retries1`, `tcp_retries2` -高并发同上 -nps会在系统主动关闭连接的时候拿到报错,进而重新建立隧道连接 - -## 环境变量渲染 -npc支持环境变量渲染以适应在某些特殊场景下的要求。 - -**在无配置文件启动模式下:** -设置环境变量 -``` -export NPC_SERVER_ADDR=1.1.1.1:8024 -export NPC_SERVER_VKEY=xxxxx -``` -直接执行./npc即可运行 - -**在配置文件启动模式下:** -```ini -[common] -server_addr={{.NPC_SERVER_ADDR}} -conn_type=tcp -vkey={{.NPC_SERVER_VKEY}} -auto_reconnection=true -[web] -host={{.NPC_WEB_HOST}} -target_addr={{.NPC_WEB_TARGET}} -``` -在配置文件中填入相应的环境变量名称,npc将自动进行渲染配置文件替换环境变量 - -## 健康检查 - -当客户端以配置文件模式启动时,支持多节点的健康检查。配置示例如下 - -```ini -[health_check_test1] -health_check_timeout=1 -health_check_max_failed=3 -health_check_interval=1 -health_http_url=/ -health_check_type=http -health_check_target=127.0.0.1:8083,127.0.0.1:8082 - -[health_check_test2] -health_check_timeout=1 -health_check_max_failed=3 -health_check_interval=1 -health_check_type=tcp -health_check_target=127.0.0.1:8083,127.0.0.1:8082 -``` -**health关键词必须在开头存在** - -第一种是http模式,也就是以get的方式请求目标+url,返回状态码为200表示成功 - -第一种是tcp模式,也就是以tcp的方式与目标建立连接,能成功建立连接表示成功 - -如果失败次数超过`health_check_max_failed`,nps则会移除该npc下的所有该目标,如果失败后目标重新上线,nps将自动将目标重新加入。 - -项 | 含义 ----|--- -health_check_timeout | 健康检查超时时间 -health_check_max_failed | 健康检查允许失败次数 -health_check_interval | 健康检查间隔 -health_check_type | 健康检查类型 -health_check_target | 健康检查目标,多个以逗号(,)分隔 -health_check_type | 健康检查类型 -health_http_url | 健康检查url,仅http模式适用 - -## 日志输出 - -日志输出级别 - -**对于npc:** -``` --log_level=0~7 -log_path=npc.log -``` -``` -LevelEmergency->0 LevelAlert->1 - -LevelCritical->2 LevelError->3 - -LevelWarning->4 LevelNotice->5 - -LevelInformational->6 LevelDebug->7 -``` -默认为全输出,级别为0到7 - -**对于nps:** - -在`nps.conf`中设置相关配置即可 - -## pprof性能分析与调试 - -可在服务端与客户端配置中开启pprof端口,用于性能分析与调试,注释或留空相应参数为关闭。 - -默认为关闭状态 - -## 自定义客户端超时检测断开时间 - -客户端与服务端间会间隔5s相互发送延迟测量包,这个时间间隔不可修改。 -可修改延迟测量包丢包的次数,默认为60也就是5分钟都收不到一个延迟测量回包,则会断开客户端连接。 -值得注意的是需要客户端的socket关闭,才会进行重连,也就是当客户端无法收到服务端的fin包时,只有客户端自行关闭socket才行。 -也就是假如服务端设置为较低值,而客户端设置较高值,而此时服务端断开连接而客户端无法收到服务端的fin包,客户端也会继续等着直到触发客户端的超时设置。 - -在`nps.conf`或`npc.conf`中设置`disconnect_timeout`即可,客户端还可附带`-disconnect_timeout=60`参数启动 diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index bd91a1f..0000000 --- a/docs/index.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - Document - - - - - - -
- - - - - - - - - diff --git a/docs/install.md b/docs/install.md deleted file mode 100644 index 975ac0d..0000000 --- a/docs/install.md +++ /dev/null @@ -1,18 +0,0 @@ -# 安装 -## 安装包安装 - [releases](https://github.com/ehang-io/nps/releases) - -下载对应的系统版本即可,服务端和客户端是单独的 - -## 源码安装 -- 安装源码 -```go get -u ehang.io/nps``` -- 编译 - -服务端```go build cmd/nps/nps.go``` - -客户端```go build cmd/npc/npc.go``` - -## docker安装 -> [server](https://hub.docker.com/r/ffdfgdfg/nps) -> [client](https://hub.docker.com/r/ffdfgdfg/npc) diff --git a/docs/introduction.md b/docs/introduction.md deleted file mode 100644 index dff49e1..0000000 --- a/docs/introduction.md +++ /dev/null @@ -1,4 +0,0 @@ -![image](https://github.com/ehang-io/nps/blob/master/image/web2.png?raw=true) -# 介绍 - -可在网页上配置和管理各个tcp、udp隧道、内网站点代理,http、https解析等,功能强大,操作方便。 diff --git a/docs/logo.png b/docs/logo.png deleted file mode 100644 index b4e29a7..0000000 Binary files a/docs/logo.png and /dev/null differ diff --git a/docs/logo.svg b/docs/logo.svg deleted file mode 100644 index f53ae1f..0000000 --- a/docs/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/npc_extend.md b/docs/npc_extend.md deleted file mode 100644 index a325edd..0000000 --- a/docs/npc_extend.md +++ /dev/null @@ -1,36 +0,0 @@ -# 增强功能 -## nat类型检测 -``` - ./npc nat -stun_addr=stun.stunprotocol.org:3478 -``` -如果p2p双方都是Symmetric Nat,肯定不能成功,其他组合都有较大成功率。`stun_addr`可以指定stun服务器地址。 -## 状态检查 -``` - ./npc status -config=npc配置文件路径 -``` -## 重载配置文件 -``` - ./npc restart -config=npc配置文件路径 -``` - -## 通过代理连接nps -有时候运行npc的内网机器无法直接访问外网,此时可以可以通过socks5代理连接nps - -对于配置文件方式启动,设置 -```ini -[common] -proxy_url=socks5://111:222@127.0.0.1:8024 -``` -对于无配置文件模式,加上参数 - -``` --proxy=socks5://111:222@127.0.0.1:8024 -``` -支持socks5和http两种模式 - -即socks5://username:password@ip:port - -或http://username:password@ip:port - -## 群晖支持 -可在releases中下载spk群晖套件,例如`npc_x64-6.1_0.19.0-1.spk` diff --git a/docs/npc_sdk.md b/docs/npc_sdk.md deleted file mode 100644 index 7ee0a49..0000000 --- a/docs/npc_sdk.md +++ /dev/null @@ -1,24 +0,0 @@ -# npc sdk文档 - -``` -命令行模式启动客户端 -从v0.26.10开始,此函数会阻塞,直到客户端退出返回,请自行管理是否重连 -p0->连接地址 -p1->vkey -p2->连接类型(tcp or udp) -p3->连接代理 - -extern GoInt StartClientByVerifyKey(char* p0, char* p1, char* p2, char* p3); - -查看当前启动的客户端状态,在线为1,离线为0 -extern GoInt GetClientStatus(); - -关闭客户端 -extern void CloseClient(); - -获取当前客户端版本 -extern char* Version(); - -获取日志,实时更新 -extern char* Logs(); -``` diff --git a/docs/nps_extend.md b/docs/nps_extend.md deleted file mode 100644 index cb613df..0000000 --- a/docs/nps_extend.md +++ /dev/null @@ -1,107 +0,0 @@ -# 增强功能 -## 使用https - -**方式一:** 类似于nginx实现https的处理 - -在配置文件中将https_proxy_port设置为443或者其他你想配置的端口,将`https_just_proxy`设置为false,nps 重启后,在web管理界面,域名新增或修改界面中修改域名证书和密钥。 - -**此外:** 可以在`nps.conf`中设置一个默认的https配置,当遇到未在web中设置https证书的域名解析时,将自动使用默认证书,另还有一种情况就是对于某些请求的clienthello不携带sni扩展信息,nps也将自动使用默认证书 - - -**方式二:** 在内网对应服务器上设置https - -在`nps.conf`中将`https_just_proxy`设置为true,并且打开`https_proxy_port`端口,然后nps将直接转发https请求到内网服务器上,由内网服务器进行https处理 - -## 与nginx配合 - -有时候我们还需要在云服务器上运行nginx来保证静态文件缓存等,本代理可和nginx配合使用,在配置文件中将httpProxyPort设置为非80端口,并在nginx中配置代理,例如httpProxyPort为8010时 -``` -server { - listen 80; - server_name *.proxy.com; - location / { - proxy_set_header Host $http_host; - proxy_pass http://127.0.0.1:8010; - } -} -``` -如需使用https也可在nginx监听443端口并配置ssl,并将本代理的httpsProxyPort设置为空关闭https即可,例如httpProxyPort为8020时 - -``` -server { - listen 443; - server_name *.proxy.com; - ssl on; - ssl_certificate certificate.crt; - ssl_certificate_key private.key; - ssl_session_timeout 5m; - ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - ssl_prefer_server_ciphers on; - location / { - proxy_set_header Host $http_host; - proxy_pass http://127.0.0.1:8020; - } -} -``` -## web管理使用https -如果web管理需要使用https,可以在配置文件`nps.conf`中设置`web_open_ssl=true`,并配置`web_cert_file`和`web_key_file` -## web使用Caddy代理 - -如果将web配置到Caddy代理,实现子路径访问nps,可以这样配置. - -假设我们想通过 `http://caddy_ip:caddy_port/nps` 来访问后台, Caddyfile 这样配置: - -```Caddyfile -caddy_ip:caddy_port/nps { - ##server_ip 为 nps 服务器IP - ##web_port 为 nps 后台端口 - proxy / http://server_ip:web_port/nps { - transparent - } -} -``` - -nps.conf 修改 `web_base_url` 为 `/nps` 即可 -``` -web_base_url=/nps -``` - - -## 关闭代理 - -如需关闭http代理可在配置文件中将http_proxy_port设置为空,如需关闭https代理可在配置文件中将https_proxy_port设置为空。 - -## 流量数据持久化 -服务端支持将流量数据持久化,默认情况下是关闭的,如果有需求可以设置`nps.conf`中的`flow_store_interval`参数,单位为分钟 - -**注意:** nps不会持久化通过公钥连接的客户端 -## 系统信息显示 -nps服务端支持在web上显示和统计服务器的相关信息,但默认一些统计图表是关闭的,如需开启请在`nps.conf`中设置`system_info_display=true` - -## 自定义客户端连接密钥 -web上可以自定义客户端连接的密钥,但是必须具有唯一性 -## 关闭公钥访问 -可以将`nps.conf`中的`public_vkey`设置为空或者删除 - -## 关闭web管理 -可以将`nps.conf`中的`web_port`设置为空或者删除 - -## 服务端多用户登陆 -如果将`nps.conf`中的`allow_user_login`设置为true,服务端web将支持多用户登陆,登陆用户名为user,默认密码为每个客户端的验证密钥,登陆后可以进入客户端编辑修改web登陆的用户名和密码,默认该功能是关闭的。 - -## 用户注册功能 -nps服务端支持用户注册功能,可将`nps.conf`中的`allow_user_register`设置为true,开启后登陆页将会有有注册功能, - -## 监听指定ip - -nps支持每个隧道监听不同的服务端端口,在`nps.conf`中设置`allow_multi_ip=true`后,可在web中控制,或者npc配置文件中(可忽略,默认为0.0.0.0) -```ini -server_ip=xxx -``` -## 代理到服务端本地 -在使用nps监听80或者443端口时,默认是将所有的请求都会转发到内网上,但有时候我们的nps服务器的上一些服务也需要使用这两个端口,nps提供类似于`nginx` `proxy_pass` 的功能,支持将代理到服务器本地,该功能支持域名解析,tcp、udp隧道,默认关闭。 - -**即:** 假设在nps的vps服务器上有一个服务使用5000端口,这时候nps占用了80端口和443,我们想能使用一个域名通过http(s)访问到5000的服务。 - -**使用方式:** 在`nps.conf`中设置`allow_local_proxy=true`,然后在web上设置想转发的隧道或者域名然后选择转发到本地选项即可成功。 diff --git a/docs/nps_use.md b/docs/nps_use.md deleted file mode 100644 index 696203b..0000000 --- a/docs/nps_use.md +++ /dev/null @@ -1,47 +0,0 @@ -# 使用 -**提示:使用web模式时,服务端执行文件必须在项目根目录,否则无法正确加载配置文件** - -## web管理 - -进入web界面,公网ip:web界面端口(默认8080),密码默认为123 - -进入web管理界面,有详细的说明 - -## 服务端配置文件重载 -对于linux、darwin -```shell - sudo nps reload -``` -对于windows -```shell - nps.exe reload -``` -**说明:** 仅支持部分配置重载,例如`allow_user_login` `auth_crypt_key` `auth_key` `web_username` `web_password` 等,未来将支持更多 - - -## 服务端停止或重启 -对于linux、darwin -```shell - sudo nps stop|restart -``` -对于windows -```shell - nps.exe stop|restart -``` -## 服务端更新 -请首先执行 `sudo nps stop` 或者 `nps.exe stop` 停止运行,然后 - -对于linux -```shell - sudo nps-update update -``` -对于windows -```shell - nps-update.exe update -``` - -更新完成后,执行执行 `sudo nps start` 或者 `nps.exe start` 重新运行即可完成升级 - -如果无法更新成功,可以直接自行下载releases压缩包然后覆盖原有的nps二进制文件和web目录 - -注意:`nps install` 之后的 nps 不在原位置,请使用 `whereis nps` 查找具体目录覆盖 nps 二进制文件 diff --git a/docs/run.md b/docs/run.md deleted file mode 100644 index 788ba35..0000000 --- a/docs/run.md +++ /dev/null @@ -1,42 +0,0 @@ -# 启动 -## 服务端 -下载完服务器压缩包后,解压,然后进入解压后的文件夹 - -- 执行安装命令 - -对于linux|darwin ```sudo ./nps install``` - -对于windows,管理员身份运行cmd,进入安装目录 ```nps.exe install``` - -- 启动 - -对于linux|darwin ```sudo nps start``` - -对于windows,管理员身份运行cmd,进入程序目录 ```nps.exe start``` - -```安装后windows配置文件位于 C:\Program Files\nps,linux和darwin位于/etc/nps``` - -停止和重启可用,stop和restart - -**如果发现没有启动成功,可以使用`nps(.exe) stop`,然后运行`nps.(exe)`运行调试,或查看日志**(Windows日志文件位于当前运行目录下,linux和darwin位于/var/log/nps.log) -- 访问服务端ip:web服务端口(默认为8080) -- 使用用户名和密码登陆(默认admin/123,正式使用一定要更改) -- 创建客户端 - -## 客户端 -- 下载客户端安装包并解压,进入到解压目录 -- 点击web管理中客户端前的+号,复制启动命令 -- 执行启动命令,linux直接执行即可,windows将./npc换成npc.exe用**cmd执行** - -如果使用`powershell`运行,**请将ip括起来!** - -如果需要注册到系统服务可查看[注册到系统服务](/use?id=注册到系统服务) - -## 版本检查 -- 对客户端以及服务的均可以使用参数`-version`打印版本 -- `nps -version`或`./nps -version` -- `npc -version`或`./npc -version` - -## 配置 -- 客户端连接后,在web中配置对应穿透服务即可 -- 可以查看[使用示例](/example) diff --git a/docs/server_config.md b/docs/server_config.md deleted file mode 100644 index 46499c6..0000000 --- a/docs/server_config.md +++ /dev/null @@ -1,24 +0,0 @@ -# 服务端配置文件 -- /etc/nps/conf/nps.conf - -名称 | 含义 ----|--- -web_port | web管理端口 -web_password | web界面管理密码 -web_username | web界面管理账号 -web_base_url | web管理主路径,用于将web管理置于代理子路径后面 -bridge_port | 服务端客户端通信端口 -https_proxy_port | 域名代理https代理监听端口 -http_proxy_port | 域名代理http代理监听端口 -auth_key|web api密钥 -bridge_type|客户端与服务端连接方式kcp或tcp -public_vkey|客户端以配置文件模式启动时的密钥,设置为空表示关闭客户端配置文件连接模式 -ip_limit|是否限制ip访问,true或false或忽略 -flow_store_interval|服务端流量数据持久化间隔,单位分钟,忽略表示不持久化 -log_level|日志输出级别 -auth_crypt_key | 获取服务端authKey时的aes加密密钥,16位 -p2p_ip| 服务端Ip,使用p2p模式必填 -p2p_port|p2p模式开启的udp端口 -pprof_ip|debug pprof 服务端ip -pprof_port|debug pprof 端口 -disconnect_timeout|客户端连接超时,单位 5s,默认值 60,即 300s = 5mins diff --git a/docs/thanks.md b/docs/thanks.md deleted file mode 100644 index a2cbe1e..0000000 --- a/docs/thanks.md +++ /dev/null @@ -1,5 +0,0 @@ -Thanks [jetbrains](https://www.jetbrains.com/?from=nps) for providing development tools for nps - - - - diff --git a/docs/use.md b/docs/use.md deleted file mode 100644 index a367454..0000000 --- a/docs/use.md +++ /dev/null @@ -1,225 +0,0 @@ -# 基本使用 -## 无配置文件模式 -此模式的各种配置在服务端web管理中完成,客户端除运行一条命令外无需任何其他设置 -``` - ./npc -server=ip:port -vkey=web界面中显示的密钥 -``` -## 注册到系统服务(开机启动、守护进程) -对于linux、darwin -- 注册:`sudo ./npc install 其他参数(例如-server=xx -vkey=xx或者-config=xxx)` -- 启动:`sudo npc start` -- 停止:`sudo npc stop` -- 如果需要更换命令内容需要先卸载`./npc uninstall`,再重新注册 - -对于windows,使用管理员身份运行cmd - -- 注册:`npc.exe install 其他参数(例如-server=xx -vkey=xx或者-config=xxx)` -- 启动:`npc.exe start` -- 停止:`npc.exe stop` -- 如果需要更换命令内容需要先卸载`npc.exe uninstall`,再重新注册 -- 如果需要当客户端退出时自动重启客户端,请按照如图所示配置 -![image](https://github.com/ehang-io/nps/blob/master/docs/windows_client_service_configuration.png?raw=true) - -注册到服务后,日志文件windows位于当前目录下,linux和darwin位于/var/log/npc.log - -## 客户端更新 -首先进入到对于的客户端二进制文件目录 - -请首先执行`sudo npc stop`或者`npc.exe stop`停止运行,然后 - -对于linux -```shell - sudo npc-update update -``` -对于windows -```shell -npc-update.exe update -``` - -更新完成后,执行执行`sudo npc start`或者`npc.exe start`重新运行即可完成升级 - -如果无法更新成功,可以直接自行下载releases压缩包然后覆盖原有的npc二进制文件 - -## 配置文件模式 -此模式使用nps的公钥或者客户端私钥验证,各种配置在客户端完成,同时服务端web也可以进行管理 -``` - ./npc -config=npc配置文件路径 -``` -## 配置文件说明 -[示例配置文件](https://github.com/ehang-io/nps/tree/master/conf/npc.conf) -#### 全局配置 -```ini -[common] -server_addr=1.1.1.1:8024 -conn_type=tcp -vkey=123 -username=111 -password=222 -compress=true -crypt=true -rate_limit=10000 -flow_limit=100 -remark=test -max_conn=10 -#pprof_addr=0.0.0.0:9999 -``` -项 | 含义 ----|--- -server_addr | 服务端ip/域名:port -conn_type | 与服务端通信模式(tcp或kcp) -vkey|服务端配置文件中的密钥(非web) -username|socks5或http(s)密码保护用户名(可忽略) -password|socks5或http(s)密码保护密码(可忽略) -compress|是否压缩传输(true或false或忽略) -crypt|是否加密传输(true或false或忽略) -rate_limit|速度限制,可忽略 -flow_limit|流量限制,可忽略 -remark|客户端备注,可忽略 -max_conn|最大连接数,可忽略 -pprof_addr|debug pprof ip:port -#### 域名代理 - -```ini -[common] -server_addr=1.1.1.1:8024 -vkey=123 -[web1] -host=a.proxy.com -target_addr=127.0.0.1:8080,127.0.0.1:8082 -host_change=www.proxy.com -header_set_proxy=nps -``` -项 | 含义 ----|--- -web1 | 备注 -host | 域名(http|https都可解析) -target_addr|内网目标,负载均衡时多个目标,逗号隔开 -host_change|请求host修改 -header_xxx|请求header修改或添加,header_proxy表示添加header proxy:nps - -#### tcp隧道模式 - -```ini -[common] -server_addr=1.1.1.1:8024 -vkey=123 -[tcp] -mode=tcp -target_addr=127.0.0.1:8080 -server_port=9001 -``` -项 | 含义 ----|--- -mode | tcp -server_port | 在服务端的代理端口 -tartget_addr|内网目标 - -#### udp隧道模式 - -```ini -[common] -server_addr=1.1.1.1:8024 -vkey=123 -[udp] -mode=udp -target_addr=127.0.0.1:8080 -server_port=9002 -``` -项 | 含义 ----|--- -mode | udp -server_port | 在服务端的代理端口 -target_addr|内网目标 -#### http代理模式 - -```ini -[common] -server_addr=1.1.1.1:8024 -vkey=123 -[http] -mode=httpProxy -server_port=9003 -``` -项 | 含义 ----|--- -mode | httpProxy -server_port | 在服务端的代理端口 -#### socks5代理模式 - -```ini -[common] -server_addr=1.1.1.1:8024 -vkey=123 -[socks5] -mode=socks5 -server_port=9004 -multi_account=multi_account.conf -``` -项 | 含义 ----|--- -mode | socks5 -server_port | 在服务端的代理端口 -multi_account | socks5多账号配置文件(可选),配置后使用basic_username和basic_password无法通过认证 -#### 私密代理模式 - -```ini -[common] -server_addr=1.1.1.1:8024 -vkey=123 -[secret_ssh] -mode=secret -password=ssh2 -target_addr=10.1.50.2:22 -``` -项 | 含义 ----|--- -mode | secret -password | 唯一密钥 -target_addr|内网目标 - -#### p2p代理模式 - -```ini -[common] -server_addr=1.1.1.1:8024 -vkey=123 -[p2p_ssh] -mode=p2p -password=ssh2 -target_addr=10.1.50.2:22 -``` -项 | 含义 ----|--- -mode | p2p -password | 唯一密钥 -target_addr|内网目标 - - -#### 文件访问模式 -利用nps提供一个公网可访问的本地文件服务,此模式仅客户端使用配置文件模式方可启动 - -```ini -[common] -server_addr=1.1.1.1:8024 -vkey=123 -[file] -mode=file -server_port=9100 -local_path=/tmp/ -strip_pre=/web/ -```` - -项 | 含义 ----|--- -mode | file -server_port | 服务端开启的端口 -local_path|本地文件目录 -strip_pre|前缀 - -对于`strip_pre`,访问公网`ip:9100/web/`相当于访问`/tmp/`目录 - -#### 断线重连 -```ini -[common] -auto_reconnection=true -``` diff --git a/docs/webapi.md b/docs/webapi.md deleted file mode 100644 index dfd7253..0000000 --- a/docs/webapi.md +++ /dev/null @@ -1,233 +0,0 @@ -获取客户端列表 - -``` -POST /client/list/ -``` - - -| 参数 | 含义 | -| --- | --- | -| search | 搜索 | -| order | 排序asc 正序 desc倒序 | -| offset | 分页(第几页) | -| limit | 条数(分页显示的条数) | - -*** -获取单个客户端 - -``` -POST /client/getclient/ -``` - - -| 参数 | 含义 | -| --- | --- | -| id | 客户端id | - -*** -添加客户端 - -``` -POST /client/add/ -``` - -| 参数 | 含义 | -| --- | --- | -| remark | 备注 | -| u | basic权限认证用户名 | -| p | basic权限认证密码 | -| limit | 条数(分页显示的条数) | -| vkey | 客户端验证密钥 | -| config\_conn\_allow | 是否允许客户端以配置文件模式连接 1允许 0不允许 | -| compress | 压缩1允许 0不允许 | -| crypt | 是否加密(1或者0)1允许 0不允许 | -| rate\_limit | 带宽限制 单位KB/S 空则为不限制 | -| flow\_limit | 流量限制 单位M 空则为不限制 | -| max\_conn | 客户端最大连接数量 空则为不限制 | -| max\_tunnel | 客户端最大隧道数量 空则为不限制 | - -*** -修改客户端 - -``` -POST /client/edit/ -``` - -| 参数 | 含义 | -| --- | --- | -| remark | 备注 | -| u | basic权限认证用户名 | -| p | basic权限认证密码 | -| limit | 条数(分页显示的条数) | -| vkey | 客户端验证密钥 | -| config\_conn\_allow | 是否允许客户端以配置文件模式连接 1允许 0不允许 | -| compress | 压缩1允许 0不允许 | -| crypt | 是否加密(1或者0)1允许 0不允许 | -| rate\_limit | 带宽限制 单位KB/S 空则为不限制 | -| flow\_limit | 流量限制 单位M 空则为不限制 | -| max\_conn | 客户端最大连接数量 空则为不限制 | -| max\_tunnel | 客户端最大隧道数量 空则为不限制 | -| id | 要修改的客户端id | - -*** -删除客户端 - -``` -POST /client/del/ -``` - -| 参数 | 含义 | -| --- | --- | -| id | 要删除的客户端id | - -*** -获取域名解析列表 - -``` -POST /index/hostlist/ -``` - -| 参数 | 含义 | -| --- | --- | -| search | 搜索(可以搜域名/备注什么的) | -| offset | 分页(第几页) | -| limit | 条数(分页显示的条数) | - -*** -添加域名解析 - -``` -POST /index/addhost/ -``` - - -| 参数 | 含义 | -| --- | --- | -| remark | 备注 | -| host | 域名 | -| scheme | 协议类型(三种 all http https) | -| location | url路由 空则为不限制 | -| client\_id | 客户端id | -| target | 内网目标(ip:端口) | -| header | request header 请求头 | -| hostchange | request host 请求主机 | - -*** -修改域名解析 - -``` -POST /index/edithost/ -``` - -| 参数 | 含义 | -| --- | --- | -| remark | 备注 | -| host | 域名 | -| scheme | 协议类型(三种 all http https) | -| location | url路由 空则为不限制 | -| client\_id | 客户端id | -| target | 内网目标(ip:端口) | -| header | request header 请求头 | -| hostchange | request host 请求主机 | -| id | 需要修改的域名解析id | - -*** -删除域名解析 - -``` -POST /index/delhost/ -``` - -| 参数 | 含义 | -| --- | --- | -| id | 需要删除的域名解析id | - -*** -获取单条隧道信息 - -``` -POST /index/getonetunnel/ -``` - -| 参数 | 含义 | -| --- | --- | -| id | 隧道的id | - -*** -获取隧道列表 - -``` -POST /index/gettunnel/ -``` - -| 参数 | 含义 | -| --- | --- | -| client\_id | 穿透隧道的客户端id | -| type | 类型tcp udp httpProx socks5 secret p2p | -| search | 搜索 | -| offset | 分页(第几页) | -| limit | 条数(分页显示的条数) | - -*** -添加隧道 - -``` -POST /index/add/ -``` - -| 参数 | 含义 | -| --- | --- | -| type | 类型tcp udp httpProx socks5 secret p2p | -| remark | 备注 | -| port | 服务端端口 | -| target | 目标(ip:端口) | -| client\_id | 客户端id | - -*** -修改隧道 - -``` -POST /index/edit/ -``` - -| 参数 | 含义 | -| --- | --- | -| type | 类型tcp udp httpProx socks5 secret p2p | -| remark | 备注 | -| port | 服务端端口 | -| target | 目标(ip:端口) | -| client\_id | 客户端id | -| id | 隧道id | - -*** -删除隧道 - -``` -POST /index/del/ -``` - -| 参数 | 含义 | -| --- | --- | -| id | 隧道id | - -*** -隧道停止工作 - -``` -POST /index/stop/ -``` - -| 参数 | 含义 | -| --- | --- | -| id | 隧道id | - -*** -隧道开始工作 - -``` -POST /index/start/ -``` - -| 参数 | 含义 | -| --- | --- | -| id | 隧道id | diff --git a/docs/windows_client_service_configuration.png b/docs/windows_client_service_configuration.png deleted file mode 100644 index 0a1d2c0..0000000 Binary files a/docs/windows_client_service_configuration.png and /dev/null differ diff --git a/gui/npc/AndroidManifest.xml b/gui/npc/AndroidManifest.xml deleted file mode 100755 index 2aa5dcd..0000000 --- a/gui/npc/AndroidManifest.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/gui/npc/npc.go b/gui/npc/npc.go deleted file mode 100644 index 17728b5..0000000 --- a/gui/npc/npc.go +++ /dev/null @@ -1,194 +0,0 @@ -package main - -import ( - "ehang.io/nps/client" - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/daemon" - "ehang.io/nps/lib/version" - "fmt" - "fyne.io/fyne/v2" - "fyne.io/fyne/v2/app" - "fyne.io/fyne/v2/container" - "fyne.io/fyne/v2/layout" - "fyne.io/fyne/v2/widget" - "github.com/astaxie/beego/logs" - "io/ioutil" - "os" - "path" - "runtime" - "strings" - "time" -) - -func main() { - daemon.InitDaemon("npc", common.GetRunPath(), common.GetTmpPath()) - logs.SetLogger("store") - application := app.New() - window := application.NewWindow("Npc " + version.VERSION) - window.SetContent(WidgetScreen()) - window.Resize(fyne.NewSize(910, 350)) - - window.ShowAndRun() - -} - -var ( - start bool - closing bool - status = "Start!" - connType = "tcp" - cl = new(client.TRPClient) - refreshCh = make(chan struct{}) -) - -func WidgetScreen() fyne.CanvasObject { - return fyne.NewContainerWithLayout(layout.NewBorderLayout(nil, nil, nil, nil), - makeMainTab(), - ) -} - -func makeMainTab() *fyne.Container { - serverPort := widget.NewEntry() - serverPort.SetPlaceHolder("Server:Port") - - vKey := widget.NewEntry() - vKey.SetPlaceHolder("Vkey") - radio := widget.NewRadioGroup([]string{"tcp", "kcp"}, func(s string) { connType = s }) - radio.Horizontal = true - - button := widget.NewButton(status, func() { - onclick(serverPort.Text, vKey.Text, connType) - }) - go func() { - for { - <-refreshCh - button.SetText(status) - } - }() - - lo := widget.NewMultiLineEntry() - lo.Disable() - lo.Resize(fyne.NewSize(910, 250)) - slo := container.NewScroll(lo) - slo.Resize(fyne.NewSize(910, 250)) - go func() { - for { - time.Sleep(time.Second) - lo.SetText(common.GetLogMsg()) - slo.Resize(fyne.NewSize(910, 250)) - } - }() - - sp, vk, ct := loadConfig() - if sp != "" && vk != "" && ct != "" { - serverPort.SetText(sp) - vKey.SetText(vk) - connType = ct - radio.SetSelected(ct) - onclick(sp, vk, ct) - } - - return container.NewVBox( - widget.NewLabel("Npc "+version.VERSION), - serverPort, - vKey, - radio, - button, - slo, - ) -} - -func onclick(s, v, c string) { - start = !start - if start { - closing = false - status = "Stop!" - // init the npc - fmt.Println("submit", s, v, c) - sp, vk, ct := loadConfig() - if sp != s || vk != v || ct != c { - saveConfig(s, v, c) - } - go func() { - for { - cl = client.NewRPClient(s, v, c, "", nil, 60) - status = "Stop!" - refreshCh <- struct{}{} - cl.Start() - logs.Warn("client closed, reconnecting in 5 seconds...") - if closing { - return - } - status = "Reconnecting..." - refreshCh <- struct{}{} - time.Sleep(time.Second * 5) - } - }() - } else { - // close the npc - status = "Start!" - closing = true - if cl != nil { - go cl.Close() - cl = nil - } - } - refreshCh <- struct{}{} -} - -func getDir() (dir string, err error) { - if runtime.GOOS != "android" { - dir, err = os.UserConfigDir() - if err != nil { - return - } - } else { - dir = "/data/data/org.nps.client/files" - } - return -} - -func saveConfig(host, vkey, connType string) { - data := strings.Join([]string{host, vkey, connType}, "\n") - ph, err := getDir() - if err != nil { - logs.Warn("not found config dir") - return - } - _ = os.Remove(path.Join(ph, "npc.conf")) - f, err := os.OpenFile(path.Join(ph, "npc.conf"), os.O_CREATE|os.O_WRONLY, 0644) - defer f.Close() - if err != nil { - logs.Error(err) - return - } - if _, err := f.Write([]byte(data)); err != nil { - _ = f.Close() // ignore error; Write error takes precedence - logs.Error(err) - return - } -} - -func loadConfig() (host, vkey, connType string) { - ph, err := getDir() - if err != nil { - logs.Warn("not found config dir") - return - } - f, err := os.OpenFile(path.Join(ph, "npc.conf"), os.O_RDONLY, 0644) - defer f.Close() - if err != nil { - logs.Error(err) - return - } - data, err := ioutil.ReadAll(f) - if err != nil { - logs.Error(err) - return - } - li := strings.Split(string(data), "\n") - host = li[0] - vkey = li[1] - connType = li[2] - return -} diff --git a/image/cpu1.png b/image/cpu1.png deleted file mode 100644 index 314544f..0000000 Binary files a/image/cpu1.png and /dev/null differ diff --git a/image/cpu2.png b/image/cpu2.png deleted file mode 100644 index ce95e2c..0000000 Binary files a/image/cpu2.png and /dev/null differ diff --git a/image/donation_wx.png b/image/donation_wx.png deleted file mode 100644 index f0c6725..0000000 Binary files a/image/donation_wx.png and /dev/null differ diff --git a/image/donation_zfb.png b/image/donation_zfb.png deleted file mode 100644 index 7d57e43..0000000 Binary files a/image/donation_zfb.png and /dev/null differ diff --git a/image/http.png b/image/http.png deleted file mode 100644 index be93148..0000000 Binary files a/image/http.png and /dev/null differ diff --git a/image/httpProxy.png b/image/httpProxy.png deleted file mode 100644 index 8581d25..0000000 Binary files a/image/httpProxy.png and /dev/null differ diff --git a/image/qps.png b/image/qps.png deleted file mode 100644 index 9d27b3f..0000000 Binary files a/image/qps.png and /dev/null differ diff --git a/image/sock5.png b/image/sock5.png deleted file mode 100644 index 960555e..0000000 Binary files a/image/sock5.png and /dev/null differ diff --git a/image/speed.png b/image/speed.png deleted file mode 100644 index e39b454..0000000 Binary files a/image/speed.png and /dev/null differ diff --git a/image/tcp.png b/image/tcp.png deleted file mode 100644 index 883fbce..0000000 Binary files a/image/tcp.png and /dev/null differ diff --git a/image/udp.png b/image/udp.png deleted file mode 100644 index a53f517..0000000 Binary files a/image/udp.png and /dev/null differ diff --git a/image/web.png b/image/web.png deleted file mode 100644 index c82fc36..0000000 Binary files a/image/web.png and /dev/null differ diff --git a/image/web2.png b/image/web2.png deleted file mode 100644 index 529ee4a..0000000 Binary files a/image/web2.png and /dev/null differ diff --git a/image/work_flow.svg b/image/work_flow.svg deleted file mode 100644 index 5b3794b..0000000 --- a/image/work_flow.svg +++ /dev/null @@ -1,821 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - 页-1 - - - - - - - - - - - - - - - - - - - - - - - - 防火墙 - NAT - - 工作表.2 - - - - 工作表.3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NAT - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 服务器 - Application2 10.0.0.4:PORT - - 工作表.5 - - - - 工作表.6 - - - - 工作表.7 - - - - - - - - - - Application210.0.0.4:PORT - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 服务器.8 - Application1 10.0.0.3:PORT - - 工作表.9 - - - - 工作表.10 - - - - 工作表.11 - - - - - - - - - - Application110.0.0.3:PORT - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 服务器.12 - Application3 10.0.0.5:PORT - - 工作表.13 - - - - 工作表.14 - - - - 工作表.15 - - - - - - - - - - Application310.0.0.5:PORT - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 主机 - NPC Client 10.0.0.2 Dial To: ->10.0.0.3:PORT ->10.0.0.4:PORT ... - - 工作表.17 - - - - - - 工作表.18 - - - - - - - - 工作表.19 - - - - - - NPCClient10.0.0.2Dial To:->10.0.0.3:PORT->10.0.0.4:PORT->10.0.0.5:PORT - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 主机.20 - NPS Server 1.1.1.1 Listen On: 8003->10.0.0.3:PORT 8004->10.0.... - - 工作表.21 - - - - - - 工作表.22 - - - - - - - - 工作表.23 - - - - - - NPSServer1.1.1.1Listen On:8003->10.0.0.3:PORT8004->10.0.0.4:PORT8005->10.0.0.5:PORT - - - - - - - - - - - - - - - - - - 用户 - User1 Wants:APP1 - - 工作表.25 - - - - - - User1Wants:APP1 - - - - - - - - - - - - - - - - - - 用户.26 - User2 Wants:APP2 - - 工作表.27 - - - - - - User2Wants:APP2 - - - - - - - - - - - - - - - - - - 用户.28 - User3 Wants:APP3 - - 工作表.29 - - - - - - User3Wants:APP3 - - - 动态连接线.1003 - ->8003 Multi Conn - - - - - - - - ->8003Multi Conn - - 动态连接线.1004 - ->8004 Multi Conn - - - - - - - - ->8004Multi Conn - - 动态连接线.1005 - ->8005 Multi Conn - - - - - - - - ->8005Multi Conn - - 动态连接线.1006 - NPS & NPC Multiplexing Connection TCP or KCP Only One Conn Pe... - - - - - - - - NPS & NPCMultiplexingConnectionTCP or KCPOnly OneConn PeerNPC - - 动态连接线.1007 - ->PORT Multi Conn - - - - - - - - ->PORTMulti Conn - - 动态连接线.1008 - ->PORT Multi Conn - - - - - - - - ->PORTMulti Conn - - 动态连接线.1009 - ->PORT Multi Conn - - - - - - - - ->PORTMulti Conn - - 动态连接线.1010 - NPS & NPC - - - - - - - - NPS&NPC - - 工作表.1011 - - - - 工作表.1012 - - - - 工作表.1013 - Internet - - - - Internet - - 工作表.1014 - Intranet - - - - Intranet - - diff --git a/lib/cache/lru.go b/lib/cache/lru.go deleted file mode 100644 index 41db229..0000000 --- a/lib/cache/lru.go +++ /dev/null @@ -1,102 +0,0 @@ -package cache - -import ( - "container/list" - "sync" -) - -// Cache is an LRU cache. It is safe for concurrent access. -type Cache struct { - // MaxEntries is the maximum number of cache entries before - // an item is evicted. Zero means no limit. - MaxEntries int - - //Execute this callback function when an element is culled - OnEvicted func(key Key, value interface{}) - - ll *list.List //list - cache sync.Map -} - -// A Key may be any value that is comparable. See http://golang.org/ref/spec#Comparison_operators -type Key interface{} - -type entry struct { - key Key - value interface{} -} - -// New creates a new Cache. -// If maxEntries is 0, the cache has no length limit. -// that eviction is done by the caller. -func New(maxEntries int) *Cache { - return &Cache{ - MaxEntries: maxEntries, - ll: list.New(), - //cache: make(map[interface{}]*list.Element), - } -} - -// If the key value already exists, move the key to the front -func (c *Cache) Add(key Key, value interface{}) { - if ee, ok := c.cache.Load(key); ok { - c.ll.MoveToFront(ee.(*list.Element)) // move to the front - ee.(*list.Element).Value.(*entry).value = value - return - } - ele := c.ll.PushFront(&entry{key, value}) - c.cache.Store(key, ele) - if c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries { // Remove the oldest element if the limit is exceeded - c.RemoveOldest() - } -} - -// Get looks up a key's value from the cache. -func (c *Cache) Get(key Key) (value interface{}, ok bool) { - if ele, hit := c.cache.Load(key); hit { - c.ll.MoveToFront(ele.(*list.Element)) - return ele.(*list.Element).Value.(*entry).value, true - } - return -} - -// Remove removes the provided key from the cache. -func (c *Cache) Remove(key Key) { - if ele, hit := c.cache.Load(key); hit { - c.removeElement(ele.(*list.Element)) - } -} - -// RemoveOldest removes the oldest item from the cache. -func (c *Cache) RemoveOldest() { - ele := c.ll.Back() - if ele != nil { - c.removeElement(ele) - } -} - -func (c *Cache) removeElement(e *list.Element) { - c.ll.Remove(e) - kv := e.Value.(*entry) - c.cache.Delete(kv.key) - if c.OnEvicted != nil { - c.OnEvicted(kv.key, kv.value) - } -} - -// Len returns the number of items in the cache. -func (c *Cache) Len() int { - return c.ll.Len() -} - -// Clear purges all stored items from the cache. -func (c *Cache) Clear() { - if c.OnEvicted != nil { - c.cache.Range(func(key, value interface{}) bool { - kv := value.(*list.Element).Value.(*entry) - c.OnEvicted(kv.key, kv.value) - return true - }) - } - c.ll = nil -} diff --git a/lib/common/const.go b/lib/common/const.go deleted file mode 100644 index ffb2fa6..0000000 --- a/lib/common/const.go +++ /dev/null @@ -1,38 +0,0 @@ -package common - -const ( - CONN_DATA_SEQ = "*#*" //Separator - VERIFY_EER = "vkey" - VERIFY_SUCCESS = "sucs" - WORK_MAIN = "main" - WORK_CHAN = "chan" - WORK_CONFIG = "conf" - WORK_REGISTER = "rgst" - WORK_SECRET = "sert" - WORK_FILE = "file" - WORK_P2P = "p2pm" - WORK_P2P_VISITOR = "p2pv" - WORK_P2P_PROVIDER = "p2pp" - WORK_P2P_CONNECT = "p2pc" - WORK_P2P_SUCCESS = "p2ps" - WORK_P2P_END = "p2pe" - WORK_P2P_LAST = "p2pl" - WORK_STATUS = "stus" - RES_MSG = "msg0" - RES_CLOSE = "clse" - NEW_UDP_CONN = "udpc" //p2p udp conn - NEW_TASK = "task" - NEW_CONF = "conf" - NEW_HOST = "host" - CONN_TCP = "tcp" - CONN_UDP = "udp" - CONN_TEST = "TST" - UnauthorizedBytes = `HTTP/1.1 401 Unauthorized -Content-Type: text/plain; charset=utf-8 -WWW-Authenticate: Basic realm="easyProxy" - -401 Unauthorized` - ConnectionFailBytes = `HTTP/1.1 404 Not Found - -` -) diff --git a/lib/common/logs.go b/lib/common/logs.go deleted file mode 100644 index 477ac5b..0000000 --- a/lib/common/logs.go +++ /dev/null @@ -1,48 +0,0 @@ -package common - -import ( - "github.com/astaxie/beego/logs" - "time" -) - -const MaxMsgLen = 5000 - -var logMsgs string - -func init() { - logs.Register("store", func() logs.Logger { - return new(StoreMsg) - }) -} - -func GetLogMsg() string { - return logMsgs -} - -type StoreMsg struct { -} - -func (lg *StoreMsg) Init(config string) error { - return nil -} - -func (lg *StoreMsg) WriteMsg(when time.Time, msg string, level int) error { - m := when.Format("2006-01-02 15:04:05") + " " + msg + "\r\n" - if len(logMsgs) > MaxMsgLen { - start := MaxMsgLen - len(m) - if start <= 0 { - start = MaxMsgLen - } - logMsgs = logMsgs[start:] - } - logMsgs += m - return nil -} - -func (lg *StoreMsg) Destroy() { - return -} - -func (lg *StoreMsg) Flush() { - return -} diff --git a/lib/common/netpackager.go b/lib/common/netpackager.go deleted file mode 100644 index b54121f..0000000 --- a/lib/common/netpackager.go +++ /dev/null @@ -1,219 +0,0 @@ -package common - -import ( - "bytes" - "encoding/binary" - "errors" - "io" - "io/ioutil" - "net" - "strconv" -) - -type NetPackager interface { - Pack(writer io.Writer) (err error) - UnPack(reader io.Reader) (err error) -} - -const ( - ipV4 = 1 - domainName = 3 - ipV6 = 4 -) - -type UDPHeader struct { - Rsv uint16 - Frag uint8 - Addr *Addr -} - -func NewUDPHeader(rsv uint16, frag uint8, addr *Addr) *UDPHeader { - return &UDPHeader{ - Rsv: rsv, - Frag: frag, - Addr: addr, - } -} - -type Addr struct { - Type uint8 - Host string - Port uint16 -} - -func (addr *Addr) String() string { - return net.JoinHostPort(addr.Host, strconv.Itoa(int(addr.Port))) -} - -func (addr *Addr) Decode(b []byte) error { - addr.Type = b[0] - pos := 1 - switch addr.Type { - case ipV4: - addr.Host = net.IP(b[pos : pos+net.IPv4len]).String() - pos += net.IPv4len - case ipV6: - addr.Host = net.IP(b[pos : pos+net.IPv6len]).String() - pos += net.IPv6len - case domainName: - addrlen := int(b[pos]) - pos++ - addr.Host = string(b[pos : pos+addrlen]) - pos += addrlen - default: - return errors.New("decode error") - } - - addr.Port = binary.BigEndian.Uint16(b[pos:]) - - return nil -} - -func (addr *Addr) Encode(b []byte) (int, error) { - b[0] = addr.Type - pos := 1 - switch addr.Type { - case ipV4: - ip4 := net.ParseIP(addr.Host).To4() - if ip4 == nil { - ip4 = net.IPv4zero.To4() - } - pos += copy(b[pos:], ip4) - case domainName: - b[pos] = byte(len(addr.Host)) - pos++ - pos += copy(b[pos:], []byte(addr.Host)) - case ipV6: - ip16 := net.ParseIP(addr.Host).To16() - if ip16 == nil { - ip16 = net.IPv6zero.To16() - } - pos += copy(b[pos:], ip16) - default: - b[0] = ipV4 - copy(b[pos:pos+4], net.IPv4zero.To4()) - pos += 4 - } - binary.BigEndian.PutUint16(b[pos:], addr.Port) - pos += 2 - - return pos, nil -} - -func (h *UDPHeader) Write(w io.Writer) error { - b := BufPoolUdp.Get().([]byte) - defer BufPoolUdp.Put(b) - - binary.BigEndian.PutUint16(b[:2], h.Rsv) - b[2] = h.Frag - - addr := h.Addr - if addr == nil { - addr = &Addr{} - } - length, _ := addr.Encode(b[3:]) - - _, err := w.Write(b[:3+length]) - return err -} - -type UDPDatagram struct { - Header *UDPHeader - Data []byte -} - -func ReadUDPDatagram(r io.Reader) (*UDPDatagram, error) { - b := BufPoolUdp.Get().([]byte) - defer BufPoolUdp.Put(b) - - // when r is a streaming (such as TCP connection), we may read more than the required data, - // but we don't know how to handle it. So we use io.ReadFull to instead of io.ReadAtLeast - // to make sure that no redundant data will be discarded. - n, err := io.ReadFull(r, b[:5]) - if err != nil { - return nil, err - } - - header := &UDPHeader{ - Rsv: binary.BigEndian.Uint16(b[:2]), - Frag: b[2], - } - - atype := b[3] - hlen := 0 - switch atype { - case ipV4: - hlen = 10 - case ipV6: - hlen = 22 - case domainName: - hlen = 7 + int(b[4]) - default: - return nil, errors.New("addr not support") - } - dlen := int(header.Rsv) - if dlen == 0 { // standard SOCKS5 UDP datagram - extra, err := ioutil.ReadAll(r) // we assume no redundant data - if err != nil { - return nil, err - } - copy(b[n:], extra) - n += len(extra) // total length - dlen = n - hlen // data length - } else { // extended feature, for UDP over TCP, using reserved field as data length - if _, err := io.ReadFull(r, b[n:hlen+dlen]); err != nil { - return nil, err - } - n = hlen + dlen - } - header.Addr = new(Addr) - if err := header.Addr.Decode(b[3:hlen]); err != nil { - return nil, err - } - data := make([]byte, dlen) - copy(data, b[hlen:n]) - d := &UDPDatagram{ - Header: header, - Data: data, - } - return d, nil -} - -func NewUDPDatagram(header *UDPHeader, data []byte) *UDPDatagram { - return &UDPDatagram{ - Header: header, - Data: data, - } -} - -func (d *UDPDatagram) Write(w io.Writer) error { - h := d.Header - if h == nil { - h = &UDPHeader{} - } - buf := bytes.Buffer{} - if err := h.Write(&buf); err != nil { - return err - } - if _, err := buf.Write(d.Data); err != nil { - return err - } - - _, err := buf.WriteTo(w) - return err -} - -func ToSocksAddr(addr net.Addr) *Addr { - host := "0.0.0.0" - port := 0 - if addr != nil { - h, p, _ := net.SplitHostPort(addr.String()) - host = h - port, _ = strconv.Atoi(p) - } - return &Addr{ - Type: ipV4, - Host: host, - Port: uint16(port), - } -} diff --git a/lib/common/pool.go b/lib/common/pool.go deleted file mode 100644 index 8a487aa..0000000 --- a/lib/common/pool.go +++ /dev/null @@ -1,95 +0,0 @@ -package common - -import ( - "sync" -) - -const PoolSize = 64 * 1024 -const PoolSizeSmall = 100 -const PoolSizeUdp = 1472 + 200 -const PoolSizeCopy = 32 << 10 - -var BufPool = sync.Pool{ - New: func() interface{} { - return make([]byte, PoolSize) - }, -} - -var BufPoolUdp = sync.Pool{ - New: func() interface{} { - return make([]byte, PoolSizeUdp) - }, -} -var BufPoolMax = sync.Pool{ - New: func() interface{} { - return make([]byte, PoolSize) - }, -} -var BufPoolSmall = sync.Pool{ - New: func() interface{} { - return make([]byte, PoolSizeSmall) - }, -} -var BufPoolCopy = sync.Pool{ - New: func() interface{} { - return make([]byte, PoolSizeCopy) - }, -} - -func PutBufPoolUdp(buf []byte) { - if cap(buf) == PoolSizeUdp { - BufPoolUdp.Put(buf[:PoolSizeUdp]) - } -} - -func PutBufPoolCopy(buf []byte) { - if cap(buf) == PoolSizeCopy { - BufPoolCopy.Put(buf[:PoolSizeCopy]) - } -} - -func GetBufPoolCopy() []byte { - return (BufPoolCopy.Get().([]byte))[:PoolSizeCopy] -} - -func PutBufPoolMax(buf []byte) { - if cap(buf) == PoolSize { - BufPoolMax.Put(buf[:PoolSize]) - } -} - -type copyBufferPool struct { - pool sync.Pool -} - -func (Self *copyBufferPool) New() { - Self.pool = sync.Pool{ - New: func() interface{} { - return make([]byte, PoolSizeCopy, PoolSizeCopy) - }, - } -} - -func (Self *copyBufferPool) Get() []byte { - buf := Self.pool.Get().([]byte) - return buf[:PoolSizeCopy] // just like make a new slice, but data may not be 0 -} - -func (Self *copyBufferPool) Put(x []byte) { - if len(x) == PoolSizeCopy { - Self.pool.Put(x) - } else { - x = nil // buf is not full, not allowed, New method returns a full buf - } -} - -var once = sync.Once{} -var CopyBuff = copyBufferPool{} - -func newPool() { - CopyBuff.New() -} - -func init() { - once.Do(newPool) -} diff --git a/lib/common/pprof.go b/lib/common/pprof.go deleted file mode 100644 index 7f9ee76..0000000 --- a/lib/common/pprof.go +++ /dev/null @@ -1,29 +0,0 @@ -package common - -import ( - "github.com/astaxie/beego" - "github.com/astaxie/beego/logs" - "net/http" - _ "net/http/pprof" -) - -func InitPProfFromFile() { - ip := beego.AppConfig.String("pprof_ip") - p := beego.AppConfig.String("pprof_port") - if len(ip) > 0 && len(p) > 0 && IsPort(p) { - runPProf(ip + ":" + p) - } -} - -func InitPProfFromArg(arg string) { - if len(arg) > 0 { - runPProf(arg) - } -} - -func runPProf(ipPort string) { - go func() { - _ = http.ListenAndServe(ipPort, nil) - }() - logs.Info("PProf debug listen on", ipPort) -} diff --git a/lib/common/run.go b/lib/common/run.go deleted file mode 100644 index e124915..0000000 --- a/lib/common/run.go +++ /dev/null @@ -1,89 +0,0 @@ -package common - -import ( - "os" - "path/filepath" - "runtime" -) - -//Get the currently selected configuration file directory -//For non-Windows systems, select the /etc/nps as config directory if exist, or select ./ -//windows system, select the C:\Program Files\nps as config directory if exist, or select ./ -func GetRunPath() string { - var path string - if path = GetInstallPath(); !FileExists(path) { - return GetAppPath() - } - return path -} - -//Different systems get different installation paths -func GetInstallPath() string { - var path string - if IsWindows() { - path = `C:\Program Files\nps` - } else { - path = "/etc/nps" - } - return path -} - -//Get the absolute path to the running directory -func GetAppPath() string { - if path, err := filepath.Abs(filepath.Dir(os.Args[0])); err == nil { - return path - } - return os.Args[0] -} - -//Determine whether the current system is a Windows system? -func IsWindows() bool { - if runtime.GOOS == "windows" { - return true - } - return false -} - -//interface log file path -func GetLogPath() string { - var path string - if IsWindows() { - path = filepath.Join(GetAppPath(), "nps.log") - } else { - path = "/var/log/nps.log" - } - return path -} - -//interface npc log file path -func GetNpcLogPath() string { - var path string - if IsWindows() { - path = filepath.Join(GetAppPath(), "npc.log") - } else { - path = "/var/log/npc.log" - } - return path -} - -//interface pid file path -func GetTmpPath() string { - var path string - if IsWindows() { - path = GetAppPath() - } else { - path = "/tmp" - } - return path -} - -//config file path -func GetConfigPath() string { - var path string - if IsWindows() { - path = filepath.Join(GetAppPath(), "conf/npc.conf") - } else { - path = "conf/npc.conf" - } - return path -} diff --git a/lib/common/util.go b/lib/common/util.go deleted file mode 100755 index cb87daa..0000000 --- a/lib/common/util.go +++ /dev/null @@ -1,469 +0,0 @@ -package common - -import ( - "bytes" - "ehang.io/nps/lib/version" - "encoding/base64" - "encoding/binary" - "errors" - "fmt" - "html/template" - "io" - "io/ioutil" - "net" - "net/http" - "os" - "regexp" - "strconv" - "strings" - "sync" - - "ehang.io/nps/lib/crypt" -) - -//Get the corresponding IP address through domain name -func GetHostByName(hostname string) string { - if !DomainCheck(hostname) { - return hostname - } - ips, _ := net.LookupIP(hostname) - if ips != nil { - for _, v := range ips { - if v.To4() != nil { - return v.String() - } - } - } - return "" -} - -//Check the legality of domain -func DomainCheck(domain string) bool { - var match bool - IsLine := "^((http://)|(https://))?([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}(/)" - NotLine := "^((http://)|(https://))?([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}" - match, _ = regexp.MatchString(IsLine, domain) - if !match { - match, _ = regexp.MatchString(NotLine, domain) - } - return match -} - -//Check if the Request request is validated -func CheckAuth(r *http.Request, user, passwd string) bool { - s := strings.SplitN(r.Header.Get("Authorization"), " ", 2) - if len(s) != 2 { - s = strings.SplitN(r.Header.Get("Proxy-Authorization"), " ", 2) - if len(s) != 2 { - return false - } - } - - b, err := base64.StdEncoding.DecodeString(s[1]) - if err != nil { - return false - } - - pair := strings.SplitN(string(b), ":", 2) - if len(pair) != 2 { - return false - } - return pair[0] == user && pair[1] == passwd -} - -//get bool by str -func GetBoolByStr(s string) bool { - switch s { - case "1", "true": - return true - } - return false -} - -//get str by bool -func GetStrByBool(b bool) string { - if b { - return "1" - } - return "0" -} - -//int -func GetIntNoErrByStr(str string) int { - i, _ := strconv.Atoi(strings.TrimSpace(str)) - return i -} - -//Get verify value -func Getverifyval(vkey string) string { - return crypt.Md5(vkey) -} - -//Change headers and host of request -func ChangeHostAndHeader(r *http.Request, host string, header string, addr string, addOrigin bool) { - if host != "" { - r.Host = host - } - if header != "" { - h := strings.Split(header, "\n") - for _, v := range h { - hd := strings.Split(v, ":") - if len(hd) == 2 { - r.Header.Set(hd[0], hd[1]) - } - } - } - addr = strings.Split(addr, ":")[0] - if prior, ok := r.Header["X-Forwarded-For"]; ok { - addr = strings.Join(prior, ", ") + ", " + addr - } - if addOrigin { - r.Header.Set("X-Forwarded-For", addr) - r.Header.Set("X-Real-IP", addr) - } -} - -//Read file content by file path -func ReadAllFromFile(filePath string) ([]byte, error) { - f, err := os.Open(filePath) - if err != nil { - return nil, err - } - defer f.Close() - return ioutil.ReadAll(f) -} - -// FileExists reports whether the named file or directory exists. -func FileExists(name string) bool { - if _, err := os.Stat(name); err != nil { - if os.IsNotExist(err) { - return false - } - } - return true -} - -//Judge whether the TCP port can open normally -func TestTcpPort(port int) bool { - l, err := net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), port, ""}) - defer func() { - if l != nil { - l.Close() - } - }() - if err != nil { - return false - } - return true -} - -//Judge whether the UDP port can open normally -func TestUdpPort(port int) bool { - l, err := net.ListenUDP("udp", &net.UDPAddr{net.ParseIP("0.0.0.0"), port, ""}) - defer func() { - if l != nil { - l.Close() - } - }() - if err != nil { - return false - } - return true -} - -//Write length and individual byte data -//Length prevents sticking -//# Characters are used to separate data -func BinaryWrite(raw *bytes.Buffer, v ...string) { - b := GetWriteStr(v...) - binary.Write(raw, binary.LittleEndian, int32(len(b))) - binary.Write(raw, binary.LittleEndian, b) -} - -// get seq str -func GetWriteStr(v ...string) []byte { - buffer := new(bytes.Buffer) - var l int32 - for _, v := range v { - l += int32(len([]byte(v))) + int32(len([]byte(CONN_DATA_SEQ))) - binary.Write(buffer, binary.LittleEndian, []byte(v)) - binary.Write(buffer, binary.LittleEndian, []byte(CONN_DATA_SEQ)) - } - return buffer.Bytes() -} - -//inArray str interface -func InStrArr(arr []string, val string) bool { - for _, v := range arr { - if v == val { - return true - } - } - return false -} - -//inArray int interface -func InIntArr(arr []int, val int) bool { - for _, v := range arr { - if v == val { - return true - } - } - return false -} - -//format ports str to a int array -func GetPorts(p string) []int { - var ps []int - arr := strings.Split(p, ",") - for _, v := range arr { - fw := strings.Split(v, "-") - if len(fw) == 2 { - if IsPort(fw[0]) && IsPort(fw[1]) { - start, _ := strconv.Atoi(fw[0]) - end, _ := strconv.Atoi(fw[1]) - for i := start; i <= end; i++ { - ps = append(ps, i) - } - } else { - continue - } - } else if IsPort(v) { - p, _ := strconv.Atoi(v) - ps = append(ps, p) - } - } - return ps -} - -//is the string a port -func IsPort(p string) bool { - pi, err := strconv.Atoi(p) - if err != nil { - return false - } - if pi > 65536 || pi < 1 { - return false - } - return true -} - -//if the s is just a port,return 127.0.0.1:s -func FormatAddress(s string) string { - if strings.Contains(s, ":") { - return s - } - return "127.0.0.1:" + s -} - -//get address from the complete address -func GetIpByAddr(addr string) string { - arr := strings.Split(addr, ":") - return arr[0] -} - -//get port from the complete address -func GetPortByAddr(addr string) int { - arr := strings.Split(addr, ":") - if len(arr) < 2 { - return 0 - } - p, err := strconv.Atoi(arr[1]) - if err != nil { - return 0 - } - return p -} - -func CopyBuffer(dst io.Writer, src io.Reader, label ...string) (written int64, err error) { - buf := CopyBuff.Get() - defer CopyBuff.Put(buf) - for { - nr, er := src.Read(buf) - //if len(pr)>0 && pr[0] && nr > 50 { - // logs.Warn(string(buf[:50])) - //} - if nr > 0 { - nw, ew := dst.Write(buf[0:nr]) - if nw > 0 { - written += int64(nw) - } - if ew != nil { - err = ew - break - } - if nr != nw { - err = io.ErrShortWrite - break - } - } - if er != nil { - err = er - break - } - } - return written, err -} - -//send this ip forget to get a local udp port -func GetLocalUdpAddr() (net.Conn, error) { - tmpConn, err := net.Dial("udp", "114.114.114.114:53") - if err != nil { - return nil, err - } - return tmpConn, tmpConn.Close() -} - -//parse template -func ParseStr(str string) (string, error) { - tmp := template.New("npc") - var err error - w := new(bytes.Buffer) - if tmp, err = tmp.Parse(str); err != nil { - return "", err - } - if err = tmp.Execute(w, GetEnvMap()); err != nil { - return "", err - } - return w.String(), nil -} - -//get env -func GetEnvMap() map[string]string { - m := make(map[string]string) - environ := os.Environ() - for i := range environ { - tmp := strings.Split(environ[i], "=") - if len(tmp) == 2 { - m[tmp[0]] = tmp[1] - } - } - return m -} - -//throw the empty element of the string array -func TrimArr(arr []string) []string { - newArr := make([]string, 0) - for _, v := range arr { - if v != "" { - newArr = append(newArr, v) - } - } - return newArr -} - -// -func IsArrContains(arr []string, val string) bool { - if arr == nil { - return false - } - for _, v := range arr { - if v == val { - return true - } - } - return false -} - -//remove value from string array -func RemoveArrVal(arr []string, val string) []string { - for k, v := range arr { - if v == val { - arr = append(arr[:k], arr[k+1:]...) - return arr - } - } - return arr -} - -//convert bytes to num -func BytesToNum(b []byte) int { - var str string - for i := 0; i < len(b); i++ { - str += strconv.Itoa(int(b[i])) - } - x, _ := strconv.Atoi(str) - return int(x) -} - -//get the length of the sync map -func GeSynctMapLen(m sync.Map) int { - var c int - m.Range(func(key, value interface{}) bool { - c++ - return true - }) - return c -} - -func GetExtFromPath(path string) string { - s := strings.Split(path, ".") - re, err := regexp.Compile(`(\w+)`) - if err != nil { - return "" - } - return string(re.Find([]byte(s[0]))) -} - -var externalIp string - -func GetExternalIp() string { - if externalIp != "" { - return externalIp - } - resp, err := http.Get("http://myexternalip.com/raw") - if err != nil { - return "" - } - defer resp.Body.Close() - content, _ := ioutil.ReadAll(resp.Body) - externalIp = string(content) - return externalIp -} - -func GetIntranetIp() (error, string) { - addrs, err := net.InterfaceAddrs() - if err != nil { - return nil, "" - } - for _, address := range addrs { - // 检查ip地址判断是否回环地址 - if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { - if ipnet.IP.To4() != nil { - return nil, ipnet.IP.To4().String() - } - } - } - return errors.New("get intranet ip error"), "" -} - -func IsPublicIP(IP net.IP) bool { - if IP.IsLoopback() || IP.IsLinkLocalMulticast() || IP.IsLinkLocalUnicast() { - return false - } - if ip4 := IP.To4(); ip4 != nil { - switch true { - case ip4[0] == 10: - return false - case ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31: - return false - case ip4[0] == 192 && ip4[1] == 168: - return false - default: - return true - } - } - return false -} - -func GetServerIpByClientIp(clientIp net.IP) string { - if IsPublicIP(clientIp) { - return GetExternalIp() - } - _, ip := GetIntranetIp() - return ip -} - -func PrintVersion() { - fmt.Printf("Version: %s\nCore version: %s\nSame core version of client and server can connect each other\n", version.VERSION, version.GetVersion()) -} diff --git a/lib/config/config.go b/lib/config/config.go deleted file mode 100644 index f7dec15..0000000 --- a/lib/config/config.go +++ /dev/null @@ -1,329 +0,0 @@ -package config - -import ( - "errors" - "fmt" - "regexp" - "strings" - - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/file" -) - -type CommonConfig struct { - Server string - VKey string - Tp string //bridgeType kcp or tcp - AutoReconnection bool - ProxyUrl string - Client *file.Client - DisconnectTime int -} - -type LocalServer struct { - Type string - Port int - Ip string - Password string - Target string -} - -type Config struct { - content string - title []string - CommonConfig *CommonConfig - Hosts []*file.Host - Tasks []*file.Tunnel - Healths []*file.Health - LocalServer []*LocalServer -} - -func NewConfig(path string) (c *Config, err error) { - c = new(Config) - var b []byte - if b, err = common.ReadAllFromFile(path); err != nil { - return - } else { - if c.content, err = common.ParseStr(string(b)); err != nil { - return nil, err - } - if c.title, err = getAllTitle(c.content); err != nil { - return - } - var nowIndex int - var nextIndex int - var nowContent string - for i := 0; i < len(c.title); i++ { - nowIndex = strings.Index(c.content, c.title[i]) + len(c.title[i]) - if i < len(c.title)-1 { - nextIndex = strings.Index(c.content, c.title[i+1]) - } else { - nextIndex = len(c.content) - } - nowContent = c.content[nowIndex:nextIndex] - - if strings.Index(getTitleContent(c.title[i]), "secret") == 0 && !strings.Contains(nowContent, "mode") { - local := delLocalService(nowContent) - local.Type = "secret" - c.LocalServer = append(c.LocalServer, local) - continue - } - //except mode - if strings.Index(getTitleContent(c.title[i]), "p2p") == 0 && !strings.Contains(nowContent, "mode") { - local := delLocalService(nowContent) - local.Type = "p2p" - c.LocalServer = append(c.LocalServer, local) - continue - } - //health set - if strings.Index(getTitleContent(c.title[i]), "health") == 0 { - c.Healths = append(c.Healths, dealHealth(nowContent)) - continue - } - switch c.title[i] { - case "[common]": - c.CommonConfig = dealCommon(nowContent) - default: - if strings.Index(nowContent, "host") > -1 { - h := dealHost(nowContent) - h.Remark = getTitleContent(c.title[i]) - c.Hosts = append(c.Hosts, h) - } else { - t := dealTunnel(nowContent) - t.Remark = getTitleContent(c.title[i]) - c.Tasks = append(c.Tasks, t) - } - } - } - } - return -} - -func getTitleContent(s string) string { - re, _ := regexp.Compile(`[\[\]]`) - return re.ReplaceAllString(s, "") -} - -func dealCommon(s string) *CommonConfig { - c := &CommonConfig{} - c.Client = file.NewClient("", true, true) - c.Client.Cnf = new(file.Config) - for _, v := range splitStr(s) { - item := strings.Split(v, "=") - if len(item) == 0 { - continue - } else if len(item) == 1 { - item = append(item, "") - } - switch item[0] { - case "server_addr": - c.Server = item[1] - case "vkey": - c.VKey = item[1] - case "conn_type": - c.Tp = item[1] - case "auto_reconnection": - c.AutoReconnection = common.GetBoolByStr(item[1]) - case "basic_username": - c.Client.Cnf.U = item[1] - case "basic_password": - c.Client.Cnf.P = item[1] - case "web_password": - c.Client.WebPassword = item[1] - case "web_username": - c.Client.WebUserName = item[1] - case "compress": - c.Client.Cnf.Compress = common.GetBoolByStr(item[1]) - case "crypt": - c.Client.Cnf.Crypt = common.GetBoolByStr(item[1]) - case "proxy_url": - c.ProxyUrl = item[1] - case "rate_limit": - c.Client.RateLimit = common.GetIntNoErrByStr(item[1]) - case "flow_limit": - c.Client.Flow.FlowLimit = int64(common.GetIntNoErrByStr(item[1])) - case "max_conn": - c.Client.MaxConn = common.GetIntNoErrByStr(item[1]) - case "remark": - c.Client.Remark = item[1] - case "pprof_addr": - common.InitPProfFromArg(item[1]) - case "disconnect_timeout": - c.DisconnectTime = common.GetIntNoErrByStr(item[1]) - } - } - return c -} - -func dealHost(s string) *file.Host { - h := &file.Host{} - h.Target = new(file.Target) - h.Scheme = "all" - var headerChange string - for _, v := range splitStr(s) { - item := strings.Split(v, "=") - if len(item) == 0 { - continue - } else if len(item) == 1 { - item = append(item, "") - } - switch strings.TrimSpace(item[0]) { - case "host": - h.Host = item[1] - case "target_addr": - h.Target.TargetStr = strings.Replace(item[1], ",", "\n", -1) - case "host_change": - h.HostChange = item[1] - case "scheme": - h.Scheme = item[1] - case "location": - h.Location = item[1] - default: - if strings.Contains(item[0], "header") { - headerChange += strings.Replace(item[0], "header_", "", -1) + ":" + item[1] + "\n" - } - h.HeaderChange = headerChange - } - } - return h -} - -func dealHealth(s string) *file.Health { - h := &file.Health{} - for _, v := range splitStr(s) { - item := strings.Split(v, "=") - if len(item) == 0 { - continue - } else if len(item) == 1 { - item = append(item, "") - } - switch strings.TrimSpace(item[0]) { - case "health_check_timeout": - h.HealthCheckTimeout = common.GetIntNoErrByStr(item[1]) - case "health_check_max_failed": - h.HealthMaxFail = common.GetIntNoErrByStr(item[1]) - case "health_check_interval": - h.HealthCheckInterval = common.GetIntNoErrByStr(item[1]) - case "health_http_url": - h.HttpHealthUrl = item[1] - case "health_check_type": - h.HealthCheckType = item[1] - case "health_check_target": - h.HealthCheckTarget = item[1] - } - } - return h -} - -func dealTunnel(s string) *file.Tunnel { - t := &file.Tunnel{} - t.Target = new(file.Target) - for _, v := range splitStr(s) { - item := strings.Split(v, "=") - if len(item) == 0 { - continue - } else if len(item) == 1 { - item = append(item, "") - } - switch strings.TrimSpace(item[0]) { - case "server_port": - t.Ports = item[1] - case "server_ip": - t.ServerIp = item[1] - case "mode": - t.Mode = item[1] - case "target_addr": - t.Target.TargetStr = strings.Replace(item[1], ",", "\n", -1) - case "target_port": - t.Target.TargetStr = item[1] - case "target_ip": - t.TargetAddr = item[1] - case "password": - t.Password = item[1] - case "local_path": - t.LocalPath = item[1] - case "strip_pre": - t.StripPre = item[1] - case "multi_account": - t.MultiAccount = &file.MultiAccount{} - if common.FileExists(item[1]) { - if b, err := common.ReadAllFromFile(item[1]); err != nil { - panic(err) - } else { - if content, err := common.ParseStr(string(b)); err != nil { - panic(err) - } else { - t.MultiAccount.AccountMap = dealMultiUser(content) - } - } - } - } - } - return t - -} - -func dealMultiUser(s string) map[string]string { - multiUserMap := make(map[string]string) - for _, v := range splitStr(s) { - item := strings.Split(v, "=") - if len(item) == 0 { - continue - } else if len(item) == 1 { - item = append(item, "") - } - multiUserMap[strings.TrimSpace(item[0])] = item[1] - } - return multiUserMap -} - -func delLocalService(s string) *LocalServer { - l := new(LocalServer) - for _, v := range splitStr(s) { - item := strings.Split(v, "=") - if len(item) == 0 { - continue - } else if len(item) == 1 { - item = append(item, "") - } - switch item[0] { - case "local_port": - l.Port = common.GetIntNoErrByStr(item[1]) - case "local_ip": - l.Ip = item[1] - case "password": - l.Password = item[1] - case "target_addr": - l.Target = item[1] - } - } - return l -} - -func getAllTitle(content string) (arr []string, err error) { - var re *regexp.Regexp - re, err = regexp.Compile(`(?m)^\[[^\[\]\r\n]+\]`) - if err != nil { - return - } - arr = re.FindAllString(content, -1) - m := make(map[string]bool) - for _, v := range arr { - if _, ok := m[v]; ok { - err = errors.New(fmt.Sprintf("Item names %s are not allowed to be duplicated", v)) - return - } - m[v] = true - } - return -} - -func splitStr(s string) (configDataArr []string) { - if common.IsWindows() { - configDataArr = strings.Split(s, "\r\n") - } - if len(configDataArr) < 3 { - configDataArr = strings.Split(s, "\n") - } - return -} diff --git a/lib/config/config_test.go b/lib/config/config_test.go deleted file mode 100644 index 06023be..0000000 --- a/lib/config/config_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package config - -import ( - "log" - "regexp" - "testing" -) - -func TestReg(t *testing.T) { - content := ` -[common] -server=127.0.0.1:8284 -tp=tcp -vkey=123 -[web2] -host=www.baidu.com -host_change=www.sina.com -target=127.0.0.1:8080,127.0.0.1:8082 -header_cookkile=122123 -header_user-Agent=122123 -[web2] -host=www.baidu.com -host_change=www.sina.com -target=127.0.0.1:8080,127.0.0.1:8082 -header_cookkile="122123" -header_user-Agent=122123 -[tunnel1] -type=udp -target=127.0.0.1:8080 -port=9001 -compress=snappy -crypt=true -u=1 -p=2 -[tunnel2] -type=tcp -target=127.0.0.1:8080 -port=9001 -compress=snappy -crypt=true -u=1 -p=2 -` - re, err := regexp.Compile(`\[.+?\]`) - if err != nil { - t.Fail() - } - log.Println(re.FindAllString(content, -1)) -} - -func TestDealCommon(t *testing.T) { - s := `server=127.0.0.1:8284 -tp=tcp -vkey=123` - f := new(CommonConfig) - f.Server = "127.0.0.1:8284" - f.Tp = "tcp" - f.VKey = "123" - if c := dealCommon(s); *c != *f { - t.Fail() - } -} - -func TestGetTitleContent(t *testing.T) { - s := "[common]" - if getTitleContent(s) != "common" { - t.Fail() - } -} diff --git a/lib/conn/conn.go b/lib/conn/conn.go deleted file mode 100755 index dbc7d88..0000000 --- a/lib/conn/conn.go +++ /dev/null @@ -1,431 +0,0 @@ -package conn - -import ( - "bufio" - "bytes" - "ehang.io/nps/lib/goroutine" - "encoding/binary" - "encoding/json" - "errors" - "github.com/astaxie/beego/logs" - "io" - "net" - "net/http" - "net/url" - "strconv" - "strings" - "sync" - "time" - - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/crypt" - "ehang.io/nps/lib/file" - "ehang.io/nps/lib/pmux" - "ehang.io/nps/lib/rate" - "github.com/xtaci/kcp-go" -) - -type Conn struct { - Conn net.Conn - Rb []byte -} - -//new conn -func NewConn(conn net.Conn) *Conn { - return &Conn{Conn: conn} -} - -func (s *Conn) readRequest(buf []byte) (n int, err error) { - var rd int - for { - rd, err = s.Read(buf[n:]) - if err != nil { - return - } - n += rd - if n < 4 { - continue - } - if string(buf[n-4:n]) == "\r\n\r\n" { - return - } - // buf is full, can't contain the request - if n == cap(buf) { - err = io.ErrUnexpectedEOF - return - } - } -} - -//get host 、connection type、method...from connection -func (s *Conn) GetHost() (method, address string, rb []byte, err error, r *http.Request) { - var b [32 * 1024]byte - var n int - if n, err = s.readRequest(b[:]); err != nil { - return - } - rb = b[:n] - r, err = http.ReadRequest(bufio.NewReader(bytes.NewReader(rb))) - if err != nil { - return - } - hostPortURL, err := url.Parse(r.Host) - if err != nil { - address = r.Host - err = nil - return - } - if hostPortURL.Opaque == "443" { - if strings.Index(r.Host, ":") == -1 { - address = r.Host + ":443" - } else { - address = r.Host - } - } else { - if strings.Index(r.Host, ":") == -1 { - address = r.Host + ":80" - } else { - address = r.Host - } - } - return -} - -func (s *Conn) GetShortLenContent() (b []byte, err error) { - var l int - if l, err = s.GetLen(); err != nil { - return - } - if l < 0 || l > 32<<10 { - err = errors.New("read length error") - return - } - return s.GetShortContent(l) -} - -func (s *Conn) GetShortContent(l int) (b []byte, err error) { - buf := make([]byte, l) - return buf, binary.Read(s, binary.LittleEndian, &buf) -} - -//读取指定长度内容 -func (s *Conn) ReadLen(cLen int, buf []byte) (int, error) { - if cLen > len(buf) || cLen <= 0 { - return 0, errors.New("长度错误" + strconv.Itoa(cLen)) - } - if n, err := io.ReadFull(s, buf[:cLen]); err != nil || n != cLen { - return n, errors.New("Error reading specified length " + err.Error()) - } - return cLen, nil -} - -func (s *Conn) GetLen() (int, error) { - var l int32 - err := binary.Read(s, binary.LittleEndian, &l) - return int(l), err -} - -func (s *Conn) WriteLenContent(buf []byte) (err error) { - var b []byte - if b, err = GetLenBytes(buf); err != nil { - return - } - return binary.Write(s.Conn, binary.LittleEndian, b) -} - -//read flag -func (s *Conn) ReadFlag() (string, error) { - buf := make([]byte, 4) - return string(buf), binary.Read(s, binary.LittleEndian, &buf) -} - -//set alive -func (s *Conn) SetAlive(tp string) { - switch s.Conn.(type) { - case *kcp.UDPSession: - s.Conn.(*kcp.UDPSession).SetReadDeadline(time.Time{}) - case *net.TCPConn: - conn := s.Conn.(*net.TCPConn) - conn.SetReadDeadline(time.Time{}) - //conn.SetKeepAlive(false) - //conn.SetKeepAlivePeriod(time.Duration(2 * time.Second)) - case *pmux.PortConn: - s.Conn.(*pmux.PortConn).SetReadDeadline(time.Time{}) - } -} - -//set read deadline -func (s *Conn) SetReadDeadlineBySecond(t time.Duration) { - switch s.Conn.(type) { - case *kcp.UDPSession: - s.Conn.(*kcp.UDPSession).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second)) - case *net.TCPConn: - s.Conn.(*net.TCPConn).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second)) - case *pmux.PortConn: - s.Conn.(*pmux.PortConn).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second)) - } -} - -//get link info from conn -func (s *Conn) GetLinkInfo() (lk *Link, err error) { - err = s.getInfo(&lk) - return -} - -//send info for link -func (s *Conn) SendHealthInfo(info, status string) (int, error) { - raw := bytes.NewBuffer([]byte{}) - common.BinaryWrite(raw, info, status) - return s.Write(raw.Bytes()) -} - -//get health info from conn -func (s *Conn) GetHealthInfo() (info string, status bool, err error) { - var l int - buf := common.BufPoolMax.Get().([]byte) - defer common.PutBufPoolMax(buf) - if l, err = s.GetLen(); err != nil { - return - } else if _, err = s.ReadLen(l, buf); err != nil { - return - } else { - arr := strings.Split(string(buf[:l]), common.CONN_DATA_SEQ) - if len(arr) >= 2 { - return arr[0], common.GetBoolByStr(arr[1]), nil - } - } - return "", false, errors.New("receive health info error") -} - -//get task info -func (s *Conn) GetHostInfo() (h *file.Host, err error) { - err = s.getInfo(&h) - h.Id = int(file.GetDb().JsonDb.GetHostId()) - h.Flow = new(file.Flow) - h.NoStore = true - return -} - -//get task info -func (s *Conn) GetConfigInfo() (c *file.Client, err error) { - err = s.getInfo(&c) - c.NoStore = true - c.Status = true - if c.Flow == nil { - c.Flow = new(file.Flow) - } - c.NoDisplay = false - return -} - -//get task info -func (s *Conn) GetTaskInfo() (t *file.Tunnel, err error) { - err = s.getInfo(&t) - t.Id = int(file.GetDb().JsonDb.GetTaskId()) - t.NoStore = true - t.Flow = new(file.Flow) - return -} - -//send info -func (s *Conn) SendInfo(t interface{}, flag string) (int, error) { - /* - The task info is formed as follows: - +----+-----+---------+ - |type| len | content | - +----+---------------+ - | 4 | 4 | ... | - +----+---------------+ - */ - raw := bytes.NewBuffer([]byte{}) - if flag != "" { - binary.Write(raw, binary.LittleEndian, []byte(flag)) - } - b, err := json.Marshal(t) - if err != nil { - return 0, err - } - lenBytes, err := GetLenBytes(b) - if err != nil { - return 0, err - } - binary.Write(raw, binary.LittleEndian, lenBytes) - return s.Write(raw.Bytes()) -} - -//get task info -func (s *Conn) getInfo(t interface{}) (err error) { - var l int - buf := common.BufPoolMax.Get().([]byte) - defer common.PutBufPoolMax(buf) - if l, err = s.GetLen(); err != nil { - return - } else if _, err = s.ReadLen(l, buf); err != nil { - return - } else { - json.Unmarshal(buf[:l], &t) - } - return -} - -//close -func (s *Conn) Close() error { - return s.Conn.Close() -} - -//write -func (s *Conn) Write(b []byte) (int, error) { - return s.Conn.Write(b) -} - -//read -func (s *Conn) Read(b []byte) (n int, err error) { - if s.Rb != nil { - //if the rb is not nil ,read rb first - if len(s.Rb) > 0 { - n = copy(b, s.Rb) - s.Rb = s.Rb[n:] - return - } - s.Rb = nil - } - return s.Conn.Read(b) -} - -//write sign flag -func (s *Conn) WriteClose() (int, error) { - return s.Write([]byte(common.RES_CLOSE)) -} - -//write main -func (s *Conn) WriteMain() (int, error) { - return s.Write([]byte(common.WORK_MAIN)) -} - -//write main -func (s *Conn) WriteConfig() (int, error) { - return s.Write([]byte(common.WORK_CONFIG)) -} - -//write chan -func (s *Conn) WriteChan() (int, error) { - return s.Write([]byte(common.WORK_CHAN)) -} - -//get task or host result of add -func (s *Conn) GetAddStatus() (b bool) { - binary.Read(s.Conn, binary.LittleEndian, &b) - return -} - -func (s *Conn) WriteAddOk() error { - return binary.Write(s.Conn, binary.LittleEndian, true) -} - -func (s *Conn) WriteAddFail() error { - defer s.Close() - return binary.Write(s.Conn, binary.LittleEndian, false) -} - -func (s *Conn) LocalAddr() net.Addr { - return s.Conn.LocalAddr() -} - -func (s *Conn) RemoteAddr() net.Addr { - return s.Conn.RemoteAddr() -} - -func (s *Conn) SetDeadline(t time.Time) error { - return s.Conn.SetDeadline(t) -} - -func (s *Conn) SetWriteDeadline(t time.Time) error { - return s.Conn.SetWriteDeadline(t) -} - -func (s *Conn) SetReadDeadline(t time.Time) error { - return s.Conn.SetReadDeadline(t) -} - -//get the assembled amount data(len 4 and content) -func GetLenBytes(buf []byte) (b []byte, err error) { - raw := bytes.NewBuffer([]byte{}) - if err = binary.Write(raw, binary.LittleEndian, int32(len(buf))); err != nil { - return - } - if err = binary.Write(raw, binary.LittleEndian, buf); err != nil { - return - } - b = raw.Bytes() - return -} - -//udp connection setting -func SetUdpSession(sess *kcp.UDPSession) { - sess.SetStreamMode(true) - sess.SetWindowSize(1024, 1024) - sess.SetReadBuffer(64 * 1024) - sess.SetWriteBuffer(64 * 1024) - sess.SetNoDelay(1, 10, 2, 1) - sess.SetMtu(1600) - sess.SetACKNoDelay(true) - sess.SetWriteDelay(false) -} - -//conn1 mux conn -func CopyWaitGroup(conn1, conn2 net.Conn, crypt bool, snappy bool, rate *rate.Rate, flow *file.Flow, isServer bool, rb []byte) { - //var in, out int64 - //var wg sync.WaitGroup - connHandle := GetConn(conn1, crypt, snappy, rate, isServer) - if rb != nil { - connHandle.Write(rb) - } - //go func(in *int64) { - // wg.Add(1) - // *in, _ = common.CopyBuffer(connHandle, conn2) - // connHandle.Close() - // conn2.Close() - // wg.Done() - //}(&in) - //out, _ = common.CopyBuffer(conn2, connHandle) - //connHandle.Close() - //conn2.Close() - //wg.Wait() - //if flow != nil { - // flow.Add(in, out) - //} - wg := new(sync.WaitGroup) - wg.Add(1) - err := goroutine.CopyConnsPool.Invoke(goroutine.NewConns(connHandle, conn2, flow, wg)) - wg.Wait() - if err != nil { - logs.Error(err) - } -} - -//get crypt or snappy conn -func GetConn(conn net.Conn, cpt, snappy bool, rt *rate.Rate, isServer bool) io.ReadWriteCloser { - if cpt { - if isServer { - return rate.NewRateConn(crypt.NewTlsServerConn(conn), rt) - } - return rate.NewRateConn(crypt.NewTlsClientConn(conn), rt) - } else if snappy { - return rate.NewRateConn(NewSnappyConn(conn), rt) - } - return rate.NewRateConn(conn, rt) -} - -type LenConn struct { - conn io.Writer - Len int -} - -func NewLenConn(conn io.Writer) *LenConn { - return &LenConn{conn: conn} -} -func (c *LenConn) Write(p []byte) (n int, err error) { - n, err = c.conn.Write(p) - c.Len += n - return -} diff --git a/lib/conn/link.go b/lib/conn/link.go deleted file mode 100644 index 653894e..0000000 --- a/lib/conn/link.go +++ /dev/null @@ -1,63 +0,0 @@ -package conn - -import "time" - -type Secret struct { - Password string - Conn *Conn -} - -func NewSecret(p string, conn *Conn) *Secret { - return &Secret{ - Password: p, - Conn: conn, - } -} - -type Link struct { - ConnType string //连接类型 - Host string //目标 - Crypt bool //加密 - Compress bool - LocalProxy bool - RemoteAddr string - Option Options -} - -type Option func(*Options) - -type Options struct { - Timeout time.Duration -} - -var defaultTimeOut = time.Second * 5 - -func NewLink(connType string, host string, crypt bool, compress bool, remoteAddr string, localProxy bool, opts ...Option) *Link { - options := newOptions(opts...) - - return &Link{ - RemoteAddr: remoteAddr, - ConnType: connType, - Host: host, - Crypt: crypt, - Compress: compress, - LocalProxy: localProxy, - Option: options, - } -} - -func newOptions(opts ...Option) Options { - opt := Options{ - Timeout: defaultTimeOut, - } - for _, o := range opts { - o(&opt) - } - return opt -} - -func LinkTimeout(t time.Duration) Option { - return func(opt *Options) { - opt.Timeout = t - } -} diff --git a/lib/conn/listener.go b/lib/conn/listener.go deleted file mode 100644 index bd8e443..0000000 --- a/lib/conn/listener.go +++ /dev/null @@ -1,58 +0,0 @@ -package conn - -import ( - "net" - "strings" - - "github.com/astaxie/beego/logs" - "github.com/xtaci/kcp-go" -) - -func NewTcpListenerAndProcess(addr string, f func(c net.Conn), listener *net.Listener) error { - var err error - *listener, err = net.Listen("tcp", addr) - if err != nil { - return err - } - Accept(*listener, f) - return nil -} - -func NewKcpListenerAndProcess(addr string, f func(c net.Conn)) error { - kcpListener, err := kcp.ListenWithOptions(addr, nil, 150, 3) - if err != nil { - logs.Error(err) - return err - } - for { - c, err := kcpListener.AcceptKCP() - SetUdpSession(c) - if err != nil { - logs.Warn(err) - continue - } - go f(c) - } - return nil -} - -func Accept(l net.Listener, f func(c net.Conn)) { - for { - c, err := l.Accept() - if err != nil { - if strings.Contains(err.Error(), "use of closed network connection") { - break - } - if strings.Contains(err.Error(), "the mux has closed") { - break - } - logs.Warn(err) - continue - } - if c == nil { - logs.Warn("nil connection") - break - } - go f(c) - } -} diff --git a/lib/conn/snappy.go b/lib/conn/snappy.go deleted file mode 100644 index b4764ab..0000000 --- a/lib/conn/snappy.go +++ /dev/null @@ -1,53 +0,0 @@ -package conn - -import ( - "errors" - "io" - - "github.com/golang/snappy" -) - -type SnappyConn struct { - w *snappy.Writer - r *snappy.Reader - c io.Closer -} - -func NewSnappyConn(conn io.ReadWriteCloser) *SnappyConn { - c := new(SnappyConn) - c.w = snappy.NewBufferedWriter(conn) - c.r = snappy.NewReader(conn) - c.c = conn.(io.Closer) - return c -} - -//snappy压缩写 -func (s *SnappyConn) Write(b []byte) (n int, err error) { - if n, err = s.w.Write(b); err != nil { - return - } - if err = s.w.Flush(); err != nil { - return - } - return -} - -//snappy压缩读 -func (s *SnappyConn) Read(b []byte) (n int, err error) { - return s.r.Read(b) -} - -func (s *SnappyConn) Close() error { - err := s.w.Close() - err2 := s.c.Close() - if err != nil && err2 == nil { - return err - } - if err == nil && err2 != nil { - return err2 - } - if err != nil && err2 != nil { - return errors.New(err.Error() + err2.Error()) - } - return nil -} diff --git a/lib/crypt/clientHello.go b/lib/crypt/clientHello.go deleted file mode 100644 index 3b5a272..0000000 --- a/lib/crypt/clientHello.go +++ /dev/null @@ -1,253 +0,0 @@ -package crypt - -import ( - "strings" -) - -type CurveID uint16 -type SignatureScheme uint16 - -const ( - statusTypeOCSP uint8 = 1 - extensionServerName uint16 = 0 - extensionStatusRequest uint16 = 5 - extensionSupportedCurves uint16 = 10 - extensionSupportedPoints uint16 = 11 - extensionSignatureAlgorithms uint16 = 13 - extensionALPN uint16 = 16 - extensionSCT uint16 = 18 // https://tools.ietf.org/html/rfc6962#section-6 - extensionSessionTicket uint16 = 35 - extensionNextProtoNeg uint16 = 13172 // not IANA assigned - extensionRenegotiationInfo uint16 = 0xff01 - scsvRenegotiation uint16 = 0x00ff -) - -type ClientHelloMsg struct { - raw []byte - vers uint16 - random []byte - sessionId []byte - cipherSuites []uint16 - compressionMethods []uint8 - nextProtoNeg bool - serverName string - ocspStapling bool - scts bool - supportedCurves []CurveID - supportedPoints []uint8 - ticketSupported bool - sessionTicket []uint8 - supportedSignatureAlgorithms []SignatureScheme - secureRenegotiation []byte - secureRenegotiationSupported bool - alpnProtocols []string -} - -func (m *ClientHelloMsg) GetServerName() string { - return m.serverName -} - -func (m *ClientHelloMsg) Unmarshal(data []byte) bool { - if len(data) < 42 { - return false - } - m.raw = data - m.vers = uint16(data[4])<<8 | uint16(data[5]) - m.random = data[6:38] - sessionIdLen := int(data[38]) - if sessionIdLen > 32 || len(data) < 39+sessionIdLen { - return false - } - m.sessionId = data[39 : 39+sessionIdLen] - data = data[39+sessionIdLen:] - if len(data) < 2 { - return false - } - // cipherSuiteLen is the number of bytes of cipher suite numbers. Since - // they are uint16s, the number must be even. - cipherSuiteLen := int(data[0])<<8 | int(data[1]) - if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen { - return false - } - numCipherSuites := cipherSuiteLen / 2 - m.cipherSuites = make([]uint16, numCipherSuites) - for i := 0; i < numCipherSuites; i++ { - m.cipherSuites[i] = uint16(data[2+2*i])<<8 | uint16(data[3+2*i]) - if m.cipherSuites[i] == scsvRenegotiation { - m.secureRenegotiationSupported = true - } - } - data = data[2+cipherSuiteLen:] - if len(data) < 1 { - return false - } - compressionMethodsLen := int(data[0]) - if len(data) < 1+compressionMethodsLen { - return false - } - m.compressionMethods = data[1 : 1+compressionMethodsLen] - data = data[1+compressionMethodsLen:] - - m.nextProtoNeg = false - m.serverName = "" - m.ocspStapling = false - m.ticketSupported = false - m.sessionTicket = nil - m.supportedSignatureAlgorithms = nil - m.alpnProtocols = nil - m.scts = false - - if len(data) == 0 { - // ClientHello is optionally followed by extension data - return true - } - if len(data) < 2 { - return false - } - - extensionsLength := int(data[0])<<8 | int(data[1]) - data = data[2:] - if extensionsLength != len(data) { - return false - } - - for len(data) != 0 { - if len(data) < 4 { - return false - } - extension := uint16(data[0])<<8 | uint16(data[1]) - length := int(data[2])<<8 | int(data[3]) - data = data[4:] - if len(data) < length { - return false - } - - switch extension { - case extensionServerName: - d := data[:length] - if len(d) < 2 { - return false - } - namesLen := int(d[0])<<8 | int(d[1]) - d = d[2:] - if len(d) != namesLen { - return false - } - for len(d) > 0 { - if len(d) < 3 { - return false - } - nameType := d[0] - nameLen := int(d[1])<<8 | int(d[2]) - d = d[3:] - if len(d) < nameLen { - return false - } - if nameType == 0 { - m.serverName = string(d[:nameLen]) - // An SNI value may not include a - // trailing dot. See - // https://tools.ietf.org/html/rfc6066#section-3. - if strings.HasSuffix(m.serverName, ".") { - return false - } - break - } - d = d[nameLen:] - } - case extensionNextProtoNeg: - if length > 0 { - return false - } - m.nextProtoNeg = true - case extensionStatusRequest: - m.ocspStapling = length > 0 && data[0] == statusTypeOCSP - case extensionSupportedCurves: - // https://tools.ietf.org/html/rfc4492#section-5.5.1 - if length < 2 { - return false - } - l := int(data[0])<<8 | int(data[1]) - if l%2 == 1 || length != l+2 { - return false - } - numCurves := l / 2 - m.supportedCurves = make([]CurveID, numCurves) - d := data[2:] - for i := 0; i < numCurves; i++ { - m.supportedCurves[i] = CurveID(d[0])<<8 | CurveID(d[1]) - d = d[2:] - } - case extensionSupportedPoints: - // https://tools.ietf.org/html/rfc4492#section-5.5.2 - if length < 1 { - return false - } - l := int(data[0]) - if length != l+1 { - return false - } - m.supportedPoints = make([]uint8, l) - copy(m.supportedPoints, data[1:]) - case extensionSessionTicket: - // https://tools.ietf.org/html/rfc5077#section-3.2 - m.ticketSupported = true - m.sessionTicket = data[:length] - case extensionSignatureAlgorithms: - // https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 - if length < 2 || length&1 != 0 { - return false - } - l := int(data[0])<<8 | int(data[1]) - if l != length-2 { - return false - } - n := l / 2 - d := data[2:] - m.supportedSignatureAlgorithms = make([]SignatureScheme, n) - for i := range m.supportedSignatureAlgorithms { - m.supportedSignatureAlgorithms[i] = SignatureScheme(d[0])<<8 | SignatureScheme(d[1]) - d = d[2:] - } - case extensionRenegotiationInfo: - if length == 0 { - return false - } - d := data[:length] - l := int(d[0]) - d = d[1:] - if l != len(d) { - return false - } - - m.secureRenegotiation = d - m.secureRenegotiationSupported = true - case extensionALPN: - if length < 2 { - return false - } - l := int(data[0])<<8 | int(data[1]) - if l != length-2 { - return false - } - d := data[2:length] - for len(d) != 0 { - stringLen := int(d[0]) - d = d[1:] - if stringLen == 0 || stringLen > len(d) { - return false - } - m.alpnProtocols = append(m.alpnProtocols, string(d[:stringLen])) - d = d[stringLen:] - } - case extensionSCT: - m.scts = true - if length != 0 { - return false - } - } - data = data[length:] - } - - return true -} diff --git a/lib/crypt/crypt.go b/lib/crypt/crypt.go deleted file mode 100644 index 3f723ae..0000000 --- a/lib/crypt/crypt.go +++ /dev/null @@ -1,76 +0,0 @@ -package crypt - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/md5" - "encoding/hex" - "errors" - "math/rand" - "time" -) - -//en -func AesEncrypt(origData, key []byte) ([]byte, error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - blockSize := block.BlockSize() - origData = PKCS5Padding(origData, blockSize) - blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) - crypted := make([]byte, len(origData)) - blockMode.CryptBlocks(crypted, origData) - return crypted, nil -} - -//de -func AesDecrypt(crypted, key []byte) ([]byte, error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - blockSize := block.BlockSize() - blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) - origData := make([]byte, len(crypted)) - blockMode.CryptBlocks(origData, crypted) - err, origData = PKCS5UnPadding(origData) - return origData, err -} - -//Completion when the length is insufficient -func PKCS5Padding(ciphertext []byte, blockSize int) []byte { - padding := blockSize - len(ciphertext)%blockSize - padtext := bytes.Repeat([]byte{byte(padding)}, padding) - return append(ciphertext, padtext...) -} - -//Remove excess -func PKCS5UnPadding(origData []byte) (error, []byte) { - length := len(origData) - unpadding := int(origData[length-1]) - if (length - unpadding) < 0 { - return errors.New("len error"), nil - } - return nil, origData[:(length - unpadding)] -} - -//Generate 32-bit MD5 strings -func Md5(s string) string { - h := md5.New() - h.Write([]byte(s)) - return hex.EncodeToString(h.Sum(nil)) -} - -//Generating Random Verification Key -func GetRandomString(l int) string { - str := "0123456789abcdefghijklmnopqrstuvwxyz" - bytes := []byte(str) - result := []byte{} - r := rand.New(rand.NewSource(time.Now().UnixNano())) - for i := 0; i < l; i++ { - result = append(result, bytes[r.Intn(len(bytes))]) - } - return string(result) -} diff --git a/lib/crypt/tls.go b/lib/crypt/tls.go deleted file mode 100644 index 799a1c0..0000000 --- a/lib/crypt/tls.go +++ /dev/null @@ -1,87 +0,0 @@ -package crypt - -import ( - "crypto/rand" - "crypto/rsa" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "log" - "math/big" - "net" - "os" - "time" - - "github.com/astaxie/beego/logs" -) - -var ( - cert tls.Certificate -) - -func InitTls() { - c, k, err := generateKeyPair("NPS Org") - if err == nil { - cert, err = tls.X509KeyPair(c, k) - } - if err != nil { - log.Fatalln("Error initializing crypto certs", err) - } -} - -func NewTlsServerConn(conn net.Conn) net.Conn { - var err error - if err != nil { - logs.Error(err) - os.Exit(0) - return nil - } - config := &tls.Config{Certificates: []tls.Certificate{cert}} - return tls.Server(conn, config) -} - -func NewTlsClientConn(conn net.Conn) net.Conn { - conf := &tls.Config{ - InsecureSkipVerify: true, - } - return tls.Client(conn, conf) -} - -func generateKeyPair(CommonName string) (rawCert, rawKey []byte, err error) { - // Create private key and self-signed certificate - // Adapted from https://golang.org/src/crypto/tls/generate_cert.go - - priv, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - return - } - validFor := time.Hour * 24 * 365 * 10 // ten years - notBefore := time.Now() - notAfter := notBefore.Add(validFor) - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - template := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - Organization: []string{"My Company Name LTD."}, - CommonName: CommonName, - Country: []string{"US"}, - }, - NotBefore: notBefore, - NotAfter: notAfter, - - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - } - derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) - if err != nil { - return - } - - rawCert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) - rawKey = pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}) - - return -} diff --git a/lib/daemon/daemon.go b/lib/daemon/daemon.go deleted file mode 100644 index 521745c..0000000 --- a/lib/daemon/daemon.go +++ /dev/null @@ -1,126 +0,0 @@ -package daemon - -import ( - "io/ioutil" - "log" - "os" - "os/exec" - "path/filepath" - "strconv" - "strings" - - "ehang.io/nps/lib/common" -) - -func InitDaemon(f string, runPath string, pidPath string) { - if len(os.Args) < 2 { - return - } - var args []string - args = append(args, os.Args[0]) - if len(os.Args) >= 2 { - args = append(args, os.Args[2:]...) - } - args = append(args, "-log=file") - switch os.Args[1] { - case "start": - start(args, f, pidPath, runPath) - os.Exit(0) - case "stop": - stop(f, args[0], pidPath) - os.Exit(0) - case "restart": - stop(f, args[0], pidPath) - start(args, f, pidPath, runPath) - os.Exit(0) - case "status": - if status(f, pidPath) { - log.Printf("%s is running", f) - } else { - log.Printf("%s is not running", f) - } - os.Exit(0) - case "reload": - reload(f, pidPath) - os.Exit(0) - } -} - -func reload(f string, pidPath string) { - if f == "nps" && !common.IsWindows() && !status(f, pidPath) { - log.Println("reload fail") - return - } - var c *exec.Cmd - var err error - b, err := ioutil.ReadFile(filepath.Join(pidPath, f+".pid")) - if err == nil { - c = exec.Command("/bin/bash", "-c", `kill -30 `+string(b)) - } else { - log.Fatalln("reload error,pid file does not exist") - } - if c.Run() == nil { - log.Println("reload success") - } else { - log.Println("reload fail") - } -} - -func status(f string, pidPath string) bool { - var cmd *exec.Cmd - b, err := ioutil.ReadFile(filepath.Join(pidPath, f+".pid")) - if err == nil { - if !common.IsWindows() { - cmd = exec.Command("/bin/sh", "-c", "ps -ax | awk '{ print $1 }' | grep "+string(b)) - } else { - cmd = exec.Command("tasklist") - } - out, _ := cmd.Output() - if strings.Index(string(out), string(b)) > -1 { - return true - } - } - return false -} - -func start(osArgs []string, f string, pidPath, runPath string) { - if status(f, pidPath) { - log.Printf(" %s is running", f) - return - } - cmd := exec.Command(osArgs[0], osArgs[1:]...) - cmd.Start() - if cmd.Process.Pid > 0 { - log.Println("start ok , pid:", cmd.Process.Pid, "config path:", runPath) - d1 := []byte(strconv.Itoa(cmd.Process.Pid)) - ioutil.WriteFile(filepath.Join(pidPath, f+".pid"), d1, 0600) - } else { - log.Println("start error") - } -} - -func stop(f string, p string, pidPath string) { - if !status(f, pidPath) { - log.Printf(" %s is not running", f) - return - } - var c *exec.Cmd - var err error - if common.IsWindows() { - p := strings.Split(p, `\`) - c = exec.Command("taskkill", "/F", "/IM", p[len(p)-1]) - } else { - b, err := ioutil.ReadFile(filepath.Join(pidPath, f+".pid")) - if err == nil { - c = exec.Command("/bin/bash", "-c", `kill -9 `+string(b)) - } else { - log.Fatalln("stop error,pid file does not exist") - } - } - err = c.Run() - if err != nil { - log.Println("stop error,", err) - } else { - log.Println("stop ok") - } -} diff --git a/lib/daemon/reload.go b/lib/daemon/reload.go deleted file mode 100644 index 5216eff..0000000 --- a/lib/daemon/reload.go +++ /dev/null @@ -1,24 +0,0 @@ -// +build !windows - -package daemon - -import ( - "os" - "os/signal" - "path/filepath" - "syscall" - - "ehang.io/nps/lib/common" - "github.com/astaxie/beego" -) - -func init() { - s := make(chan os.Signal, 1) - signal.Notify(s, syscall.SIGUSR1) - go func() { - for { - <-s - beego.LoadAppConfig("ini", filepath.Join(common.GetRunPath(), "conf", "nps.conf")) - } - }() -} diff --git a/lib/file/db.go b/lib/file/db.go deleted file mode 100644 index 8552d5e..0000000 --- a/lib/file/db.go +++ /dev/null @@ -1,361 +0,0 @@ -package file - -import ( - "errors" - "fmt" - "net/http" - "sort" - "strings" - "sync" - - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/crypt" - "ehang.io/nps/lib/rate" -) - -type DbUtils struct { - JsonDb *JsonDb -} - -var ( - Db *DbUtils - once sync.Once -) - -//init csv from file -func GetDb() *DbUtils { - once.Do(func() { - jsonDb := NewJsonDb(common.GetRunPath()) - jsonDb.LoadClientFromJsonFile() - jsonDb.LoadTaskFromJsonFile() - jsonDb.LoadHostFromJsonFile() - Db = &DbUtils{JsonDb: jsonDb} - }) - return Db -} - -func GetMapKeys(m sync.Map, isSort bool, sortKey, order string) (keys []int) { - if sortKey != "" && isSort { - return sortClientByKey(m, sortKey, order) - } - m.Range(func(key, value interface{}) bool { - keys = append(keys, key.(int)) - return true - }) - sort.Ints(keys) - return -} - -func (s *DbUtils) GetClientList(start, length int, search, sort, order string, clientId int) ([]*Client, int) { - list := make([]*Client, 0) - var cnt int - keys := GetMapKeys(s.JsonDb.Clients, true, sort, order) - for _, key := range keys { - if value, ok := s.JsonDb.Clients.Load(key); ok { - v := value.(*Client) - if v.NoDisplay { - continue - } - if clientId != 0 && clientId != v.Id { - continue - } - if search != "" && !(v.Id == common.GetIntNoErrByStr(search) || strings.Contains(v.VerifyKey, search) || strings.Contains(v.Remark, search)) { - continue - } - cnt++ - if start--; start < 0 { - if length--; length >= 0 { - list = append(list, v) - } - } - } - } - return list, cnt -} - -func (s *DbUtils) GetIdByVerifyKey(vKey string, addr string) (id int, err error) { - var exist bool - s.JsonDb.Clients.Range(func(key, value interface{}) bool { - v := value.(*Client) - if common.Getverifyval(v.VerifyKey) == vKey && v.Status { - v.Addr = common.GetIpByAddr(addr) - id = v.Id - exist = true - return false - } - return true - }) - if exist { - return - } - return 0, errors.New("not found") -} - -func (s *DbUtils) NewTask(t *Tunnel) (err error) { - s.JsonDb.Tasks.Range(func(key, value interface{}) bool { - v := value.(*Tunnel) - if (v.Mode == "secret" || v.Mode == "p2p") && v.Password == t.Password { - err = errors.New(fmt.Sprintf("secret mode keys %s must be unique", t.Password)) - return false - } - return true - }) - if err != nil { - return - } - t.Flow = new(Flow) - s.JsonDb.Tasks.Store(t.Id, t) - s.JsonDb.StoreTasksToJsonFile() - return -} - -func (s *DbUtils) UpdateTask(t *Tunnel) error { - s.JsonDb.Tasks.Store(t.Id, t) - s.JsonDb.StoreTasksToJsonFile() - return nil -} - -func (s *DbUtils) DelTask(id int) error { - s.JsonDb.Tasks.Delete(id) - s.JsonDb.StoreTasksToJsonFile() - return nil -} - -//md5 password -func (s *DbUtils) GetTaskByMd5Password(p string) (t *Tunnel) { - s.JsonDb.Tasks.Range(func(key, value interface{}) bool { - if crypt.Md5(value.(*Tunnel).Password) == p { - t = value.(*Tunnel) - return false - } - return true - }) - return -} - -func (s *DbUtils) GetTask(id int) (t *Tunnel, err error) { - if v, ok := s.JsonDb.Tasks.Load(id); ok { - t = v.(*Tunnel) - return - } - err = errors.New("not found") - return -} - -func (s *DbUtils) DelHost(id int) error { - s.JsonDb.Hosts.Delete(id) - s.JsonDb.StoreHostToJsonFile() - return nil -} - -func (s *DbUtils) IsHostExist(h *Host) bool { - var exist bool - s.JsonDb.Hosts.Range(func(key, value interface{}) bool { - v := value.(*Host) - if v.Id != h.Id && v.Host == h.Host && h.Location == v.Location && (v.Scheme == "all" || v.Scheme == h.Scheme) { - exist = true - return false - } - return true - }) - return exist -} - -func (s *DbUtils) NewHost(t *Host) error { - if t.Location == "" { - t.Location = "/" - } - if s.IsHostExist(t) { - return errors.New("host has exist") - } - t.Flow = new(Flow) - s.JsonDb.Hosts.Store(t.Id, t) - s.JsonDb.StoreHostToJsonFile() - return nil -} - -func (s *DbUtils) GetHost(start, length int, id int, search string) ([]*Host, int) { - list := make([]*Host, 0) - var cnt int - keys := GetMapKeys(s.JsonDb.Hosts, false, "", "") - for _, key := range keys { - if value, ok := s.JsonDb.Hosts.Load(key); ok { - v := value.(*Host) - if search != "" && !(v.Id == common.GetIntNoErrByStr(search) || strings.Contains(v.Host, search) || strings.Contains(v.Remark, search)) { - continue - } - if id == 0 || v.Client.Id == id { - cnt++ - if start--; start < 0 { - if length--; length >= 0 { - list = append(list, v) - } - } - } - } - } - return list, cnt -} - -func (s *DbUtils) DelClient(id int) error { - s.JsonDb.Clients.Delete(id) - s.JsonDb.StoreClientsToJsonFile() - return nil -} - -func (s *DbUtils) NewClient(c *Client) error { - var isNotSet bool - if c.WebUserName != "" && !s.VerifyUserName(c.WebUserName, c.Id) { - return errors.New("web login username duplicate, please reset") - } -reset: - if c.VerifyKey == "" || isNotSet { - isNotSet = true - c.VerifyKey = crypt.GetRandomString(16) - } - if c.RateLimit == 0 { - c.Rate = rate.NewRate(int64(2 << 23)) - } else if c.Rate == nil { - c.Rate = rate.NewRate(int64(c.RateLimit * 1024)) - } - c.Rate.Start() - if !s.VerifyVkey(c.VerifyKey, c.Id) { - if isNotSet { - goto reset - } - return errors.New("Vkey duplicate, please reset") - } - if c.Id == 0 { - c.Id = int(s.JsonDb.GetClientId()) - } - if c.Flow == nil { - c.Flow = new(Flow) - } - s.JsonDb.Clients.Store(c.Id, c) - s.JsonDb.StoreClientsToJsonFile() - return nil -} - -func (s *DbUtils) VerifyVkey(vkey string, id int) (res bool) { - res = true - s.JsonDb.Clients.Range(func(key, value interface{}) bool { - v := value.(*Client) - if v.VerifyKey == vkey && v.Id != id { - res = false - return false - } - return true - }) - return res -} - -func (s *DbUtils) VerifyUserName(username string, id int) (res bool) { - res = true - s.JsonDb.Clients.Range(func(key, value interface{}) bool { - v := value.(*Client) - if v.WebUserName == username && v.Id != id { - res = false - return false - } - return true - }) - return res -} - -func (s *DbUtils) UpdateClient(t *Client) error { - s.JsonDb.Clients.Store(t.Id, t) - if t.RateLimit == 0 { - t.Rate = rate.NewRate(int64(2 << 23)) - t.Rate.Start() - } - return nil -} - -func (s *DbUtils) IsPubClient(id int) bool { - client, err := s.GetClient(id) - if err == nil { - return client.NoDisplay - } - return false -} - -func (s *DbUtils) GetClient(id int) (c *Client, err error) { - if v, ok := s.JsonDb.Clients.Load(id); ok { - c = v.(*Client) - return - } - err = errors.New("未找到客户端") - return -} - -func (s *DbUtils) GetClientIdByVkey(vkey string) (id int, err error) { - var exist bool - s.JsonDb.Clients.Range(func(key, value interface{}) bool { - v := value.(*Client) - if crypt.Md5(v.VerifyKey) == vkey { - exist = true - id = v.Id - return false - } - return true - }) - if exist { - return - } - err = errors.New("未找到客户端") - return -} - -func (s *DbUtils) GetHostById(id int) (h *Host, err error) { - if v, ok := s.JsonDb.Hosts.Load(id); ok { - h = v.(*Host) - return - } - err = errors.New("The host could not be parsed") - return -} - -//get key by host from x -func (s *DbUtils) GetInfoByHost(host string, r *http.Request) (h *Host, err error) { - var hosts []*Host - //Handling Ported Access - host = common.GetIpByAddr(host) - s.JsonDb.Hosts.Range(func(key, value interface{}) bool { - v := value.(*Host) - if v.IsClose { - return true - } - //Remove http(s) http(s)://a.proxy.com - //*.proxy.com *.a.proxy.com Do some pan-parsing - if v.Scheme != "all" && v.Scheme != r.URL.Scheme { - return true - } - tmpHost := v.Host - if strings.Contains(tmpHost, "*") { - tmpHost = strings.Replace(tmpHost, "*", "", -1) - if strings.Contains(host, tmpHost) { - hosts = append(hosts, v) - } - } else if v.Host == host { - hosts = append(hosts, v) - } - return true - }) - - for _, v := range hosts { - //If not set, default matches all - if v.Location == "" { - v.Location = "/" - } - if strings.Index(r.RequestURI, v.Location) == 0 { - if h == nil || (len(v.Location) > len(h.Location)) { - h = v - } - } - } - if h != nil { - return - } - err = errors.New("The host could not be parsed") - return -} diff --git a/lib/file/file.go b/lib/file/file.go deleted file mode 100644 index f8aafb6..0000000 --- a/lib/file/file.go +++ /dev/null @@ -1,201 +0,0 @@ -package file - -import ( - "encoding/json" - "errors" - "github.com/astaxie/beego/logs" - "os" - "path/filepath" - "strings" - "sync" - "sync/atomic" - - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/rate" -) - -func NewJsonDb(runPath string) *JsonDb { - return &JsonDb{ - RunPath: runPath, - TaskFilePath: filepath.Join(runPath, "conf", "tasks.json"), - HostFilePath: filepath.Join(runPath, "conf", "hosts.json"), - ClientFilePath: filepath.Join(runPath, "conf", "clients.json"), - } -} - -type JsonDb struct { - Tasks sync.Map - Hosts sync.Map - HostsTmp sync.Map - Clients sync.Map - RunPath string - ClientIncreaseId int32 //client increased id - TaskIncreaseId int32 //task increased id - HostIncreaseId int32 //host increased id - TaskFilePath string //task file path - HostFilePath string //host file path - ClientFilePath string //client file path -} - -func (s *JsonDb) LoadTaskFromJsonFile() { - loadSyncMapFromFile(s.TaskFilePath, func(v string) { - var err error - post := new(Tunnel) - if json.Unmarshal([]byte(v), &post) != nil { - return - } - if post.Client, err = s.GetClient(post.Client.Id); err != nil { - return - } - s.Tasks.Store(post.Id, post) - if post.Id > int(s.TaskIncreaseId) { - s.TaskIncreaseId = int32(post.Id) - } - }) -} - -func (s *JsonDb) LoadClientFromJsonFile() { - loadSyncMapFromFile(s.ClientFilePath, func(v string) { - post := new(Client) - if json.Unmarshal([]byte(v), &post) != nil { - return - } - if post.RateLimit > 0 { - post.Rate = rate.NewRate(int64(post.RateLimit * 1024)) - } else { - post.Rate = rate.NewRate(int64(2 << 23)) - } - post.Rate.Start() - post.NowConn = 0 - s.Clients.Store(post.Id, post) - if post.Id > int(s.ClientIncreaseId) { - s.ClientIncreaseId = int32(post.Id) - } - }) -} - -func (s *JsonDb) LoadHostFromJsonFile() { - loadSyncMapFromFile(s.HostFilePath, func(v string) { - var err error - post := new(Host) - if json.Unmarshal([]byte(v), &post) != nil { - return - } - if post.Client, err = s.GetClient(post.Client.Id); err != nil { - return - } - s.Hosts.Store(post.Id, post) - if post.Id > int(s.HostIncreaseId) { - s.HostIncreaseId = int32(post.Id) - } - }) -} - -func (s *JsonDb) GetClient(id int) (c *Client, err error) { - if v, ok := s.Clients.Load(id); ok { - c = v.(*Client) - return - } - err = errors.New("未找到客户端") - return -} - -var hostLock sync.Mutex - -func (s *JsonDb) StoreHostToJsonFile() { - hostLock.Lock() - storeSyncMapToFile(s.Hosts, s.HostFilePath) - hostLock.Unlock() -} - -var taskLock sync.Mutex - -func (s *JsonDb) StoreTasksToJsonFile() { - taskLock.Lock() - storeSyncMapToFile(s.Tasks, s.TaskFilePath) - taskLock.Unlock() -} - -var clientLock sync.Mutex - -func (s *JsonDb) StoreClientsToJsonFile() { - clientLock.Lock() - storeSyncMapToFile(s.Clients, s.ClientFilePath) - clientLock.Unlock() -} - -func (s *JsonDb) GetClientId() int32 { - return atomic.AddInt32(&s.ClientIncreaseId, 1) -} - -func (s *JsonDb) GetTaskId() int32 { - return atomic.AddInt32(&s.TaskIncreaseId, 1) -} - -func (s *JsonDb) GetHostId() int32 { - return atomic.AddInt32(&s.HostIncreaseId, 1) -} - -func loadSyncMapFromFile(filePath string, f func(value string)) { - b, err := common.ReadAllFromFile(filePath) - if err != nil { - panic(err) - } - for _, v := range strings.Split(string(b), "\n"+common.CONN_DATA_SEQ) { - f(v) - } -} - -func storeSyncMapToFile(m sync.Map, filePath string) { - file, err := os.Create(filePath + ".tmp") - // first create a temporary file to store - if err != nil { - panic(err) - } - m.Range(func(key, value interface{}) bool { - var b []byte - var err error - switch value.(type) { - case *Tunnel: - obj := value.(*Tunnel) - if obj.NoStore { - return true - } - b, err = json.Marshal(obj) - case *Host: - obj := value.(*Host) - if obj.NoStore { - return true - } - b, err = json.Marshal(obj) - case *Client: - obj := value.(*Client) - if obj.NoStore { - return true - } - b, err = json.Marshal(obj) - default: - return true - } - if err != nil { - return true - } - _, err = file.Write(b) - if err != nil { - panic(err) - } - _, err = file.Write([]byte("\n" + common.CONN_DATA_SEQ)) - if err != nil { - panic(err) - } - return true - }) - _ = file.Sync() - _ = file.Close() - // must close file first, then rename it - err = os.Rename(filePath+".tmp", filePath) - if err != nil { - logs.Error(err, "store to file err, data will lost") - } - // replace the file, maybe provides atomic operation -} diff --git a/lib/file/obj.go b/lib/file/obj.go deleted file mode 100644 index 31f3e63..0000000 --- a/lib/file/obj.go +++ /dev/null @@ -1,210 +0,0 @@ -package file - -import ( - "strings" - "sync" - "sync/atomic" - "time" - - "ehang.io/nps/lib/rate" - "github.com/pkg/errors" -) - -type Flow struct { - ExportFlow int64 - InletFlow int64 - FlowLimit int64 - sync.RWMutex -} - -func (s *Flow) Add(in, out int64) { - s.Lock() - defer s.Unlock() - s.InletFlow += int64(in) - s.ExportFlow += int64(out) -} - -type Config struct { - U string - P string - Compress bool - Crypt bool -} - -type Client struct { - Cnf *Config - Id int //id - VerifyKey string //verify key - Addr string //the ip of client - Remark string //remark - Status bool //is allow connect - IsConnect bool //is the client connect - RateLimit int //rate /kb - Flow *Flow //flow setting - Rate *rate.Rate //rate limit - NoStore bool //no store to file - NoDisplay bool //no display on web - MaxConn int //the max connection num of client allow - NowConn int32 //the connection num of now - WebUserName string //the username of web login - WebPassword string //the password of web login - ConfigConnAllow bool //is allow connected by config file - MaxTunnelNum int - Version string - sync.RWMutex -} - -func NewClient(vKey string, noStore bool, noDisplay bool) *Client { - return &Client{ - Cnf: new(Config), - Id: 0, - VerifyKey: vKey, - Addr: "", - Remark: "", - Status: true, - IsConnect: false, - RateLimit: 0, - Flow: new(Flow), - Rate: nil, - NoStore: noStore, - RWMutex: sync.RWMutex{}, - NoDisplay: noDisplay, - } -} - -func (s *Client) CutConn() { - atomic.AddInt32(&s.NowConn, 1) -} - -func (s *Client) AddConn() { - atomic.AddInt32(&s.NowConn, -1) -} - -func (s *Client) GetConn() bool { - if s.MaxConn == 0 || int(s.NowConn) < s.MaxConn { - s.CutConn() - return true - } - return false -} - -func (s *Client) HasTunnel(t *Tunnel) (exist bool) { - GetDb().JsonDb.Tasks.Range(func(key, value interface{}) bool { - v := value.(*Tunnel) - if v.Client.Id == s.Id && v.Port == t.Port && t.Port != 0 { - exist = true - return false - } - return true - }) - return -} - -func (s *Client) GetTunnelNum() (num int) { - GetDb().JsonDb.Tasks.Range(func(key, value interface{}) bool { - v := value.(*Tunnel) - if v.Client.Id == s.Id { - num++ - } - return true - }) - return -} - -func (s *Client) HasHost(h *Host) bool { - var has bool - GetDb().JsonDb.Hosts.Range(func(key, value interface{}) bool { - v := value.(*Host) - if v.Client.Id == s.Id && v.Host == h.Host && h.Location == v.Location { - has = true - return false - } - return true - }) - return has -} - -type Tunnel struct { - Id int - Port int - ServerIp string - Mode string - Status bool - RunStatus bool - Client *Client - Ports string - Flow *Flow - Password string - Remark string - TargetAddr string - NoStore bool - LocalPath string - StripPre string - Target *Target - MultiAccount *MultiAccount - Health - sync.RWMutex -} - -type Health struct { - HealthCheckTimeout int - HealthMaxFail int - HealthCheckInterval int - HealthNextTime time.Time - HealthMap map[string]int - HttpHealthUrl string - HealthRemoveArr []string - HealthCheckType string - HealthCheckTarget string - sync.RWMutex -} - -type Host struct { - Id int - Host string //host - HeaderChange string //header change - HostChange string //host change - Location string //url router - Remark string //remark - Scheme string //http https all - CertFilePath string - KeyFilePath string - NoStore bool - IsClose bool - Flow *Flow - Client *Client - Target *Target //目标 - Health `json:"-"` - sync.RWMutex -} - -type Target struct { - nowIndex int - TargetStr string - TargetArr []string - LocalProxy bool - sync.RWMutex -} - -type MultiAccount struct { - AccountMap map[string]string // multi account and pwd -} - -func (s *Target) GetRandomTarget() (string, error) { - if s.TargetArr == nil { - s.TargetArr = strings.Split(s.TargetStr, "\n") - } - if len(s.TargetArr) == 1 { - return s.TargetArr[0], nil - } - if len(s.TargetArr) == 0 { - return "", errors.New("all inward-bending targets are offline") - } - s.Lock() - defer s.Unlock() - if s.nowIndex >= len(s.TargetArr)-1 { - s.nowIndex = -1 - } - s.nowIndex++ - return s.TargetArr[s.nowIndex], nil -} diff --git a/lib/file/sort.go b/lib/file/sort.go deleted file mode 100644 index ef74b3a..0000000 --- a/lib/file/sort.go +++ /dev/null @@ -1,41 +0,0 @@ -package file - -import ( - "reflect" - "sort" - "sync" -) - -// A data structure to hold a key/value pair. -type Pair struct { - key string //sort key - cId int - order string - clientFlow *Flow -} - -// A slice of Pairs that implements sort.Interface to sort by Value. -type PairList []*Pair - -func (p PairList) Swap(i, j int) { p[i], p[j] = p[j], p[i] } -func (p PairList) Len() int { return len(p) } -func (p PairList) Less(i, j int) bool { - if p[i].order == "desc" { - return reflect.ValueOf(*p[i].clientFlow).FieldByName(p[i].key).Int() < reflect.ValueOf(*p[j].clientFlow).FieldByName(p[j].key).Int() - } - return reflect.ValueOf(*p[i].clientFlow).FieldByName(p[i].key).Int() > reflect.ValueOf(*p[j].clientFlow).FieldByName(p[j].key).Int() -} - -// A function to turn a map into a PairList, then sort and return it. -func sortClientByKey(m sync.Map, sortKey, order string) (res []int) { - p := make(PairList, 0) - m.Range(func(key, value interface{}) bool { - p = append(p, &Pair{sortKey, value.(*Client).Id, order, value.(*Client).Flow}) - return true - }) - sort.Sort(p) - for _, v := range p { - res = append(res, v.cId) - } - return -} diff --git a/lib/goroutine/pool.go b/lib/goroutine/pool.go deleted file mode 100644 index 60717e6..0000000 --- a/lib/goroutine/pool.go +++ /dev/null @@ -1,76 +0,0 @@ -package goroutine - -import ( - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/file" - "github.com/panjf2000/ants/v2" - "io" - "net" - "sync" -) - -type connGroup struct { - src io.ReadWriteCloser - dst io.ReadWriteCloser - wg *sync.WaitGroup - n *int64 -} - -func newConnGroup(dst, src io.ReadWriteCloser, wg *sync.WaitGroup, n *int64) connGroup { - return connGroup{ - src: src, - dst: dst, - wg: wg, - n: n, - } -} - -func copyConnGroup(group interface{}) { - cg, ok := group.(connGroup) - if !ok { - return - } - var err error - *cg.n, err = common.CopyBuffer(cg.dst, cg.src) - if err != nil { - cg.src.Close() - cg.dst.Close() - //logs.Warn("close npc by copy from nps", err, c.connId) - } - cg.wg.Done() -} - -type Conns struct { - conn1 io.ReadWriteCloser // mux connection - conn2 net.Conn // outside connection - flow *file.Flow - wg *sync.WaitGroup -} - -func NewConns(c1 io.ReadWriteCloser, c2 net.Conn, flow *file.Flow, wg *sync.WaitGroup) Conns { - return Conns{ - conn1: c1, - conn2: c2, - flow: flow, - wg: wg, - } -} - -func copyConns(group interface{}) { - conns := group.(Conns) - wg := new(sync.WaitGroup) - wg.Add(2) - var in, out int64 - _ = connCopyPool.Invoke(newConnGroup(conns.conn1, conns.conn2, wg, &in)) - // outside to mux : incoming - _ = connCopyPool.Invoke(newConnGroup(conns.conn2, conns.conn1, wg, &out)) - // mux to outside : outgoing - wg.Wait() - if conns.flow != nil { - conns.flow.Add(in, out) - } - conns.wg.Done() -} - -var connCopyPool, _ = ants.NewPoolWithFunc(200000, copyConnGroup, ants.WithNonblocking(false)) -var CopyConnsPool, _ = ants.NewPoolWithFunc(100000, copyConns, ants.WithNonblocking(false)) diff --git a/lib/install/install.go b/lib/install/install.go deleted file mode 100644 index 01d484a..0000000 --- a/lib/install/install.go +++ /dev/null @@ -1,363 +0,0 @@ -package install - -import ( - "ehang.io/nps/lib/common" - "encoding/json" - "errors" - "fmt" - "github.com/c4milo/unpackit" - "io" - "io/ioutil" - "log" - "net/http" - "os" - "path/filepath" - "runtime" - "strings" -) - -// Keep it in sync with the template from service_sysv_linux.go file -// Use "ps | grep -v grep | grep $(get_pid)" because "ps PID" may not work on OpenWrt -const SysvScript = `#!/bin/sh -# For RedHat and cousins: -# chkconfig: - 99 01 -# description: {{.Description}} -# processname: {{.Path}} -### BEGIN INIT INFO -# Provides: {{.Path}} -# Required-Start: -# Required-Stop: -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: {{.DisplayName}} -# Description: {{.Description}} -### END INIT INFO -cmd="{{.Path}}{{range .Arguments}} {{.|cmd}}{{end}}" -name=$(basename $(readlink -f $0)) -pid_file="/var/run/$name.pid" -stdout_log="/var/log/$name.log" -stderr_log="/var/log/$name.err" -[ -e /etc/sysconfig/$name ] && . /etc/sysconfig/$name -get_pid() { - cat "$pid_file" -} -is_running() { - [ -f "$pid_file" ] && ps | grep -v grep | grep $(get_pid) > /dev/null 2>&1 -} -case "$1" in - start) - if is_running; then - echo "Already started" - else - echo "Starting $name" - {{if .WorkingDirectory}}cd '{{.WorkingDirectory}}'{{end}} - $cmd >> "$stdout_log" 2>> "$stderr_log" & - echo $! > "$pid_file" - if ! is_running; then - echo "Unable to start, see $stdout_log and $stderr_log" - exit 1 - fi - fi - ;; - stop) - if is_running; then - echo -n "Stopping $name.." - kill $(get_pid) - for i in $(seq 1 10) - do - if ! is_running; then - break - fi - echo -n "." - sleep 1 - done - echo - if is_running; then - echo "Not stopped; may still be shutting down or shutdown may have failed" - exit 1 - else - echo "Stopped" - if [ -f "$pid_file" ]; then - rm "$pid_file" - fi - fi - else - echo "Not running" - fi - ;; - restart) - $0 stop - if is_running; then - echo "Unable to stop, will not attempt to start" - exit 1 - fi - $0 start - ;; - status) - if is_running; then - echo "Running" - else - echo "Stopped" - exit 1 - fi - ;; - *) - echo "Usage: $0 {start|stop|restart|status}" - exit 1 - ;; -esac -exit 0 -` - -const SystemdScript = `[Unit] -Description={{.Description}} -ConditionFileIsExecutable={{.Path|cmdEscape}} -{{range $i, $dep := .Dependencies}} -{{$dep}} {{end}} -[Service] -LimitNOFILE=65536 -StartLimitInterval=5 -StartLimitBurst=10 -ExecStart={{.Path|cmdEscape}}{{range .Arguments}} {{.|cmd}}{{end}} -{{if .ChRoot}}RootDirectory={{.ChRoot|cmd}}{{end}} -{{if .WorkingDirectory}}WorkingDirectory={{.WorkingDirectory|cmdEscape}}{{end}} -{{if .UserName}}User={{.UserName}}{{end}} -{{if .ReloadSignal}}ExecReload=/bin/kill -{{.ReloadSignal}} "$MAINPID"{{end}} -{{if .PIDFile}}PIDFile={{.PIDFile|cmd}}{{end}} -{{if and .LogOutput .HasOutputFileSupport -}} -StandardOutput=file:/var/log/{{.Name}}.out -StandardError=file:/var/log/{{.Name}}.err -{{- end}} -Restart=always -RestartSec=120 -[Install] -WantedBy=multi-user.target -` - -func UpdateNps() { - destPath := downloadLatest("server") - //复制文件到对应目录 - copyStaticFile(destPath, "nps") - fmt.Println("Update completed, please restart") -} - -func UpdateNpc() { - destPath := downloadLatest("client") - //复制文件到对应目录 - copyStaticFile(destPath, "npc") - fmt.Println("Update completed, please restart") -} - -type release struct { - TagName string `json:"tag_name"` -} - -func downloadLatest(bin string) string { - // get version - data, err := http.Get("https://api.github.com/repos/ehang-io/nps/releases/latest") - if err != nil { - log.Fatal(err.Error()) - } - b, err := ioutil.ReadAll(data.Body) - if err != nil { - log.Fatal(err) - } - rl := new(release) - json.Unmarshal(b, &rl) - version := rl.TagName - fmt.Println("the latest version is", version) - filename := runtime.GOOS + "_" + runtime.GOARCH + "_" + bin + ".tar.gz" - // download latest package - downloadUrl := fmt.Sprintf("https://ehang.io/nps/releases/download/%s/%s", version, filename) - fmt.Println("download package from ", downloadUrl) - resp, err := http.Get(downloadUrl) - if err != nil { - log.Fatal(err.Error()) - } - destPath, err := unpackit.Unpack(resp.Body, "") - if err != nil { - log.Fatal(err) - } - if bin == "server" { - destPath = strings.Replace(destPath, "/web", "", -1) - destPath = strings.Replace(destPath, `\web`, "", -1) - destPath = strings.Replace(destPath, "/views", "", -1) - destPath = strings.Replace(destPath, `\views`, "", -1) - } else { - destPath = strings.Replace(destPath, `\conf`, "", -1) - destPath = strings.Replace(destPath, "/conf", "", -1) - } - return destPath -} - -func copyStaticFile(srcPath, bin string) string { - path := common.GetInstallPath() - if bin == "nps" { - //复制文件到对应目录 - if err := CopyDir(filepath.Join(srcPath, "web", "views"), filepath.Join(path, "web", "views")); err != nil { - log.Fatalln(err) - } - chMod(filepath.Join(path, "web", "views"), 0766) - if err := CopyDir(filepath.Join(srcPath, "web", "static"), filepath.Join(path, "web", "static")); err != nil { - log.Fatalln(err) - } - chMod(filepath.Join(path, "web", "static"), 0766) - } - binPath, _ := filepath.Abs(os.Args[0]) - if !common.IsWindows() { - if _, err := copyFile(filepath.Join(srcPath, bin), "/usr/bin/"+bin); err != nil { - if _, err := copyFile(filepath.Join(srcPath, bin), "/usr/local/bin/"+bin); err != nil { - log.Fatalln(err) - } else { - copyFile(filepath.Join(srcPath, bin), "/usr/local/bin/"+bin+"-update") - chMod("/usr/local/bin/"+bin+"-update", 0755) - binPath = "/usr/local/bin/" + bin - } - } else { - copyFile(filepath.Join(srcPath, bin), "/usr/bin/"+bin+"-update") - chMod("/usr/bin/"+bin+"-update", 0755) - binPath = "/usr/bin/" + bin - } - } else { - copyFile(filepath.Join(srcPath, bin+".exe"), filepath.Join(common.GetAppPath(), bin+"-update.exe")) - copyFile(filepath.Join(srcPath, bin+".exe"), filepath.Join(common.GetAppPath(), bin+".exe")) - } - chMod(binPath, 0755) - return binPath -} - -func InstallNpc() { - path := common.GetInstallPath() - if !common.FileExists(path) { - err := os.Mkdir(path, 0755) - if err != nil { - log.Fatal(err) - } - } - copyStaticFile(common.GetAppPath(), "npc") -} - -func InstallNps() string { - path := common.GetInstallPath() - if common.FileExists(path) { - MkidrDirAll(path, "web/static", "web/views") - } else { - MkidrDirAll(path, "conf", "web/static", "web/views") - // not copy config if the config file is exist - if err := CopyDir(filepath.Join(common.GetAppPath(), "conf"), filepath.Join(path, "conf")); err != nil { - log.Fatalln(err) - } - chMod(filepath.Join(path, "conf"), 0766) - } - binPath := copyStaticFile(common.GetAppPath(), "nps") - log.Println("install ok!") - log.Println("Static files and configuration files in the current directory will be useless") - log.Println("The new configuration file is located in", path, "you can edit them") - if !common.IsWindows() { - log.Println(`You can start with: -nps start|stop|restart|uninstall|update or nps-update update -anywhere!`) - } else { - log.Println(`You can copy executable files to any directory and start working with: -nps.exe start|stop|restart|uninstall|update or nps-update.exe update -now!`) - } - chMod(common.GetLogPath(), 0777) - return binPath -} -func MkidrDirAll(path string, v ...string) { - for _, item := range v { - if err := os.MkdirAll(filepath.Join(path, item), 0755); err != nil { - log.Fatalf("Failed to create directory %s error:%s", path, err.Error()) - } - } -} - -func CopyDir(srcPath string, destPath string) error { - //检测目录正确性 - if srcInfo, err := os.Stat(srcPath); err != nil { - fmt.Println(err.Error()) - return err - } else { - if !srcInfo.IsDir() { - e := errors.New("SrcPath is not the right directory!") - return e - } - } - if destInfo, err := os.Stat(destPath); err != nil { - return err - } else { - if !destInfo.IsDir() { - e := errors.New("DestInfo is not the right directory!") - return e - } - } - err := filepath.Walk(srcPath, func(path string, f os.FileInfo, err error) error { - if f == nil { - return err - } - if !f.IsDir() { - destNewPath := strings.Replace(path, srcPath, destPath, -1) - log.Println("copy file ::" + path + " to " + destNewPath) - copyFile(path, destNewPath) - if !common.IsWindows() { - chMod(destNewPath, 0766) - } - } - return nil - }) - return err -} - -//生成目录并拷贝文件 -func copyFile(src, dest string) (w int64, err error) { - srcFile, err := os.Open(src) - if err != nil { - return - } - defer srcFile.Close() - //分割path目录 - destSplitPathDirs := strings.Split(dest, string(filepath.Separator)) - - //检测时候存在目录 - destSplitPath := "" - for index, dir := range destSplitPathDirs { - if index < len(destSplitPathDirs)-1 { - destSplitPath = destSplitPath + dir + string(filepath.Separator) - b, _ := pathExists(destSplitPath) - if b == false { - log.Println("mkdir:" + destSplitPath) - //创建目录 - err := os.Mkdir(destSplitPath, os.ModePerm) - if err != nil { - log.Fatalln(err) - } - } - } - } - dstFile, err := os.Create(dest) - if err != nil { - return - } - defer dstFile.Close() - - return io.Copy(dstFile, srcFile) -} - -//检测文件夹路径时候存在 -func pathExists(path string) (bool, error) { - _, err := os.Stat(path) - if err == nil { - return true, nil - } - if os.IsNotExist(err) { - return false, nil - } - return false, err -} - -func chMod(name string, mode os.FileMode) { - if !common.IsWindows() { - os.Chmod(name, mode) - } -} diff --git a/lib/pmux/pconn.go b/lib/pmux/pconn.go deleted file mode 100644 index d4330ef..0000000 --- a/lib/pmux/pconn.go +++ /dev/null @@ -1,71 +0,0 @@ -package pmux - -import ( - "net" - "time" -) - -type PortConn struct { - Conn net.Conn - rs []byte - readMore bool - start int -} - -func newPortConn(conn net.Conn, rs []byte, readMore bool) *PortConn { - return &PortConn{ - Conn: conn, - rs: rs, - readMore: readMore, - } -} - -func (pConn *PortConn) Read(b []byte) (n int, err error) { - if len(b) < len(pConn.rs)-pConn.start { - defer func() { - pConn.start = pConn.start + len(b) - }() - return copy(b, pConn.rs), nil - } - if pConn.start < len(pConn.rs) { - defer func() { - pConn.start = len(pConn.rs) - }() - n = copy(b, pConn.rs[pConn.start:]) - if !pConn.readMore { - return - } - } - var n2 = 0 - n2, err = pConn.Conn.Read(b[n:]) - n = n + n2 - return -} - -func (pConn *PortConn) Write(b []byte) (n int, err error) { - return pConn.Conn.Write(b) -} - -func (pConn *PortConn) Close() error { - return pConn.Conn.Close() -} - -func (pConn *PortConn) LocalAddr() net.Addr { - return pConn.Conn.LocalAddr() -} - -func (pConn *PortConn) RemoteAddr() net.Addr { - return pConn.Conn.RemoteAddr() -} - -func (pConn *PortConn) SetDeadline(t time.Time) error { - return pConn.Conn.SetDeadline(t) -} - -func (pConn *PortConn) SetReadDeadline(t time.Time) error { - return pConn.Conn.SetReadDeadline(t) -} - -func (pConn *PortConn) SetWriteDeadline(t time.Time) error { - return pConn.Conn.SetWriteDeadline(t) -} diff --git a/lib/pmux/plistener.go b/lib/pmux/plistener.go deleted file mode 100644 index deef001..0000000 --- a/lib/pmux/plistener.go +++ /dev/null @@ -1,44 +0,0 @@ -package pmux - -import ( - "errors" - "net" -) - -type PortListener struct { - net.Listener - connCh chan *PortConn - addr net.Addr - isClose bool -} - -func NewPortListener(connCh chan *PortConn, addr net.Addr) *PortListener { - return &PortListener{ - connCh: connCh, - addr: addr, - } -} - -func (pListener *PortListener) Accept() (net.Conn, error) { - if pListener.isClose { - return nil, errors.New("the listener has closed") - } - conn := <-pListener.connCh - if conn != nil { - return conn, nil - } - return nil, errors.New("the listener has closed") -} - -func (pListener *PortListener) Close() error { - //close - if pListener.isClose { - return errors.New("the listener has closed") - } - pListener.isClose = true - return nil -} - -func (pListener *PortListener) Addr() net.Addr { - return pListener.addr -} diff --git a/lib/pmux/pmux.go b/lib/pmux/pmux.go deleted file mode 100644 index 0dffb3e..0000000 --- a/lib/pmux/pmux.go +++ /dev/null @@ -1,166 +0,0 @@ -// This module is used for port reuse -// Distinguish client, web manager , HTTP and HTTPS according to the difference of protocol -package pmux - -import ( - "bufio" - "bytes" - "io" - "net" - "os" - "strconv" - "strings" - "time" - - "ehang.io/nps/lib/common" - "github.com/astaxie/beego/logs" - "github.com/pkg/errors" -) - -const ( - HTTP_GET = 716984 - HTTP_POST = 807983 - HTTP_HEAD = 726965 - HTTP_PUT = 808585 - HTTP_DELETE = 686976 - HTTP_CONNECT = 677978 - HTTP_OPTIONS = 798084 - HTTP_TRACE = 848265 - CLIENT = 848384 - ACCEPT_TIME_OUT = 10 -) - -type PortMux struct { - net.Listener - port int - isClose bool - managerHost string - clientConn chan *PortConn - httpConn chan *PortConn - httpsConn chan *PortConn - managerConn chan *PortConn -} - -func NewPortMux(port int, managerHost string) *PortMux { - pMux := &PortMux{ - managerHost: managerHost, - port: port, - clientConn: make(chan *PortConn), - httpConn: make(chan *PortConn), - httpsConn: make(chan *PortConn), - managerConn: make(chan *PortConn), - } - pMux.Start() - return pMux -} - -func (pMux *PortMux) Start() error { - // Port multiplexing is based on TCP only - tcpAddr, err := net.ResolveTCPAddr("tcp", "0.0.0.0:"+strconv.Itoa(pMux.port)) - if err != nil { - return err - } - pMux.Listener, err = net.ListenTCP("tcp", tcpAddr) - if err != nil { - logs.Error(err) - os.Exit(0) - } - go func() { - for { - conn, err := pMux.Listener.Accept() - if err != nil { - logs.Warn(err) - //close - pMux.Close() - } - go pMux.process(conn) - } - }() - return nil -} - -func (pMux *PortMux) process(conn net.Conn) { - // Recognition according to different signs - // read 3 byte - buf := make([]byte, 3) - if n, err := io.ReadFull(conn, buf); err != nil || n != 3 { - return - } - var ch chan *PortConn - var rs []byte - var buffer bytes.Buffer - var readMore = false - switch common.BytesToNum(buf) { - case HTTP_CONNECT, HTTP_DELETE, HTTP_GET, HTTP_HEAD, HTTP_OPTIONS, HTTP_POST, HTTP_PUT, HTTP_TRACE: //http and manager - buffer.Reset() - r := bufio.NewReader(conn) - buffer.Write(buf) - for { - b, _, err := r.ReadLine() - if err != nil { - logs.Warn("read line error", err.Error()) - conn.Close() - break - } - buffer.Write(b) - buffer.Write([]byte("\r\n")) - if strings.Index(string(b), "Host:") == 0 || strings.Index(string(b), "host:") == 0 { - // Remove host and space effects - str := strings.Replace(string(b), "Host:", "", -1) - str = strings.Replace(str, "host:", "", -1) - str = strings.TrimSpace(str) - // Determine whether it is the same as the manager domain name - if common.GetIpByAddr(str) == pMux.managerHost { - ch = pMux.managerConn - } else { - ch = pMux.httpConn - } - b, _ := r.Peek(r.Buffered()) - buffer.Write(b) - rs = buffer.Bytes() - break - } - } - case CLIENT: // client connection - ch = pMux.clientConn - default: // https - readMore = true - ch = pMux.httpsConn - } - if len(rs) == 0 { - rs = buf - } - timer := time.NewTimer(ACCEPT_TIME_OUT) - select { - case <-timer.C: - case ch <- newPortConn(conn, rs, readMore): - } -} - -func (pMux *PortMux) Close() error { - if pMux.isClose { - return errors.New("the port pmux has closed") - } - pMux.isClose = true - close(pMux.clientConn) - close(pMux.httpsConn) - close(pMux.httpConn) - close(pMux.managerConn) - return pMux.Listener.Close() -} - -func (pMux *PortMux) GetClientListener() net.Listener { - return NewPortListener(pMux.clientConn, pMux.Listener.Addr()) -} - -func (pMux *PortMux) GetHttpListener() net.Listener { - return NewPortListener(pMux.httpConn, pMux.Listener.Addr()) -} - -func (pMux *PortMux) GetHttpsListener() net.Listener { - return NewPortListener(pMux.httpsConn, pMux.Listener.Addr()) -} - -func (pMux *PortMux) GetManagerListener() net.Listener { - return NewPortListener(pMux.managerConn, pMux.Listener.Addr()) -} diff --git a/lib/pmux/pmux_test.go b/lib/pmux/pmux_test.go deleted file mode 100644 index 6a17ddd..0000000 --- a/lib/pmux/pmux_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package pmux - -import ( - "testing" - "time" - - "github.com/astaxie/beego/logs" -) - -func TestPortMux_Close(t *testing.T) { - logs.Reset() - logs.EnableFuncCallDepth(true) - logs.SetLogFuncCallDepth(3) - - pMux := NewPortMux(8888, "Ds") - go func() { - if pMux.Start() != nil { - logs.Warn("Error") - } - }() - time.Sleep(time.Second * 3) - go func() { - l := pMux.GetHttpListener() - conn, err := l.Accept() - logs.Warn(conn, err) - }() - go func() { - l := pMux.GetHttpListener() - conn, err := l.Accept() - logs.Warn(conn, err) - }() - go func() { - l := pMux.GetHttpListener() - conn, err := l.Accept() - logs.Warn(conn, err) - }() - l := pMux.GetHttpListener() - conn, err := l.Accept() - logs.Warn(conn, err) -} diff --git a/lib/rate/conn.go b/lib/rate/conn.go deleted file mode 100644 index 7e4ef01..0000000 --- a/lib/rate/conn.go +++ /dev/null @@ -1,37 +0,0 @@ -package rate - -import ( - "io" -) - -type rateConn struct { - conn io.ReadWriteCloser - rate *Rate -} - -func NewRateConn(conn io.ReadWriteCloser, rate *Rate) io.ReadWriteCloser { - return &rateConn{ - conn: conn, - rate: rate, - } -} - -func (s *rateConn) Read(b []byte) (n int, err error) { - n, err = s.conn.Read(b) - if s.rate != nil { - s.rate.Get(int64(n)) - } - return -} - -func (s *rateConn) Write(b []byte) (n int, err error) { - n, err = s.conn.Write(b) - if s.rate != nil { - s.rate.Get(int64(n)) - } - return -} - -func (s *rateConn) Close() error { - return s.conn.Close() -} diff --git a/lib/rate/rate.go b/lib/rate/rate.go deleted file mode 100644 index 948c078..0000000 --- a/lib/rate/rate.go +++ /dev/null @@ -1,81 +0,0 @@ -package rate - -import ( - "sync/atomic" - "time" -) - -type Rate struct { - bucketSize int64 - bucketSurplusSize int64 - bucketAddSize int64 - stopChan chan bool - NowRate int64 -} - -func NewRate(addSize int64) *Rate { - return &Rate{ - bucketSize: addSize * 2, - bucketSurplusSize: 0, - bucketAddSize: addSize, - stopChan: make(chan bool), - } -} - -func (s *Rate) Start() { - go s.session() -} - -func (s *Rate) add(size int64) { - if res := s.bucketSize - s.bucketSurplusSize; res < s.bucketAddSize { - atomic.AddInt64(&s.bucketSurplusSize, res) - return - } - atomic.AddInt64(&s.bucketSurplusSize, size) -} - -//回桶 -func (s *Rate) ReturnBucket(size int64) { - s.add(size) -} - -//停止 -func (s *Rate) Stop() { - s.stopChan <- true -} - -func (s *Rate) Get(size int64) { - if s.bucketSurplusSize >= size { - atomic.AddInt64(&s.bucketSurplusSize, -size) - return - } - ticker := time.NewTicker(time.Millisecond * 100) - for { - select { - case <-ticker.C: - if s.bucketSurplusSize >= size { - atomic.AddInt64(&s.bucketSurplusSize, -size) - ticker.Stop() - return - } - } - } -} - -func (s *Rate) session() { - ticker := time.NewTicker(time.Second * 1) - for { - select { - case <-ticker.C: - if rs := s.bucketAddSize - s.bucketSurplusSize; rs > 0 { - s.NowRate = rs - } else { - s.NowRate = s.bucketSize - s.bucketSurplusSize - } - s.add(s.bucketAddSize) - case <-s.stopChan: - ticker.Stop() - return - } - } -} diff --git a/lib/sheap/heap.go b/lib/sheap/heap.go deleted file mode 100644 index c892c39..0000000 --- a/lib/sheap/heap.go +++ /dev/null @@ -1,21 +0,0 @@ -package sheap - -type IntHeap []int64 - -func (h IntHeap) Len() int { return len(h) } -func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] } -func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } - -func (h *IntHeap) Push(x interface{}) { - // Push and Pop use pointer receivers because they modify the slice's length, - // not just its contents. - *h = append(*h, x.(int64)) -} - -func (h *IntHeap) Pop() interface{} { - old := *h - n := len(old) - x := old[n-1] - *h = old[0 : n-1] - return x -} diff --git a/lib/version/version.go b/lib/version/version.go deleted file mode 100644 index 8945e8c..0000000 --- a/lib/version/version.go +++ /dev/null @@ -1,8 +0,0 @@ -package version - -const VERSION = "0.26.10" - -// Compulsory minimum version, Minimum downward compatibility to this version -func GetVersion() string { - return "0.26.0" -} diff --git a/server/connection/connection.go b/server/connection/connection.go deleted file mode 100644 index f614622..0000000 --- a/server/connection/connection.go +++ /dev/null @@ -1,85 +0,0 @@ -package connection - -import ( - "net" - "os" - "strconv" - - "ehang.io/nps/lib/pmux" - "github.com/astaxie/beego" - "github.com/astaxie/beego/logs" -) - -var pMux *pmux.PortMux -var bridgePort string -var httpsPort string -var httpPort string -var webPort string - -func InitConnectionService() { - bridgePort = beego.AppConfig.String("bridge_port") - httpsPort = beego.AppConfig.String("https_proxy_port") - httpPort = beego.AppConfig.String("http_proxy_port") - webPort = beego.AppConfig.String("web_port") - - if httpPort == bridgePort || httpsPort == bridgePort || webPort == bridgePort { - port, err := strconv.Atoi(bridgePort) - if err != nil { - logs.Error(err) - os.Exit(0) - } - pMux = pmux.NewPortMux(port, beego.AppConfig.String("web_host")) - } -} - -func GetBridgeListener(tp string) (net.Listener, error) { - logs.Info("server start, the bridge type is %s, the bridge port is %s", tp, bridgePort) - var p int - var err error - if p, err = strconv.Atoi(bridgePort); err != nil { - return nil, err - } - if pMux != nil { - return pMux.GetClientListener(), nil - } - return net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP(beego.AppConfig.String("bridge_ip")), p, ""}) -} - -func GetHttpListener() (net.Listener, error) { - if pMux != nil && httpPort == bridgePort { - logs.Info("start http listener, port is", bridgePort) - return pMux.GetHttpListener(), nil - } - logs.Info("start http listener, port is", httpPort) - return getTcpListener(beego.AppConfig.String("http_proxy_ip"), httpPort) -} - -func GetHttpsListener() (net.Listener, error) { - if pMux != nil && httpsPort == bridgePort { - logs.Info("start https listener, port is", bridgePort) - return pMux.GetHttpsListener(), nil - } - logs.Info("start https listener, port is", httpsPort) - return getTcpListener(beego.AppConfig.String("http_proxy_ip"), httpsPort) -} - -func GetWebManagerListener() (net.Listener, error) { - if pMux != nil && webPort == bridgePort { - logs.Info("Web management start, access port is", bridgePort) - return pMux.GetManagerListener(), nil - } - logs.Info("web management start, access port is", webPort) - return getTcpListener(beego.AppConfig.String("web_ip"), webPort) -} - -func getTcpListener(ip, p string) (net.Listener, error) { - port, err := strconv.Atoi(p) - if err != nil { - logs.Error(err) - os.Exit(0) - } - if ip == "" { - ip = "0.0.0.0" - } - return net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP(ip), port, ""}) -} diff --git a/server/proxy/base.go b/server/proxy/base.go deleted file mode 100644 index 7df5921..0000000 --- a/server/proxy/base.go +++ /dev/null @@ -1,100 +0,0 @@ -package proxy - -import ( - "errors" - "net" - "net/http" - "sync" - - "ehang.io/nps/bridge" - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/conn" - "ehang.io/nps/lib/file" - "github.com/astaxie/beego/logs" -) - -type Service interface { - Start() error - Close() error -} - -type NetBridge interface { - SendLinkInfo(clientId int, link *conn.Link, t *file.Tunnel) (target net.Conn, err error) -} - -//BaseServer struct -type BaseServer struct { - id int - bridge NetBridge - task *file.Tunnel - errorContent []byte - sync.Mutex -} - -func NewBaseServer(bridge *bridge.Bridge, task *file.Tunnel) *BaseServer { - return &BaseServer{ - bridge: bridge, - task: task, - errorContent: nil, - Mutex: sync.Mutex{}, - } -} - -//add the flow -func (s *BaseServer) FlowAdd(in, out int64) { - s.Lock() - defer s.Unlock() - s.task.Flow.ExportFlow += out - s.task.Flow.InletFlow += in -} - -//change the flow -func (s *BaseServer) FlowAddHost(host *file.Host, in, out int64) { - s.Lock() - defer s.Unlock() - host.Flow.ExportFlow += out - host.Flow.InletFlow += in -} - -//write fail bytes to the connection -func (s *BaseServer) writeConnFail(c net.Conn) { - c.Write([]byte(common.ConnectionFailBytes)) - c.Write(s.errorContent) -} - -//auth check -func (s *BaseServer) auth(r *http.Request, c *conn.Conn, u, p string) error { - if u != "" && p != "" && !common.CheckAuth(r, u, p) { - c.Write([]byte(common.UnauthorizedBytes)) - c.Close() - return errors.New("401 Unauthorized") - } - return nil -} - -//check flow limit of the client ,and decrease the allow num of client -func (s *BaseServer) CheckFlowAndConnNum(client *file.Client) error { - if client.Flow.FlowLimit > 0 && (client.Flow.FlowLimit<<20) < (client.Flow.ExportFlow+client.Flow.InletFlow) { - return errors.New("Traffic exceeded") - } - if !client.GetConn() { - return errors.New("Connections exceed the current client limit") - } - return nil -} - -//create a new connection and start bytes copying -func (s *BaseServer) DealClient(c *conn.Conn, client *file.Client, addr string, rb []byte, tp string, f func(), flow *file.Flow, localProxy bool) error { - link := conn.NewLink(tp, addr, client.Cnf.Crypt, client.Cnf.Compress, c.Conn.RemoteAddr().String(), localProxy) - if target, err := s.bridge.SendLinkInfo(client.Id, link, s.task); err != nil { - logs.Warn("get connection from client id %d error %s", client.Id, err.Error()) - c.Close() - return err - } else { - if f != nil { - f() - } - conn.CopyWaitGroup(target, c.Conn, link.Crypt, link.Compress, client.Rate, flow, true, rb) - } - return nil -} diff --git a/server/proxy/http.go b/server/proxy/http.go deleted file mode 100644 index 3bc1e6e..0000000 --- a/server/proxy/http.go +++ /dev/null @@ -1,275 +0,0 @@ -package proxy - -import ( - "bufio" - "crypto/tls" - "io" - "net" - "net/http" - "net/http/httputil" - "os" - "path/filepath" - "strconv" - "strings" - "sync" - - "ehang.io/nps/bridge" - "ehang.io/nps/lib/cache" - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/conn" - "ehang.io/nps/lib/file" - "ehang.io/nps/server/connection" - "github.com/astaxie/beego/logs" -) - -type httpServer struct { - BaseServer - httpPort int - httpsPort int - httpServer *http.Server - httpsServer *http.Server - httpsListener net.Listener - useCache bool - addOrigin bool - cache *cache.Cache - cacheLen int -} - -func NewHttp(bridge *bridge.Bridge, c *file.Tunnel, httpPort, httpsPort int, useCache bool, cacheLen int, addOrigin bool) *httpServer { - httpServer := &httpServer{ - BaseServer: BaseServer{ - task: c, - bridge: bridge, - Mutex: sync.Mutex{}, - }, - httpPort: httpPort, - httpsPort: httpsPort, - useCache: useCache, - cacheLen: cacheLen, - addOrigin: addOrigin, - } - if useCache { - httpServer.cache = cache.New(cacheLen) - } - return httpServer -} - -func (s *httpServer) Start() error { - var err error - if s.errorContent, err = common.ReadAllFromFile(filepath.Join(common.GetRunPath(), "web", "static", "page", "error.html")); err != nil { - s.errorContent = []byte("nps 404") - } - if s.httpPort > 0 { - s.httpServer = s.NewServer(s.httpPort, "http") - go func() { - l, err := connection.GetHttpListener() - if err != nil { - logs.Error(err) - os.Exit(0) - } - err = s.httpServer.Serve(l) - if err != nil { - logs.Error(err) - os.Exit(0) - } - }() - } - if s.httpsPort > 0 { - s.httpsServer = s.NewServer(s.httpsPort, "https") - go func() { - s.httpsListener, err = connection.GetHttpsListener() - if err != nil { - logs.Error(err) - os.Exit(0) - } - logs.Error(NewHttpsServer(s.httpsListener, s.bridge, s.useCache, s.cacheLen).Start()) - }() - } - return nil -} - -func (s *httpServer) Close() error { - if s.httpsListener != nil { - s.httpsListener.Close() - } - if s.httpsServer != nil { - s.httpsServer.Close() - } - if s.httpServer != nil { - s.httpServer.Close() - } - return nil -} - -func (s *httpServer) handleTunneling(w http.ResponseWriter, r *http.Request) { - hijacker, ok := w.(http.Hijacker) - if !ok { - http.Error(w, "Hijacking not supported", http.StatusInternalServerError) - return - } - c, _, err := hijacker.Hijack() - if err != nil { - http.Error(w, err.Error(), http.StatusServiceUnavailable) - } - s.handleHttp(conn.NewConn(c), r) -} - -func (s *httpServer) handleHttp(c *conn.Conn, r *http.Request) { - var ( - host *file.Host - target net.Conn - err error - connClient io.ReadWriteCloser - scheme = r.URL.Scheme - lk *conn.Link - targetAddr string - lenConn *conn.LenConn - isReset bool - wg sync.WaitGroup - ) - defer func() { - if connClient != nil { - connClient.Close() - } else { - s.writeConnFail(c.Conn) - } - c.Close() - }() -reset: - if isReset { - host.Client.AddConn() - } - if host, err = file.GetDb().GetInfoByHost(r.Host, r); err != nil { - logs.Notice("the url %s %s %s can't be parsed!", r.URL.Scheme, r.Host, r.RequestURI) - return - } - if err := s.CheckFlowAndConnNum(host.Client); err != nil { - logs.Warn("client id %d, host id %d, error %s, when https connection", host.Client.Id, host.Id, err.Error()) - return - } - if !isReset { - defer host.Client.AddConn() - } - if err = s.auth(r, c, host.Client.Cnf.U, host.Client.Cnf.P); err != nil { - logs.Warn("auth error", err, r.RemoteAddr) - return - } - if targetAddr, err = host.Target.GetRandomTarget(); err != nil { - logs.Warn(err.Error()) - return - } - lk = conn.NewLink("http", targetAddr, host.Client.Cnf.Crypt, host.Client.Cnf.Compress, r.RemoteAddr, host.Target.LocalProxy) - if target, err = s.bridge.SendLinkInfo(host.Client.Id, lk, nil); err != nil { - logs.Notice("connect to target %s error %s", lk.Host, err) - return - } - connClient = conn.GetConn(target, lk.Crypt, lk.Compress, host.Client.Rate, true) - - //read from inc-client - go func() { - wg.Add(1) - isReset = false - defer connClient.Close() - defer func() { - wg.Done() - if !isReset { - c.Close() - } - }() - for { - if resp, err := http.ReadResponse(bufio.NewReader(connClient), r); err != nil || resp == nil || r == nil { - // if there got broken pipe, http.ReadResponse will get a nil - return - } else { - //if the cache is start and the response is in the extension,store the response to the cache list - if s.useCache && r.URL != nil && strings.Contains(r.URL.Path, ".") { - b, err := httputil.DumpResponse(resp, true) - if err != nil { - return - } - c.Write(b) - host.Flow.Add(0, int64(len(b))) - s.cache.Add(filepath.Join(host.Host, r.URL.Path), b) - } else { - lenConn := conn.NewLenConn(c) - if err := resp.Write(lenConn); err != nil { - logs.Error(err) - return - } - host.Flow.Add(0, int64(lenConn.Len)) - } - } - } - }() - - for { - //if the cache start and the request is in the cache list, return the cache - if s.useCache { - if v, ok := s.cache.Get(filepath.Join(host.Host, r.URL.Path)); ok { - n, err := c.Write(v.([]byte)) - if err != nil { - break - } - logs.Trace("%s request, method %s, host %s, url %s, remote address %s, return cache", r.URL.Scheme, r.Method, r.Host, r.URL.Path, c.RemoteAddr().String()) - host.Flow.Add(0, int64(n)) - //if return cache and does not create a new conn with client and Connection is not set or close, close the connection. - if strings.ToLower(r.Header.Get("Connection")) == "close" || strings.ToLower(r.Header.Get("Connection")) == "" { - break - } - goto readReq - } - } - - //change the host and header and set proxy setting - common.ChangeHostAndHeader(r, host.HostChange, host.HeaderChange, c.Conn.RemoteAddr().String(), s.addOrigin) - logs.Trace("%s request, method %s, host %s, url %s, remote address %s, target %s", r.URL.Scheme, r.Method, r.Host, r.URL.Path, c.RemoteAddr().String(), lk.Host) - //write - lenConn = conn.NewLenConn(connClient) - if err := r.Write(lenConn); err != nil { - logs.Error(err) - break - } - host.Flow.Add(int64(lenConn.Len), 0) - - readReq: - //read req from connection - if r, err = http.ReadRequest(bufio.NewReader(c)); err != nil { - break - } - r.URL.Scheme = scheme - //What happened ,Why one character less??? - r.Method = resetReqMethod(r.Method) - if hostTmp, err := file.GetDb().GetInfoByHost(r.Host, r); err != nil { - logs.Notice("the url %s %s %s can't be parsed!", r.URL.Scheme, r.Host, r.RequestURI) - break - } else if host != hostTmp { - host = hostTmp - isReset = true - connClient.Close() - goto reset - } - } - wg.Wait() -} - -func resetReqMethod(method string) string { - if method == "ET" { - return "GET" - } - if method == "OST" { - return "POST" - } - return method -} - -func (s *httpServer) NewServer(port int, scheme string) *http.Server { - return &http.Server{ - Addr: ":" + strconv.Itoa(port), - Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - r.URL.Scheme = scheme - s.handleTunneling(w, r) - }), - // Disable HTTP/2. - TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)), - } -} diff --git a/server/proxy/https.go b/server/proxy/https.go deleted file mode 100644 index 3f0be1d..0000000 --- a/server/proxy/https.go +++ /dev/null @@ -1,185 +0,0 @@ -package proxy - -import ( - "net" - "net/http" - "net/url" - "sync" - - "ehang.io/nps/lib/cache" - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/conn" - "ehang.io/nps/lib/crypt" - "ehang.io/nps/lib/file" - "github.com/astaxie/beego" - "github.com/astaxie/beego/logs" - "github.com/pkg/errors" -) - -type HttpsServer struct { - httpServer - listener net.Listener - httpsListenerMap sync.Map -} - -func NewHttpsServer(l net.Listener, bridge NetBridge, useCache bool, cacheLen int) *HttpsServer { - https := &HttpsServer{listener: l} - https.bridge = bridge - https.useCache = useCache - if useCache { - https.cache = cache.New(cacheLen) - } - return https -} - -//start https server -func (https *HttpsServer) Start() error { - if b, err := beego.AppConfig.Bool("https_just_proxy"); err == nil && b { - conn.Accept(https.listener, func(c net.Conn) { - https.handleHttps(c) - }) - } else { - //start the default listener - certFile := beego.AppConfig.String("https_default_cert_file") - keyFile := beego.AppConfig.String("https_default_key_file") - if common.FileExists(certFile) && common.FileExists(keyFile) { - l := NewHttpsListener(https.listener) - https.NewHttps(l, certFile, keyFile) - https.httpsListenerMap.Store("default", l) - } - conn.Accept(https.listener, func(c net.Conn) { - serverName, rb := GetServerNameFromClientHello(c) - //if the clientHello does not contains sni ,use the default ssl certificate - if serverName == "" { - serverName = "default" - } - var l *HttpsListener - if v, ok := https.httpsListenerMap.Load(serverName); ok { - l = v.(*HttpsListener) - } else { - r := buildHttpsRequest(serverName) - if host, err := file.GetDb().GetInfoByHost(serverName, r); err != nil { - c.Close() - logs.Notice("the url %s can't be parsed!,remote addr %s", serverName, c.RemoteAddr().String()) - return - } else { - if !common.FileExists(host.CertFilePath) || !common.FileExists(host.KeyFilePath) { - //if the host cert file or key file is not set ,use the default file - if v, ok := https.httpsListenerMap.Load("default"); ok { - l = v.(*HttpsListener) - } else { - c.Close() - logs.Error("the key %s cert %s file is not exist", host.KeyFilePath, host.CertFilePath) - return - } - } else { - l = NewHttpsListener(https.listener) - https.NewHttps(l, host.CertFilePath, host.KeyFilePath) - https.httpsListenerMap.Store(serverName, l) - } - } - } - acceptConn := conn.NewConn(c) - acceptConn.Rb = rb - l.acceptConn <- acceptConn - }) - } - return nil -} - -// close -func (https *HttpsServer) Close() error { - return https.listener.Close() -} - -// new https server by cert and key file -func (https *HttpsServer) NewHttps(l net.Listener, certFile string, keyFile string) { - go func() { - logs.Error(https.NewServer(0, "https").ServeTLS(l, certFile, keyFile)) - }() -} - -//handle the https which is just proxy to other client -func (https *HttpsServer) handleHttps(c net.Conn) { - hostName, rb := GetServerNameFromClientHello(c) - var targetAddr string - r := buildHttpsRequest(hostName) - var host *file.Host - var err error - if host, err = file.GetDb().GetInfoByHost(hostName, r); err != nil { - c.Close() - logs.Notice("the url %s can't be parsed!", hostName) - return - } - if err := https.CheckFlowAndConnNum(host.Client); err != nil { - logs.Warn("client id %d, host id %d, error %s, when https connection", host.Client.Id, host.Id, err.Error()) - c.Close() - return - } - defer host.Client.AddConn() - if err = https.auth(r, conn.NewConn(c), host.Client.Cnf.U, host.Client.Cnf.P); err != nil { - logs.Warn("auth error", err, r.RemoteAddr) - return - } - if targetAddr, err = host.Target.GetRandomTarget(); err != nil { - logs.Warn(err.Error()) - } - logs.Trace("new https connection,clientId %d,host %s,remote address %s", host.Client.Id, r.Host, c.RemoteAddr().String()) - https.DealClient(conn.NewConn(c), host.Client, targetAddr, rb, common.CONN_TCP, nil, host.Flow, host.Target.LocalProxy) -} - -type HttpsListener struct { - acceptConn chan *conn.Conn - parentListener net.Listener -} - -// https listener -func NewHttpsListener(l net.Listener) *HttpsListener { - return &HttpsListener{parentListener: l, acceptConn: make(chan *conn.Conn)} -} - -// accept -func (httpsListener *HttpsListener) Accept() (net.Conn, error) { - httpsConn := <-httpsListener.acceptConn - if httpsConn == nil { - return nil, errors.New("get connection error") - } - return httpsConn, nil -} - -// close -func (httpsListener *HttpsListener) Close() error { - return nil -} - -// addr -func (httpsListener *HttpsListener) Addr() net.Addr { - return httpsListener.parentListener.Addr() -} - -// get server name from connection by read client hello bytes -func GetServerNameFromClientHello(c net.Conn) (string, []byte) { - buf := make([]byte, 4096) - data := make([]byte, 4096) - n, err := c.Read(buf) - if err != nil { - return "", nil - } - if n < 42 { - return "", nil - } - copy(data, buf[:n]) - clientHello := new(crypt.ClientHelloMsg) - clientHello.Unmarshal(data[5:n]) - return clientHello.GetServerName(), buf[:n] -} - -// build https request -func buildHttpsRequest(hostName string) *http.Request { - r := new(http.Request) - r.RequestURI = "/" - r.URL = new(url.URL) - r.URL.Scheme = "https" - r.Host = hostName - return r -} diff --git a/server/proxy/p2p.go b/server/proxy/p2p.go deleted file mode 100644 index 7c9b70f..0000000 --- a/server/proxy/p2p.go +++ /dev/null @@ -1,80 +0,0 @@ -package proxy - -import ( - "net" - "strings" - "time" - - "ehang.io/nps/lib/common" - "github.com/astaxie/beego/logs" -) - -type P2PServer struct { - BaseServer - p2pPort int - p2p map[string]*p2p - listener *net.UDPConn -} - -type p2p struct { - visitorAddr *net.UDPAddr - providerAddr *net.UDPAddr -} - -func NewP2PServer(p2pPort int) *P2PServer { - return &P2PServer{ - p2pPort: p2pPort, - p2p: make(map[string]*p2p), - } -} - -func (s *P2PServer) Start() error { - logs.Info("start p2p server port", s.p2pPort) - var err error - s.listener, err = net.ListenUDP("udp", &net.UDPAddr{net.ParseIP("0.0.0.0"), s.p2pPort, ""}) - if err != nil { - return err - } - for { - buf := common.BufPoolUdp.Get().([]byte) - n, addr, err := s.listener.ReadFromUDP(buf) - if err != nil { - if strings.Contains(err.Error(), "use of closed network connection") { - break - } - continue - } - go s.handleP2P(addr, string(buf[:n])) - } - return nil -} - -func (s *P2PServer) handleP2P(addr *net.UDPAddr, str string) { - var ( - v *p2p - ok bool - ) - arr := strings.Split(str, common.CONN_DATA_SEQ) - if len(arr) < 2 { - return - } - if v, ok = s.p2p[arr[0]]; !ok { - v = new(p2p) - s.p2p[arr[0]] = v - } - logs.Trace("new p2p connection ,role %s , password %s ,local address %s", arr[1], arr[0], addr.String()) - if arr[1] == common.WORK_P2P_VISITOR { - v.visitorAddr = addr - for i := 20; i > 0; i-- { - if v.providerAddr != nil { - s.listener.WriteTo([]byte(v.providerAddr.String()), v.visitorAddr) - s.listener.WriteTo([]byte(v.visitorAddr.String()), v.providerAddr) - break - } - time.Sleep(time.Second) - } - delete(s.p2p, arr[0]) - } else { - v.providerAddr = addr - } -} diff --git a/server/proxy/socks5.go b/server/proxy/socks5.go deleted file mode 100755 index 3faefe5..0000000 --- a/server/proxy/socks5.go +++ /dev/null @@ -1,395 +0,0 @@ -package proxy - -import ( - "encoding/binary" - "errors" - "io" - "net" - "strconv" - - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/conn" - "ehang.io/nps/lib/file" - "github.com/astaxie/beego/logs" -) - -const ( - ipV4 = 1 - domainName = 3 - ipV6 = 4 - connectMethod = 1 - bindMethod = 2 - associateMethod = 3 - // The maximum packet size of any udp Associate packet, based on ethernet's max size, - // minus the IP and UDP headers. IPv4 has a 20 byte header, UDP adds an - // additional 4 bytes. This is a total overhead of 24 bytes. Ethernet's - // max packet size is 1500 bytes, 1500 - 24 = 1476. - maxUDPPacketSize = 1476 -) - -const ( - succeeded uint8 = iota - serverFailure - notAllowed - networkUnreachable - hostUnreachable - connectionRefused - ttlExpired - commandNotSupported - addrTypeNotSupported -) - -const ( - UserPassAuth = uint8(2) - userAuthVersion = uint8(1) - authSuccess = uint8(0) - authFailure = uint8(1) -) - -type Sock5ModeServer struct { - BaseServer - listener net.Listener -} - -//req -func (s *Sock5ModeServer) handleRequest(c net.Conn) { - /* - The SOCKS request is formed as follows: - +----+-----+-------+------+----------+----------+ - |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | - +----+-----+-------+------+----------+----------+ - | 1 | 1 | X'00' | 1 | Variable | 2 | - +----+-----+-------+------+----------+----------+ - */ - header := make([]byte, 3) - - _, err := io.ReadFull(c, header) - - if err != nil { - logs.Warn("illegal request", err) - c.Close() - return - } - - switch header[1] { - case connectMethod: - s.handleConnect(c) - case bindMethod: - s.handleBind(c) - case associateMethod: - s.handleUDP(c) - default: - s.sendReply(c, commandNotSupported) - c.Close() - } -} - -//reply -func (s *Sock5ModeServer) sendReply(c net.Conn, rep uint8) { - reply := []byte{ - 5, - rep, - 0, - 1, - } - - localAddr := c.LocalAddr().String() - localHost, localPort, _ := net.SplitHostPort(localAddr) - ipBytes := net.ParseIP(localHost).To4() - nPort, _ := strconv.Atoi(localPort) - reply = append(reply, ipBytes...) - portBytes := make([]byte, 2) - binary.BigEndian.PutUint16(portBytes, uint16(nPort)) - reply = append(reply, portBytes...) - - c.Write(reply) -} - -//do conn -func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) { - addrType := make([]byte, 1) - c.Read(addrType) - var host string - switch addrType[0] { - case ipV4: - ipv4 := make(net.IP, net.IPv4len) - c.Read(ipv4) - host = ipv4.String() - case ipV6: - ipv6 := make(net.IP, net.IPv6len) - c.Read(ipv6) - host = ipv6.String() - case domainName: - var domainLen uint8 - binary.Read(c, binary.BigEndian, &domainLen) - domain := make([]byte, domainLen) - c.Read(domain) - host = string(domain) - default: - s.sendReply(c, addrTypeNotSupported) - return - } - - var port uint16 - binary.Read(c, binary.BigEndian, &port) - // connect to host - addr := net.JoinHostPort(host, strconv.Itoa(int(port))) - var ltype string - if command == associateMethod { - ltype = common.CONN_UDP - } else { - ltype = common.CONN_TCP - } - s.DealClient(conn.NewConn(c), s.task.Client, addr, nil, ltype, func() { - s.sendReply(c, succeeded) - }, s.task.Flow, s.task.Target.LocalProxy) - return -} - -//conn -func (s *Sock5ModeServer) handleConnect(c net.Conn) { - s.doConnect(c, connectMethod) -} - -// passive mode -func (s *Sock5ModeServer) handleBind(c net.Conn) { -} -func (s *Sock5ModeServer) sendUdpReply(writeConn net.Conn, c net.Conn, rep uint8, serverIp string) { - reply := []byte{ - 5, - rep, - 0, - 1, - } - localHost, localPort, _ := net.SplitHostPort(c.LocalAddr().String()) - localHost = serverIp - ipBytes := net.ParseIP(localHost).To4() - nPort, _ := strconv.Atoi(localPort) - reply = append(reply, ipBytes...) - portBytes := make([]byte, 2) - binary.BigEndian.PutUint16(portBytes, uint16(nPort)) - reply = append(reply, portBytes...) - writeConn.Write(reply) - -} - -func (s *Sock5ModeServer) handleUDP(c net.Conn) { - defer c.Close() - addrType := make([]byte, 1) - c.Read(addrType) - var host string - switch addrType[0] { - case ipV4: - ipv4 := make(net.IP, net.IPv4len) - c.Read(ipv4) - host = ipv4.String() - case ipV6: - ipv6 := make(net.IP, net.IPv6len) - c.Read(ipv6) - host = ipv6.String() - case domainName: - var domainLen uint8 - binary.Read(c, binary.BigEndian, &domainLen) - domain := make([]byte, domainLen) - c.Read(domain) - host = string(domain) - default: - s.sendReply(c, addrTypeNotSupported) - return - } - //读取端口 - var port uint16 - binary.Read(c, binary.BigEndian, &port) - logs.Warn(host, string(port)) - replyAddr, err := net.ResolveUDPAddr("udp", s.task.ServerIp+":0") - if err != nil { - logs.Error("build local reply addr error", err) - return - } - reply, err := net.ListenUDP("udp", replyAddr) - if err != nil { - s.sendReply(c, addrTypeNotSupported) - logs.Error("listen local reply udp port error") - return - } - // reply the local addr - s.sendUdpReply(c, reply, succeeded, common.GetServerIpByClientIp(c.RemoteAddr().(*net.TCPAddr).IP)) - defer reply.Close() - // new a tunnel to client - link := conn.NewLink("udp5", "", s.task.Client.Cnf.Crypt, s.task.Client.Cnf.Compress, c.RemoteAddr().String(), false) - target, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, s.task) - if err != nil { - logs.Warn("get connection from client id %d error %s", s.task.Client.Id, err.Error()) - return - } - - var clientAddr net.Addr - // copy buffer - go func() { - b := common.BufPoolUdp.Get().([]byte) - defer common.BufPoolUdp.Put(b) - defer c.Close() - - for { - n, laddr, err := reply.ReadFrom(b) - if err != nil { - logs.Error("read data from %s err %s", reply.LocalAddr().String(), err.Error()) - return - } - if clientAddr == nil { - clientAddr = laddr - } - if _, err := target.Write(b[:n]); err != nil { - logs.Error("write data to client error", err.Error()) - return - } - } - }() - - go func() { - var l int32 - b := common.BufPoolUdp.Get().([]byte) - defer common.BufPoolUdp.Put(b) - defer c.Close() - for { - if err := binary.Read(target, binary.LittleEndian, &l); err != nil || l >= common.PoolSizeUdp || l <= 0 { - logs.Warn("read len bytes error", err.Error()) - return - } - binary.Read(target, binary.LittleEndian, b[:l]) - if err != nil { - logs.Warn("read data form client error", err.Error()) - return - } - if _, err := reply.WriteTo(b[:l], clientAddr); err != nil { - logs.Warn("write data to user ", err.Error()) - return - } - } - }() - - b := common.BufPoolUdp.Get().([]byte) - defer common.BufPoolUdp.Put(b) - defer target.Close() - for { - _, err := c.Read(b) - if err != nil { - c.Close() - return - } - } -} - -//new conn -func (s *Sock5ModeServer) handleConn(c net.Conn) { - buf := make([]byte, 2) - if _, err := io.ReadFull(c, buf); err != nil { - logs.Warn("negotiation err", err) - c.Close() - return - } - - if version := buf[0]; version != 5 { - logs.Warn("only support socks5, request from: ", c.RemoteAddr()) - c.Close() - return - } - nMethods := buf[1] - - methods := make([]byte, nMethods) - if len, err := c.Read(methods); len != int(nMethods) || err != nil { - logs.Warn("wrong method") - c.Close() - return - } - if (s.task.Client.Cnf.U != "" && s.task.Client.Cnf.P != "") || (s.task.MultiAccount != nil && len(s.task.MultiAccount.AccountMap) > 0) { - buf[1] = UserPassAuth - c.Write(buf) - if err := s.Auth(c); err != nil { - c.Close() - logs.Warn("Validation failed:", err) - return - } - } else { - buf[1] = 0 - c.Write(buf) - } - s.handleRequest(c) -} - -//socks5 auth -func (s *Sock5ModeServer) Auth(c net.Conn) error { - header := []byte{0, 0} - if _, err := io.ReadAtLeast(c, header, 2); err != nil { - return err - } - if header[0] != userAuthVersion { - return errors.New("验证方式不被支持") - } - userLen := int(header[1]) - user := make([]byte, userLen) - if _, err := io.ReadAtLeast(c, user, userLen); err != nil { - return err - } - if _, err := c.Read(header[:1]); err != nil { - return errors.New("密码长度获取错误") - } - passLen := int(header[0]) - pass := make([]byte, passLen) - if _, err := io.ReadAtLeast(c, pass, passLen); err != nil { - return err - } - - var U, P string - if s.task.MultiAccount != nil { - // enable multi user auth - U = string(user) - var ok bool - P, ok = s.task.MultiAccount.AccountMap[U] - if !ok { - return errors.New("验证不通过") - } - } else { - U = s.task.Client.Cnf.U - P = s.task.Client.Cnf.P - } - - if string(user) == U && string(pass) == P { - if _, err := c.Write([]byte{userAuthVersion, authSuccess}); err != nil { - return err - } - return nil - } else { - if _, err := c.Write([]byte{userAuthVersion, authFailure}); err != nil { - return err - } - return errors.New("验证不通过") - } -} - -//start -func (s *Sock5ModeServer) Start() error { - return conn.NewTcpListenerAndProcess(s.task.ServerIp+":"+strconv.Itoa(s.task.Port), func(c net.Conn) { - if err := s.CheckFlowAndConnNum(s.task.Client); err != nil { - logs.Warn("client id %d, task id %d, error %s, when socks5 connection", s.task.Client.Id, s.task.Id, err.Error()) - c.Close() - return - } - logs.Trace("New socks5 connection,client %d,remote address %s", s.task.Client.Id, c.RemoteAddr()) - s.handleConn(c) - s.task.Client.AddConn() - }, &s.listener) -} - -//new -func NewSock5ModeServer(bridge NetBridge, task *file.Tunnel) *Sock5ModeServer { - s := new(Sock5ModeServer) - s.bridge = bridge - s.task = task - return s -} - -//close -func (s *Sock5ModeServer) Close() error { - return s.listener.Close() -} diff --git a/server/proxy/tcp.go b/server/proxy/tcp.go deleted file mode 100755 index 58ce3e0..0000000 --- a/server/proxy/tcp.go +++ /dev/null @@ -1,125 +0,0 @@ -package proxy - -import ( - "errors" - "net" - "net/http" - "path/filepath" - "strconv" - - "ehang.io/nps/bridge" - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/conn" - "ehang.io/nps/lib/file" - "ehang.io/nps/server/connection" - "github.com/astaxie/beego" - "github.com/astaxie/beego/logs" -) - -type TunnelModeServer struct { - BaseServer - process process - listener net.Listener -} - -//tcp|http|host -func NewTunnelModeServer(process process, bridge NetBridge, task *file.Tunnel) *TunnelModeServer { - s := new(TunnelModeServer) - s.bridge = bridge - s.process = process - s.task = task - return s -} - -//开始 -func (s *TunnelModeServer) Start() error { - return conn.NewTcpListenerAndProcess(s.task.ServerIp+":"+strconv.Itoa(s.task.Port), func(c net.Conn) { - if err := s.CheckFlowAndConnNum(s.task.Client); err != nil { - logs.Warn("client id %d, task id %d,error %s, when tcp connection", s.task.Client.Id, s.task.Id, err.Error()) - c.Close() - return - } - logs.Trace("new tcp connection,local port %d,client %d,remote address %s", s.task.Port, s.task.Client.Id, c.RemoteAddr()) - s.process(conn.NewConn(c), s) - s.task.Client.AddConn() - }, &s.listener) -} - -//close -func (s *TunnelModeServer) Close() error { - return s.listener.Close() -} - -//web管理方式 -type WebServer struct { - BaseServer -} - -//开始 -func (s *WebServer) Start() error { - p, _ := beego.AppConfig.Int("web_port") - if p == 0 { - stop := make(chan struct{}) - <-stop - } - beego.BConfig.WebConfig.Session.SessionOn = true - beego.SetStaticPath(beego.AppConfig.String("web_base_url")+"/static", filepath.Join(common.GetRunPath(), "web", "static")) - beego.SetViewsPath(filepath.Join(common.GetRunPath(), "web", "views")) - err := errors.New("Web management startup failure ") - var l net.Listener - if l, err = connection.GetWebManagerListener(); err == nil { - beego.InitBeforeHTTPRun() - if beego.AppConfig.String("web_open_ssl") == "true" { - keyPath := beego.AppConfig.String("web_key_file") - certPath := beego.AppConfig.String("web_cert_file") - err = http.ServeTLS(l, beego.BeeApp.Handlers, certPath, keyPath) - } else { - err = http.Serve(l, beego.BeeApp.Handlers) - } - } else { - logs.Error(err) - } - return err -} - -func (s *WebServer) Close() error { - return nil -} - -//new -func NewWebServer(bridge *bridge.Bridge) *WebServer { - s := new(WebServer) - s.bridge = bridge - return s -} - -type process func(c *conn.Conn, s *TunnelModeServer) error - -//tcp proxy -func ProcessTunnel(c *conn.Conn, s *TunnelModeServer) error { - targetAddr, err := s.task.Target.GetRandomTarget() - if err != nil { - c.Close() - logs.Warn("tcp port %d ,client id %d,task id %d connect error %s", s.task.Port, s.task.Client.Id, s.task.Id, err.Error()) - return err - } - return s.DealClient(c, s.task.Client, targetAddr, nil, common.CONN_TCP, nil, s.task.Flow, s.task.Target.LocalProxy) -} - -//http proxy -func ProcessHttp(c *conn.Conn, s *TunnelModeServer) error { - _, addr, rb, err, r := c.GetHost() - if err != nil { - c.Close() - logs.Info(err) - return err - } - if r.Method == "CONNECT" { - c.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n")) - rb = nil - } - if err := s.auth(r, c, s.task.Client.Cnf.U, s.task.Client.Cnf.P); err != nil { - return err - } - return s.DealClient(c, s.task.Client, addr, rb, common.CONN_TCP, nil, s.task.Flow, s.task.Target.LocalProxy) -} diff --git a/server/proxy/transport.go b/server/proxy/transport.go deleted file mode 100644 index e08bfd0..0000000 --- a/server/proxy/transport.go +++ /dev/null @@ -1,44 +0,0 @@ -// +build !windows - -package proxy - -import ( - "net" - "strconv" - "syscall" - - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/conn" -) - -func HandleTrans(c *conn.Conn, s *TunnelModeServer) error { - if addr, err := getAddress(c.Conn); err != nil { - return err - } else { - return s.DealClient(c, s.task.Client, addr, nil, common.CONN_TCP, nil, s.task.Flow, s.task.Target.LocalProxy) - } -} - -const SO_ORIGINAL_DST = 80 - -func getAddress(conn net.Conn) (string, error) { - sysrawconn, f := conn.(syscall.Conn) - if !f { - return "", nil - } - rawConn, err := sysrawconn.SyscallConn() - if err != nil { - return "", nil - } - var ip string - var port uint16 - err = rawConn.Control(func(fd uintptr) { - addr, err := syscall.GetsockoptIPv6Mreq(int(fd), syscall.IPPROTO_IP, SO_ORIGINAL_DST) - if err != nil { - return - } - ip = net.IP(addr.Multiaddr[4:8]).String() - port = uint16(addr.Multiaddr[2])<<8 + uint16(addr.Multiaddr[3]) - }) - return ip + ":" + strconv.Itoa(int(port)), nil -} diff --git a/server/proxy/transport_windows.go b/server/proxy/transport_windows.go deleted file mode 100644 index def673b..0000000 --- a/server/proxy/transport_windows.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build windows - -package proxy - -import ( - "ehang.io/nps/lib/conn" -) - -func HandleTrans(c *conn.Conn, s *TunnelModeServer) error { - return nil -} diff --git a/server/proxy/udp.go b/server/proxy/udp.go deleted file mode 100755 index e89259a..0000000 --- a/server/proxy/udp.go +++ /dev/null @@ -1,99 +0,0 @@ -package proxy - -import ( - "io" - "net" - "strings" - "sync" - "time" - - "ehang.io/nps/bridge" - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/conn" - "ehang.io/nps/lib/file" - "github.com/astaxie/beego/logs" -) - -type UdpModeServer struct { - BaseServer - addrMap sync.Map - listener *net.UDPConn -} - -func NewUdpModeServer(bridge *bridge.Bridge, task *file.Tunnel) *UdpModeServer { - s := new(UdpModeServer) - s.bridge = bridge - s.task = task - return s -} - -//开始 -func (s *UdpModeServer) Start() error { - var err error - if s.task.ServerIp == "" { - s.task.ServerIp = "0.0.0.0" - } - s.listener, err = net.ListenUDP("udp", &net.UDPAddr{net.ParseIP(s.task.ServerIp), s.task.Port, ""}) - if err != nil { - return err - } - for { - buf := common.BufPoolUdp.Get().([]byte) - n, addr, err := s.listener.ReadFromUDP(buf) - if err != nil { - if strings.Contains(err.Error(), "use of closed network connection") { - break - } - continue - } - logs.Trace("New udp connection,client %d,remote address %s", s.task.Client.Id, addr) - go s.process(addr, buf[:n]) - } - return nil -} - -func (s *UdpModeServer) process(addr *net.UDPAddr, data []byte) { - if v, ok := s.addrMap.Load(addr.String()); ok { - clientConn, ok := v.(io.ReadWriteCloser) - if ok { - clientConn.Write(data) - s.task.Flow.Add(int64(len(data)), 0) - } - } else { - if err := s.CheckFlowAndConnNum(s.task.Client); err != nil { - logs.Warn("client id %d, task id %d,error %s, when udp connection", s.task.Client.Id, s.task.Id, err.Error()) - return - } - defer s.task.Client.AddConn() - link := conn.NewLink(common.CONN_UDP, s.task.Target.TargetStr, s.task.Client.Cnf.Crypt, s.task.Client.Cnf.Compress, addr.String(), s.task.Target.LocalProxy) - if clientConn, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, s.task); err != nil { - return - } else { - target := conn.GetConn(clientConn, s.task.Client.Cnf.Crypt, s.task.Client.Cnf.Compress, nil, true) - s.addrMap.Store(addr.String(), target) - defer target.Close() - - target.Write(data) - - buf := common.BufPoolUdp.Get().([]byte) - defer common.BufPoolUdp.Put(buf) - - s.task.Flow.Add(int64(len(data)), 0) - for { - clientConn.SetReadDeadline(time.Now().Add(time.Minute * 10)) - if n, err := target.Read(buf); err != nil { - s.addrMap.Delete(addr.String()) - logs.Warn(err) - return - } else { - s.listener.WriteTo(buf[:n], addr) - s.task.Flow.Add(0, int64(n)) - } - } - } - } -} - -func (s *UdpModeServer) Close() error { - return s.listener.Close() -} diff --git a/server/server.go b/server/server.go deleted file mode 100644 index 148e8e4..0000000 --- a/server/server.go +++ /dev/null @@ -1,460 +0,0 @@ -package server - -import ( - "ehang.io/nps/lib/version" - "errors" - "math" - "os" - "strconv" - "strings" - "sync" - "time" - - "ehang.io/nps/bridge" - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/file" - "ehang.io/nps/server/proxy" - "ehang.io/nps/server/tool" - "github.com/astaxie/beego" - "github.com/astaxie/beego/logs" - "github.com/shirou/gopsutil/v3/cpu" - "github.com/shirou/gopsutil/v3/load" - "github.com/shirou/gopsutil/v3/mem" - "github.com/shirou/gopsutil/v3/net" -) - -var ( - Bridge *bridge.Bridge - RunList sync.Map //map[int]interface{} -) - -func init() { - RunList = sync.Map{} -} - -//init task from db -func InitFromCsv() { - //Add a public password - if vkey := beego.AppConfig.String("public_vkey"); vkey != "" { - c := file.NewClient(vkey, true, true) - file.GetDb().NewClient(c) - RunList.Store(c.Id, nil) - //RunList[c.Id] = nil - } - //Initialize services in server-side files - file.GetDb().JsonDb.Tasks.Range(func(key, value interface{}) bool { - if value.(*file.Tunnel).Status { - AddTask(value.(*file.Tunnel)) - } - return true - }) -} - -//get bridge command -func DealBridgeTask() { - for { - select { - case t := <-Bridge.OpenTask: - AddTask(t) - case t := <-Bridge.CloseTask: - StopServer(t.Id) - case id := <-Bridge.CloseClient: - DelTunnelAndHostByClientId(id, true) - if v, ok := file.GetDb().JsonDb.Clients.Load(id); ok { - if v.(*file.Client).NoStore { - file.GetDb().DelClient(id) - } - } - case tunnel := <-Bridge.OpenTask: - StartTask(tunnel.Id) - case s := <-Bridge.SecretChan: - logs.Trace("New secret connection, addr", s.Conn.Conn.RemoteAddr()) - if t := file.GetDb().GetTaskByMd5Password(s.Password); t != nil { - if t.Status { - go proxy.NewBaseServer(Bridge, t).DealClient(s.Conn, t.Client, t.Target.TargetStr, nil, common.CONN_TCP, nil, t.Flow, t.Target.LocalProxy) - } else { - s.Conn.Close() - logs.Trace("This key %s cannot be processed,status is close", s.Password) - } - } else { - logs.Trace("This key %s cannot be processed", s.Password) - s.Conn.Close() - } - } - } -} - -//start a new server -func StartNewServer(bridgePort int, cnf *file.Tunnel, bridgeType string, bridgeDisconnect int) { - Bridge = bridge.NewTunnel(bridgePort, bridgeType, common.GetBoolByStr(beego.AppConfig.String("ip_limit")), RunList, bridgeDisconnect) - go func() { - if err := Bridge.StartTunnel(); err != nil { - logs.Error("start server bridge error", err) - os.Exit(0) - } - }() - if p, err := beego.AppConfig.Int("p2p_port"); err == nil { - go proxy.NewP2PServer(p).Start() - go proxy.NewP2PServer(p + 1).Start() - go proxy.NewP2PServer(p + 2).Start() - } - go DealBridgeTask() - go dealClientFlow() - if svr := NewMode(Bridge, cnf); svr != nil { - if err := svr.Start(); err != nil { - logs.Error(err) - } - RunList.Store(cnf.Id, svr) - //RunList[cnf.Id] = svr - } else { - logs.Error("Incorrect startup mode %s", cnf.Mode) - } -} - -func dealClientFlow() { - ticker := time.NewTicker(time.Minute) - defer ticker.Stop() - for { - select { - case <-ticker.C: - dealClientData() - } - } -} - -//new a server by mode name -func NewMode(Bridge *bridge.Bridge, c *file.Tunnel) proxy.Service { - var service proxy.Service - switch c.Mode { - case "tcp", "file": - service = proxy.NewTunnelModeServer(proxy.ProcessTunnel, Bridge, c) - case "socks5": - service = proxy.NewSock5ModeServer(Bridge, c) - case "httpProxy": - service = proxy.NewTunnelModeServer(proxy.ProcessHttp, Bridge, c) - case "tcpTrans": - service = proxy.NewTunnelModeServer(proxy.HandleTrans, Bridge, c) - case "udp": - service = proxy.NewUdpModeServer(Bridge, c) - case "webServer": - InitFromCsv() - t := &file.Tunnel{ - Port: 0, - Mode: "httpHostServer", - Status: true, - } - AddTask(t) - service = proxy.NewWebServer(Bridge) - case "httpHostServer": - httpPort, _ := beego.AppConfig.Int("http_proxy_port") - httpsPort, _ := beego.AppConfig.Int("https_proxy_port") - useCache, _ := beego.AppConfig.Bool("http_cache") - cacheLen, _ := beego.AppConfig.Int("http_cache_length") - addOrigin, _ := beego.AppConfig.Bool("http_add_origin_header") - service = proxy.NewHttp(Bridge, c, httpPort, httpsPort, useCache, cacheLen, addOrigin) - } - return service -} - -//stop server -func StopServer(id int) error { - //if v, ok := RunList[id]; ok { - if v, ok := RunList.Load(id); ok { - if svr, ok := v.(proxy.Service); ok { - if err := svr.Close(); err != nil { - return err - } - logs.Info("stop server id %d", id) - } else { - logs.Warn("stop server id %d error", id) - } - if t, err := file.GetDb().GetTask(id); err != nil { - return err - } else { - t.Status = false - file.GetDb().UpdateTask(t) - } - //delete(RunList, id) - RunList.Delete(id) - return nil - } - return errors.New("task is not running") -} - -//add task -func AddTask(t *file.Tunnel) error { - if t.Mode == "secret" || t.Mode == "p2p" { - logs.Info("secret task %s start ", t.Remark) - //RunList[t.Id] = nil - RunList.Store(t.Id, nil) - return nil - } - if b := tool.TestServerPort(t.Port, t.Mode); !b && t.Mode != "httpHostServer" { - logs.Error("taskId %d start error port %d open failed", t.Id, t.Port) - return errors.New("the port open error") - } - if minute, err := beego.AppConfig.Int("flow_store_interval"); err == nil && minute > 0 { - go flowSession(time.Minute * time.Duration(minute)) - } - if svr := NewMode(Bridge, t); svr != nil { - logs.Info("tunnel task %s start mode:%s port %d", t.Remark, t.Mode, t.Port) - //RunList[t.Id] = svr - RunList.Store(t.Id, svr) - go func() { - if err := svr.Start(); err != nil { - logs.Error("clientId %d taskId %d start error %s", t.Client.Id, t.Id, err) - //delete(RunList, t.Id) - RunList.Delete(t.Id) - return - } - }() - } else { - return errors.New("the mode is not correct") - } - return nil -} - -//start task -func StartTask(id int) error { - if t, err := file.GetDb().GetTask(id); err != nil { - return err - } else { - AddTask(t) - t.Status = true - file.GetDb().UpdateTask(t) - } - return nil -} - -//delete task -func DelTask(id int) error { - //if _, ok := RunList[id]; ok { - if _, ok := RunList.Load(id); ok { - if err := StopServer(id); err != nil { - return err - } - } - return file.GetDb().DelTask(id) -} - -//get task list by page num -func GetTunnel(start, length int, typeVal string, clientId int, search string) ([]*file.Tunnel, int) { - list := make([]*file.Tunnel, 0) - var cnt int - keys := file.GetMapKeys(file.GetDb().JsonDb.Tasks, false, "", "") - for _, key := range keys { - if value, ok := file.GetDb().JsonDb.Tasks.Load(key); ok { - v := value.(*file.Tunnel) - if (typeVal != "" && v.Mode != typeVal || (clientId != 0 && v.Client.Id != clientId)) || (typeVal == "" && clientId != v.Client.Id) { - continue - } - if search != "" && !(v.Id == common.GetIntNoErrByStr(search) || v.Port == common.GetIntNoErrByStr(search) || strings.Contains(v.Password, search) || strings.Contains(v.Remark, search)) { - continue - } - cnt++ - if _, ok := Bridge.Client.Load(v.Client.Id); ok { - v.Client.IsConnect = true - } else { - v.Client.IsConnect = false - } - if start--; start < 0 { - if length--; length >= 0 { - //if _, ok := RunList[v.Id]; ok { - if _, ok := RunList.Load(v.Id); ok { - v.RunStatus = true - } else { - v.RunStatus = false - } - list = append(list, v) - } - } - } - } - return list, cnt -} - -//get client list -func GetClientList(start, length int, search, sort, order string, clientId int) (list []*file.Client, cnt int) { - list, cnt = file.GetDb().GetClientList(start, length, search, sort, order, clientId) - dealClientData() - return -} - -func dealClientData() { - file.GetDb().JsonDb.Clients.Range(func(key, value interface{}) bool { - v := value.(*file.Client) - if vv, ok := Bridge.Client.Load(v.Id); ok { - v.IsConnect = true - v.Version = vv.(*bridge.Client).Version - } else { - v.IsConnect = false - } - v.Flow.InletFlow = 0 - v.Flow.ExportFlow = 0 - file.GetDb().JsonDb.Hosts.Range(func(key, value interface{}) bool { - h := value.(*file.Host) - if h.Client.Id == v.Id { - v.Flow.InletFlow += h.Flow.InletFlow - v.Flow.ExportFlow += h.Flow.ExportFlow - } - return true - }) - file.GetDb().JsonDb.Tasks.Range(func(key, value interface{}) bool { - t := value.(*file.Tunnel) - if t.Client.Id == v.Id { - v.Flow.InletFlow += t.Flow.InletFlow - v.Flow.ExportFlow += t.Flow.ExportFlow - } - return true - }) - return true - }) - return -} - -//delete all host and tasks by client id -func DelTunnelAndHostByClientId(clientId int, justDelNoStore bool) { - var ids []int - file.GetDb().JsonDb.Tasks.Range(func(key, value interface{}) bool { - v := value.(*file.Tunnel) - if justDelNoStore && !v.NoStore { - return true - } - if v.Client.Id == clientId { - ids = append(ids, v.Id) - } - return true - }) - for _, id := range ids { - DelTask(id) - } - ids = ids[:0] - file.GetDb().JsonDb.Hosts.Range(func(key, value interface{}) bool { - v := value.(*file.Host) - if justDelNoStore && !v.NoStore { - return true - } - if v.Client.Id == clientId { - ids = append(ids, v.Id) - } - return true - }) - for _, id := range ids { - file.GetDb().DelHost(id) - } -} - -//close the client -func DelClientConnect(clientId int) { - Bridge.DelClient(clientId) -} - -func GetDashboardData() map[string]interface{} { - data := make(map[string]interface{}) - data["version"] = version.VERSION - data["hostCount"] = common.GeSynctMapLen(file.GetDb().JsonDb.Hosts) - data["clientCount"] = common.GeSynctMapLen(file.GetDb().JsonDb.Clients) - if beego.AppConfig.String("public_vkey") != "" { //remove public vkey - data["clientCount"] = data["clientCount"].(int) - 1 - } - dealClientData() - c := 0 - var in, out int64 - file.GetDb().JsonDb.Clients.Range(func(key, value interface{}) bool { - v := value.(*file.Client) - if v.IsConnect { - c += 1 - } - in += v.Flow.InletFlow - out += v.Flow.ExportFlow - return true - }) - data["clientOnlineCount"] = c - data["inletFlowCount"] = int(in) - data["exportFlowCount"] = int(out) - var tcp, udp, secret, socks5, p2p, http int - file.GetDb().JsonDb.Tasks.Range(func(key, value interface{}) bool { - switch value.(*file.Tunnel).Mode { - case "tcp": - tcp += 1 - case "socks5": - socks5 += 1 - case "httpProxy": - http += 1 - case "udp": - udp += 1 - case "p2p": - p2p += 1 - case "secret": - secret += 1 - } - return true - }) - - data["tcpC"] = tcp - data["udpCount"] = udp - data["socks5Count"] = socks5 - data["httpProxyCount"] = http - data["secretCount"] = secret - data["p2pCount"] = p2p - data["bridgeType"] = beego.AppConfig.String("bridge_type") - data["httpProxyPort"] = beego.AppConfig.String("http_proxy_port") - data["httpsProxyPort"] = beego.AppConfig.String("https_proxy_port") - data["ipLimit"] = beego.AppConfig.String("ip_limit") - data["flowStoreInterval"] = beego.AppConfig.String("flow_store_interval") - data["serverIp"] = beego.AppConfig.String("p2p_ip") - data["p2pPort"] = beego.AppConfig.String("p2p_port") - data["logLevel"] = beego.AppConfig.String("log_level") - tcpCount := 0 - - file.GetDb().JsonDb.Clients.Range(func(key, value interface{}) bool { - tcpCount += int(value.(*file.Client).NowConn) - return true - }) - data["tcpCount"] = tcpCount - cpuPercet, _ := cpu.Percent(0, true) - var cpuAll float64 - for _, v := range cpuPercet { - cpuAll += v - } - loads, _ := load.Avg() - data["load"] = loads.String() - data["cpu"] = math.Round(cpuAll / float64(len(cpuPercet))) - swap, _ := mem.SwapMemory() - data["swap_mem"] = math.Round(swap.UsedPercent) - vir, _ := mem.VirtualMemory() - data["virtual_mem"] = math.Round(vir.UsedPercent) - conn, _ := net.ProtoCounters(nil) - io1, _ := net.IOCounters(false) - time.Sleep(time.Millisecond * 500) - io2, _ := net.IOCounters(false) - if len(io2) > 0 && len(io1) > 0 { - data["io_send"] = (io2[0].BytesSent - io1[0].BytesSent) * 2 - data["io_recv"] = (io2[0].BytesRecv - io1[0].BytesRecv) * 2 - } - for _, v := range conn { - data[v.Protocol] = v.Stats["CurrEstab"] - } - //chart - var fg int - if len(tool.ServerStatus) >= 10 { - fg = len(tool.ServerStatus) / 10 - for i := 0; i <= 9; i++ { - data["sys"+strconv.Itoa(i+1)] = tool.ServerStatus[i*fg] - } - } - return data -} - -func flowSession(m time.Duration) { - ticker := time.NewTicker(m) - defer ticker.Stop() - for { - select { - case <-ticker.C: - file.GetDb().JsonDb.StoreHostToJsonFile() - file.GetDb().JsonDb.StoreTasksToJsonFile() - file.GetDb().JsonDb.StoreClientsToJsonFile() - } - } -} diff --git a/server/test/test.go b/server/test/test.go deleted file mode 100644 index 3fce9ee..0000000 --- a/server/test/test.go +++ /dev/null @@ -1,84 +0,0 @@ -package test - -import ( - "log" - "path/filepath" - "strconv" - - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/file" - "github.com/astaxie/beego" -) - -func TestServerConfig() { - var postTcpArr []int - var postUdpArr []int - file.GetDb().JsonDb.Tasks.Range(func(key, value interface{}) bool { - v := value.(*file.Tunnel) - if v.Mode == "udp" { - isInArr(&postUdpArr, v.Port, v.Remark, "udp") - } else if v.Port != 0 { - - isInArr(&postTcpArr, v.Port, v.Remark, "tcp") - } - return true - }) - p, err := beego.AppConfig.Int("web_port") - if err != nil { - log.Fatalln("Getting web management port error :", err) - } else { - isInArr(&postTcpArr, p, "Web Management port", "tcp") - } - - if p := beego.AppConfig.String("bridge_port"); p != "" { - if port, err := strconv.Atoi(p); err != nil { - log.Fatalln("get Server and client communication portserror:", err) - } else if beego.AppConfig.String("bridge_type") == "kcp" { - isInArr(&postUdpArr, port, "Server and client communication ports", "udp") - } else { - isInArr(&postTcpArr, port, "Server and client communication ports", "tcp") - } - } - - if p := beego.AppConfig.String("httpProxyPort"); p != "" { - if port, err := strconv.Atoi(p); err != nil { - log.Fatalln("get http port error:", err) - } else { - isInArr(&postTcpArr, port, "https port", "tcp") - } - } - if p := beego.AppConfig.String("https_proxy_port"); p != "" { - if b, err := beego.AppConfig.Bool("https_just_proxy"); !(err == nil && b) { - if port, err := strconv.Atoi(p); err != nil { - log.Fatalln("get https port error", err) - } else { - if beego.AppConfig.String("pemPath") != "" && !common.FileExists(filepath.Join(common.GetRunPath(), beego.AppConfig.String("pemPath"))) { - log.Fatalf("ssl certFile %s is not exist", beego.AppConfig.String("pemPath")) - } - if beego.AppConfig.String("keyPath") != "" && !common.FileExists(filepath.Join(common.GetRunPath(), beego.AppConfig.String("keyPath"))) { - log.Fatalf("ssl keyFile %s is not exist", beego.AppConfig.String("pemPath")) - } - isInArr(&postTcpArr, port, "http port", "tcp") - } - } - } -} - -func isInArr(arr *[]int, val int, remark string, tp string) { - for _, v := range *arr { - if v == val { - log.Fatalf("the port %d is reused,remark: %s", val, remark) - } - } - if tp == "tcp" { - if !common.TestTcpPort(val) { - log.Fatalf("open the %d port error ,remark: %s", val, remark) - } - } else { - if !common.TestUdpPort(val) { - log.Fatalf("open the %d port error ,remark: %s", val, remark) - } - } - *arr = append(*arr, val) - return -} diff --git a/server/tool/utils.go b/server/tool/utils.go deleted file mode 100644 index 38e40dc..0000000 --- a/server/tool/utils.go +++ /dev/null @@ -1,94 +0,0 @@ -package tool - -import ( - "math" - "strconv" - "time" - - "ehang.io/nps/lib/common" - "github.com/astaxie/beego" - "github.com/shirou/gopsutil/v3/cpu" - "github.com/shirou/gopsutil/v3/load" - "github.com/shirou/gopsutil/v3/mem" - "github.com/shirou/gopsutil/v3/net" -) - -var ( - ports []int - ServerStatus []map[string]interface{} -) - -func StartSystemInfo() { - if b, err := beego.AppConfig.Bool("system_info_display"); err == nil && b { - ServerStatus = make([]map[string]interface{}, 0, 1500) - go getSeverStatus() - } -} - -func InitAllowPort() { - p := beego.AppConfig.String("allow_ports") - ports = common.GetPorts(p) -} - -func TestServerPort(p int, m string) (b bool) { - if m == "p2p" || m == "secret" { - return true - } - if p > 65535 || p < 0 { - return false - } - if len(ports) != 0 { - if !common.InIntArr(ports, p) { - return false - } - } - if m == "udp" { - b = common.TestUdpPort(p) - } else { - b = common.TestTcpPort(p) - } - return -} - -func getSeverStatus() { - for { - if len(ServerStatus) < 10 { - time.Sleep(time.Second) - } else { - time.Sleep(time.Minute) - } - cpuPercet, _ := cpu.Percent(0, true) - var cpuAll float64 - for _, v := range cpuPercet { - cpuAll += v - } - m := make(map[string]interface{}) - loads, _ := load.Avg() - m["load1"] = loads.Load1 - m["load5"] = loads.Load5 - m["load15"] = loads.Load15 - m["cpu"] = math.Round(cpuAll / float64(len(cpuPercet))) - swap, _ := mem.SwapMemory() - m["swap_mem"] = math.Round(swap.UsedPercent) - vir, _ := mem.VirtualMemory() - m["virtual_mem"] = math.Round(vir.UsedPercent) - conn, _ := net.ProtoCounters(nil) - io1, _ := net.IOCounters(false) - time.Sleep(time.Millisecond * 500) - io2, _ := net.IOCounters(false) - if len(io2) > 0 && len(io1) > 0 { - m["io_send"] = (io2[0].BytesSent - io1[0].BytesSent) * 2 - m["io_recv"] = (io2[0].BytesRecv - io1[0].BytesRecv) * 2 - } - t := time.Now() - m["time"] = strconv.Itoa(t.Hour()) + ":" + strconv.Itoa(t.Minute()) + ":" + strconv.Itoa(t.Second()) - - for _, v := range conn { - m[v.Protocol] = v.Stats["CurrEstab"] - } - if len(ServerStatus) >= 1440 { - ServerStatus = ServerStatus[1:] - } - ServerStatus = append(ServerStatus, m) - } -} diff --git a/web/controllers/auth.go b/web/controllers/auth.go deleted file mode 100644 index 9eb22ca..0000000 --- a/web/controllers/auth.go +++ /dev/null @@ -1,42 +0,0 @@ -package controllers - -import ( - "encoding/hex" - "time" - - "ehang.io/nps/lib/crypt" - "github.com/astaxie/beego" -) - -type AuthController struct { - beego.Controller -} - -func (s *AuthController) GetAuthKey() { - m := make(map[string]interface{}) - defer func() { - s.Data["json"] = m - s.ServeJSON() - }() - if cryptKey := beego.AppConfig.String("auth_crypt_key"); len(cryptKey) != 16 { - m["status"] = 0 - return - } else { - b, err := crypt.AesEncrypt([]byte(beego.AppConfig.String("auth_key")), []byte(cryptKey)) - if err != nil { - m["status"] = 0 - return - } - m["status"] = 1 - m["crypt_auth_key"] = hex.EncodeToString(b) - m["crypt_type"] = "aes cbc" - return - } -} - -func (s *AuthController) GetTime() { - m := make(map[string]interface{}) - m["time"] = time.Now().Unix() - s.Data["json"] = m - s.ServeJSON() -} diff --git a/web/controllers/base.go b/web/controllers/base.go deleted file mode 100755 index d1cd799..0000000 --- a/web/controllers/base.go +++ /dev/null @@ -1,207 +0,0 @@ -package controllers - -import ( - "html" - "math" - "strconv" - "strings" - "time" - - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/crypt" - "ehang.io/nps/lib/file" - "ehang.io/nps/server" - "github.com/astaxie/beego" -) - -type BaseController struct { - beego.Controller - controllerName string - actionName string -} - -//初始化参数 -func (s *BaseController) Prepare() { - s.Data["web_base_url"] = beego.AppConfig.String("web_base_url") - controllerName, actionName := s.GetControllerAndAction() - s.controllerName = strings.ToLower(controllerName[0 : len(controllerName)-10]) - s.actionName = strings.ToLower(actionName) - // web api verify - // param 1 is md5(authKey+Current timestamp) - // param 2 is timestamp (It's limited to 20 seconds.) - md5Key := s.getEscapeString("auth_key") - timestamp := s.GetIntNoErr("timestamp") - configKey := beego.AppConfig.String("auth_key") - timeNowUnix := time.Now().Unix() - if !(md5Key != "" && (math.Abs(float64(timeNowUnix-int64(timestamp))) <= 20) && (crypt.Md5(configKey+strconv.Itoa(timestamp)) == md5Key)) { - if s.GetSession("auth") != true { - s.Redirect(beego.AppConfig.String("web_base_url")+"/login/index", 302) - } - } else { - s.SetSession("isAdmin", true) - s.Data["isAdmin"] = true - } - if s.GetSession("isAdmin") != nil && !s.GetSession("isAdmin").(bool) { - s.Ctx.Input.SetData("client_id", s.GetSession("clientId").(int)) - s.Ctx.Input.SetParam("client_id", strconv.Itoa(s.GetSession("clientId").(int))) - s.Data["isAdmin"] = false - s.Data["username"] = s.GetSession("username") - s.CheckUserAuth() - } else { - s.Data["isAdmin"] = true - } - s.Data["https_just_proxy"], _ = beego.AppConfig.Bool("https_just_proxy") - s.Data["allow_user_login"], _ = beego.AppConfig.Bool("allow_user_login") - s.Data["allow_flow_limit"], _ = beego.AppConfig.Bool("allow_flow_limit") - s.Data["allow_rate_limit"], _ = beego.AppConfig.Bool("allow_rate_limit") - s.Data["allow_connection_num_limit"], _ = beego.AppConfig.Bool("allow_connection_num_limit") - s.Data["allow_multi_ip"], _ = beego.AppConfig.Bool("allow_multi_ip") - s.Data["system_info_display"], _ = beego.AppConfig.Bool("system_info_display") - s.Data["allow_tunnel_num_limit"], _ = beego.AppConfig.Bool("allow_tunnel_num_limit") - s.Data["allow_local_proxy"], _ = beego.AppConfig.Bool("allow_local_proxy") - s.Data["allow_user_change_username"], _ = beego.AppConfig.Bool("allow_user_change_username") -} - -//加载模板 -func (s *BaseController) display(tpl ...string) { - s.Data["web_base_url"] = beego.AppConfig.String("web_base_url") - var tplname string - if s.Data["menu"] == nil { - s.Data["menu"] = s.actionName - } - if len(tpl) > 0 { - tplname = strings.Join([]string{tpl[0], "html"}, ".") - } else { - tplname = s.controllerName + "/" + s.actionName + ".html" - } - ip := s.Ctx.Request.Host - s.Data["ip"] = common.GetIpByAddr(ip) - s.Data["bridgeType"] = beego.AppConfig.String("bridge_type") - if common.IsWindows() { - s.Data["win"] = ".exe" - } - s.Data["p"] = server.Bridge.TunnelPort - s.Data["proxyPort"] = beego.AppConfig.String("hostPort") - s.Layout = "public/layout.html" - s.TplName = tplname -} - -//错误 -func (s *BaseController) error() { - s.Data["web_base_url"] = beego.AppConfig.String("web_base_url") - s.Layout = "public/layout.html" - s.TplName = "public/error.html" -} - -//getEscapeString -func (s *BaseController) getEscapeString(key string) string { - return html.EscapeString(s.GetString(key)) -} - -//去掉没有err返回值的int -func (s *BaseController) GetIntNoErr(key string, def ...int) int { - strv := s.Ctx.Input.Query(key) - if len(strv) == 0 && len(def) > 0 { - return def[0] - } - val, _ := strconv.Atoi(strv) - return val -} - -//获取去掉错误的bool值 -func (s *BaseController) GetBoolNoErr(key string, def ...bool) bool { - strv := s.Ctx.Input.Query(key) - if len(strv) == 0 && len(def) > 0 { - return def[0] - } - val, _ := strconv.ParseBool(strv) - return val -} - -//ajax正确返回 -func (s *BaseController) AjaxOk(str string) { - s.Data["json"] = ajax(str, 1) - s.ServeJSON() - s.StopRun() -} - -//ajax错误返回 -func (s *BaseController) AjaxErr(str string) { - s.Data["json"] = ajax(str, 0) - s.ServeJSON() - s.StopRun() -} - -//组装ajax -func ajax(str string, status int) map[string]interface{} { - json := make(map[string]interface{}) - json["status"] = status - json["msg"] = str - return json -} - -//ajax table返回 -func (s *BaseController) AjaxTable(list interface{}, cnt int, recordsTotal int, kwargs map[string]interface{}) { - json := make(map[string]interface{}) - json["rows"] = list - json["total"] = recordsTotal - if kwargs != nil { - for k, v := range kwargs { - if v != nil { - json[k] = v - } - } - } - s.Data["json"] = json - s.ServeJSON() - s.StopRun() -} - -//ajax table参数 -func (s *BaseController) GetAjaxParams() (start, limit int) { - return s.GetIntNoErr("offset"), s.GetIntNoErr("limit") -} - -func (s *BaseController) SetInfo(name string) { - s.Data["name"] = name -} - -func (s *BaseController) SetType(name string) { - s.Data["type"] = name -} - -func (s *BaseController) CheckUserAuth() { - if s.controllerName == "client" { - if s.actionName == "add" { - s.StopRun() - return - } - if id := s.GetIntNoErr("id"); id != 0 { - if id != s.GetSession("clientId").(int) { - s.StopRun() - return - } - } - } - if s.controllerName == "index" { - if id := s.GetIntNoErr("id"); id != 0 { - belong := false - if strings.Contains(s.actionName, "h") { - if v, ok := file.GetDb().JsonDb.Hosts.Load(id); ok { - if v.(*file.Host).Client.Id == s.GetSession("clientId").(int) { - belong = true - } - } - } else { - if v, ok := file.GetDb().JsonDb.Tasks.Load(id); ok { - if v.(*file.Tunnel).Client.Id == s.GetSession("clientId").(int) { - belong = true - } - } - } - if !belong { - s.StopRun() - } - } - } -} diff --git a/web/controllers/client.go b/web/controllers/client.go deleted file mode 100644 index 97bc1ca..0000000 --- a/web/controllers/client.go +++ /dev/null @@ -1,174 +0,0 @@ -package controllers - -import ( - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/file" - "ehang.io/nps/lib/rate" - "ehang.io/nps/server" - "github.com/astaxie/beego" -) - -type ClientController struct { - BaseController -} - -func (s *ClientController) List() { - if s.Ctx.Request.Method == "GET" { - s.Data["menu"] = "client" - s.SetInfo("client") - s.display("client/list") - return - } - start, length := s.GetAjaxParams() - clientIdSession := s.GetSession("clientId") - var clientId int - if clientIdSession == nil { - clientId = 0 - } else { - clientId = clientIdSession.(int) - } - list, cnt := server.GetClientList(start, length, s.getEscapeString("search"), s.getEscapeString("sort"), s.getEscapeString("order"), clientId) - cmd := make(map[string]interface{}) - ip := s.Ctx.Request.Host - cmd["ip"] = common.GetIpByAddr(ip) - cmd["bridgeType"] = beego.AppConfig.String("bridge_type") - cmd["bridgePort"] = server.Bridge.TunnelPort - s.AjaxTable(list, cnt, cnt, cmd) -} - -//添加客户端 -func (s *ClientController) Add() { - if s.Ctx.Request.Method == "GET" { - s.Data["menu"] = "client" - s.SetInfo("add client") - s.display() - } else { - t := &file.Client{ - VerifyKey: s.getEscapeString("vkey"), - Id: int(file.GetDb().JsonDb.GetClientId()), - Status: true, - Remark: s.getEscapeString("remark"), - Cnf: &file.Config{ - U: s.getEscapeString("u"), - P: s.getEscapeString("p"), - Compress: common.GetBoolByStr(s.getEscapeString("compress")), - Crypt: s.GetBoolNoErr("crypt"), - }, - ConfigConnAllow: s.GetBoolNoErr("config_conn_allow"), - RateLimit: s.GetIntNoErr("rate_limit"), - MaxConn: s.GetIntNoErr("max_conn"), - WebUserName: s.getEscapeString("web_username"), - WebPassword: s.getEscapeString("web_password"), - MaxTunnelNum: s.GetIntNoErr("max_tunnel"), - Flow: &file.Flow{ - ExportFlow: 0, - InletFlow: 0, - FlowLimit: int64(s.GetIntNoErr("flow_limit")), - }, - } - if err := file.GetDb().NewClient(t); err != nil { - s.AjaxErr(err.Error()) - } - s.AjaxOk("add success") - } -} -func (s *ClientController) GetClient() { - if s.Ctx.Request.Method == "POST" { - id := s.GetIntNoErr("id") - data := make(map[string]interface{}) - if c, err := file.GetDb().GetClient(id); err != nil { - data["code"] = 0 - } else { - data["code"] = 1 - data["data"] = c - } - s.Data["json"] = data - s.ServeJSON() - } -} - -//修改客户端 -func (s *ClientController) Edit() { - id := s.GetIntNoErr("id") - if s.Ctx.Request.Method == "GET" { - s.Data["menu"] = "client" - if c, err := file.GetDb().GetClient(id); err != nil { - s.error() - } else { - s.Data["c"] = c - } - s.SetInfo("edit client") - s.display() - } else { - if c, err := file.GetDb().GetClient(id); err != nil { - s.error() - s.AjaxErr("client ID not found") - return - } else { - if s.getEscapeString("web_username") != "" { - if s.getEscapeString("web_username") == beego.AppConfig.String("web_username") || !file.GetDb().VerifyUserName(s.getEscapeString("web_username"), c.Id) { - s.AjaxErr("web login username duplicate, please reset") - return - } - } - if s.GetSession("isAdmin").(bool) { - if !file.GetDb().VerifyVkey(s.getEscapeString("vkey"), c.Id) { - s.AjaxErr("Vkey duplicate, please reset") - return - } - c.VerifyKey = s.getEscapeString("vkey") - c.Flow.FlowLimit = int64(s.GetIntNoErr("flow_limit")) - c.RateLimit = s.GetIntNoErr("rate_limit") - c.MaxConn = s.GetIntNoErr("max_conn") - c.MaxTunnelNum = s.GetIntNoErr("max_tunnel") - } - c.Remark = s.getEscapeString("remark") - c.Cnf.U = s.getEscapeString("u") - c.Cnf.P = s.getEscapeString("p") - c.Cnf.Compress = common.GetBoolByStr(s.getEscapeString("compress")) - c.Cnf.Crypt = s.GetBoolNoErr("crypt") - b, err := beego.AppConfig.Bool("allow_user_change_username") - if s.GetSession("isAdmin").(bool) || (err == nil && b) { - c.WebUserName = s.getEscapeString("web_username") - } - c.WebPassword = s.getEscapeString("web_password") - c.ConfigConnAllow = s.GetBoolNoErr("config_conn_allow") - if c.Rate != nil { - c.Rate.Stop() - } - if c.RateLimit > 0 { - c.Rate = rate.NewRate(int64(c.RateLimit * 1024)) - c.Rate.Start() - } else { - c.Rate = rate.NewRate(int64(2 << 23)) - c.Rate.Start() - } - file.GetDb().JsonDb.StoreClientsToJsonFile() - } - s.AjaxOk("save success") - } -} - -//更改状态 -func (s *ClientController) ChangeStatus() { - id := s.GetIntNoErr("id") - if client, err := file.GetDb().GetClient(id); err == nil { - client.Status = s.GetBoolNoErr("status") - if client.Status == false { - server.DelClientConnect(client.Id) - } - s.AjaxOk("modified success") - } - s.AjaxErr("modified fail") -} - -//删除客户端 -func (s *ClientController) Del() { - id := s.GetIntNoErr("id") - if err := file.GetDb().DelClient(id); err != nil { - s.AjaxErr("delete error") - } - server.DelTunnelAndHostByClientId(id, false) - server.DelClientConnect(id) - s.AjaxOk("delete success") -} diff --git a/web/controllers/index.go b/web/controllers/index.go deleted file mode 100755 index 0e7fc53..0000000 --- a/web/controllers/index.go +++ /dev/null @@ -1,319 +0,0 @@ -package controllers - -import ( - "ehang.io/nps/lib/file" - "ehang.io/nps/server" - "ehang.io/nps/server/tool" - - "github.com/astaxie/beego" -) - -type IndexController struct { - BaseController -} - -func (s *IndexController) Index() { - s.Data["web_base_url"] = beego.AppConfig.String("web_base_url") - s.Data["data"] = server.GetDashboardData() - s.SetInfo("dashboard") - s.display("index/index") -} -func (s *IndexController) Help() { - s.SetInfo("about") - s.display("index/help") -} - -func (s *IndexController) Tcp() { - s.SetInfo("tcp") - s.SetType("tcp") - s.display("index/list") -} - -func (s *IndexController) Udp() { - s.SetInfo("udp") - s.SetType("udp") - s.display("index/list") -} - -func (s *IndexController) Socks5() { - s.SetInfo("socks5") - s.SetType("socks5") - s.display("index/list") -} - -func (s *IndexController) Http() { - s.SetInfo("http proxy") - s.SetType("httpProxy") - s.display("index/list") -} -func (s *IndexController) File() { - s.SetInfo("file server") - s.SetType("file") - s.display("index/list") -} - -func (s *IndexController) Secret() { - s.SetInfo("secret") - s.SetType("secret") - s.display("index/list") -} -func (s *IndexController) P2p() { - s.SetInfo("p2p") - s.SetType("p2p") - s.display("index/list") -} - -func (s *IndexController) Host() { - s.SetInfo("host") - s.SetType("hostServer") - s.display("index/list") -} - -func (s *IndexController) All() { - s.Data["menu"] = "client" - clientId := s.getEscapeString("client_id") - s.Data["client_id"] = clientId - s.SetInfo("client id:" + clientId) - s.display("index/list") -} - -func (s *IndexController) GetTunnel() { - start, length := s.GetAjaxParams() - taskType := s.getEscapeString("type") - clientId := s.GetIntNoErr("client_id") - list, cnt := server.GetTunnel(start, length, taskType, clientId, s.getEscapeString("search")) - s.AjaxTable(list, cnt, cnt, nil) -} - -func (s *IndexController) Add() { - if s.Ctx.Request.Method == "GET" { - s.Data["type"] = s.getEscapeString("type") - s.Data["client_id"] = s.getEscapeString("client_id") - s.SetInfo("add tunnel") - s.display() - } else { - t := &file.Tunnel{ - Port: s.GetIntNoErr("port"), - ServerIp: s.getEscapeString("server_ip"), - Mode: s.getEscapeString("type"), - Target: &file.Target{TargetStr: s.getEscapeString("target"), LocalProxy: s.GetBoolNoErr("local_proxy")}, - Id: int(file.GetDb().JsonDb.GetTaskId()), - Status: true, - Remark: s.getEscapeString("remark"), - Password: s.getEscapeString("password"), - LocalPath: s.getEscapeString("local_path"), - StripPre: s.getEscapeString("strip_pre"), - Flow: &file.Flow{}, - } - if !tool.TestServerPort(t.Port, t.Mode) { - s.AjaxErr("The port cannot be opened because it may has been occupied or is no longer allowed.") - } - var err error - if t.Client, err = file.GetDb().GetClient(s.GetIntNoErr("client_id")); err != nil { - s.AjaxErr(err.Error()) - } - if t.Client.MaxTunnelNum != 0 && t.Client.GetTunnelNum() >= t.Client.MaxTunnelNum { - s.AjaxErr("The number of tunnels exceeds the limit") - } - if err := file.GetDb().NewTask(t); err != nil { - s.AjaxErr(err.Error()) - } - if err := server.AddTask(t); err != nil { - s.AjaxErr(err.Error()) - } else { - s.AjaxOk("add success") - } - } -} -func (s *IndexController) GetOneTunnel() { - id := s.GetIntNoErr("id") - data := make(map[string]interface{}) - if t, err := file.GetDb().GetTask(id); err != nil { - data["code"] = 0 - } else { - data["code"] = 1 - data["data"] = t - } - s.Data["json"] = data - s.ServeJSON() -} -func (s *IndexController) Edit() { - id := s.GetIntNoErr("id") - if s.Ctx.Request.Method == "GET" { - if t, err := file.GetDb().GetTask(id); err != nil { - s.error() - } else { - s.Data["t"] = t - } - s.SetInfo("edit tunnel") - s.display() - } else { - if t, err := file.GetDb().GetTask(id); err != nil { - s.error() - } else { - if client, err := file.GetDb().GetClient(s.GetIntNoErr("client_id")); err != nil { - s.AjaxErr("modified error,the client is not exist") - return - } else { - t.Client = client - } - if s.GetIntNoErr("port") != t.Port { - if !tool.TestServerPort(s.GetIntNoErr("port"), t.Mode) { - s.AjaxErr("The port cannot be opened because it may has been occupied or is no longer allowed.") - return - } - t.Port = s.GetIntNoErr("port") - } - t.ServerIp = s.getEscapeString("server_ip") - t.Mode = s.getEscapeString("type") - t.Target = &file.Target{TargetStr: s.getEscapeString("target")} - t.Password = s.getEscapeString("password") - t.Id = id - t.LocalPath = s.getEscapeString("local_path") - t.StripPre = s.getEscapeString("strip_pre") - t.Remark = s.getEscapeString("remark") - t.Target.LocalProxy = s.GetBoolNoErr("local_proxy") - file.GetDb().UpdateTask(t) - server.StopServer(t.Id) - server.StartTask(t.Id) - } - s.AjaxOk("modified success") - } -} - -func (s *IndexController) Stop() { - id := s.GetIntNoErr("id") - if err := server.StopServer(id); err != nil { - s.AjaxErr("stop error") - } - s.AjaxOk("stop success") -} - -func (s *IndexController) Del() { - id := s.GetIntNoErr("id") - if err := server.DelTask(id); err != nil { - s.AjaxErr("delete error") - } - s.AjaxOk("delete success") -} - -func (s *IndexController) Start() { - id := s.GetIntNoErr("id") - if err := server.StartTask(id); err != nil { - s.AjaxErr("start error") - } - s.AjaxOk("start success") -} - -func (s *IndexController) HostList() { - if s.Ctx.Request.Method == "GET" { - s.Data["client_id"] = s.getEscapeString("client_id") - s.Data["menu"] = "host" - s.SetInfo("host list") - s.display("index/hlist") - } else { - start, length := s.GetAjaxParams() - clientId := s.GetIntNoErr("client_id") - list, cnt := file.GetDb().GetHost(start, length, clientId, s.getEscapeString("search")) - s.AjaxTable(list, cnt, cnt, nil) - } -} - -func (s *IndexController) GetHost() { - if s.Ctx.Request.Method == "POST" { - data := make(map[string]interface{}) - if h, err := file.GetDb().GetHostById(s.GetIntNoErr("id")); err != nil { - data["code"] = 0 - } else { - data["data"] = h - data["code"] = 1 - } - s.Data["json"] = data - s.ServeJSON() - } -} - -func (s *IndexController) DelHost() { - id := s.GetIntNoErr("id") - if err := file.GetDb().DelHost(id); err != nil { - s.AjaxErr("delete error") - } - s.AjaxOk("delete success") -} - -func (s *IndexController) AddHost() { - if s.Ctx.Request.Method == "GET" { - s.Data["client_id"] = s.getEscapeString("client_id") - s.Data["menu"] = "host" - s.SetInfo("add host") - s.display("index/hadd") - } else { - h := &file.Host{ - Id: int(file.GetDb().JsonDb.GetHostId()), - Host: s.getEscapeString("host"), - Target: &file.Target{TargetStr: s.getEscapeString("target"), LocalProxy: s.GetBoolNoErr("local_proxy")}, - HeaderChange: s.getEscapeString("header"), - HostChange: s.getEscapeString("hostchange"), - Remark: s.getEscapeString("remark"), - Location: s.getEscapeString("location"), - Flow: &file.Flow{}, - Scheme: s.getEscapeString("scheme"), - KeyFilePath: s.getEscapeString("key_file_path"), - CertFilePath: s.getEscapeString("cert_file_path"), - } - var err error - if h.Client, err = file.GetDb().GetClient(s.GetIntNoErr("client_id")); err != nil { - s.AjaxErr("add error the client can not be found") - } - if err := file.GetDb().NewHost(h); err != nil { - s.AjaxErr("add fail" + err.Error()) - } - s.AjaxOk("add success") - } -} - -func (s *IndexController) EditHost() { - id := s.GetIntNoErr("id") - if s.Ctx.Request.Method == "GET" { - s.Data["menu"] = "host" - if h, err := file.GetDb().GetHostById(id); err != nil { - s.error() - } else { - s.Data["h"] = h - } - s.SetInfo("edit") - s.display("index/hedit") - } else { - if h, err := file.GetDb().GetHostById(id); err != nil { - s.error() - } else { - if h.Host != s.getEscapeString("host") { - tmpHost := new(file.Host) - tmpHost.Host = s.getEscapeString("host") - tmpHost.Location = s.getEscapeString("location") - tmpHost.Scheme = s.getEscapeString("scheme") - if file.GetDb().IsHostExist(tmpHost) { - s.AjaxErr("host has exist") - return - } - } - if client, err := file.GetDb().GetClient(s.GetIntNoErr("client_id")); err != nil { - s.AjaxErr("modified error,the client is not exist") - } else { - h.Client = client - } - h.Host = s.getEscapeString("host") - h.Target = &file.Target{TargetStr: s.getEscapeString("target")} - h.HeaderChange = s.getEscapeString("header") - h.HostChange = s.getEscapeString("hostchange") - h.Remark = s.getEscapeString("remark") - h.Location = s.getEscapeString("location") - h.Scheme = s.getEscapeString("scheme") - h.KeyFilePath = s.getEscapeString("key_file_path") - h.CertFilePath = s.getEscapeString("cert_file_path") - h.Target.LocalProxy = s.GetBoolNoErr("local_proxy") - file.GetDb().JsonDb.StoreHostToJsonFile() - } - s.AjaxOk("modified success") - } -} diff --git a/web/controllers/login.go b/web/controllers/login.go deleted file mode 100755 index 770a2c0..0000000 --- a/web/controllers/login.go +++ /dev/null @@ -1,157 +0,0 @@ -package controllers - -import ( - "math/rand" - "net" - "sync" - "time" - - "ehang.io/nps/lib/common" - "ehang.io/nps/lib/file" - "ehang.io/nps/server" - "github.com/astaxie/beego" -) - -type LoginController struct { - beego.Controller -} - -var ipRecord sync.Map - -type record struct { - hasLoginFailTimes int - lastLoginTime time.Time -} - -func (self *LoginController) Index() { - // Try login implicitly, will succeed if it's configured as no-auth(empty username&password). - webBaseUrl := beego.AppConfig.String("web_base_url") - if self.doLogin("", "", false) { - self.Redirect(webBaseUrl+"/index/index", 302) - } - self.Data["web_base_url"] = webBaseUrl - self.Data["register_allow"], _ = beego.AppConfig.Bool("allow_user_register") - self.TplName = "login/index.html" -} - -func (self *LoginController) Verify() { - username := self.GetString("username") - password := self.GetString("password") - if self.doLogin(username, password, true) { - self.Data["json"] = map[string]interface{}{"status": 1, "msg": "login success"} - } else { - self.Data["json"] = map[string]interface{}{"status": 0, "msg": "username or password incorrect"} - } - self.ServeJSON() -} - -func (self *LoginController) doLogin(username, password string, explicit bool) bool { - clearIprecord() - ip, _, _ := net.SplitHostPort(self.Ctx.Request.RemoteAddr) - if v, ok := ipRecord.Load(ip); ok { - vv := v.(*record) - if (time.Now().Unix() - vv.lastLoginTime.Unix()) >= 60 { - vv.hasLoginFailTimes = 0 - } - if vv.hasLoginFailTimes >= 10 { - return false - } - } - var auth bool - if password == beego.AppConfig.String("web_password") && username == beego.AppConfig.String("web_username") { - self.SetSession("isAdmin", true) - self.DelSession("clientId") - self.DelSession("username") - auth = true - server.Bridge.Register.Store(common.GetIpByAddr(self.Ctx.Input.IP()), time.Now().Add(time.Hour*time.Duration(2))) - } - b, err := beego.AppConfig.Bool("allow_user_login") - if err == nil && b && !auth { - file.GetDb().JsonDb.Clients.Range(func(key, value interface{}) bool { - v := value.(*file.Client) - if !v.Status || v.NoDisplay { - return true - } - if v.WebUserName == "" && v.WebPassword == "" { - if username != "user" || v.VerifyKey != password { - return true - } else { - auth = true - } - } - if !auth && v.WebPassword == password && v.WebUserName == username { - auth = true - } - if auth { - self.SetSession("isAdmin", false) - self.SetSession("clientId", v.Id) - self.SetSession("username", v.WebUserName) - return false - } - return true - }) - } - if auth { - self.SetSession("auth", true) - ipRecord.Delete(ip) - return true - - } - if v, load := ipRecord.LoadOrStore(ip, &record{hasLoginFailTimes: 1, lastLoginTime: time.Now()}); load && explicit { - vv := v.(*record) - vv.lastLoginTime = time.Now() - vv.hasLoginFailTimes += 1 - ipRecord.Store(ip, vv) - } - return false -} -func (self *LoginController) Register() { - if self.Ctx.Request.Method == "GET" { - self.Data["web_base_url"] = beego.AppConfig.String("web_base_url") - self.TplName = "login/register.html" - } else { - if b, err := beego.AppConfig.Bool("allow_user_register"); err != nil || !b { - self.Data["json"] = map[string]interface{}{"status": 0, "msg": "register is not allow"} - self.ServeJSON() - return - } - if self.GetString("username") == "" || self.GetString("password") == "" || self.GetString("username") == beego.AppConfig.String("web_username") { - self.Data["json"] = map[string]interface{}{"status": 0, "msg": "please check your input"} - self.ServeJSON() - return - } - t := &file.Client{ - Id: int(file.GetDb().JsonDb.GetClientId()), - Status: true, - Cnf: &file.Config{}, - WebUserName: self.GetString("username"), - WebPassword: self.GetString("password"), - Flow: &file.Flow{}, - } - if err := file.GetDb().NewClient(t); err != nil { - self.Data["json"] = map[string]interface{}{"status": 0, "msg": err.Error()} - } else { - self.Data["json"] = map[string]interface{}{"status": 1, "msg": "register success"} - } - self.ServeJSON() - } -} - -func (self *LoginController) Out() { - self.SetSession("auth", false) - self.Redirect(beego.AppConfig.String("web_base_url")+"/login/index", 302) -} - -func clearIprecord() { - rand.Seed(time.Now().UnixNano()) - x := rand.Intn(100) - if x == 1 { - ipRecord.Range(func(key, value interface{}) bool { - v := value.(*record) - if time.Now().Unix()-v.lastLoginTime.Unix() >= 60 { - ipRecord.Delete(key) - } - return true - }) - } -} diff --git a/web/routers/router.go b/web/routers/router.go deleted file mode 100755 index 51ab97d..0000000 --- a/web/routers/router.go +++ /dev/null @@ -1,26 +0,0 @@ -package routers - -import ( - "ehang.io/nps/web/controllers" - "github.com/astaxie/beego" -) - -func Init() { - web_base_url := beego.AppConfig.String("web_base_url") - if len(web_base_url) > 0 { - ns := beego.NewNamespace(web_base_url, - beego.NSRouter("/", &controllers.IndexController{}, "*:Index"), - beego.NSAutoRouter(&controllers.IndexController{}), - beego.NSAutoRouter(&controllers.LoginController{}), - beego.NSAutoRouter(&controllers.ClientController{}), - beego.NSAutoRouter(&controllers.AuthController{}), - ) - beego.AddNamespace(ns) - } else { - beego.Router("/", &controllers.IndexController{}, "*:Index") - beego.AutoRouter(&controllers.IndexController{}) - beego.AutoRouter(&controllers.LoginController{}) - beego.AutoRouter(&controllers.ClientController{}) - beego.AutoRouter(&controllers.AuthController{}) - } -} diff --git a/web/static/css/bootstrap-table.min.css b/web/static/css/bootstrap-table.min.css deleted file mode 100644 index 72a8f74..0000000 --- a/web/static/css/bootstrap-table.min.css +++ /dev/null @@ -1,10 +0,0 @@ -/** - * bootstrap-table - An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation) - * - * @version v1.16.0 - * @homepage https://bootstrap-table.com - * @author wenzhixin (http://wenzhixin.net.cn/) - * @license MIT - */ - -.bootstrap-table .fixed-table-toolbar::after{content:"";display:block;clear:both}.bootstrap-table .fixed-table-toolbar .bs-bars,.bootstrap-table .fixed-table-toolbar .columns,.bootstrap-table .fixed-table-toolbar .search{position:relative;margin-top:10px;margin-bottom:10px}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group{display:inline-block;margin-left:-1px!important}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group>.btn{border-radius:0}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group:first-child>.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group:last-child>.btn{border-top-right-radius:4px;border-bottom-right-radius:4px}.bootstrap-table .fixed-table-toolbar .columns .dropdown-menu{text-align:left;max-height:300px;overflow:auto;-ms-overflow-style:scrollbar;z-index:1001}.bootstrap-table .fixed-table-toolbar .columns label{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.428571429}.bootstrap-table .fixed-table-toolbar .columns-left{margin-right:5px}.bootstrap-table .fixed-table-toolbar .columns-right{margin-left:5px}.bootstrap-table .fixed-table-toolbar .pull-right .dropdown-menu{right:0;left:auto}.bootstrap-table .fixed-table-container{position:relative;clear:both}.bootstrap-table .fixed-table-container .table{width:100%;margin-bottom:0!important}.bootstrap-table .fixed-table-container .table td,.bootstrap-table .fixed-table-container .table th{vertical-align:middle;box-sizing:border-box}.bootstrap-table .fixed-table-container .table thead th{vertical-align:bottom;padding:0;margin:0}.bootstrap-table .fixed-table-container .table thead th:focus{outline:0 solid transparent}.bootstrap-table .fixed-table-container .table thead th.detail{width:30px}.bootstrap-table .fixed-table-container .table thead th .th-inner{padding:.75rem;vertical-align:bottom;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bootstrap-table .fixed-table-container .table thead th .sortable{cursor:pointer;background-position:right;background-repeat:no-repeat;padding-right:30px!important}.bootstrap-table .fixed-table-container .table thead th .both{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC")}.bootstrap-table .fixed-table-container .table thead th .asc{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg==)}.bootstrap-table .fixed-table-container .table thead th .desc{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII=)}.bootstrap-table .fixed-table-container .table tbody tr.selected td{background-color:rgba(0,0,0,.075)}.bootstrap-table .fixed-table-container .table tbody tr.no-records-found td{text-align:center}.bootstrap-table .fixed-table-container .table tbody tr .card-view{display:flex}.bootstrap-table .fixed-table-container .table tbody tr .card-view .card-view-title{font-weight:700;display:inline-block;min-width:30%;text-align:left!important}.bootstrap-table .fixed-table-container .table .bs-checkbox{text-align:center}.bootstrap-table .fixed-table-container .table .bs-checkbox label{margin-bottom:0}.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type=checkbox],.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type=radio]{margin:0 auto!important}.bootstrap-table .fixed-table-container .table.table-sm .th-inner{padding:.3rem}.bootstrap-table .fixed-table-container.fixed-height:not(.has-footer){border-bottom:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height.has-card-view{border-top:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height .fixed-table-border{border-left:1px solid #dee2e6;border-right:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height .table thead th{border-bottom:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height .table-dark thead th{border-bottom:1px solid #32383e}.bootstrap-table .fixed-table-container .fixed-table-header{overflow:hidden}.bootstrap-table .fixed-table-container .fixed-table-body{overflow-x:auto;overflow-y:auto;height:100%}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading{align-items:center;background:#fff;display:none;justify-content:center;position:absolute;bottom:0;width:100%;z-index:1000}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap{align-items:baseline;display:flex;justify-content:center}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .loading-text{font-size:2rem;margin-right:6px}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap{align-items:center;display:flex;justify-content:center}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::before{content:"";animation-duration:1.5s;animation-iteration-count:infinite;animation-name:LOADING;background:#212529;border-radius:50%;display:block;height:5px;margin:0 4px;opacity:0;width:5px}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot{animation-delay:.3s}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after{animation-delay:.6s}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark{background:#212529}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-dot,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::after,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::before{background:#fff}.bootstrap-table .fixed-table-container .fixed-table-footer{overflow:hidden}.bootstrap-table .fixed-table-pagination::after{content:"";display:block;clear:both}.bootstrap-table .fixed-table-pagination>.pagination,.bootstrap-table .fixed-table-pagination>.pagination-detail{margin-top:10px;margin-bottom:10px}.bootstrap-table .fixed-table-pagination>.pagination-detail .pagination-info{line-height:34px;margin-right:5px}.bootstrap-table .fixed-table-pagination>.pagination-detail .page-list{display:inline-block}.bootstrap-table .fixed-table-pagination>.pagination-detail .page-list .btn-group{position:relative;display:inline-block;vertical-align:middle}.bootstrap-table .fixed-table-pagination>.pagination-detail .page-list .btn-group .dropdown-menu{margin-bottom:0}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination{margin:0}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination a{padding:6px 12px;line-height:1.428571429}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.page-intermediate a{color:#c8c8c8}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.page-intermediate a::before{content:'\2B05'}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.page-intermediate a::after{content:'\27A1'}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.disabled a{pointer-events:none;cursor:default}.bootstrap-table.fullscreen{position:fixed;top:0;left:0;z-index:1050;width:100%!important;background:#fff;height:calc(100vh);overflow-y:scroll}div.fixed-table-scroll-inner{width:100%;height:200px}div.fixed-table-scroll-outer{top:0;left:0;visibility:hidden;width:200px;height:150px;overflow:hidden}@keyframes LOADING{0%{opacity:0}50%{opacity:1}to{opacity:0}} \ No newline at end of file diff --git a/web/static/css/bootstrap.min.css b/web/static/css/bootstrap.min.css deleted file mode 100644 index 86b6845..0000000 --- a/web/static/css/bootstrap.min.css +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v4.4.1 (https://getbootstrap.com/) - * Copyright 2011-2019 The Bootstrap Authors - * Copyright 2011-2019 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]){color:inherit;text-decoration:none}a:not([href]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-sm-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-md-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-lg-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-xl-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#0069d9;border-color:#0062cc;box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{color:#fff;background-color:#5a6268;border-color:#545b62;box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#218838;border-color:#1e7e34;box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#138496;border-color:#117a8b;box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{color:#212529;background-color:#e0a800;border-color:#d39e00;box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c82333;border-color:#bd2130;box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{color:#212529;background-color:#e2e6ea;border-color:#dae0e5;box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{color:#fff;background-color:#23272b;border-color:#1d2124;box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 0%;flex:1 1 0%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before,.custom-control-input[disabled]~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-ms-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img,.card-img-bottom,.card-img-top{-ms-flex-negative:0;flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-ms-flex:1 0 0%;flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,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-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal .list-group-item.active{margin-top:0}.list-group-horizontal .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm .list-group-item.active{margin-top:0}.list-group-horizontal-sm .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md .list-group-item.active{margin-top:0}.list-group-horizontal-md .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg .list-group-item.active{margin-top:0}.list-group-horizontal-lg .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl .list-group-item.active{margin-top:0}.list-group-horizontal-xl .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush .list-group-item{border-right-width:0;border-left-width:0;border-radius:0}.list-group-flush .list-group-item:first-child{border-top-width:0}.list-group-flush:last-child .list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;overflow-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} -/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/web/static/css/datatables.css b/web/static/css/datatables.css deleted file mode 100644 index afd4737..0000000 --- a/web/static/css/datatables.css +++ /dev/null @@ -1,462 +0,0 @@ -/* - * This combined file was created by the DataTables downloader builder: - * https://datatables.net/download - * - * To rebuild or modify this file with the latest versions of the included - * software please visit: - * https://datatables.net/download/#dt/dt-1.10.20 - * - * Included libraries: - * DataTables 1.10.20 - */ - -/* - * Table styles - */ -table.dataTable { - width: 100%; - margin: 0 auto; - clear: both; - border-collapse: separate; - border-spacing: 0; - /* - * Header and footer styles - */ - /* - * Body styles - */ -} -table.dataTable thead th, -table.dataTable tfoot th { - font-weight: bold; -} -table.dataTable thead th, -table.dataTable thead td { - padding: 10px 18px; - border-bottom: 1px solid #111; -} -table.dataTable thead th:active, -table.dataTable thead td:active { - outline: none; -} -table.dataTable tfoot th, -table.dataTable tfoot td { - padding: 10px 18px 6px 18px; - border-top: 1px solid #111; -} -table.dataTable thead .sorting, -table.dataTable thead .sorting_asc, -table.dataTable thead .sorting_desc, -table.dataTable thead .sorting_asc_disabled, -table.dataTable thead .sorting_desc_disabled { - cursor: pointer; - *cursor: hand; - background-repeat: no-repeat; - background-position: center right; -} -table.dataTable thead .sorting { - background-image: url("DataTables-1.10.20/images/sort_both.png"); -} -table.dataTable thead .sorting_asc { - background-image: url("DataTables-1.10.20/images/sort_asc.png"); -} -table.dataTable thead .sorting_desc { - background-image: url("DataTables-1.10.20/images/sort_desc.png"); -} -table.dataTable thead .sorting_asc_disabled { - background-image: url("DataTables-1.10.20/images/sort_asc_disabled.png"); -} -table.dataTable thead .sorting_desc_disabled { - background-image: url("DataTables-1.10.20/images/sort_desc_disabled.png"); -} -table.dataTable tbody tr { - background-color: #ffffff; -} -table.dataTable tbody tr.selected { - background-color: #B0BED9; -} -table.dataTable tbody th, -table.dataTable tbody td { - padding: 8px 10px; -} -table.dataTable.row-border tbody th, table.dataTable.row-border tbody td, table.dataTable.display tbody th, table.dataTable.display tbody td { - border-top: 1px solid #ddd; -} -table.dataTable.row-border tbody tr:first-child th, -table.dataTable.row-border tbody tr:first-child td, table.dataTable.display tbody tr:first-child th, -table.dataTable.display tbody tr:first-child td { - border-top: none; -} -table.dataTable.cell-border tbody th, table.dataTable.cell-border tbody td { - border-top: 1px solid #ddd; - border-right: 1px solid #ddd; -} -table.dataTable.cell-border tbody tr th:first-child, -table.dataTable.cell-border tbody tr td:first-child { - border-left: 1px solid #ddd; -} -table.dataTable.cell-border tbody tr:first-child th, -table.dataTable.cell-border tbody tr:first-child td { - border-top: none; -} -table.dataTable.stripe tbody tr.odd, table.dataTable.display tbody tr.odd { - background-color: #f9f9f9; -} -table.dataTable.stripe tbody tr.odd.selected, table.dataTable.display tbody tr.odd.selected { - background-color: #acbad4; -} -table.dataTable.hover tbody tr:hover, table.dataTable.display tbody tr:hover { - background-color: #f6f6f6; -} -table.dataTable.hover tbody tr:hover.selected, table.dataTable.display tbody tr:hover.selected { - background-color: #aab7d1; -} -table.dataTable.order-column tbody tr > .sorting_1, -table.dataTable.order-column tbody tr > .sorting_2, -table.dataTable.order-column tbody tr > .sorting_3, table.dataTable.display tbody tr > .sorting_1, -table.dataTable.display tbody tr > .sorting_2, -table.dataTable.display tbody tr > .sorting_3 { - background-color: #fafafa; -} -table.dataTable.order-column tbody tr.selected > .sorting_1, -table.dataTable.order-column tbody tr.selected > .sorting_2, -table.dataTable.order-column tbody tr.selected > .sorting_3, table.dataTable.display tbody tr.selected > .sorting_1, -table.dataTable.display tbody tr.selected > .sorting_2, -table.dataTable.display tbody tr.selected > .sorting_3 { - background-color: #acbad5; -} -table.dataTable.display tbody tr.odd > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd > .sorting_1 { - background-color: #f1f1f1; -} -table.dataTable.display tbody tr.odd > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd > .sorting_2 { - background-color: #f3f3f3; -} -table.dataTable.display tbody tr.odd > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd > .sorting_3 { - background-color: whitesmoke; -} -table.dataTable.display tbody tr.odd.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_1 { - background-color: #a6b4cd; -} -table.dataTable.display tbody tr.odd.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_2 { - background-color: #a8b5cf; -} -table.dataTable.display tbody tr.odd.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_3 { - background-color: #a9b7d1; -} -table.dataTable.display tbody tr.even > .sorting_1, table.dataTable.order-column.stripe tbody tr.even > .sorting_1 { - background-color: #fafafa; -} -table.dataTable.display tbody tr.even > .sorting_2, table.dataTable.order-column.stripe tbody tr.even > .sorting_2 { - background-color: #fcfcfc; -} -table.dataTable.display tbody tr.even > .sorting_3, table.dataTable.order-column.stripe tbody tr.even > .sorting_3 { - background-color: #fefefe; -} -table.dataTable.display tbody tr.even.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_1 { - background-color: #acbad5; -} -table.dataTable.display tbody tr.even.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_2 { - background-color: #aebcd6; -} -table.dataTable.display tbody tr.even.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_3 { - background-color: #afbdd8; -} -table.dataTable.display tbody tr:hover > .sorting_1, table.dataTable.order-column.hover tbody tr:hover > .sorting_1 { - background-color: #eaeaea; -} -table.dataTable.display tbody tr:hover > .sorting_2, table.dataTable.order-column.hover tbody tr:hover > .sorting_2 { - background-color: #ececec; -} -table.dataTable.display tbody tr:hover > .sorting_3, table.dataTable.order-column.hover tbody tr:hover > .sorting_3 { - background-color: #efefef; -} -table.dataTable.display tbody tr:hover.selected > .sorting_1, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_1 { - background-color: #a2aec7; -} -table.dataTable.display tbody tr:hover.selected > .sorting_2, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_2 { - background-color: #a3b0c9; -} -table.dataTable.display tbody tr:hover.selected > .sorting_3, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_3 { - background-color: #a5b2cb; -} -table.dataTable.no-footer { - border-bottom: 1px solid #111; -} -table.dataTable.nowrap th, table.dataTable.nowrap td { - white-space: nowrap; -} -table.dataTable.compact thead th, -table.dataTable.compact thead td { - padding: 4px 17px 4px 4px; -} -table.dataTable.compact tfoot th, -table.dataTable.compact tfoot td { - padding: 4px; -} -table.dataTable.compact tbody th, -table.dataTable.compact tbody td { - padding: 4px; -} -table.dataTable th.dt-left, -table.dataTable td.dt-left { - text-align: left; -} -table.dataTable th.dt-center, -table.dataTable td.dt-center, -table.dataTable td.dataTables_empty { - text-align: center; -} -table.dataTable th.dt-right, -table.dataTable td.dt-right { - text-align: right; -} -table.dataTable th.dt-justify, -table.dataTable td.dt-justify { - text-align: justify; -} -table.dataTable th.dt-nowrap, -table.dataTable td.dt-nowrap { - white-space: nowrap; -} -table.dataTable thead th.dt-head-left, -table.dataTable thead td.dt-head-left, -table.dataTable tfoot th.dt-head-left, -table.dataTable tfoot td.dt-head-left { - text-align: left; -} -table.dataTable thead th.dt-head-center, -table.dataTable thead td.dt-head-center, -table.dataTable tfoot th.dt-head-center, -table.dataTable tfoot td.dt-head-center { - text-align: center; -} -table.dataTable thead th.dt-head-right, -table.dataTable thead td.dt-head-right, -table.dataTable tfoot th.dt-head-right, -table.dataTable tfoot td.dt-head-right { - text-align: right; -} -table.dataTable thead th.dt-head-justify, -table.dataTable thead td.dt-head-justify, -table.dataTable tfoot th.dt-head-justify, -table.dataTable tfoot td.dt-head-justify { - text-align: justify; -} -table.dataTable thead th.dt-head-nowrap, -table.dataTable thead td.dt-head-nowrap, -table.dataTable tfoot th.dt-head-nowrap, -table.dataTable tfoot td.dt-head-nowrap { - white-space: nowrap; -} -table.dataTable tbody th.dt-body-left, -table.dataTable tbody td.dt-body-left { - text-align: left; -} -table.dataTable tbody th.dt-body-center, -table.dataTable tbody td.dt-body-center { - text-align: center; -} -table.dataTable tbody th.dt-body-right, -table.dataTable tbody td.dt-body-right { - text-align: right; -} -table.dataTable tbody th.dt-body-justify, -table.dataTable tbody td.dt-body-justify { - text-align: justify; -} -table.dataTable tbody th.dt-body-nowrap, -table.dataTable tbody td.dt-body-nowrap { - white-space: nowrap; -} - -table.dataTable, -table.dataTable th, -table.dataTable td { - box-sizing: content-box; -} - -/* - * Control feature layout - */ -.dataTables_wrapper { - position: relative; - clear: both; - *zoom: 1; - zoom: 1; -} -.dataTables_wrapper .dataTables_length { - float: left; -} -.dataTables_wrapper .dataTables_filter { - float: right; - text-align: right; -} -.dataTables_wrapper .dataTables_filter input { - margin-left: 0.5em; -} -.dataTables_wrapper .dataTables_info { - clear: both; - float: left; - padding-top: 0.755em; -} -.dataTables_wrapper .dataTables_paginate { - float: right; - text-align: right; - padding-top: 0.25em; -} -.dataTables_wrapper .dataTables_paginate .paginate_button { - box-sizing: border-box; - display: inline-block; - min-width: 1.5em; - padding: 0.5em 1em; - margin-left: 2px; - text-align: center; - text-decoration: none !important; - cursor: pointer; - *cursor: hand; - color: #333 !important; - border: 1px solid transparent; - border-radius: 2px; -} -.dataTables_wrapper .dataTables_paginate .paginate_button.current, .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover { - color: #333 !important; - border: 1px solid #979797; - background-color: white; - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, white), color-stop(100%, #dcdcdc)); - /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, white 0%, #dcdcdc 100%); - /* Chrome10+,Safari5.1+ */ - background: -moz-linear-gradient(top, white 0%, #dcdcdc 100%); - /* FF3.6+ */ - background: -ms-linear-gradient(top, white 0%, #dcdcdc 100%); - /* IE10+ */ - background: -o-linear-gradient(top, white 0%, #dcdcdc 100%); - /* Opera 11.10+ */ - background: linear-gradient(to bottom, white 0%, #dcdcdc 100%); - /* W3C */ -} -.dataTables_wrapper .dataTables_paginate .paginate_button.disabled, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active { - cursor: default; - color: #666 !important; - border: 1px solid transparent; - background: transparent; - box-shadow: none; -} -.dataTables_wrapper .dataTables_paginate .paginate_button:hover { - color: white !important; - border: 1px solid #111; - background-color: #585858; - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111)); - /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #585858 0%, #111 100%); - /* Chrome10+,Safari5.1+ */ - background: -moz-linear-gradient(top, #585858 0%, #111 100%); - /* FF3.6+ */ - background: -ms-linear-gradient(top, #585858 0%, #111 100%); - /* IE10+ */ - background: -o-linear-gradient(top, #585858 0%, #111 100%); - /* Opera 11.10+ */ - background: linear-gradient(to bottom, #585858 0%, #111 100%); - /* W3C */ -} -.dataTables_wrapper .dataTables_paginate .paginate_button:active { - outline: none; - background-color: #2b2b2b; - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c)); - /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); - /* Chrome10+,Safari5.1+ */ - background: -moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); - /* FF3.6+ */ - background: -ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); - /* IE10+ */ - background: -o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); - /* Opera 11.10+ */ - background: linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%); - /* W3C */ - box-shadow: inset 0 0 3px #111; -} -.dataTables_wrapper .dataTables_paginate .ellipsis { - padding: 0 1em; -} -.dataTables_wrapper .dataTables_processing { - position: absolute; - top: 50%; - left: 50%; - width: 100%; - height: 40px; - margin-left: -50%; - margin-top: -25px; - padding-top: 20px; - text-align: center; - font-size: 1.2em; - background-color: white; - background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255, 255, 255, 0)), color-stop(25%, rgba(255, 255, 255, 0.9)), color-stop(75%, rgba(255, 255, 255, 0.9)), color-stop(100%, rgba(255, 255, 255, 0))); - background: -webkit-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%); - background: -moz-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%); - background: -ms-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%); - background: -o-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%); - background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%); -} -.dataTables_wrapper .dataTables_length, -.dataTables_wrapper .dataTables_filter, -.dataTables_wrapper .dataTables_info, -.dataTables_wrapper .dataTables_processing, -.dataTables_wrapper .dataTables_paginate { - color: #333; -} -.dataTables_wrapper .dataTables_scroll { - clear: both; -} -.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody { - *margin-top: -1px; - -webkit-overflow-scrolling: touch; -} -.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td { - vertical-align: middle; -} -.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th > div.dataTables_sizing, -.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td > div.dataTables_sizing, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th > div.dataTables_sizing, -.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td > div.dataTables_sizing { - height: 0; - overflow: hidden; - margin: 0 !important; - padding: 0 !important; -} -.dataTables_wrapper.no-footer .dataTables_scrollBody { - border-bottom: 1px solid #111; -} -.dataTables_wrapper.no-footer div.dataTables_scrollHead table.dataTable, -.dataTables_wrapper.no-footer div.dataTables_scrollBody > table { - border-bottom: none; -} -.dataTables_wrapper:after { - visibility: hidden; - display: block; - content: ""; - clear: both; - height: 0; -} - -@media screen and (max-width: 767px) { - .dataTables_wrapper .dataTables_info, - .dataTables_wrapper .dataTables_paginate { - float: none; - text-align: center; - } - .dataTables_wrapper .dataTables_paginate { - margin-top: 0.5em; - } -} -@media screen and (max-width: 640px) { - .dataTables_wrapper .dataTables_length, - .dataTables_wrapper .dataTables_filter { - float: none; - text-align: center; - } - .dataTables_wrapper .dataTables_filter { - margin-top: 0.5em; - } -} - - diff --git a/web/static/css/fontawesome.min.css b/web/static/css/fontawesome.min.css deleted file mode 100644 index d51b6a7..0000000 --- a/web/static/css/fontawesome.min.css +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * Font Awesome Free 5.11.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - */ -.fa,.fab,.fad,.fal,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}:root .fa-flip-both,:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{-webkit-filter:none;filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-acquisitions-incorporated:before{content:"\f6af"}.fa-ad:before{content:"\f641"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adobe:before{content:"\f778"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-air-freshener:before{content:"\f5d0"}.fa-airbnb:before{content:"\f834"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-alipay:before{content:"\f642"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angry:before{content:"\f556"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-ankh:before{content:"\f644"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-alt:before{content:"\f5d1"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-archway:before{content:"\f557"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-artstation:before{content:"\f77a"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asterisk:before{content:"\f069"}.fa-asymmetrik:before{content:"\f372"}.fa-at:before{content:"\f1fa"}.fa-atlas:before{content:"\f558"}.fa-atlassian:before{content:"\f77b"}.fa-atom:before{content:"\f5d2"}.fa-audible:before{content:"\f373"}.fa-audio-description:before{content:"\f29e"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-award:before{content:"\f559"}.fa-aws:before{content:"\f375"}.fa-baby:before{content:"\f77c"}.fa-baby-carriage:before{content:"\f77d"}.fa-backspace:before{content:"\f55a"}.fa-backward:before{content:"\f04a"}.fa-bacon:before{content:"\f7e5"}.fa-balance-scale:before{content:"\f24e"}.fa-balance-scale-left:before{content:"\f515"}.fa-balance-scale-right:before{content:"\f516"}.fa-ban:before{content:"\f05e"}.fa-band-aid:before{content:"\f462"}.fa-bandcamp:before{content:"\f2d5"}.fa-barcode:before{content:"\f02a"}.fa-bars:before{content:"\f0c9"}.fa-baseball-ball:before{content:"\f433"}.fa-basketball-ball:before{content:"\f434"}.fa-bath:before{content:"\f2cd"}.fa-battery-empty:before{content:"\f244"}.fa-battery-full:before{content:"\f240"}.fa-battery-half:before{content:"\f242"}.fa-battery-quarter:before{content:"\f243"}.fa-battery-three-quarters:before{content:"\f241"}.fa-battle-net:before{content:"\f835"}.fa-bed:before{content:"\f236"}.fa-beer:before{content:"\f0fc"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-bell:before{content:"\f0f3"}.fa-bell-slash:before{content:"\f1f6"}.fa-bezier-curve:before{content:"\f55b"}.fa-bible:before{content:"\f647"}.fa-bicycle:before{content:"\f206"}.fa-biking:before{content:"\f84a"}.fa-bimobject:before{content:"\f378"}.fa-binoculars:before{content:"\f1e5"}.fa-biohazard:before{content:"\f780"}.fa-birthday-cake:before{content:"\f1fd"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blender:before{content:"\f517"}.fa-blender-phone:before{content:"\f6b6"}.fa-blind:before{content:"\f29d"}.fa-blog:before{content:"\f781"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bold:before{content:"\f032"}.fa-bolt:before{content:"\f0e7"}.fa-bomb:before{content:"\f1e2"}.fa-bone:before{content:"\f5d7"}.fa-bong:before{content:"\f55c"}.fa-book:before{content:"\f02d"}.fa-book-dead:before{content:"\f6b7"}.fa-book-medical:before{content:"\f7e6"}.fa-book-open:before{content:"\f518"}.fa-book-reader:before{content:"\f5da"}.fa-bookmark:before{content:"\f02e"}.fa-bootstrap:before{content:"\f836"}.fa-border-all:before{content:"\f84c"}.fa-border-none:before{content:"\f850"}.fa-border-style:before{content:"\f853"}.fa-bowling-ball:before{content:"\f436"}.fa-box:before{content:"\f466"}.fa-box-open:before{content:"\f49e"}.fa-boxes:before{content:"\f468"}.fa-braille:before{content:"\f2a1"}.fa-brain:before{content:"\f5dc"}.fa-bread-slice:before{content:"\f7ec"}.fa-briefcase:before{content:"\f0b1"}.fa-briefcase-medical:before{content:"\f469"}.fa-broadcast-tower:before{content:"\f519"}.fa-broom:before{content:"\f51a"}.fa-brush:before{content:"\f55d"}.fa-btc:before{content:"\f15a"}.fa-buffer:before{content:"\f837"}.fa-bug:before{content:"\f188"}.fa-building:before{content:"\f1ad"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-burn:before{content:"\f46a"}.fa-buromobelexperte:before{content:"\f37f"}.fa-bus:before{content:"\f207"}.fa-bus-alt:before{content:"\f55e"}.fa-business-time:before{content:"\f64a"}.fa-buy-n-large:before{content:"\f8a6"}.fa-buysellads:before{content:"\f20d"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-alt:before{content:"\f073"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-day:before{content:"\f783"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-times:before{content:"\f273"}.fa-calendar-week:before{content:"\f784"}.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-campground:before{content:"\f6bb"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-candy-cane:before{content:"\f786"}.fa-cannabis:before{content:"\f55f"}.fa-capsules:before{content:"\f46b"}.fa-car:before{content:"\f1b9"}.fa-car-alt:before{content:"\f5de"}.fa-car-battery:before{content:"\f5df"}.fa-car-crash:before{content:"\f5e1"}.fa-car-side:before{content:"\f5e4"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-square-down:before{content:"\f150"}.fa-caret-square-left:before{content:"\f191"}.fa-caret-square-right:before{content:"\f152"}.fa-caret-square-up:before{content:"\f151"}.fa-caret-up:before{content:"\f0d8"}.fa-carrot:before{content:"\f787"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-plus:before{content:"\f217"}.fa-cash-register:before{content:"\f788"}.fa-cat:before{content:"\f6be"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-centos:before{content:"\f789"}.fa-certificate:before{content:"\f0a3"}.fa-chair:before{content:"\f6c0"}.fa-chalkboard:before{content:"\f51b"}.fa-chalkboard-teacher:before{content:"\f51c"}.fa-charging-station:before{content:"\f5e7"}.fa-chart-area:before{content:"\f1fe"}.fa-chart-bar:before{content:"\f080"}.fa-chart-line:before{content:"\f201"}.fa-chart-pie:before{content:"\f200"}.fa-check:before{content:"\f00c"}.fa-check-circle:before{content:"\f058"}.fa-check-double:before{content:"\f560"}.fa-check-square:before{content:"\f14a"}.fa-cheese:before{content:"\f7ef"}.fa-chess:before{content:"\f439"}.fa-chess-bishop:before{content:"\f43a"}.fa-chess-board:before{content:"\f43c"}.fa-chess-king:before{content:"\f43f"}.fa-chess-knight:before{content:"\f441"}.fa-chess-pawn:before{content:"\f443"}.fa-chess-queen:before{content:"\f445"}.fa-chess-rook:before{content:"\f447"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-chrome:before{content:"\f268"}.fa-chromecast:before{content:"\f838"}.fa-church:before{content:"\f51d"}.fa-circle:before{content:"\f111"}.fa-circle-notch:before{content:"\f1ce"}.fa-city:before{content:"\f64f"}.fa-clinic-medical:before{content:"\f7f2"}.fa-clipboard:before{content:"\f328"}.fa-clipboard-check:before{content:"\f46c"}.fa-clipboard-list:before{content:"\f46d"}.fa-clock:before{content:"\f017"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-download-alt:before{content:"\f381"}.fa-cloud-meatball:before{content:"\f73b"}.fa-cloud-moon:before{content:"\f6c3"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-cloud-rain:before{content:"\f73d"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-cloud-sun:before{content:"\f6c4"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-cloud-upload-alt:before{content:"\f382"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-cocktail:before{content:"\f561"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-coffee:before{content:"\f0f4"}.fa-cog:before{content:"\f013"}.fa-cogs:before{content:"\f085"}.fa-coins:before{content:"\f51e"}.fa-columns:before{content:"\f0db"}.fa-comment:before{content:"\f075"}.fa-comment-alt:before{content:"\f27a"}.fa-comment-dollar:before{content:"\f651"}.fa-comment-dots:before{content:"\f4ad"}.fa-comment-medical:before{content:"\f7f5"}.fa-comment-slash:before{content:"\f4b3"}.fa-comments:before{content:"\f086"}.fa-comments-dollar:before{content:"\f653"}.fa-compact-disc:before{content:"\f51f"}.fa-compass:before{content:"\f14e"}.fa-compress:before{content:"\f066"}.fa-compress-arrows-alt:before{content:"\f78c"}.fa-concierge-bell:before{content:"\f562"}.fa-confluence:before{content:"\f78d"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-cookie:before{content:"\f563"}.fa-cookie-bite:before{content:"\f564"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-cotton-bureau:before{content:"\f89e"}.fa-couch:before{content:"\f4b8"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-credit-card:before{content:"\f09d"}.fa-critical-role:before{content:"\f6c9"}.fa-crop:before{content:"\f125"}.fa-crop-alt:before{content:"\f565"}.fa-cross:before{content:"\f654"}.fa-crosshairs:before{content:"\f05b"}.fa-crow:before{content:"\f520"}.fa-crown:before{content:"\f521"}.fa-crutch:before{content:"\f7f7"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cut:before{content:"\f0c4"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-dashcube:before{content:"\f210"}.fa-database:before{content:"\f1c0"}.fa-deaf:before{content:"\f2a4"}.fa-delicious:before{content:"\f1a5"}.fa-democrat:before{content:"\f747"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-desktop:before{content:"\f108"}.fa-dev:before{content:"\f6cc"}.fa-deviantart:before{content:"\f1bd"}.fa-dharmachakra:before{content:"\f655"}.fa-dhl:before{content:"\f790"}.fa-diagnoses:before{content:"\f470"}.fa-diaspora:before{content:"\f791"}.fa-dice:before{content:"\f522"}.fa-dice-d20:before{content:"\f6cf"}.fa-dice-d6:before{content:"\f6d1"}.fa-dice-five:before{content:"\f523"}.fa-dice-four:before{content:"\f524"}.fa-dice-one:before{content:"\f525"}.fa-dice-six:before{content:"\f526"}.fa-dice-three:before{content:"\f527"}.fa-dice-two:before{content:"\f528"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-digital-tachograph:before{content:"\f566"}.fa-directions:before{content:"\f5eb"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-divide:before{content:"\f529"}.fa-dizzy:before{content:"\f567"}.fa-dna:before{content:"\f471"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-dog:before{content:"\f6d3"}.fa-dollar-sign:before{content:"\f155"}.fa-dolly:before{content:"\f472"}.fa-dolly-flatbed:before{content:"\f474"}.fa-donate:before{content:"\f4b9"}.fa-door-closed:before{content:"\f52a"}.fa-door-open:before{content:"\f52b"}.fa-dot-circle:before{content:"\f192"}.fa-dove:before{content:"\f4ba"}.fa-download:before{content:"\f019"}.fa-draft2digital:before{content:"\f396"}.fa-drafting-compass:before{content:"\f568"}.fa-dragon:before{content:"\f6d5"}.fa-draw-polygon:before{content:"\f5ee"}.fa-dribbble:before{content:"\f17d"}.fa-dribbble-square:before{content:"\f397"}.fa-dropbox:before{content:"\f16b"}.fa-drum:before{content:"\f569"}.fa-drum-steelpan:before{content:"\f56a"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-drupal:before{content:"\f1a9"}.fa-dumbbell:before{content:"\f44b"}.fa-dumpster:before{content:"\f793"}.fa-dumpster-fire:before{content:"\f794"}.fa-dungeon:before{content:"\f6d9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-ebay:before{content:"\f4f4"}.fa-edge:before{content:"\f282"}.fa-edit:before{content:"\f044"}.fa-egg:before{content:"\f7fb"}.fa-eject:before{content:"\f052"}.fa-elementor:before{content:"\f430"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-ello:before{content:"\f5f1"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-text:before{content:"\f658"}.fa-envelope-square:before{content:"\f199"}.fa-envira:before{content:"\f299"}.fa-equals:before{content:"\f52c"}.fa-eraser:before{content:"\f12d"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-ethernet:before{content:"\f796"}.fa-etsy:before{content:"\f2d7"}.fa-euro-sign:before{content:"\f153"}.fa-evernote:before{content:"\f839"}.fa-exchange-alt:before{content:"\f362"}.fa-exclamation:before{content:"\f12a"}.fa-exclamation-circle:before{content:"\f06a"}.fa-exclamation-triangle:before{content:"\f071"}.fa-expand:before{content:"\f065"}.fa-expand-arrows-alt:before{content:"\f31e"}.fa-expeditedssl:before{content:"\f23e"}.fa-external-link-alt:before{content:"\f35d"}.fa-external-link-square-alt:before{content:"\f360"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper:before{content:"\f1fb"}.fa-eye-slash:before{content:"\f070"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-facebook-square:before{content:"\f082"}.fa-fan:before{content:"\f863"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-fast-backward:before{content:"\f049"}.fa-fast-forward:before{content:"\f050"}.fa-fax:before{content:"\f1ac"}.fa-feather:before{content:"\f52d"}.fa-feather-alt:before{content:"\f56b"}.fa-fedex:before{content:"\f797"}.fa-fedora:before{content:"\f798"}.fa-female:before{content:"\f182"}.fa-fighter-jet:before{content:"\f0fb"}.fa-figma:before{content:"\f799"}.fa-file:before{content:"\f15b"}.fa-file-alt:before{content:"\f15c"}.fa-file-archive:before{content:"\f1c6"}.fa-file-audio:before{content:"\f1c7"}.fa-file-code:before{content:"\f1c9"}.fa-file-contract:before{content:"\f56c"}.fa-file-csv:before{content:"\f6dd"}.fa-file-download:before{content:"\f56d"}.fa-file-excel:before{content:"\f1c3"}.fa-file-export:before{content:"\f56e"}.fa-file-image:before{content:"\f1c5"}.fa-file-import:before{content:"\f56f"}.fa-file-invoice:before{content:"\f570"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-file-medical:before{content:"\f477"}.fa-file-medical-alt:before{content:"\f478"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-prescription:before{content:"\f572"}.fa-file-signature:before{content:"\f573"}.fa-file-upload:before{content:"\f574"}.fa-file-video:before{content:"\f1c8"}.fa-file-word:before{content:"\f1c2"}.fa-fill:before{content:"\f575"}.fa-fill-drip:before{content:"\f576"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-fingerprint:before{content:"\f577"}.fa-fire:before{content:"\f06d"}.fa-fire-alt:before{content:"\f7e4"}.fa-fire-extinguisher:before{content:"\f134"}.fa-firefox:before{content:"\f269"}.fa-first-aid:before{content:"\f479"}.fa-first-order:before{content:"\f2b0"}.fa-first-order-alt:before{content:"\f50a"}.fa-firstdraft:before{content:"\f3a1"}.fa-fish:before{content:"\f578"}.fa-fist-raised:before{content:"\f6de"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flag-usa:before{content:"\f74d"}.fa-flask:before{content:"\f0c3"}.fa-flickr:before{content:"\f16e"}.fa-flipboard:before{content:"\f44d"}.fa-flushed:before{content:"\f579"}.fa-fly:before{content:"\f417"}.fa-folder:before{content:"\f07b"}.fa-folder-minus:before{content:"\f65d"}.fa-folder-open:before{content:"\f07c"}.fa-folder-plus:before{content:"\f65e"}.fa-font:before{content:"\f031"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-font-awesome-flag:before{content:"\f425"}.fa-font-awesome-logo-full:before{content:"\f4e6"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-football-ball:before{content:"\f44e"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-forward:before{content:"\f04e"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-frog:before{content:"\f52e"}.fa-frown:before{content:"\f119"}.fa-frown-open:before{content:"\f57a"}.fa-fulcrum:before{content:"\f50b"}.fa-funnel-dollar:before{content:"\f662"}.fa-futbol:before{content:"\f1e3"}.fa-galactic-republic:before{content:"\f50c"}.fa-galactic-senate:before{content:"\f50d"}.fa-gamepad:before{content:"\f11b"}.fa-gas-pump:before{content:"\f52f"}.fa-gavel:before{content:"\f0e3"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-ghost:before{content:"\f6e2"}.fa-gift:before{content:"\f06b"}.fa-gifts:before{content:"\f79c"}.fa-git:before{content:"\f1d3"}.fa-git-alt:before{content:"\f841"}.fa-git-square:before{content:"\f1d2"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-github-square:before{content:"\f092"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glass-cheers:before{content:"\f79f"}.fa-glass-martini:before{content:"\f000"}.fa-glass-martini-alt:before{content:"\f57b"}.fa-glass-whiskey:before{content:"\f7a0"}.fa-glasses:before{content:"\f530"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-globe:before{content:"\f0ac"}.fa-globe-africa:before{content:"\f57c"}.fa-globe-americas:before{content:"\f57d"}.fa-globe-asia:before{content:"\f57e"}.fa-globe-europe:before{content:"\f7a2"}.fa-gofore:before{content:"\f3a7"}.fa-golf-ball:before{content:"\f450"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-wallet:before{content:"\f1ee"}.fa-gopuram:before{content:"\f664"}.fa-graduation-cap:before{content:"\f19d"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-greater-than:before{content:"\f531"}.fa-greater-than-equal:before{content:"\f532"}.fa-grimace:before{content:"\f57f"}.fa-grin:before{content:"\f580"}.fa-grin-alt:before{content:"\f581"}.fa-grin-beam:before{content:"\f582"}.fa-grin-beam-sweat:before{content:"\f583"}.fa-grin-hearts:before{content:"\f584"}.fa-grin-squint:before{content:"\f585"}.fa-grin-squint-tears:before{content:"\f586"}.fa-grin-stars:before{content:"\f587"}.fa-grin-tears:before{content:"\f588"}.fa-grin-tongue:before{content:"\f589"}.fa-grin-tongue-squint:before{content:"\f58a"}.fa-grin-tongue-wink:before{content:"\f58b"}.fa-grin-wink:before{content:"\f58c"}.fa-grip-horizontal:before{content:"\f58d"}.fa-grip-lines:before{content:"\f7a4"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-grip-vertical:before{content:"\f58e"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-guitar:before{content:"\f7a6"}.fa-gulp:before{content:"\f3ae"}.fa-h-square:before{content:"\f0fd"}.fa-hacker-news:before{content:"\f1d4"}.fa-hacker-news-square:before{content:"\f3af"}.fa-hackerrank:before{content:"\f5f7"}.fa-hamburger:before{content:"\f805"}.fa-hammer:before{content:"\f6e3"}.fa-hamsa:before{content:"\f665"}.fa-hand-holding:before{content:"\f4bd"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-hand-holding-usd:before{content:"\f4c0"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-middle-finger:before{content:"\f806"}.fa-hand-paper:before{content:"\f256"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-rock:before{content:"\f255"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-spock:before{content:"\f259"}.fa-hands:before{content:"\f4c2"}.fa-hands-helping:before{content:"\f4c4"}.fa-handshake:before{content:"\f2b5"}.fa-hanukiah:before{content:"\f6e6"}.fa-hard-hat:before{content:"\f807"}.fa-hashtag:before{content:"\f292"}.fa-hat-cowboy:before{content:"\f8c0"}.fa-hat-cowboy-side:before{content:"\f8c1"}.fa-hat-wizard:before{content:"\f6e8"}.fa-haykal:before{content:"\f666"}.fa-hdd:before{content:"\f0a0"}.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-headphones-alt:before{content:"\f58f"}.fa-headset:before{content:"\f590"}.fa-heart:before{content:"\f004"}.fa-heart-broken:before{content:"\f7a9"}.fa-heartbeat:before{content:"\f21e"}.fa-helicopter:before{content:"\f533"}.fa-highlighter:before{content:"\f591"}.fa-hiking:before{content:"\f6ec"}.fa-hippo:before{content:"\f6ed"}.fa-hips:before{content:"\f452"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-history:before{content:"\f1da"}.fa-hockey-puck:before{content:"\f453"}.fa-holly-berry:before{content:"\f7aa"}.fa-home:before{content:"\f015"}.fa-hooli:before{content:"\f427"}.fa-hornbill:before{content:"\f592"}.fa-horse:before{content:"\f6f0"}.fa-horse-head:before{content:"\f7ab"}.fa-hospital:before{content:"\f0f8"}.fa-hospital-alt:before{content:"\f47d"}.fa-hospital-symbol:before{content:"\f47e"}.fa-hot-tub:before{content:"\f593"}.fa-hotdog:before{content:"\f80f"}.fa-hotel:before{content:"\f594"}.fa-hotjar:before{content:"\f3b1"}.fa-hourglass:before{content:"\f254"}.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-start:before{content:"\f251"}.fa-house-damage:before{content:"\f6f1"}.fa-houzz:before{content:"\f27c"}.fa-hryvnia:before{content:"\f6f2"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-i-cursor:before{content:"\f246"}.fa-ice-cream:before{content:"\f810"}.fa-icicles:before{content:"\f7ad"}.fa-icons:before{content:"\f86d"}.fa-id-badge:before{content:"\f2c1"}.fa-id-card:before{content:"\f2c2"}.fa-id-card-alt:before{content:"\f47f"}.fa-igloo:before{content:"\f7ae"}.fa-image:before{content:"\f03e"}.fa-images:before{content:"\f302"}.fa-imdb:before{content:"\f2d8"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-industry:before{content:"\f275"}.fa-infinity:before{content:"\f534"}.fa-info:before{content:"\f129"}.fa-info-circle:before{content:"\f05a"}.fa-instagram:before{content:"\f16d"}.fa-intercom:before{content:"\f7af"}.fa-internet-explorer:before{content:"\f26b"}.fa-invision:before{content:"\f7b0"}.fa-ioxhost:before{content:"\f208"}.fa-italic:before{content:"\f033"}.fa-itch-io:before{content:"\f83a"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-java:before{content:"\f4e4"}.fa-jedi:before{content:"\f669"}.fa-jedi-order:before{content:"\f50e"}.fa-jenkins:before{content:"\f3b6"}.fa-jira:before{content:"\f7b1"}.fa-joget:before{content:"\f3b7"}.fa-joint:before{content:"\f595"}.fa-joomla:before{content:"\f1aa"}.fa-journal-whills:before{content:"\f66a"}.fa-js:before{content:"\f3b8"}.fa-js-square:before{content:"\f3b9"}.fa-jsfiddle:before{content:"\f1cc"}.fa-kaaba:before{content:"\f66b"}.fa-kaggle:before{content:"\f5fa"}.fa-key:before{content:"\f084"}.fa-keybase:before{content:"\f4f5"}.fa-keyboard:before{content:"\f11c"}.fa-keycdn:before{content:"\f3ba"}.fa-khanda:before{content:"\f66d"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-kiss:before{content:"\f596"}.fa-kiss-beam:before{content:"\f597"}.fa-kiss-wink-heart:before{content:"\f598"}.fa-kiwi-bird:before{content:"\f535"}.fa-korvue:before{content:"\f42f"}.fa-landmark:before{content:"\f66f"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laptop-code:before{content:"\f5fc"}.fa-laptop-medical:before{content:"\f812"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-laugh:before{content:"\f599"}.fa-laugh-beam:before{content:"\f59a"}.fa-laugh-squint:before{content:"\f59b"}.fa-laugh-wink:before{content:"\f59c"}.fa-layer-group:before{content:"\f5fd"}.fa-leaf:before{content:"\f06c"}.fa-leanpub:before{content:"\f212"}.fa-lemon:before{content:"\f094"}.fa-less:before{content:"\f41d"}.fa-less-than:before{content:"\f536"}.fa-less-than-equal:before{content:"\f537"}.fa-level-down-alt:before{content:"\f3be"}.fa-level-up-alt:before{content:"\f3bf"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-line:before{content:"\f3c0"}.fa-link:before{content:"\f0c1"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lira-sign:before{content:"\f195"}.fa-list:before{content:"\f03a"}.fa-list-alt:before{content:"\f022"}.fa-list-ol:before{content:"\f0cb"}.fa-list-ul:before{content:"\f0ca"}.fa-location-arrow:before{content:"\f124"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-long-arrow-alt-down:before{content:"\f309"}.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-long-arrow-alt-right:before{content:"\f30b"}.fa-long-arrow-alt-up:before{content:"\f30c"}.fa-low-vision:before{content:"\f2a8"}.fa-luggage-cart:before{content:"\f59d"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-magic:before{content:"\f0d0"}.fa-magnet:before{content:"\f076"}.fa-mail-bulk:before{content:"\f674"}.fa-mailchimp:before{content:"\f59e"}.fa-male:before{content:"\f183"}.fa-mandalorian:before{content:"\f50f"}.fa-map:before{content:"\f279"}.fa-map-marked:before{content:"\f59f"}.fa-map-marked-alt:before{content:"\f5a0"}.fa-map-marker:before{content:"\f041"}.fa-map-marker-alt:before{content:"\f3c5"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-markdown:before{content:"\f60f"}.fa-marker:before{content:"\f5a1"}.fa-mars:before{content:"\f222"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mask:before{content:"\f6fa"}.fa-mastodon:before{content:"\f4f6"}.fa-maxcdn:before{content:"\f136"}.fa-mdb:before{content:"\f8ca"}.fa-medal:before{content:"\f5a2"}.fa-medapps:before{content:"\f3c6"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f3c7"}.fa-medkit:before{content:"\f0fa"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-megaport:before{content:"\f5a3"}.fa-meh:before{content:"\f11a"}.fa-meh-blank:before{content:"\f5a4"}.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-memory:before{content:"\f538"}.fa-mendeley:before{content:"\f7b3"}.fa-menorah:before{content:"\f676"}.fa-mercury:before{content:"\f223"}.fa-meteor:before{content:"\f753"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-alt:before{content:"\f3c9"}.fa-microphone-alt-slash:before{content:"\f539"}.fa-microphone-slash:before{content:"\f131"}.fa-microscope:before{content:"\f610"}.fa-microsoft:before{content:"\f3ca"}.fa-minus:before{content:"\f068"}.fa-minus-circle:before{content:"\f056"}.fa-minus-square:before{content:"\f146"}.fa-mitten:before{content:"\f7b5"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mizuni:before{content:"\f3cc"}.fa-mobile:before{content:"\f10b"}.fa-mobile-alt:before{content:"\f3cd"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-money-bill:before{content:"\f0d6"}.fa-money-bill-alt:before{content:"\f3d1"}.fa-money-bill-wave:before{content:"\f53a"}.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-money-check:before{content:"\f53c"}.fa-money-check-alt:before{content:"\f53d"}.fa-monument:before{content:"\f5a6"}.fa-moon:before{content:"\f186"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-mosque:before{content:"\f678"}.fa-motorcycle:before{content:"\f21c"}.fa-mountain:before{content:"\f6fc"}.fa-mouse:before{content:"\f8cc"}.fa-mouse-pointer:before{content:"\f245"}.fa-mug-hot:before{content:"\f7b6"}.fa-music:before{content:"\f001"}.fa-napster:before{content:"\f3d2"}.fa-neos:before{content:"\f612"}.fa-network-wired:before{content:"\f6ff"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-nimblr:before{content:"\f5a8"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-not-equal:before{content:"\f53e"}.fa-notes-medical:before{content:"\f481"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-oil-can:before{content:"\f613"}.fa-old-republic:before{content:"\f510"}.fa-om:before{content:"\f679"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-orcid:before{content:"\f8d2"}.fa-osi:before{content:"\f41a"}.fa-otter:before{content:"\f700"}.fa-outdent:before{content:"\f03b"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-pager:before{content:"\f815"}.fa-paint-brush:before{content:"\f1fc"}.fa-paint-roller:before{content:"\f5aa"}.fa-palette:before{content:"\f53f"}.fa-palfed:before{content:"\f3d8"}.fa-pallet:before{content:"\f482"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-parachute-box:before{content:"\f4cd"}.fa-paragraph:before{content:"\f1dd"}.fa-parking:before{content:"\f540"}.fa-passport:before{content:"\f5ab"}.fa-pastafarianism:before{content:"\f67b"}.fa-paste:before{content:"\f0ea"}.fa-patreon:before{content:"\f3d9"}.fa-pause:before{content:"\f04c"}.fa-pause-circle:before{content:"\f28b"}.fa-paw:before{content:"\f1b0"}.fa-paypal:before{content:"\f1ed"}.fa-peace:before{content:"\f67c"}.fa-pen:before{content:"\f304"}.fa-pen-alt:before{content:"\f305"}.fa-pen-fancy:before{content:"\f5ac"}.fa-pen-nib:before{content:"\f5ad"}.fa-pen-square:before{content:"\f14b"}.fa-pencil-alt:before{content:"\f303"}.fa-pencil-ruler:before{content:"\f5ae"}.fa-penny-arcade:before{content:"\f704"}.fa-people-carry:before{content:"\f4ce"}.fa-pepper-hot:before{content:"\f816"}.fa-percent:before{content:"\f295"}.fa-percentage:before{content:"\f541"}.fa-periscope:before{content:"\f3da"}.fa-person-booth:before{content:"\f756"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phoenix-squadron:before{content:"\f511"}.fa-phone:before{content:"\f095"}.fa-phone-alt:before{content:"\f879"}.fa-phone-slash:before{content:"\f3dd"}.fa-phone-square:before{content:"\f098"}.fa-phone-square-alt:before{content:"\f87b"}.fa-phone-volume:before{content:"\f2a0"}.fa-photo-video:before{content:"\f87c"}.fa-php:before{content:"\f457"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-piggy-bank:before{content:"\f4d3"}.fa-pills:before{content:"\f484"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pinterest-square:before{content:"\f0d3"}.fa-pizza-slice:before{content:"\f818"}.fa-place-of-worship:before{content:"\f67f"}.fa-plane:before{content:"\f072"}.fa-plane-arrival:before{content:"\f5af"}.fa-plane-departure:before{content:"\f5b0"}.fa-play:before{content:"\f04b"}.fa-play-circle:before{content:"\f144"}.fa-playstation:before{content:"\f3df"}.fa-plug:before{content:"\f1e6"}.fa-plus:before{content:"\f067"}.fa-plus-circle:before{content:"\f055"}.fa-plus-square:before{content:"\f0fe"}.fa-podcast:before{content:"\f2ce"}.fa-poll:before{content:"\f681"}.fa-poll-h:before{content:"\f682"}.fa-poo:before{content:"\f2fe"}.fa-poo-storm:before{content:"\f75a"}.fa-poop:before{content:"\f619"}.fa-portrait:before{content:"\f3e0"}.fa-pound-sign:before{content:"\f154"}.fa-power-off:before{content:"\f011"}.fa-pray:before{content:"\f683"}.fa-praying-hands:before{content:"\f684"}.fa-prescription:before{content:"\f5b1"}.fa-prescription-bottle:before{content:"\f485"}.fa-prescription-bottle-alt:before{content:"\f486"}.fa-print:before{content:"\f02f"}.fa-procedures:before{content:"\f487"}.fa-product-hunt:before{content:"\f288"}.fa-project-diagram:before{content:"\f542"}.fa-pushed:before{content:"\f3e1"}.fa-puzzle-piece:before{content:"\f12e"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\f128"}.fa-question-circle:before{content:"\f059"}.fa-quidditch:before{content:"\f458"}.fa-quinscape:before{content:"\f459"}.fa-quora:before{content:"\f2c4"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-quran:before{content:"\f687"}.fa-r-project:before{content:"\f4f7"}.fa-radiation:before{content:"\f7b9"}.fa-radiation-alt:before{content:"\f7ba"}.fa-rainbow:before{content:"\f75b"}.fa-random:before{content:"\f074"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-reacteurope:before{content:"\f75d"}.fa-readme:before{content:"\f4d5"}.fa-rebel:before{content:"\f1d0"}.fa-receipt:before{content:"\f543"}.fa-record-vinyl:before{content:"\f8d9"}.fa-recycle:before{content:"\f1b8"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-reddit-square:before{content:"\f1a2"}.fa-redhat:before{content:"\f7bc"}.fa-redo:before{content:"\f01e"}.fa-redo-alt:before{content:"\f2f9"}.fa-registered:before{content:"\f25d"}.fa-remove-format:before{content:"\f87d"}.fa-renren:before{content:"\f18b"}.fa-reply:before{content:"\f3e5"}.fa-reply-all:before{content:"\f122"}.fa-replyd:before{content:"\f3e6"}.fa-republican:before{content:"\f75e"}.fa-researchgate:before{content:"\f4f8"}.fa-resolving:before{content:"\f3e7"}.fa-restroom:before{content:"\f7bd"}.fa-retweet:before{content:"\f079"}.fa-rev:before{content:"\f5b2"}.fa-ribbon:before{content:"\f4d6"}.fa-ring:before{content:"\f70b"}.fa-road:before{content:"\f018"}.fa-robot:before{content:"\f544"}.fa-rocket:before{content:"\f135"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-route:before{content:"\f4d7"}.fa-rss:before{content:"\f09e"}.fa-rss-square:before{content:"\f143"}.fa-ruble-sign:before{content:"\f158"}.fa-ruler:before{content:"\f545"}.fa-ruler-combined:before{content:"\f546"}.fa-ruler-horizontal:before{content:"\f547"}.fa-ruler-vertical:before{content:"\f548"}.fa-running:before{content:"\f70c"}.fa-rupee-sign:before{content:"\f156"}.fa-sad-cry:before{content:"\f5b3"}.fa-sad-tear:before{content:"\f5b4"}.fa-safari:before{content:"\f267"}.fa-salesforce:before{content:"\f83b"}.fa-sass:before{content:"\f41e"}.fa-satellite:before{content:"\f7bf"}.fa-satellite-dish:before{content:"\f7c0"}.fa-save:before{content:"\f0c7"}.fa-schlix:before{content:"\f3ea"}.fa-school:before{content:"\f549"}.fa-screwdriver:before{content:"\f54a"}.fa-scribd:before{content:"\f28a"}.fa-scroll:before{content:"\f70e"}.fa-sd-card:before{content:"\f7c2"}.fa-search:before{content:"\f002"}.fa-search-dollar:before{content:"\f688"}.fa-search-location:before{content:"\f689"}.fa-search-minus:before{content:"\f010"}.fa-search-plus:before{content:"\f00e"}.fa-searchengin:before{content:"\f3eb"}.fa-seedling:before{content:"\f4d8"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-server:before{content:"\f233"}.fa-servicestack:before{content:"\f3ec"}.fa-shapes:before{content:"\f61f"}.fa-share:before{content:"\f064"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-share-square:before{content:"\f14d"}.fa-shekel-sign:before{content:"\f20b"}.fa-shield-alt:before{content:"\f3ed"}.fa-ship:before{content:"\f21a"}.fa-shipping-fast:before{content:"\f48b"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shoe-prints:before{content:"\f54b"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-shopping-cart:before{content:"\f07a"}.fa-shopware:before{content:"\f5b5"}.fa-shower:before{content:"\f2cc"}.fa-shuttle-van:before{content:"\f5b6"}.fa-sign:before{content:"\f4d9"}.fa-sign-in-alt:before{content:"\f2f6"}.fa-sign-language:before{content:"\f2a7"}.fa-sign-out-alt:before{content:"\f2f5"}.fa-signal:before{content:"\f012"}.fa-signature:before{content:"\f5b7"}.fa-sim-card:before{content:"\f7c4"}.fa-simplybuilt:before{content:"\f215"}.fa-sistrix:before{content:"\f3ee"}.fa-sitemap:before{content:"\f0e8"}.fa-sith:before{content:"\f512"}.fa-skating:before{content:"\f7c5"}.fa-sketch:before{content:"\f7c6"}.fa-skiing:before{content:"\f7c9"}.fa-skiing-nordic:before{content:"\f7ca"}.fa-skull:before{content:"\f54c"}.fa-skull-crossbones:before{content:"\f714"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f3ef"}.fa-slash:before{content:"\f715"}.fa-sleigh:before{content:"\f7cc"}.fa-sliders-h:before{content:"\f1de"}.fa-slideshare:before{content:"\f1e7"}.fa-smile:before{content:"\f118"}.fa-smile-beam:before{content:"\f5b8"}.fa-smile-wink:before{content:"\f4da"}.fa-smog:before{content:"\f75f"}.fa-smoking:before{content:"\f48d"}.fa-smoking-ban:before{content:"\f54d"}.fa-sms:before{content:"\f7cd"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-snowboarding:before{content:"\f7ce"}.fa-snowflake:before{content:"\f2dc"}.fa-snowman:before{content:"\f7d0"}.fa-snowplow:before{content:"\f7d2"}.fa-socks:before{content:"\f696"}.fa-solar-panel:before{content:"\f5ba"}.fa-sort:before{content:"\f0dc"}.fa-sort-alpha-down:before{content:"\f15d"}.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-sort-alpha-up:before{content:"\f15e"}.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-sort-amount-down:before{content:"\f160"}.fa-sort-amount-down-alt:before{content:"\f884"}.fa-sort-amount-up:before{content:"\f161"}.fa-sort-amount-up-alt:before{content:"\f885"}.fa-sort-down:before{content:"\f0dd"}.fa-sort-numeric-down:before{content:"\f162"}.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-sort-numeric-up:before{content:"\f163"}.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-sort-up:before{content:"\f0de"}.fa-soundcloud:before{content:"\f1be"}.fa-sourcetree:before{content:"\f7d3"}.fa-spa:before{content:"\f5bb"}.fa-space-shuttle:before{content:"\f197"}.fa-speakap:before{content:"\f3f3"}.fa-speaker-deck:before{content:"\f83c"}.fa-spell-check:before{content:"\f891"}.fa-spider:before{content:"\f717"}.fa-spinner:before{content:"\f110"}.fa-splotch:before{content:"\f5bc"}.fa-spotify:before{content:"\f1bc"}.fa-spray-can:before{content:"\f5bd"}.fa-square:before{content:"\f0c8"}.fa-square-full:before{content:"\f45c"}.fa-square-root-alt:before{content:"\f698"}.fa-squarespace:before{content:"\f5be"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-stackpath:before{content:"\f842"}.fa-stamp:before{content:"\f5bf"}.fa-star:before{content:"\f005"}.fa-star-and-crescent:before{content:"\f699"}.fa-star-half:before{content:"\f089"}.fa-star-half-alt:before{content:"\f5c0"}.fa-star-of-david:before{content:"\f69a"}.fa-star-of-life:before{content:"\f621"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-steam-symbol:before{content:"\f3f6"}.fa-step-backward:before{content:"\f048"}.fa-step-forward:before{content:"\f051"}.fa-stethoscope:before{content:"\f0f1"}.fa-sticker-mule:before{content:"\f3f7"}.fa-sticky-note:before{content:"\f249"}.fa-stop:before{content:"\f04d"}.fa-stop-circle:before{content:"\f28d"}.fa-stopwatch:before{content:"\f2f2"}.fa-store:before{content:"\f54e"}.fa-store-alt:before{content:"\f54f"}.fa-strava:before{content:"\f428"}.fa-stream:before{content:"\f550"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-stroopwafel:before{content:"\f551"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-subscript:before{content:"\f12c"}.fa-subway:before{content:"\f239"}.fa-suitcase:before{content:"\f0f2"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-sun:before{content:"\f185"}.fa-superpowers:before{content:"\f2dd"}.fa-superscript:before{content:"\f12b"}.fa-supple:before{content:"\f3f9"}.fa-surprise:before{content:"\f5c2"}.fa-suse:before{content:"\f7d6"}.fa-swatchbook:before{content:"\f5c3"}.fa-swift:before{content:"\f8e1"}.fa-swimmer:before{content:"\f5c4"}.fa-swimming-pool:before{content:"\f5c5"}.fa-symfony:before{content:"\f83d"}.fa-synagogue:before{content:"\f69b"}.fa-sync:before{content:"\f021"}.fa-sync-alt:before{content:"\f2f1"}.fa-syringe:before{content:"\f48e"}.fa-table:before{content:"\f0ce"}.fa-table-tennis:before{content:"\f45d"}.fa-tablet:before{content:"\f10a"}.fa-tablet-alt:before{content:"\f3fa"}.fa-tablets:before{content:"\f490"}.fa-tachometer-alt:before{content:"\f3fd"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tape:before{content:"\f4db"}.fa-tasks:before{content:"\f0ae"}.fa-taxi:before{content:"\f1ba"}.fa-teamspeak:before{content:"\f4f9"}.fa-teeth:before{content:"\f62e"}.fa-teeth-open:before{content:"\f62f"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f3fe"}.fa-temperature-high:before{content:"\f769"}.fa-temperature-low:before{content:"\f76b"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-tenge:before{content:"\f7d7"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-th:before{content:"\f00a"}.fa-th-large:before{content:"\f009"}.fa-th-list:before{content:"\f00b"}.fa-the-red-yeti:before{content:"\f69d"}.fa-theater-masks:before{content:"\f630"}.fa-themeco:before{content:"\f5c6"}.fa-themeisle:before{content:"\f2b2"}.fa-thermometer:before{content:"\f491"}.fa-thermometer-empty:before{content:"\f2cb"}.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-think-peaks:before{content:"\f731"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbtack:before{content:"\f08d"}.fa-ticket-alt:before{content:"\f3ff"}.fa-times:before{content:"\f00d"}.fa-times-circle:before{content:"\f057"}.fa-tint:before{content:"\f043"}.fa-tint-slash:before{content:"\f5c7"}.fa-tired:before{content:"\f5c8"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-toilet:before{content:"\f7d8"}.fa-toilet-paper:before{content:"\f71e"}.fa-toolbox:before{content:"\f552"}.fa-tools:before{content:"\f7d9"}.fa-tooth:before{content:"\f5c9"}.fa-torah:before{content:"\f6a0"}.fa-torii-gate:before{content:"\f6a1"}.fa-tractor:before{content:"\f722"}.fa-trade-federation:before{content:"\f513"}.fa-trademark:before{content:"\f25c"}.fa-traffic-light:before{content:"\f637"}.fa-train:before{content:"\f238"}.fa-tram:before{content:"\f7da"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-alt:before{content:"\f2ed"}.fa-trash-restore:before{content:"\f829"}.fa-trash-restore-alt:before{content:"\f82a"}.fa-tree:before{content:"\f1bb"}.fa-trello:before{content:"\f181"}.fa-tripadvisor:before{content:"\f262"}.fa-trophy:before{content:"\f091"}.fa-truck:before{content:"\f0d1"}.fa-truck-loading:before{content:"\f4de"}.fa-truck-monster:before{content:"\f63b"}.fa-truck-moving:before{content:"\f4df"}.fa-truck-pickup:before{content:"\f63c"}.fa-tshirt:before{content:"\f553"}.fa-tty:before{content:"\f1e4"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-tv:before{content:"\f26c"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-twitter-square:before{content:"\f081"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-ubuntu:before{content:"\f7df"}.fa-uikit:before{content:"\f403"}.fa-umbraco:before{content:"\f8e8"}.fa-umbrella:before{content:"\f0e9"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-underline:before{content:"\f0cd"}.fa-undo:before{content:"\f0e2"}.fa-undo-alt:before{content:"\f2ea"}.fa-uniregistry:before{content:"\f404"}.fa-universal-access:before{content:"\f29a"}.fa-university:before{content:"\f19c"}.fa-unlink:before{content:"\f127"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before{content:"\f13e"}.fa-untappd:before{content:"\f405"}.fa-upload:before{content:"\f093"}.fa-ups:before{content:"\f7e0"}.fa-usb:before{content:"\f287"}.fa-user:before{content:"\f007"}.fa-user-alt:before{content:"\f406"}.fa-user-alt-slash:before{content:"\f4fa"}.fa-user-astronaut:before{content:"\f4fb"}.fa-user-check:before{content:"\f4fc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-clock:before{content:"\f4fd"}.fa-user-cog:before{content:"\f4fe"}.fa-user-edit:before{content:"\f4ff"}.fa-user-friends:before{content:"\f500"}.fa-user-graduate:before{content:"\f501"}.fa-user-injured:before{content:"\f728"}.fa-user-lock:before{content:"\f502"}.fa-user-md:before{content:"\f0f0"}.fa-user-minus:before{content:"\f503"}.fa-user-ninja:before{content:"\f504"}.fa-user-nurse:before{content:"\f82f"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-shield:before{content:"\f505"}.fa-user-slash:before{content:"\f506"}.fa-user-tag:before{content:"\f507"}.fa-user-tie:before{content:"\f508"}.fa-user-times:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-users-cog:before{content:"\f509"}.fa-usps:before{content:"\f7e1"}.fa-ussunnah:before{content:"\f407"}.fa-utensil-spoon:before{content:"\f2e5"}.fa-utensils:before{content:"\f2e7"}.fa-vaadin:before{content:"\f408"}.fa-vector-square:before{content:"\f5cb"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-vial:before{content:"\f492"}.fa-vials:before{content:"\f493"}.fa-viber:before{content:"\f409"}.fa-video:before{content:"\f03d"}.fa-video-slash:before{content:"\f4e2"}.fa-vihara:before{content:"\f6a7"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-square:before{content:"\f194"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-voicemail:before{content:"\f897"}.fa-volleyball-ball:before{content:"\f45f"}.fa-volume-down:before{content:"\f027"}.fa-volume-mute:before{content:"\f6a9"}.fa-volume-off:before{content:"\f026"}.fa-volume-up:before{content:"\f028"}.fa-vote-yea:before{content:"\f772"}.fa-vr-cardboard:before{content:"\f729"}.fa-vuejs:before{content:"\f41f"}.fa-walking:before{content:"\f554"}.fa-wallet:before{content:"\f555"}.fa-warehouse:before{content:"\f494"}.fa-water:before{content:"\f773"}.fa-wave-square:before{content:"\f83e"}.fa-waze:before{content:"\f83f"}.fa-weebly:before{content:"\f5cc"}.fa-weibo:before{content:"\f18a"}.fa-weight:before{content:"\f496"}.fa-weight-hanging:before{content:"\f5cd"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whatsapp-square:before{content:"\f40c"}.fa-wheelchair:before{content:"\f193"}.fa-whmcs:before{content:"\f40d"}.fa-wifi:before{content:"\f1eb"}.fa-wikipedia-w:before{content:"\f266"}.fa-wind:before{content:"\f72e"}.fa-window-close:before{content:"\f410"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-windows:before{content:"\f17a"}.fa-wine-bottle:before{content:"\f72f"}.fa-wine-glass:before{content:"\f4e3"}.fa-wine-glass-alt:before{content:"\f5ce"}.fa-wix:before{content:"\f5cf"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-won-sign:before{content:"\f159"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-wpressr:before{content:"\f3e4"}.fa-wrench:before{content:"\f0ad"}.fa-x-ray:before{content:"\f497"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yammer:before{content:"\f840"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yarn:before{content:"\f7e3"}.fa-yelp:before{content:"\f1e9"}.fa-yen-sign:before{content:"\f157"}.fa-yin-yang:before{content:"\f6ad"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-youtube-square:before{content:"\f431"}.fa-zhihu:before{content:"\f63f"}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto} \ No newline at end of file diff --git a/web/static/css/regular.min.css b/web/static/css/regular.min.css deleted file mode 100644 index d8b4d45..0000000 --- a/web/static/css/regular.min.css +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * Font Awesome Free 5.11.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - */ -@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:400;font-display:auto;src:url(../webfonts/fa-regular-400.eot);src:url(../webfonts/fa-regular-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.woff) format("woff"),url(../webfonts/fa-regular-400.ttf) format("truetype"),url(../webfonts/fa-regular-400.svg#fontawesome) format("svg")}.far{font-family:"Font Awesome 5 Free";font-weight:400} \ No newline at end of file diff --git a/web/static/css/solid.min.css b/web/static/css/solid.min.css deleted file mode 100644 index c2c8a46..0000000 --- a/web/static/css/solid.min.css +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * Font Awesome Free 5.11.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - */ -@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:auto;src:url(../webfonts/fa-solid-900.eot);src:url(../webfonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.woff) format("woff"),url(../webfonts/fa-solid-900.ttf) format("truetype"),url(../webfonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.fas{font-family:"Font Awesome 5 Free";font-weight:900} \ No newline at end of file diff --git a/web/static/css/style.css b/web/static/css/style.css deleted file mode 100644 index 0a9c3f3..0000000 --- a/web/static/css/style.css +++ /dev/null @@ -1,9816 +0,0 @@ -@import url("https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700"); -@import url("https://fonts.googleapis.com/css?family=Roboto:400,300,500,700"); -/* - * - * INSPINIA - Responsive Admin Theme - * version 2.9.3 - * -*/ -@font-face { - font-family: 'Glyphicons Halflings'; - src: url('../fonts/glyphicons-halflings-regular.eot'); - src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg') format('svg'); -} -.glyphicon { - position: relative; - top: 1px; - display: inline-block; - font-family: 'Glyphicons Halflings'; - font-style: normal; - font-weight: normal; - line-height: 1; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -.glyphicon-asterisk:before { - content: "\002a"; -} -.glyphicon-plus:before { - content: "\002b"; -} -.glyphicon-euro:before, -.glyphicon-eur:before { - content: "\20ac"; -} -.glyphicon-minus:before { - content: "\2212"; -} -.glyphicon-cloud:before { - content: "\2601"; -} -.glyphicon-envelope:before { - content: "\2709"; -} -.glyphicon-pencil:before { - content: "\270f"; -} -.glyphicon-glass:before { - content: "\e001"; -} -.glyphicon-music:before { - content: "\e002"; -} -.glyphicon-search:before { - content: "\e003"; -} -.glyphicon-heart:before { - content: "\e005"; -} -.glyphicon-star:before { - content: "\e006"; -} -.glyphicon-star-empty:before { - content: "\e007"; -} -.glyphicon-user:before { - content: "\e008"; -} -.glyphicon-film:before { - content: "\e009"; -} -.glyphicon-th-large:before { - content: "\e010"; -} -.glyphicon-th:before { - content: "\e011"; -} -.glyphicon-th-list:before { - content: "\e012"; -} -.glyphicon-ok:before { - content: "\e013"; -} -.glyphicon-remove:before { - content: "\e014"; -} -.glyphicon-zoom-in:before { - content: "\e015"; -} -.glyphicon-zoom-out:before { - content: "\e016"; -} -.glyphicon-off:before { - content: "\e017"; -} -.glyphicon-signal:before { - content: "\e018"; -} -.glyphicon-cog:before { - content: "\e019"; -} -.glyphicon-trash:before { - content: "\e020"; -} -.glyphicon-home:before { - content: "\e021"; -} -.glyphicon-file:before { - content: "\e022"; -} -.glyphicon-time:before { - content: "\e023"; -} -.glyphicon-road:before { - content: "\e024"; -} -.glyphicon-download-alt:before { - content: "\e025"; -} -.glyphicon-download:before { - content: "\e026"; -} -.glyphicon-upload:before { - content: "\e027"; -} -.glyphicon-inbox:before { - content: "\e028"; -} -.glyphicon-play-circle:before { - content: "\e029"; -} -.glyphicon-repeat:before { - content: "\e030"; -} -.glyphicon-refresh:before { - content: "\e031"; -} -.glyphicon-list-alt:before { - content: "\e032"; -} -.glyphicon-lock:before { - content: "\e033"; -} -.glyphicon-flag:before { - content: "\e034"; -} -.glyphicon-headphones:before { - content: "\e035"; -} -.glyphicon-volume-off:before { - content: "\e036"; -} -.glyphicon-volume-down:before { - content: "\e037"; -} -.glyphicon-volume-up:before { - content: "\e038"; -} -.glyphicon-qrcode:before { - content: "\e039"; -} -.glyphicon-barcode:before { - content: "\e040"; -} -.glyphicon-tag:before { - content: "\e041"; -} -.glyphicon-tags:before { - content: "\e042"; -} -.glyphicon-book:before { - content: "\e043"; -} -.glyphicon-bookmark:before { - content: "\e044"; -} -.glyphicon-print:before { - content: "\e045"; -} -.glyphicon-camera:before { - content: "\e046"; -} -.glyphicon-font:before { - content: "\e047"; -} -.glyphicon-bold:before { - content: "\e048"; -} -.glyphicon-italic:before { - content: "\e049"; -} -.glyphicon-text-height:before { - content: "\e050"; -} -.glyphicon-text-width:before { - content: "\e051"; -} -.glyphicon-align-left:before { - content: "\e052"; -} -.glyphicon-align-center:before { - content: "\e053"; -} -.glyphicon-align-right:before { - content: "\e054"; -} -.glyphicon-align-justify:before { - content: "\e055"; -} -.glyphicon-list:before { - content: "\e056"; -} -.glyphicon-indent-left:before { - content: "\e057"; -} -.glyphicon-indent-right:before { - content: "\e058"; -} -.glyphicon-facetime-video:before { - content: "\e059"; -} -.glyphicon-picture:before { - content: "\e060"; -} -.glyphicon-map-marker:before { - content: "\e062"; -} -.glyphicon-adjust:before { - content: "\e063"; -} -.glyphicon-tint:before { - content: "\e064"; -} -.glyphicon-edit:before { - content: "\e065"; -} -.glyphicon-share:before { - content: "\e066"; -} -.glyphicon-check:before { - content: "\e067"; -} -.glyphicon-move:before { - content: "\e068"; -} -.glyphicon-step-backward:before { - content: "\e069"; -} -.glyphicon-fast-backward:before { - content: "\e070"; -} -.glyphicon-backward:before { - content: "\e071"; -} -.glyphicon-play:before { - content: "\e072"; -} -.glyphicon-pause:before { - content: "\e073"; -} -.glyphicon-stop:before { - content: "\e074"; -} -.glyphicon-forward:before { - content: "\e075"; -} -.glyphicon-fast-forward:before { - content: "\e076"; -} -.glyphicon-step-forward:before { - content: "\e077"; -} -.glyphicon-eject:before { - content: "\e078"; -} -.glyphicon-chevron-left:before { - content: "\e079"; -} -.glyphicon-chevron-right:before { - content: "\e080"; -} -.glyphicon-plus-sign:before { - content: "\e081"; -} -.glyphicon-minus-sign:before { - content: "\e082"; -} -.glyphicon-remove-sign:before { - content: "\e083"; -} -.glyphicon-ok-sign:before { - content: "\e084"; -} -.glyphicon-question-sign:before { - content: "\e085"; -} -.glyphicon-info-sign:before { - content: "\e086"; -} -.glyphicon-screenshot:before { - content: "\e087"; -} -.glyphicon-remove-circle:before { - content: "\e088"; -} -.glyphicon-ok-circle:before { - content: "\e089"; -} -.glyphicon-ban-circle:before { - content: "\e090"; -} -.glyphicon-arrow-left:before { - content: "\e091"; -} -.glyphicon-arrow-right:before { - content: "\e092"; -} -.glyphicon-arrow-up:before { - content: "\e093"; -} -.glyphicon-arrow-down:before { - content: "\e094"; -} -.glyphicon-share-alt:before { - content: "\e095"; -} -.glyphicon-resize-full:before { - content: "\e096"; -} -.glyphicon-resize-small:before { - content: "\e097"; -} -.glyphicon-exclamation-sign:before { - content: "\e101"; -} -.glyphicon-gift:before { - content: "\e102"; -} -.glyphicon-leaf:before { - content: "\e103"; -} -.glyphicon-fire:before { - content: "\e104"; -} -.glyphicon-eye-open:before { - content: "\e105"; -} -.glyphicon-eye-close:before { - content: "\e106"; -} -.glyphicon-warning-sign:before { - content: "\e107"; -} -.glyphicon-plane:before { - content: "\e108"; -} -.glyphicon-calendar:before { - content: "\e109"; -} -.glyphicon-random:before { - content: "\e110"; -} -.glyphicon-comment:before { - content: "\e111"; -} -.glyphicon-magnet:before { - content: "\e112"; -} -.glyphicon-chevron-up:before { - content: "\e113"; -} -.glyphicon-chevron-down:before { - content: "\e114"; -} -.glyphicon-retweet:before { - content: "\e115"; -} -.glyphicon-shopping-cart:before { - content: "\e116"; -} -.glyphicon-folder-close:before { - content: "\e117"; -} -.glyphicon-folder-open:before { - content: "\e118"; -} -.glyphicon-resize-vertical:before { - content: "\e119"; -} -.glyphicon-resize-horizontal:before { - content: "\e120"; -} -.glyphicon-hdd:before { - content: "\e121"; -} -.glyphicon-bullhorn:before { - content: "\e122"; -} -.glyphicon-bell:before { - content: "\e123"; -} -.glyphicon-certificate:before { - content: "\e124"; -} -.glyphicon-thumbs-up:before { - content: "\e125"; -} -.glyphicon-thumbs-down:before { - content: "\e126"; -} -.glyphicon-hand-right:before { - content: "\e127"; -} -.glyphicon-hand-left:before { - content: "\e128"; -} -.glyphicon-hand-up:before { - content: "\e129"; -} -.glyphicon-hand-down:before { - content: "\e130"; -} -.glyphicon-circle-arrow-right:before { - content: "\e131"; -} -.glyphicon-circle-arrow-left:before { - content: "\e132"; -} -.glyphicon-circle-arrow-up:before { - content: "\e133"; -} -.glyphicon-circle-arrow-down:before { - content: "\e134"; -} -.glyphicon-globe:before { - content: "\e135"; -} -.glyphicon-wrench:before { - content: "\e136"; -} -.glyphicon-tasks:before { - content: "\e137"; -} -.glyphicon-filter:before { - content: "\e138"; -} -.glyphicon-briefcase:before { - content: "\e139"; -} -.glyphicon-fullscreen:before { - content: "\e140"; -} -.glyphicon-dashboard:before { - content: "\e141"; -} -.glyphicon-paperclip:before { - content: "\e142"; -} -.glyphicon-heart-empty:before { - content: "\e143"; -} -.glyphicon-link:before { - content: "\e144"; -} -.glyphicon-phone:before { - content: "\e145"; -} -.glyphicon-pushpin:before { - content: "\e146"; -} -.glyphicon-usd:before { - content: "\e148"; -} -.glyphicon-gbp:before { - content: "\e149"; -} -.glyphicon-sort:before { - content: "\e150"; -} -.glyphicon-sort-by-alphabet:before { - content: "\e151"; -} -.glyphicon-sort-by-alphabet-alt:before { - content: "\e152"; -} -.glyphicon-sort-by-order:before { - content: "\e153"; -} -.glyphicon-sort-by-order-alt:before { - content: "\e154"; -} -.glyphicon-sort-by-attributes:before { - content: "\e155"; -} -.glyphicon-sort-by-attributes-alt:before { - content: "\e156"; -} -.glyphicon-unchecked:before { - content: "\e157"; -} -.glyphicon-expand:before { - content: "\e158"; -} -.glyphicon-collapse-down:before { - content: "\e159"; -} -.glyphicon-collapse-up:before { - content: "\e160"; -} -.glyphicon-log-in:before { - content: "\e161"; -} -.glyphicon-flash:before { - content: "\e162"; -} -.glyphicon-log-out:before { - content: "\e163"; -} -.glyphicon-new-window:before { - content: "\e164"; -} -.glyphicon-record:before { - content: "\e165"; -} -.glyphicon-save:before { - content: "\e166"; -} -.glyphicon-open:before { - content: "\e167"; -} -.glyphicon-saved:before { - content: "\e168"; -} -.glyphicon-import:before { - content: "\e169"; -} -.glyphicon-export:before { - content: "\e170"; -} -.glyphicon-send:before { - content: "\e171"; -} -.glyphicon-floppy-disk:before { - content: "\e172"; -} -.glyphicon-floppy-saved:before { - content: "\e173"; -} -.glyphicon-floppy-remove:before { - content: "\e174"; -} -.glyphicon-floppy-save:before { - content: "\e175"; -} -.glyphicon-floppy-open:before { - content: "\e176"; -} -.glyphicon-credit-card:before { - content: "\e177"; -} -.glyphicon-transfer:before { - content: "\e178"; -} -.glyphicon-cutlery:before { - content: "\e179"; -} -.glyphicon-header:before { - content: "\e180"; -} -.glyphicon-compressed:before { - content: "\e181"; -} -.glyphicon-earphone:before { - content: "\e182"; -} -.glyphicon-phone-alt:before { - content: "\e183"; -} -.glyphicon-tower:before { - content: "\e184"; -} -.glyphicon-stats:before { - content: "\e185"; -} -.glyphicon-sd-video:before { - content: "\e186"; -} -.glyphicon-hd-video:before { - content: "\e187"; -} -.glyphicon-subtitles:before { - content: "\e188"; -} -.glyphicon-sound-stereo:before { - content: "\e189"; -} -.glyphicon-sound-dolby:before { - content: "\e190"; -} -.glyphicon-sound-5-1:before { - content: "\e191"; -} -.glyphicon-sound-6-1:before { - content: "\e192"; -} -.glyphicon-sound-7-1:before { - content: "\e193"; -} -.glyphicon-copyright-mark:before { - content: "\e194"; -} -.glyphicon-registration-mark:before { - content: "\e195"; -} -.glyphicon-cloud-download:before { - content: "\e197"; -} -.glyphicon-cloud-upload:before { - content: "\e198"; -} -.glyphicon-tree-conifer:before { - content: "\e199"; -} -.glyphicon-tree-deciduous:before { - content: "\e200"; -} -.glyphicon-cd:before { - content: "\e201"; -} -.glyphicon-save-file:before { - content: "\e202"; -} -.glyphicon-open-file:before { - content: "\e203"; -} -.glyphicon-level-up:before { - content: "\e204"; -} -.glyphicon-copy:before { - content: "\e205"; -} -.glyphicon-paste:before { - content: "\e206"; -} -.glyphicon-alert:before { - content: "\e209"; -} -.glyphicon-equalizer:before { - content: "\e210"; -} -.glyphicon-king:before { - content: "\e211"; -} -.glyphicon-queen:before { - content: "\e212"; -} -.glyphicon-pawn:before { - content: "\e213"; -} -.glyphicon-bishop:before { - content: "\e214"; -} -.glyphicon-knight:before { - content: "\e215"; -} -.glyphicon-baby-formula:before { - content: "\e216"; -} -.glyphicon-tent:before { - content: "\26fa"; -} -.glyphicon-blackboard:before { - content: "\e218"; -} -.glyphicon-bed:before { - content: "\e219"; -} -.glyphicon-apple:before { - content: "\f8ff"; -} -.glyphicon-erase:before { - content: "\e221"; -} -.glyphicon-hourglass:before { - content: "\231b"; -} -.glyphicon-lamp:before { - content: "\e223"; -} -.glyphicon-duplicate:before { - content: "\e224"; -} -.glyphicon-piggy-bank:before { - content: "\e225"; -} -.glyphicon-scissors:before { - content: "\e226"; -} -.glyphicon-bitcoin:before { - content: "\e227"; -} -.glyphicon-btc:before { - content: "\e227"; -} -.glyphicon-xbt:before { - content: "\e227"; -} -.glyphicon-yen:before { - content: "\00a5"; -} -.glyphicon-jpy:before { - content: "\00a5"; -} -.glyphicon-ruble:before { - content: "\20bd"; -} -.glyphicon-rub:before { - content: "\20bd"; -} -.glyphicon-scale:before { - content: "\e230"; -} -.glyphicon-ice-lolly:before { - content: "\e231"; -} -.glyphicon-ice-lolly-tasted:before { - content: "\e232"; -} -.glyphicon-education:before { - content: "\e233"; -} -.glyphicon-option-horizontal:before { - content: "\e234"; -} -.glyphicon-option-vertical:before { - content: "\e235"; -} -.glyphicon-menu-hamburger:before { - content: "\e236"; -} -.glyphicon-modal-window:before { - content: "\e237"; -} -.glyphicon-oil:before { - content: "\e238"; -} -.glyphicon-grain:before { - content: "\e239"; -} -.glyphicon-sunglasses:before { - content: "\e240"; -} -.glyphicon-text-size:before { - content: "\e241"; -} -.glyphicon-text-color:before { - content: "\e242"; -} -.glyphicon-text-background:before { - content: "\e243"; -} -.glyphicon-object-align-top:before { - content: "\e244"; -} -.glyphicon-object-align-bottom:before { - content: "\e245"; -} -.glyphicon-object-align-horizontal:before { - content: "\e246"; -} -.glyphicon-object-align-left:before { - content: "\e247"; -} -.glyphicon-object-align-vertical:before { - content: "\e248"; -} -.glyphicon-object-align-right:before { - content: "\e249"; -} -.glyphicon-triangle-right:before { - content: "\e250"; -} -.glyphicon-triangle-left:before { - content: "\e251"; -} -.glyphicon-triangle-bottom:before { - content: "\e252"; -} -.glyphicon-triangle-top:before { - content: "\e253"; -} -.glyphicon-console:before { - content: "\e254"; -} -.glyphicon-superscript:before { - content: "\e255"; -} -.glyphicon-subscript:before { - content: "\e256"; -} -.glyphicon-menu-left:before { - content: "\e257"; -} -.glyphicon-menu-right:before { - content: "\e258"; -} -.glyphicon-menu-down:before { - content: "\e259"; -} -.glyphicon-menu-up:before { - content: "\e260"; -} -h1, -h2, -h3, -h4, -h5, -h6 { - font-weight: 100; -} -.h1, -.h2, -.h3, -h1, -h2, -h3 { - margin-top: 20px; - margin-bottom: 10px; -} -h1 { - font-size: 30px; -} -h2 { - font-size: 24px; -} -h3 { - font-size: 16px; -} -h4 { - font-size: 14px; -} -h5 { - font-size: 12px; -} -h6 { - font-size: 10px; -} -h3, -h4, -h5 { - margin-top: 5px; - font-weight: 600; -} -.nav > li > a { - color: #a7b1c2; - font-weight: 600; - padding: 14px 20px 14px 25px; - display: block; -} -.nav.metismenu > li { - display: block; - width: 100%; - position: relative; -} -.nav.metismenu .dropdown-menu > li > a { - padding: 3px 20px; - display: block; -} -.nav.navbar-right > li > a { - color: #999c9e; -} -.nav > li.active > a { - color: #ffffff; -} -.navbar-default .nav > li > a:hover, -.navbar-default .nav > li > a:focus { - background-color: #293846; - color: white; -} -.nav .open > a, -.nav .open > a:hover, -.nav .open > a:focus { - background: #fff; -} -.nav.navbar-top-links > li > a:hover, -.nav.navbar-top-links > li > a:focus { - background-color: transparent; -} -.nav > li > a i { - margin-right: 6px; -} -.navbar { - border: 0; -} -.navbar-default { - background-color: transparent; - border-color: #2f4050; -} -.navbar-top-links li { - display: inline-block; - align-self: center; -} -.body-small .navbar-top-links li:last-child { - margin-right: 0; -} -.navbar-top-links li a { - padding: 20px 10px; - min-height: 50px; -} -.dropdown-menu { - border: medium none; - border-radius: 3px; - box-shadow: 0 0 3px rgba(86, 96, 117, 0.7); - display: none; - float: left; - font-size: 12px; - left: 0; - list-style: none outside none; - padding: 0; - position: absolute; - text-shadow: none; - top: 100%; - z-index: 1000; -} -.dropdown-menu > li > a { - border-radius: 3px; - color: inherit; - line-height: 25px; - margin: 4px; - text-align: left; - font-weight: normal; - display: block; - padding: 3px 20px; -} -.dropdown-menu > li > a:focus, -.dropdown-menu > li > a:hover { - color: #262626; - text-decoration: none; - background-color: #f5f5f5; -} -.dropdown-menu > .active > a, -.dropdown-menu > .active > a:focus, -.dropdown-menu > .active > a:hover { - color: #fff; - text-decoration: none; - background-color: #1ab394; - outline: 0; -} -.dropdown-menu > li > a.font-bold { - font-weight: 600; -} -.navbar-top-links .dropdown-menu li { - display: block; -} -.navbar-top-links .dropdown-menu li:last-child { - margin-right: 0; -} -.navbar-top-links .dropdown-menu li a { - padding: 3px 20px; - min-height: 0; -} -.navbar-top-links .dropdown-menu li a div { - white-space: normal; -} -.navbar-top-links .dropdown-messages, -.navbar-top-links .dropdown-tasks, -.navbar-top-links .dropdown-alerts { - width: 310px; - min-width: 0; -} -.navbar-top-links .dropdown-messages { - margin-left: 5px; -} -.navbar-top-links .dropdown-tasks { - margin-left: -59px; -} -.navbar-top-links .dropdown-alerts { - margin-left: -123px; -} -.navbar-top-links .dropdown-user { - right: 0; - left: auto; -} -.dropdown-messages, -.dropdown-alerts { - padding: 10px 10px 10px 10px; -} -.dropdown-messages li a, -.dropdown-alerts li a { - font-size: 12px; -} -.dropdown-messages li em, -.dropdown-alerts li em { - font-size: 10px; -} -.nav.navbar-top-links .dropdown-alerts a { - font-size: 12px; -} -.nav-header { - padding: 33px 25px; - background-color: #2f4050; -// background-image: url("patterns/header-profile.png"); -} -/*.caret { - display: inline-block; - width: 0; - height: 0; - margin-left: 2px; - vertical-align: middle; - border-top: 4px dashed; - border-right: 4px solid transparent; - border-left: 4px solid transparent; -}*/ -.profile-element .dropdown-toggle::after { - display: none; -} -.pace-done .nav-header { - transition: all 0.4s; -} -ul.nav-second-level { - background: #293846; -} -.nav > li.active { - border-left: 4px solid #19aa8d; - background: #293846; -} -.nav.nav-second-level > li.active { - border: none; -} -.nav.nav-second-level.collapse[style] { - height: auto !important; -} -.nav-header a { - color: #DFE4ED; -} -.nav-header .text-muted { - color: #8095a8 !important; -} -.minimalize-styl-2 { - padding: 4px 12px; - margin: 14px 5px 5px 20px; - font-size: 14px; - float: left; -} -.navbar-form-custom { - float: left; - height: 50px; - padding: 0; - width: 200px; - display: block; -} -.navbar-form-custom .form-group { - margin-bottom: 0; -} -.nav.navbar-top-links a { - font-size: 14px; -} -.navbar-form-custom .form-control { - background: none repeat scroll 0 0 rgba(0, 0, 0, 0); - border: medium none; - font-size: 14px; - height: 60px; - margin: 0; - z-index: 2000; -} -/*.nav.navbar-top-links .dropdown-toggle::after { - display: none; -} -*/ -.navbar.navbar-static-top { - padding: 0; - width: 100%; - align-items: inherit; -} -.navbar-static-top .dropdown-menu { - right: 0; - left: auto; -} -.count-info .label { - line-height: 12px; - padding: 2px 5px; - position: absolute; - right: 6px; - top: 12px; -} -.arrow { - float: right; -} -.fa.arrow:before { - content: "\f104"; -} -.active > a > .fa.arrow:before { - content: "\f107"; -} -.nav-second-level li, -.nav-third-level li { - border-bottom: none !important; -} -.nav.nav-third-level > li.active { - border: none; -} -.nav-second-level li a { - padding: 7px 10px 7px 10px; - padding-left: 52px; -} -.fixed-sidebar.mini-navbar .nav-second-level.collapsing li a, -.nav-second-level.collapsing li a { - min-width: 220px; -} -.body-small .nav-second-level.collapsing li a, -.mini-navbar .nav-second-level.collapsing li a { - min-width: 140px; -} -.nav-third-level li a, -.fixed-sidebar.mini-navbar .nav-second-level li .nav-third-level li a { - padding-left: 62px; -} -.nav-second-level li:last-child { - padding-bottom: 10px; -} -body:not(.fixed-sidebar):not(.canvas-menu).mini-navbar .nav li:hover > .nav-second-level, -.mini-navbar .nav li:focus > .nav-second-level { - display: block; - border-radius: 0 2px 2px 0; - min-width: 160px; - height: auto; -} -body.mini-navbar .navbar-default .nav > li > .nav-second-level li a { - font-size: 12px; - border-radius: 3px; -} -.fixed-nav .slimScrollDiv #side-menu { - padding-bottom: 60px; -} -.mini-navbar .nav-second-level li a { - padding: 10px 10px 10px 15px; -} -.mini-navbar .nav .nav-second-level { - position: absolute; - left: 70px; - top: 0; - background-color: #2f4050; - padding: 10px 10px 10px 10px; - font-size: 12px; -} -.canvas-menu.mini-navbar .nav-second-level { - background: #293846; -} -.mini-navbar li.active .nav-second-level { - left: 65px; -} -.navbar-default .special_link a { - background: #1ab394; - color: white; -} -.navbar-default .special_link a:hover { - background: #17987e !important; - color: white; -} -.navbar-default .special_link a span.label { - background: #fff; - color: #1ab394; -} -.navbar-default .landing_link a { - background: #1cc09f; - color: white; -} -.navbar-default .landing_link a:hover { - background: #1ab394 !important; - color: white; -} -.navbar-default .landing_link a span.label { - background: #fff; - color: #1cc09f; -} -.logo-element { - text-align: center; - font-size: 18px; - font-weight: 600; - color: white; - display: none; - padding: 18px 0; -} -.navbar-static-side { - transition: width 0s; -} -.footer { - transition: margin 0s; -} -.pace-done .navbar-static-side, -.pace-done .nav-header, -.pace-done li.active, -.pace-done #page-wrapper, -.pace-done .footer { - -webkit-transition: all 0.4s; - -moz-transition: all 0.4s; - -o-transition: all 0.4s; - transition: all 0.4s; -} -.navbar-fixed-top { - background: #fff; - transition-duration: 0.4s; - border-bottom: 1px solid #e7eaec !important; - z-index: 2030; - position: fixed; - right: 0; - left: 0; - padding: 0; - top: 0; -} -.navbar-fixed-top .navbar-form-custom .form-control { - height: 50px; -} -.navbar-fixed-top, -.navbar-static-top { - background: #f3f3f4; -} -.fixed-nav #wrapper { - margin-top: 0; -} -.nav-tabs > li.active > a, -.nav-tabs > li.active > a:hover, -.nav-tabs > li.active > a:focus { - -moz-border-bottom-colors: none; - -moz-border-left-colors: none; - -moz-border-right-colors: none; - -moz-border-top-colors: none; - background: none; - border-color: #dddddd #dddddd rgba(0, 0, 0, 0); - border-bottom: #f3f3f4; - border-image: none; - border-style: solid; - border-width: 1px; - color: #555555; - cursor: default; -} -.nav.nav-tabs li { - background: none; - border: none; -} -body.fixed-nav #wrapper .navbar-static-side, -body.fixed-nav #wrapper #page-wrapper { - margin-top: 60px; -} -body.top-navigation.fixed-nav #wrapper #page-wrapper { - margin-top: 0; -} -body.fixed-nav.fixed-nav-basic .navbar-fixed-top { - left: 220px; -} -body.fixed-nav.fixed-nav-basic.mini-navbar .navbar-fixed-top { - left: 70px; -} -body.fixed-nav.fixed-nav-basic.fixed-sidebar.mini-navbar .navbar-fixed-top { - left: 0; -} -body.fixed-nav.fixed-nav-basic #wrapper .navbar-static-side { - margin-top: 0; -} -body.fixed-nav.fixed-nav-basic.body-small .navbar-fixed-top { - left: 0; -} -body.fixed-nav.fixed-nav-basic.fixed-sidebar.mini-navbar.body-small .navbar-fixed-top { - left: 220px; -} -.fixed-nav .minimalize-styl-2 { - margin: 10px 5px 5px 15px; -} -.body-small .navbar-fixed-top { - margin-left: 0; -} -body.mini-navbar .navbar-static-side { - width: 70px; -} -body.mini-navbar .profile-element, -body.mini-navbar .nav-label, -body.mini-navbar .navbar-default .nav li a span { - display: none; -} -body.canvas-menu .profile-element { - display: block; -} -body:not(.fixed-sidebar):not(.canvas-menu).mini-navbar .nav-second-level { - display: none; -} -body.mini-navbar .navbar-default .nav > li > a { - font-size: 16px; -} -body.mini-navbar .logo-element { - display: block; -} -body.canvas-menu .logo-element { - display: none; -} -body.mini-navbar .nav-header { - padding: 0; - background-color: #1ab394; -} -body.canvas-menu .nav-header { - padding: 33px 25px; -} -body.canvas-menu .sidebar-collapse li { - width: 100%; -} -body.mini-navbar #page-wrapper { - width: calc(100% - 70px); -} -body.canvas-menu.mini-navbar #page-wrapper, -body.canvas-menu.mini-navbar .footer { - margin: 0; - width: 100%; -} -body.fixed-sidebar .navbar-static-side, -body.canvas-menu .navbar-static-side { - width: 220px; - z-index: 2001; - height: 100vh; - position: fixed; -} -body.fixed-sidebar.mini-navbar .navbar-static-side { - width: 0; -} -body.fixed-sidebar #page-wrapper { - margin: 0 0 0 220px; -} -body.fixed-sidebar.body-small #page-wrapper { - margin: 0; -} -body.fixed-sidebar.mini-navbar #page-wrapper { - margin: 0 0 0 0; - width: 100%; -} -body.body-small.fixed-sidebar.mini-navbar #page-wrapper { - margin: 0 0 0 220px; -} -body.body-small.fixed-sidebar.mini-navbar .navbar-static-side { - width: 220px; -} -.fixed-sidebar.mini-navbar .nav li:focus > .nav-second-level, -.canvas-menu.mini-navbar .nav li:focus > .nav-second-level { - display: block; - height: auto; -} -body.fixed-sidebar.mini-navbar .navbar-default .nav > li > .nav-second-level li a { - font-size: 12px; - border-radius: 3px; -} -body.canvas-menu.mini-navbar .navbar-default .nav > li > .nav-second-level li a { - font-size: 13px; - border-radius: 3px; -} -.fixed-sidebar.mini-navbar .nav-second-level li a, -.canvas-menu.mini-navbar .nav-second-level li a { - padding: 10px 10px 10px 15px; -} -.fixed-sidebar.mini-navbar .nav-second-level, -.canvas-menu.mini-navbar .nav-second-level { - position: relative; - padding: 0; - font-size: 13px; -} -.fixed-sidebar.mini-navbar li.active .nav-second-level, -.canvas-menu.mini-navbar li.active .nav-second-level { - left: 0; -} -body.fixed-sidebar.mini-navbar .navbar-default .nav > li > a, -body.canvas-menu.mini-navbar .navbar-default .nav > li > a { - font-size: 13px; -} -body.fixed-sidebar.mini-navbar .nav-label, -body.fixed-sidebar.mini-navbar .navbar-default .nav li a span, -body.canvas-menu.mini-navbar .nav-label, -body.canvas-menu.mini-navbar .navbar-default .nav li a span { - display: inline; -} -body.canvas-menu.mini-navbar .navbar-default .nav li .profile-element a span { - display: block; -} -.canvas-menu.mini-navbar .nav-second-level li a, -.fixed-sidebar.mini-navbar .nav-second-level li a { - padding: 7px 10px 7px 52px; -} -.fixed-sidebar.mini-navbar .nav-second-level, -.canvas-menu.mini-navbar .nav-second-level { - left: 0; -} -body.canvas-menu nav.navbar-static-side { - z-index: 2001; - background: #2f4050; - height: 100%; - position: fixed; - display: none; -} -body.canvas-menu.mini-navbar nav.navbar-static-side { - display: block; - width: 220px; -} -.top-navigation #page-wrapper { - width: 100%; -} -.top-navigation .navbar-nav .dropdown-menu > .active > a { - background: white; - color: #1ab394; - font-weight: bold; -} -.white-bg .navbar-fixed-top, -.white-bg .navbar-static-top { - background: #fff; -} -.top-navigation .navbar { - margin-bottom: 0; -} -.top-navigation .nav > li > a { - padding: 15px 20px; - color: #676a6c; -} -.top-navigation .nav > li a:hover, -.top-navigation .nav > li a:focus { - background: #fff; - color: #1ab394; -} -.top-navigation .navbar .nav > li.active { - background: #fff; - border: none; -} -.top-navigation .nav > li.active > a { - color: #1ab394; -} -.top-navigation .navbar-right { - margin-right: 10px; -} -.top-navigation .navbar-nav .dropdown-menu { - box-shadow: none; - border: 1px solid #e7eaec; -} -.top-navigation .dropdown-menu > li > a { - margin: 0; - padding: 7px 20px; -} -.navbar .dropdown-menu { - margin-top: 0; -} -.top-navigation .navbar-brand { - background: #1ab394; - color: #fff; - padding: 15px 25px; - font-size: 18px; - line-height: 20px; -} -.top-navigation .navbar-top-links li:last-child { - margin-right: 0; -} -.top-navigation.mini-navbar #page-wrapper, -.top-navigation.body-small.fixed-sidebar.mini-navbar #page-wrapper, -.mini-navbar .top-navigation #page-wrapper, -.body-small.fixed-sidebar.mini-navbar .top-navigation #page-wrapper, -.canvas-menu #page-wrapper { - margin: 0; - width: 100%; -} -.top-navigation.fixed-nav #wrapper, -.fixed-nav #wrapper.top-navigation { - margin-top: 50px; -} -.top-navigation .footer.fixed { - margin-left: 0 !important; -} -.top-navigation .wrapper.wrapper-content { - padding: 40px; -} -.top-navigation.body-small .wrapper.wrapper-content, -.body-small .top-navigation .wrapper.wrapper-content { - padding: 40px 0 40px 0; -} -.navbar-toggler { - background-color: #1ab394; - color: #fff; - padding: 6px 12px; - font-size: 14px; - margin: 8px; -} -.top-navigation .navbar-nav .open .dropdown-menu > li > a, -.top-navigation .navbar-nav .open .dropdown-menu .dropdown-header { - padding: 10px 15px 10px 20px; -} -@media (max-width: 768px) { - .top-navigation .navbar-header { - display: block; - float: none; - } -} -.menu-visible-lg, -.menu-visible-md { - display: none !important; -} -@media (min-width: 1200px) { - .menu-visible-lg { - display: block !important; - } -} -@media (min-width: 992px) { - .menu-visible-md { - display: block !important; - } -} -@media (max-width: 767px) { - .menu-visible-md { - display: block !important; - } - .menu-visible-lg { - display: block !important; - } -} -button:focus { - outline: 0 !important; -} -.btn { - border-radius: 3px; - font-size: inherit; -} -.btn:focus { - box-shadow: none; -} -.btn-xs { - font-size: 0.7rem; - padding: 0.2rem 0.4rem; -} -.btn-group-sm > .btn, -.btn-sm { - font-size: .8rem; -} -.float-e-margins .btn { - margin-bottom: 5px; -} -.btn-w-m { - min-width: 120px; -} -.btn-primary.btn-outline { - color: #1ab394; -} -.btn-success.btn-outline { - color: #1c84c6; -} -.btn-info.btn-outline { - color: #23c6c8; -} -.btn-warning.btn-outline { - color: #f8ac59; -} -.btn-danger.btn-outline { - color: #ed5565; -} -.btn-primary.btn-outline:hover, -.btn-success.btn-outline:hover, -.btn-info.btn-outline:hover, -.btn-warning.btn-outline:hover, -.btn-danger.btn-outline:hover { - color: #fff; -} -.btn.active, -.btn:active { - background-image: none; - outline: 0; - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); -} -.btn-primary { - color: #fff; - background-color: #1ab394; - border-color: #1ab394; -} -.btn-primary:hover, -.btn-primary:focus, -.btn-primary.focus { - background-color: #18a689; - border-color: #18a689; - color: #FFFFFF; -} -.btn-primary.disabled, -.btn-primary:disabled { - color: #fff; - background-color: #18a689; - border-color: #18a689; -} -.btn-primary:not(:disabled):not(.disabled):active, -.btn-primary:not(:disabled):not(.disabled).active, -.show > .btn-primary.dropdown-toggle { - color: #fff; - background-color: #18a689; - border-color: #18a689; -} -.btn-primary:not(:disabled):not(.disabled):active:focus, -.btn-primary:not(:disabled):not(.disabled).active:focus, -.show > .btn-primary.dropdown-toggle:focus { - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); -} -.btn-success { - color: #fff; - background-color: #1c84c6; - border-color: #1c84c6; -} -.btn-success:hover, -.btn-success:focus, -.btn-success.focus { - color: #fff; - background-color: #1a7bb9; - border-color: #1a7bb9; -} -.btn-success.disabled, -.btn-success:disabled { - color: #fff; - background-color: #1a7bb9; - border-color: #1a7bb9; -} -.btn-success:not(:disabled):not(.disabled):active, -.btn-success:not(:disabled):not(.disabled).active, -.show > .btn-success.dropdown-toggle { - color: #fff; - background-color: #1a7bb9; - border-color: #1a7bb9; -} -.btn-success:not(:disabled):not(.disabled):active:focus, -.btn-success:not(:disabled):not(.disabled).active:focus, -.show > .btn-success.dropdown-toggle:focus { - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); -} -.btn-info { - color: #fff; - background-color: #23c6c8; - border-color: #23c6c8; -} -.btn-info:hover, -.btn-info:focus, -.btn-info.focus { - color: #fff; - background-color: #21b9bb; - border-color: #21b9bb; -} -.btn-info.disabled, -.btn-info:disabled { - color: #fff; - background-color: #21b9bb; - border-color: #21b9bb; -} -.btn-info:not(:disabled):not(.disabled):active, -.btn-info:not(:disabled):not(.disabled).active, -.show > .btn-info.dropdown-toggle { - color: #fff; - background-color: #21b9bb; - border-color: #21b9bb; -} -.btn-info:not(:disabled):not(.disabled):active:focus, -.btn-info:not(:disabled):not(.disabled).active:focus, -.show > .btn-info.dropdown-toggle:focus { - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); -} -.btn-default { - color: inherit; - background: white; - border: 1px solid #e7eaec; -} -.btn-default:hover, -.btn-default:focus, -.btn-default:active, -.btn-default.active, -.open .dropdown-toggle.btn-default, -.btn-default:active:focus, -.btn-default:active:hover, -.btn-default.active:hover, -.btn-default.active:focus { - color: inherit; - border: 1px solid #d2d2d2; -} -.btn-default:active, -.btn-default.active, -.open .dropdown-toggle.btn-default { - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15) inset; -} -.btn-default.disabled, -.btn-default.disabled:hover, -.btn-default.disabled:focus, -.btn-default.disabled:active, -.btn-default.disabled.active, -.btn-default[disabled], -.btn-default[disabled]:hover, -.btn-default[disabled]:focus, -.btn-default[disabled]:active, -.btn-default.active[disabled], -fieldset[disabled] .btn-default, -fieldset[disabled] .btn-default:hover, -fieldset[disabled] .btn-default:focus, -fieldset[disabled] .btn-default:active, -fieldset[disabled] .btn-default.active { - color: #cacaca; -} -.btn-warning { - color: #ffffff; - background-color: #f8ac59; - border-color: #f8ac59; -} -.btn-warning:hover, -.btn-warning:focus, -.btn-warning.focus { - color: #ffffff; - background-color: #f7a54a; - border-color: #f7a54a; -} -.btn-warning.disabled, -.btn-warning:disabled { - color: #ffffff; - background-color: #f7a54a; - border-color: #f7a54a; -} -.btn-warning:not(:disabled):not(.disabled):active, -.btn-warning:not(:disabled):not(.disabled).active, -.show > .btn-warning.dropdown-toggle { - color: #ffffff; - background-color: #f7a54a; - border-color: #f7a54a; -} -.btn-warning:not(:disabled):not(.disabled):active:focus, -.btn-warning:not(:disabled):not(.disabled).active:focus, -.show > .btn-warning.dropdown-toggle:focus { - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); -} -.btn-danger { - color: #fff; - background-color: #ed5565; - border-color: #ed5565; -} -.btn-danger:hover, -.btn-danger:focus, -.btn-danger.focus { - color: #fff; - background-color: #ec4758; - border-color: #ec4758; -} -.btn-danger.disabled, -.btn-danger:disabled { - color: #fff; - background-color: #ec4758; - border-color: #ec4758; -} -.btn-danger:not(:disabled):not(.disabled):active, -.btn-danger:not(:disabled):not(.disabled).active, -.show > .btn-danger.dropdown-toggle { - color: #fff; - background-color: #ec4758; - border-color: #ec4758; -} -.btn-danger:not(:disabled):not(.disabled):active:focus, -.btn-danger:not(:disabled):not(.disabled).active:focus, -.show > .btn-danger.dropdown-toggle:focus { - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); -} -.btn-link { - color: inherit; -} -.btn-link:hover, -.btn-link:focus, -.btn-link:active, -.btn-link.active, -.open .dropdown-toggle.btn-link { - color: #1ab394; - text-decoration: none; -} -.btn-link:active, -.btn-link.active, -.open .dropdown-toggle.btn-link { - background-image: none; - box-shadow: none; -} -.btn-link.disabled, -.btn-link.disabled:hover, -.btn-link.disabled:focus, -.btn-link.disabled:active, -.btn-link.disabled.active, -.btn-link[disabled], -.btn-link[disabled]:hover, -.btn-link[disabled]:focus, -.btn-link[disabled]:active, -.btn-link.active[disabled], -fieldset[disabled] .btn-link, -fieldset[disabled] .btn-link:hover, -fieldset[disabled] .btn-link:focus, -fieldset[disabled] .btn-link:active, -fieldset[disabled] .btn-link.active { - color: #cacaca; -} -.btn-white { - color: inherit; - background: white; - border: 1px solid #e7eaec; -} -.btn-white:hover, -.btn-white:focus, -.btn-white:active, -.btn-white.active, -.open .dropdown-toggle.btn-white, -.btn-white:active:focus, -.btn-white:active:hover, -.btn-white.active:hover, -.btn-white.active:focus { - color: inherit; - border: 1px solid #d2d2d2; -} -.btn-white:active, -.btn-white.active { - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15) inset; -} -.btn-white:active, -.btn-white.active, -.open .dropdown-toggle.btn-white { - background-image: none; -} -.btn-white.disabled, -.btn-white.disabled:hover, -.btn-white.disabled:focus, -.btn-white.disabled:active, -.btn-white.disabled.active, -.btn-white[disabled], -.btn-white[disabled]:hover, -.btn-white[disabled]:focus, -.btn-white[disabled]:active, -.btn-white.active[disabled], -fieldset[disabled] .btn-white, -fieldset[disabled] .btn-white:hover, -fieldset[disabled] .btn-white:focus, -fieldset[disabled] .btn-white:active, -fieldset[disabled] .btn-white.active { - color: #cacaca; -} -.form-control, -.form-control:focus, -.has-error .form-control:focus, -.has-success .form-control:focus, -.has-warning .form-control:focus, -.navbar-collapse, -.navbar-form, -.navbar-form-custom .form-control:focus, -.navbar-form-custom .form-control:hover, -.open .btn.dropdown-toggle, -.panel, -.popover, -.progress, -.progress-bar { - box-shadow: none; -} -.btn-outline { - color: inherit; - background-color: transparent; - transition: all .5s; -} -.btn-rounded { - border-radius: 50px; -} -.btn-large-dim { - width: 90px; - height: 90px; - font-size: 42px; -} -button.dim { - display: inline-block; - text-decoration: none; - text-transform: uppercase; - text-align: center; - padding-top: 6px; - margin-right: 10px; - position: relative; - cursor: pointer; - border-radius: 5px; - font-weight: 600; - margin-bottom: 20px !important; -} -button.dim:active { - top: 3px; -} -button.btn-primary.dim { - box-shadow: inset 0 0 0 #16987e, 0 5px 0 0 #16987e, 0 10px 5px #999999 !important; -} -button.btn-primary.dim:active { - box-shadow: inset 0 0 0 #16987e, 0 2px 0 0 #16987e, 0 5px 3px #999999 !important; -} -button.btn-default.dim { - box-shadow: inset 0 0 0 #b3b3b3, 0 5px 0 0 #b3b3b3, 0 10px 5px #999999 !important; -} -button.btn-default.dim:active { - box-shadow: inset 0 0 0 #b3b3b3, 0 2px 0 0 #b3b3b3, 0 5px 3px #999999 !important; -} -button.btn-warning.dim { - box-shadow: inset 0 0 0 #f79d3c, 0 5px 0 0 #f79d3c, 0 10px 5px #999999 !important; -} -button.btn-warning.dim:active { - box-shadow: inset 0 0 0 #f79d3c, 0 2px 0 0 #f79d3c, 0 5px 3px #999999 !important; -} -button.btn-info.dim { - box-shadow: inset 0 0 0 #1eacae, 0 5px 0 0 #1eacae, 0 10px 5px #999999 !important; -} -button.btn-info.dim:active { - box-shadow: inset 0 0 0 #1eacae, 0 2px 0 0 #1eacae, 0 5px 3px #999999 !important; -} -button.btn-success.dim { - box-shadow: inset 0 0 0 #1872ab, 0 5px 0 0 #1872ab, 0 10px 5px #999999 !important; -} -button.btn-success.dim:active { - box-shadow: inset 0 0 0 #1872ab, 0 2px 0 0 #1872ab, 0 5px 3px #999999 !important; -} -button.btn-danger.dim { - box-shadow: inset 0 0 0 #ea394c, 0 5px 0 0 #ea394c, 0 10px 5px #999999 !important; -} -button.btn-danger.dim:active { - box-shadow: inset 0 0 0 #ea394c, 0 2px 0 0 #ea394c, 0 5px 3px #999999 !important; -} -button.dim:before { - font-size: 50px; - line-height: 1em; - font-weight: normal; - color: #fff; - display: block; - padding-top: 10px; -} -button.dim:active:before { - top: 7px; - font-size: 50px; -} -.btn:focus { - outline: none !important; -} -.label { - background-color: #d1dade; - color: #5e5e5e; - font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; - font-weight: 600; - padding: 3px 8px; - text-shadow: none; - border-radius: 0.25em; - line-height: 1; - white-space: nowrap; -} -.nav .label, -.ibox .label { - font-size: 10px; -} -.badge { - background-color: #d1dade; - color: #5e5e5e; - font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; - font-size: 11px; - font-weight: 600; - padding-bottom: 4px; - padding-left: 6px; - padding-right: 6px; - text-shadow: none; - white-space: nowrap; -} -.label-primary, -.badge-primary { - background-color: #1ab394; - color: #FFFFFF; -} -.label-success, -.badge-success { - background-color: #1c84c6; - color: #FFFFFF; -} -.label-warning, -.badge-warning { - background-color: #f8ac59; - color: #FFFFFF; -} -.label-warning-light, -.badge-warning-light { - background-color: #f8ac59; - color: #ffffff; -} -.label-danger, -.badge-danger { - background-color: #ed5565; - color: #FFFFFF; -} -.label-info, -.badge-info { - background-color: #23c6c8; - color: #FFFFFF; -} -.label-inverse, -.badge-inverse { - background-color: #262626; - color: #FFFFFF; -} -.label-white, -.badge-white { - background-color: #FFFFFF; - color: #5E5E5E; -} -.label-white, -.badge-disable { - background-color: #2A2E36; - color: #8B91A0; -} -/* TOOGLE SWICH */ -.onoffswitch { - position: relative; - width: 64px; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; -} -.onoffswitch-checkbox { - display: none; -} -.onoffswitch-label { - display: block; - overflow: hidden; - cursor: pointer; - border: 2px solid #1ab394; - border-radius: 2px; -} -.onoffswitch-inner { - width: 200%; - margin-left: -100%; - -moz-transition: margin 0.3s ease-in 0s; - -webkit-transition: margin 0.3s ease-in 0s; - -o-transition: margin 0.3s ease-in 0s; - transition: margin 0.3s ease-in 0s; -} -.onoffswitch-inner:before, -.onoffswitch-inner:after { - float: left; - width: 50%; - height: 20px; - padding: 0; - line-height: 20px; - font-size: 12px; - color: white; - font-family: Trebuchet, Arial, sans-serif; - font-weight: bold; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; -} -.onoffswitch-inner:before { - content: "ON"; - padding-left: 10px; - background-color: #1ab394; - color: #FFFFFF; -} -.onoffswitch-inner:after { - content: "OFF"; - padding-right: 10px; - background-color: #FFFFFF; - color: #999999; - text-align: right; -} -.onoffswitch-switch { - width: 20px; - margin: 0; - background: #FFFFFF; - border: 2px solid #1ab394; - border-radius: 2px; - position: absolute; - top: 0; - bottom: 0; - right: 44px; - -moz-transition: all 0.3s ease-in 0s; - -webkit-transition: all 0.3s ease-in 0s; - -o-transition: all 0.3s ease-in 0s; - transition: all 0.3s ease-in 0s; -} -.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner { - margin-left: 0; -} -.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch { - right: 0; -} -.onoffswitch-checkbox:disabled + .onoffswitch-label .onoffswitch-inner:before { - background-color: #919191; -} -.onoffswitch-checkbox:disabled + .onoffswitch-label, -.onoffswitch-checkbox:disabled + .onoffswitch-label .onoffswitch-switch { - border-color: #919191; -} -/* CHOSEN PLUGIN */ -.chosen-container-single .chosen-single { - background: #ffffff; - box-shadow: none; - -moz-box-sizing: border-box; - border-radius: 2px; - cursor: text; - height: auto !important; - margin: 0; - min-height: 30px; - overflow: hidden; - padding: 4px 12px; - position: relative; - width: 100%; -} -.chosen-container-multi .chosen-choices li.search-choice { - background: #f1f1f1; - border: 1px solid #e5e6e7; - border-radius: 2px; - box-shadow: none; - color: #333333; - cursor: default; - line-height: 13px; - margin: 3px 0 3px 5px; - padding: 3px 20px 3px 5px; - position: relative; -} -/* Tags Input Plugin */ -.bootstrap-tagsinput { - border: 1px solid #e5e6e7; - box-shadow: none; -} -/* PAGINATIN */ -.pagination > .active > a, -.pagination > .active > span, -.pagination > .active > a:hover, -.pagination > .active > span:hover, -.pagination > .active > a:focus, -.pagination > .active > span:focus { - border-color: #DDDDDD; - cursor: default; - z-index: 2; -} -.pagination > li > a, -.pagination > li > span { - background-color: #FFFFFF; - border: 1px solid #DDDDDD; - color: inherit; - float: left; - line-height: 1.42857; - margin-left: -1px; - padding: 4px 10px; - position: relative; - text-decoration: none; -} -.page-item.active .page-link { - background-color: #1ab394; - border-color: #1ab394; -} -.page-link:focus { - box-shadow: none; -} -.page-link:hover { - color: #676a6c; -} -.pagination .footable-page.active a { - background-color: #1ab394; - border-color: #1ab394; - color: white; -} -/* TOOLTIPS */ -.tooltip-inner { - background-color: #2f4050; -} -.tooltip.top .tooltip-arrow { - border-top-color: #2f4050; -} -.tooltip.right .tooltip-arrow { - border-right-color: #2f4050; -} -.tooltip.bottom .tooltip-arrow { - border-bottom-color: #2f4050; -} -.tooltip.left .tooltip-arrow { - border-left-color: #2f4050; -} -/* EASY PIE CHART*/ -.easypiechart { - position: relative; - text-align: center; -} -.easypiechart .h2 { - margin-left: 10px; - margin-top: 10px; - display: inline-block; -} -.easypiechart canvas { - top: 0; - left: 0; -} -.easypiechart .easypie-text { - line-height: 1; - position: absolute; - top: 33px; - width: 100%; - z-index: 1; -} -.easypiechart img { - margin-top: -4px; -} -.jqstooltip { - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; -} -/* FULLCALENDAR */ -.fc-state-default { - background-color: #ffffff; - background-image: none; - background-repeat: repeat-x; - box-shadow: none; - color: #333333; - text-shadow: none; -} -.fc-state-default { - border: 1px solid; -} -.fc-button { - color: inherit; - border: 1px solid #e7eaec; - cursor: pointer; - display: inline-block; - height: 1.9em; - line-height: 1.9em; - overflow: hidden; - padding: 0 0.6em; - position: relative; - white-space: nowrap; -} -.fc-state-active { - background-color: #1ab394; - border-color: #1ab394; - color: #ffffff; -} -.fc-header-title h2 { - font-size: 16px; - font-weight: 600; - color: inherit; -} -.fc-content .fc-widget-header, -.fc-content .fc-widget-content { - border-color: #e7eaec; - font-weight: normal; -} -.fc-border-separate tbody { - background-color: #F8F8F8; -} -.fc-state-highlight { - background: none repeat scroll 0 0 #FCF8E3; -} -.external-event { - padding: 5px 10px; - border-radius: 2px; - cursor: pointer; - margin-bottom: 5px; -} -.fc-ltr .fc-event-hori.fc-event-end, -.fc-rtl .fc-event-hori.fc-event-start { - border-radius: 2px; -} -.fc-event, -.fc-agenda .fc-event-time, -.fc-event a { - padding: 4px 6px; - background-color: #1ab394; - /* background color */ - border-color: #1ab394; - /* border color */ -} -.fc-event-time, -.fc-event-title { - color: #717171; - padding: 0 1px; -} -.ui-calendar .fc-event-time, -.ui-calendar .fc-event-title { - color: #fff; -} -.fc-event-container a.fc-event { - color: #fff; -} -/* Chat */ -.chat-activity-list .chat-element { - border-bottom: 1px solid #e7eaec; -} -.chat-element:first-child { - margin-top: 0; -} -.chat-element { - padding-bottom: 15px; -} -.chat-element, -.chat-element .media { - margin-top: 15px; -} -.chat-element, -.media-body { - overflow: hidden; -} -.chat-element .media-body { - display: block; - width: auto; -} -.chat-element > .float-left { - margin-right: 10px; -} -.chat-element img.rounded-circle, -.dropdown-messages-box img.rounded-circle { - width: 38px; - height: 38px; -} -.chat-element .well { - border: 1px solid #e7eaec; - box-shadow: none; - margin-top: 10px; - margin-bottom: 5px; - padding: 10px 20px; - font-size: 11px; - line-height: 16px; -} -.chat-element .actions { - margin-top: 10px; -} -.chat-element .photos { - margin: 10px 0; -} -.right.chat-element > .float-right { - margin-left: 10px; -} -.chat-photo { - max-height: 180px; - border-radius: 4px; - overflow: hidden; - margin-right: 10px; - margin-bottom: 10px; -} -.chat { - margin: 0; - padding: 0; - list-style: none; -} -.chat li { - margin-bottom: 10px; - padding-bottom: 5px; - border-bottom: 1px dotted #B3A9A9; -} -.chat li.left .chat-body { - margin-left: 60px; -} -.chat li.right .chat-body { - margin-right: 60px; -} -.chat li .chat-body p { - margin: 0; - color: #777777; -} -.panel .slidedown .glyphicon, -.chat .glyphicon { - margin-right: 5px; -} -.chat-panel .panel-body { - height: 350px; - overflow-y: scroll; -} -/* LIST GROUP */ -a.list-group-item.active, -a.list-group-item.active:hover, -a.list-group-item.active:focus { - background-color: #1ab394; - border-color: #1ab394; - color: #FFFFFF; - z-index: 2; -} -.list-group-item-heading { - margin-top: 10px; -} -.list-group-item-text { - margin: 0 0 10px; - color: inherit; - font-size: 12px; - line-height: inherit; -} -.no-padding .list-group-item { - border-left: none; - border-right: none; - border-bottom: none; -} -.no-padding .list-group-item:first-child { - border-left: none; - border-right: none; - border-bottom: none; - border-top: none; -} -.no-padding .list-group { - margin-bottom: 0; -} -.list-group-item { - background-color: inherit; - border: 1px solid #e7eaec; - display: block; - margin-bottom: -1px; - padding: 10px 15px; - position: relative; -} -.elements-list .list-group-item { - border-left: none; - border-right: none; - padding: 0; -} -.elements-list .list-group-item:first-child { - border-left: none; - border-right: none; - border-top: none !important; -} -.elements-list .list-group { - margin-bottom: 0; -} -.elements-list a { - color: inherit; -} -.elements-list .list-group-item a.active, -.elements-list .list-group-item a:hover { - background: #f3f3f4; - color: inherit; - border-color: #e7eaec; - border-radius: 0; -} -.elements-list li.active { - transition: none; -} -.elements-list .nav-link { - padding: 15px 25px; -} -.element-detail-box { - padding: 25px; -} -/* FLOT CHART */ -.flot-chart { - display: block; - height: 200px; -} -.widget .flot-chart.dashboard-chart { - display: block; - height: 120px; - margin-top: 40px; -} -.flot-chart.dashboard-chart { - display: block; - height: 180px; - margin-top: 40px; -} -.flot-chart-content { - width: 100%; - height: 100%; -} -.flot-chart-pie-content { - width: 200px; - height: 200px; - margin: auto; -} -.jqstooltip { - position: absolute; - display: block; - left: 0; - top: 0; - visibility: hidden; - background: #2b303a; - background-color: rgba(43, 48, 58, 0.8); - color: white; - text-align: left; - white-space: nowrap; - z-index: 10000; - padding: 5px 5px 5px 5px; - min-height: 22px; - border-radius: 3px; -} -.jqsfield { - color: white; - text-align: left; -} -.fh-150 { - height: 150px; -} -.fh-200 { - height: 200px; -} -.h-150 { - min-height: 150px; -} -.h-200 { - min-height: 200px; -} -.h-300 { - min-height: 300px; -} -.w-150 { - min-width: 150px; -} -.w-200 { - min-width: 200px; -} -.w-300 { - min-width: 300px; -} -.legendLabel { - padding-left: 5px; -} -.stat-list li:first-child { - margin-top: 0; -} -.stat-list { - list-style: none; - padding: 0; - margin: 0; -} -.stat-percent { - float: right; -} -.stat-list li { - margin-top: 15px; - position: relative; -} -/* DATATABLES */ -table.dataTable thead .sorting, -table.dataTable thead .sorting_asc:after, -table.dataTable thead .sorting_desc, -table.dataTable thead .sorting_asc_disabled, -table.dataTable thead .sorting_desc_disabled { - background: transparent; -} -.dataTables_wrapper { - padding-bottom: 30px; -} -.dataTables_length { - float: left; -} -.dataTables_filter label { - margin-right: 5px; -} -.html5buttons { - float: right; -} -.html5buttons a { - border: 1px solid #e7eaec; - background: #fff; - color: #676a6c; - box-shadow: none; - padding: 6px 8px; - font-size: 12px; -} -.html5buttons a:hover, -.html5buttons a:focus:active { - background-color: #eee; - color: inherit; - border-color: #d2d2d2; -} -div.dt-button-info { - z-index: 100; -} -@media (max-width: 768px) { - .html5buttons { - float: none; - margin-top: 10px; - } - .dataTables_length { - float: none; - } -} -/* CIRCLE */ -.img-circle { - border-radius: 50%; -} -.btn-circle { - width: 30px; - height: 30px; - padding: 6px 0; - border-radius: 15px; - text-align: center; - font-size: 12px; - line-height: 1.428571429; -} -.btn-circle.btn-lg { - width: 50px; - height: 50px; - padding: 10px 16px; - border-radius: 25px; - font-size: 18px; - line-height: 1.33; -} -.btn-circle.btn-xl { - width: 70px; - height: 70px; - padding: 10px 16px; - border-radius: 35px; - font-size: 24px; - line-height: 1.33; -} -.show-grid [class^="col-"] { - padding-top: 10px; - padding-bottom: 10px; - border: 1px solid #ddd; - background-color: #eee !important; -} -.show-grid { - margin: 15px 0; -} -/* ANIMATION */ -.css-animation-box h1 { - font-size: 44px; -} -.animation-efect-links a { - padding: 4px 6px; - font-size: 12px; -} -#animation_box { - background-color: #f9f8f8; - border-radius: 16px; - width: 80%; - margin: 0 auto; - padding-top: 80px; -} -.animation-text-box { - position: absolute; - margin-top: 40px; - left: 50%; - margin-left: -100px; - width: 200px; -} -.animation-text-info { - position: absolute; - margin-top: -60px; - left: 50%; - margin-left: -100px; - width: 200px; - font-size: 10px; -} -.animation-text-box h2 { - font-size: 54px; - font-weight: 600; - margin-bottom: 5px; -} -.animation-text-box p { - font-size: 12px; - text-transform: uppercase; -} -/* PEACE */ -.pace { - -webkit-pointer-events: none; - pointer-events: none; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; -} -.pace-inactive { - display: none; -} -.pace .pace-progress { - background: #1ab394; - position: fixed; - z-index: 2040; - top: 0; - right: 100%; - width: 100%; - height: 2px; -} -.pace-inactive { - display: none; -} -/* WIDGETS */ -.widget { - border-radius: 5px; - padding: 15px 20px; - margin-bottom: 10px; - margin-top: 10px; -} -.widget.style1 h2 { - font-size: 30px; -} -.widget h2, -.widget h3 { - margin-top: 5px; - margin-bottom: 0; -} -.widget-text-box { - padding: 20px; - border: 1px solid #e7eaec; - background: #ffffff; -} -.widget-head-color-box { - border-radius: 5px 5px 0 0; - margin-top: 10px; -} -.widget .flot-chart { - height: 100px; -} -.vertical-align div { - display: inline-block; - vertical-align: middle; -} -.vertical-align h2, -.vertical-align h3 { - margin: 0; -} -.todo-list { - list-style: none outside none; - margin: 0; - padding: 0; - font-size: 14px; -} -.todo-list.small-list { - font-size: 12px; -} -.todo-list.small-list > li { - background: #f3f3f4; - border-left: none; - border-right: none; - border-radius: 4px; - color: inherit; - margin-bottom: 2px; - padding: 6px 6px 6px 12px; -} -.todo-list.small-list .btn-xs, -.todo-list.small-list .btn-group-xs > .btn { - border-radius: 5px; - font-size: 10px; - line-height: 1.5; - padding: 1px 2px 1px 5px; -} -.todo-list > li { - background: #f3f3f4; - border-left: 6px solid #e7eaec; - border-right: 6px solid #e7eaec; - border-radius: 4px; - color: inherit; - margin-bottom: 2px; - padding: 10px; -} -.todo-list .handle { - cursor: move; - display: inline-block; - font-size: 16px; - margin: 0 5px; -} -.todo-list > li .label { - font-size: 9px; - margin-left: 10px; -} -.check-link { - font-size: 16px; -} -.todo-completed { - text-decoration: line-through; -} -.geo-statistic h1 { - font-size: 36px; - margin-bottom: 0; -} -.glyphicon.fa { - font-family: "FontAwesome"; -} -/* INPUTS */ -.inline { - display: inline-block !important; -} -.input-s-sm { - width: 120px; -} -.input-s { - width: 200px; -} -.form-control { - font-size: 0.9rem; -} -select.form-control:not([size]):not([multiple]) { - height: 2.05rem; -} -.input-sm, -.form-control-sm { - height: 31px; -} -.input-s-lg { - width: 250px; -} -.i-checks { - padding-left: 0; -} -.form-control, -.single-line { - background-color: #FFFFFF; - background-image: none; - border: 1px solid #e5e6e7; - border-radius: 1px; - color: inherit; - display: block; - padding: 6px 12px; - transition: border-color 0.15s ease-in-out 0s, box-shadow 0.15s ease-in-out 0s; - width: 100%; -} -.form-control:focus, -.single-line:focus { - border-color: #1ab394; -} -.has-success .form-control, -.has-success .form-control:focus { - border-color: #1ab394; -} -.has-warning .form-control, -.has-warning .form-control:focus { - border-color: #f8ac59; -} -.has-error .form-control, -.has-error .form-control:focus { - border-color: #ed5565; -} -.has-success .control-label { - color: #1ab394; -} -.has-warning .control-label { - color: #f8ac59; -} -.has-error .control-label { - color: #ed5565; -} -.input-group-addon { - background-color: #fff; - border: 1px solid #E5E6E7; - border-radius: 1px; - color: inherit; - font-size: 14px; - font-weight: 400; - line-height: 1; - padding: 9px 12px 4px 12px; - text-align: center; -} -.input-daterange .input-group-addon { - margin: 0; -} -.input-group.date .input-group-addon { - border-right: 0; -} -.spinner-buttons.input-group-btn .btn-xs { - line-height: 1.13; -} -.spinner-buttons.input-group-btn { - width: 20%; -} -.noUi-connect { - background: none repeat scroll 0 0 #1ab394; - box-shadow: none; -} -.slider_red .noUi-connect { - background: none repeat scroll 0 0 #ed5565; - box-shadow: none; -} -/* UI Sortable */ -.ui-sortable .ibox-title { - cursor: move; -} -.ui-sortable-placeholder { - border: 1px dashed #cecece !important; - visibility: visible !important; - background: #e7eaec; -} -.ibox.ui-sortable-placeholder { - margin: 0 0 23px !important; -} -/* SWITCHES */ -.onoffswitch { - position: relative; - width: 54px; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; -} -.onoffswitch-checkbox { - display: none; -} -.onoffswitch-label { - display: block; - overflow: hidden; - cursor: pointer; - border: 2px solid #1AB394; - border-radius: 3px; -} -.onoffswitch-inner { - display: block; - width: 200%; - margin-left: -100%; - -moz-transition: margin 0.3s ease-in 0s; - -webkit-transition: margin 0.3s ease-in 0s; - -o-transition: margin 0.3s ease-in 0s; - transition: margin 0.3s ease-in 0s; -} -.onoffswitch-inner:before, -.onoffswitch-inner:after { - display: block; - float: left; - width: 50%; - height: 16px; - padding: 0; - line-height: 16px; - font-size: 10px; - color: white; - font-family: Trebuchet, Arial, sans-serif; - font-weight: bold; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; -} -.onoffswitch-inner:before { - content: "ON"; - padding-left: 7px; - background-color: #1AB394; - color: #FFFFFF; -} -.onoffswitch-inner:after { - content: "OFF"; - padding-right: 7px; - background-color: #FFFFFF; - color: #919191; - text-align: right; -} -.onoffswitch-switch { - display: block; - width: 18px; - margin: 0; - background: #FFFFFF; - border: 2px solid #1AB394; - border-radius: 3px; - position: absolute; - top: 0; - bottom: 0; - right: 36px; - -moz-transition: all 0.3s ease-in 0s; - -webkit-transition: all 0.3s ease-in 0s; - -o-transition: all 0.3s ease-in 0s; - transition: all 0.3s ease-in 0s; -} -.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner { - margin-left: 0; -} -.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch { - right: 0; -} -/* jqGrid */ -.ui-jqgrid { - -moz-box-sizing: content-box; -} -.ui-jqgrid-btable { - border-collapse: separate; -} -.ui-jqgrid-htable { - border-collapse: separate; -} -.ui-jqgrid-titlebar { - height: 40px; - line-height: 15px; - color: #676a6c; - background-color: #F9F9F9; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); -} -.ui-jqgrid .ui-jqgrid-title { - float: left; - margin: 1.1em 1em 0.2em; -} -.ui-jqgrid .ui-jqgrid-titlebar { - position: relative; - border-left: 0 solid; - border-right: 0 solid; - border-top: 0 solid; -} -.ui-widget-header { - background: none; - background-image: none; - background-color: #f5f5f6; - text-transform: uppercase; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.ui-jqgrid tr.ui-row-ltr td { - border-right-color: inherit; - border-right-style: solid; - border-right-width: 1px; - text-align: left; - border-color: #DDDDDD; - background-color: inherit; -} -.ui-search-toolbar input[type="text"] { - font-size: 12px; - height: 15px; - border: 1px solid #CCCCCC; - border-radius: 0; -} -.ui-state-default, -.ui-widget-content .ui-state-default, -.ui-widget-header .ui-state-default { - background: #F9F9F9; - border: 1px solid #DDDDDD; - line-height: 15px; - font-weight: bold; - color: #676a6c; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); -} -.ui-widget-content { - box-sizing: content-box; -} -.ui-icon-triangle-1-n { - background-position: 1px -16px; -} -.ui-jqgrid tr.ui-search-toolbar th { - border-top-width: 0 !important; - border-top-color: inherit !important; - border-top-style: ridge !important; -} -.ui-state-hover, -.ui-widget-content .ui-state-hover, -.ui-state-focus, -.ui-widget-content .ui-state-focus, -.ui-widget-header .ui-state-focus { - background: #f5f5f5; - border-collapse: separate; -} -.ui-state-highlight, -.ui-widget-content .ui-state-highlight, -.ui-widget-header .ui-state-highlight { - background: #f2fbff; -} -.ui-state-active, -.ui-widget-content .ui-state-active, -.ui-widget-header .ui-state-active { - border: 1px solid #dddddd; - background: #ffffff; - font-weight: normal; - color: #212121; -} -.ui-jqgrid .ui-pg-input { - font-size: inherit; - width: 50px; - border: 1px solid #CCCCCC; - height: 15px; -} -.ui-jqgrid .ui-pg-selbox { - display: block; - font-size: 1em; - height: 25px; - line-height: 18px; - margin: 0; - width: auto; -} -.ui-jqgrid .ui-pager-control { - position: relative; -} -.ui-jqgrid .ui-jqgrid-pager { - height: 32px; - position: relative; -} -.ui-pg-table .navtable .ui-corner-all { - border-radius: 0; -} -.ui-jqgrid .ui-pg-button:hover { - padding: 1px; - border: 0; -} -.ui-jqgrid .loading { - position: absolute; - top: 45%; - left: 45%; - width: auto; - height: auto; - z-index: 101; - padding: 6px; - margin: 5px; - text-align: center; - font-weight: bold; - display: none; - border-width: 2px !important; - font-size: 11px; -} -.ui-jqgrid .form-control { - height: 10px; - width: auto; - display: inline; - padding: 10px 12px; -} -.ui-jqgrid-pager { - height: 32px; -} -.ui-corner-all, -.ui-corner-top, -.ui-corner-left, -.ui-corner-tl { - border-top-left-radius: 0; -} -.ui-corner-all, -.ui-corner-top, -.ui-corner-right, -.ui-corner-tr { - border-top-right-radius: 0; -} -.ui-corner-all, -.ui-corner-bottom, -.ui-corner-left, -.ui-corner-bl { - border-bottom-left-radius: 0; -} -.ui-corner-all, -.ui-corner-bottom, -.ui-corner-right, -.ui-corner-br { - border-bottom-right-radius: 0; -} -.ui-widget-content { - border: 1px solid #ddd; -} -.ui-jqgrid .ui-jqgrid-titlebar { - padding: 0; -} -.ui-jqgrid .ui-jqgrid-titlebar { - border-bottom: 1px solid #ddd; -} -.ui-jqgrid tr.jqgrow td { - padding: 6px; -} -.ui-jqdialog .ui-jqdialog-titlebar { - padding: 10px 10px; -} -.ui-jqdialog .ui-jqdialog-title { - float: none !important; -} -.ui-jqdialog > .ui-resizable-se { - position: absolute; -} -/* Nestable list */ -.dd { - position: relative; - display: block; - margin: 0; - padding: 0; - list-style: none; - font-size: 13px; - line-height: 20px; -} -.dd-list { - display: block; - position: relative; - margin: 0; - padding: 0; - list-style: none; -} -.dd-list .dd-list { - padding-left: 30px; -} -.dd-collapsed .dd-list { - display: none; -} -.dd-item, -.dd-empty, -.dd-placeholder { - display: block; - position: relative; - margin: 0; - padding: 0; - min-height: 20px; - font-size: 13px; - line-height: 20px; -} -.dd-handle { - display: block; - margin: 5px 0; - padding: 5px 10px; - color: #333; - text-decoration: none; - border: 1px solid #e7eaec; - background: #f5f5f5; - -webkit-border-radius: 3px; - border-radius: 3px; - box-sizing: border-box; - -moz-box-sizing: border-box; -} -.dd-handle span { - font-weight: bold; -} -.dd-handle:hover { - background: #f0f0f0; - cursor: pointer; - font-weight: bold; -} -.dd-item > button { - display: block; - position: relative; - cursor: pointer; - float: left; - width: 25px; - height: 20px; - margin: 5px 0; - padding: 0; - text-indent: 100%; - white-space: nowrap; - overflow: hidden; - border: 0; - background: transparent; - font-size: 12px; - line-height: 1; - text-align: center; - font-weight: bold; -} -.dd-item > button:before { - content: '+'; - display: block; - position: absolute; - width: 100%; - text-align: center; - text-indent: 0; -} -.dd-item > button[data-action="collapse"]:before { - content: '-'; -} -#nestable2 .dd-item > button { - font-family: FontAwesome; - height: 34px; - width: 33px; - color: #c1c1c1; -} -#nestable2 .dd-item > button:before { - content: "\f067"; -} -#nestable2 .dd-item > button[data-action="collapse"]:before { - content: "\f068"; -} -.dd-placeholder, -.dd-empty { - margin: 5px 0; - padding: 0; - min-height: 30px; - background: #f2fbff; - border: 1px dashed #b6bcbf; - box-sizing: border-box; - -moz-box-sizing: border-box; -} -.dd-empty { - border: 1px dashed #bbb; - min-height: 100px; - background-color: #e5e5e5; - background-image: -webkit-linear-gradient(45deg, #ffffff 25%, transparent 25%, transparent 75%, #ffffff 75%, #ffffff), -webkit-linear-gradient(45deg, #ffffff 25%, transparent 25%, transparent 75%, #ffffff 75%, #ffffff); - background-image: -moz-linear-gradient(45deg, #ffffff 25%, transparent 25%, transparent 75%, #ffffff 75%, #ffffff), -moz-linear-gradient(45deg, #ffffff 25%, transparent 25%, transparent 75%, #ffffff 75%, #ffffff); - background-image: linear-gradient(45deg, #ffffff 25%, transparent 25%, transparent 75%, #ffffff 75%, #ffffff), linear-gradient(45deg, #ffffff 25%, transparent 25%, transparent 75%, #ffffff 75%, #ffffff); - background-size: 60px 60px; - background-position: 0 0, 30px 30px; -} -.dd-dragel { - position: absolute; - z-index: 9999; - pointer-events: none; -} -.dd-dragel > .dd-item .dd-handle { - margin-top: 0; -} -.dd-dragel .dd-handle { - -webkit-box-shadow: 2px 4px 6px 0 rgba(0, 0, 0, 0.1); - box-shadow: 2px 4px 6px 0 rgba(0, 0, 0, 0.1); -} -/** -* Nestable Extras -*/ -.nestable-lists { - display: block; - clear: both; - padding: 30px 0; - width: 100%; - border: 0; - border-top: 2px solid #ddd; - border-bottom: 2px solid #ddd; -} -#nestable-menu { - padding: 0; - margin: 10px 0 20px 0; -} -#nestable-output, -#nestable2-output { - width: 100%; - font-size: 0.75em; - line-height: 1.333333em; - font-family: open sans, lucida grande, lucida sans unicode, helvetica, arial, sans-serif; - padding: 5px; - box-sizing: border-box; - -moz-box-sizing: border-box; -} -#nestable2 .dd-handle { - color: inherit; - border: 1px dashed #e7eaec; - background: #f3f3f4; - padding: 10px; -} -#nestable2 span.label { - margin-right: 10px; -} -#nestable-output, -#nestable2-output { - font-size: 12px; - padding: 25px; - box-sizing: border-box; - -moz-box-sizing: border-box; -} -/* CodeMirror */ -.CodeMirror { - border: 1px solid #eee; - height: auto; -} -.CodeMirror-scroll { - overflow-y: hidden; - overflow-x: auto; -} -/* Google Maps */ -.google-map { - height: 300px; -} -/* Validation */ -label.error { - color: #cc5965; - display: inline-block; - margin-left: 5px; -} -.form-control.error { - border: 1px dotted #cc5965; -} -/* ngGrid */ -.gridStyle { - border: 1px solid #d4d4d4; - width: 100%; - height: 400px; -} -.gridStyle2 { - border: 1px solid #d4d4d4; - width: 500px; - height: 300px; -} -.ngH eaderCell { - border-right: none; - border-bottom: 1px solid #e7eaec; -} -.ngCell { - border-right: none; -} -.ngTopPanel { - background: #F5F5F6; -} -.ngRow.even { - background: #f9f9f9; -} -.ngRow.selected { - background: #EBF2F1; -} -.ngRow { - border-bottom: 1px solid #e7eaec; -} -.ngCell { - background-color: transparent; -} -.ngHeaderCell { - border-right: none; -} -/* Toastr custom style */ -#toast-container > div { - -moz-box-shadow: 0 0 3px #999; - -webkit-box-shadow: 0 0 3px #999; - box-shadow: 0 0 3px #999; - opacity: .9; - -ms-filter: alpha(opacity=90); - filter: alpha(opacity=90); -} -#toast-container > :hover { - -moz-box-shadow: 0 0 4px #999; - -webkit-box-shadow: 0 0 4px #999; - box-shadow: 0 0 4px #999; - opacity: 1; - -ms-filter: alpha(opacity=100); - filter: alpha(opacity=100); - cursor: pointer; -} -.toast { - background-color: #1ab394; - border-color: #e7eaec; -} -.toast-success { - background-color: #1ab394; -} -.toast-error { - background-color: #ed5565; -} -.toast-info { - background-color: #23c6c8; -} -.toast-warning { - background-color: #f8ac59; -} -.toast-top-full-width { - margin-top: 20px; -} -.toast-bottom-full-width { - margin-bottom: 20px; -} -.toast { - z-index: 3000; -} -.toast.toast-bootstrap { - background-color: white; -} -.toast.toast-bootstrap .toast-body { - background-color: #fbfbfb; - font-size: .775rem; -} -/* Notifie */ -.cg-notify-message.inspinia-notify { - background: #fff; - padding: 0; - box-shadow: 0 0 1px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.2); - -webkit-box-shadow: 0 0 1px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.2); - -moz-box-shadow: 0 0 1px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.2); - border: none; - margin-top: 30px; - color: inherit; -} -.inspinia-notify.alert-warning { - border-left: 6px solid #f8ac59; -} -.inspinia-notify.alert-success { - border-left: 6px solid #1c84c6; -} -.inspinia-notify.alert-danger { - border-left: 6px solid #ed5565; -} -.inspinia-notify.alert-info { - border-left: 6px solid #1ab394; -} -/* Image cropper style */ -.img-container, -.img-preview { - overflow: hidden; - text-align: center; - width: 100%; -} -.img-preview-sm { - height: 130px; - width: 200px; -} -/* Forum styles */ -.forum-post-container .media { - margin: 10px 10px 10px 10px; - padding: 20px 10px 20px 10px; - border-bottom: 1px solid #f1f1f1; -} -.forum-avatar { - float: left; - margin-right: 20px; - text-align: center; - width: 110px; -} -.forum-avatar .rounded-circle { - height: 48px; - width: 48px; -} -.author-info { - color: #676a6c; - font-size: 11px; - margin-top: 5px; - text-align: center; -} -.forum-post-info { - padding: 9px 12px 6px 12px; - background: #f9f9f9; - border: 1px solid #f1f1f1; -} -.media-body > .media { - background: #f9f9f9; - border-radius: 3px; - border: 1px solid #f1f1f1; -} -.forum-post-container .media-body .photos { - margin: 10px 0; -} -.forum-photo { - max-width: 140px; - border-radius: 3px; -} -.media-body > .media .forum-avatar { - width: 70px; - margin-right: 10px; -} -.media-body > .media .forum-avatar .rounded-circle { - height: 38px; - width: 38px; -} -.mid-icon { - font-size: 66px; -} -.forum-item { - margin: 10px 0; - padding: 10px 0 20px; - border-bottom: 1px solid #f1f1f1; -} -.views-number { - font-size: 24px; - line-height: 18px; - font-weight: 400; -} -.forum-container, -.forum-post-container { - padding: 30px !important; -} -.forum-item small { - color: #999; -} -.forum-item .forum-sub-title { - color: #999; - margin-left: 50px; -} -.forum-title { - margin: 15px 0 15px 0; -} -.forum-info { - text-align: center; -} -.forum-desc { - color: #999; -} -.forum-icon { - float: left; - width: 30px; - margin-right: 20px; - text-align: center; -} -a.forum-item-title { - color: inherit; - display: block; - font-size: 18px; - font-weight: 600; -} -a.forum-item-title:hover { - color: inherit; -} -.forum-icon .fa { - font-size: 30px; - margin-top: 8px; - color: #9b9b9b; -} -.forum-item.active .fa { - color: #1ab394; -} -.forum-item.active a.forum-item-title { - color: #1ab394; -} -@media (max-width: 992px) { - .forum-info { - margin: 15px 0 10px 0; - /* Comment this is you want to show forum info in small devices */ - display: none; - } - .forum-desc { - float: none !important; - } -} -/* New Timeline style */ -.vertical-container { - /* this class is used to give a max-width to the element it is applied to, and center it horizontally when it reaches that max-width */ - width: 90%; - max-width: 1170px; - margin: 0 auto; -} -.vertical-container::after { - /* clearfix */ - content: ''; - display: table; - clear: both; -} -#vertical-timeline { - position: relative; - padding: 0; - margin-top: 2em; - margin-bottom: 2em; -} -#vertical-timeline::before { - content: ''; - position: absolute; - top: 0; - left: 18px; - height: 100%; - width: 4px; - background: #f1f1f1; -} -.vertical-timeline-content .btn { - float: right; -} -#vertical-timeline.light-timeline:before { - background: #e7eaec; -} -.dark-timeline .vertical-timeline-content:before { - border-color: transparent #f5f5f5 transparent transparent; -} -.dark-timeline.center-orientation .vertical-timeline-content:before { - border-color: transparent transparent transparent #f5f5f5; -} -.dark-timeline .vertical-timeline-block:nth-child(2n) .vertical-timeline-content:before, -.dark-timeline.center-orientation .vertical-timeline-block:nth-child(2n) .vertical-timeline-content:before { - border-color: transparent #f5f5f5 transparent transparent; -} -.dark-timeline .vertical-timeline-content, -.dark-timeline.center-orientation .vertical-timeline-content { - background: #f5f5f5; -} -@media only screen and (min-width: 1170px) { - #vertical-timeline.center-orientation { - margin-top: 3em; - margin-bottom: 3em; - } - #vertical-timeline.center-orientation:before { - left: 50%; - margin-left: -2px; - } -} -@media only screen and (max-width: 1170px) { - .center-orientation.dark-timeline .vertical-timeline-content:before { - border-color: transparent #f5f5f5 transparent transparent; - } -} -.vertical-timeline-block { - position: relative; - margin: 2em 0; -} -.vertical-timeline-block:after { - content: ""; - display: table; - clear: both; -} -.vertical-timeline-block:first-child { - margin-top: 0; -} -.vertical-timeline-block:last-child { - margin-bottom: 0; -} -@media only screen and (min-width: 1170px) { - .center-orientation .vertical-timeline-block { - margin: 4em 0; - } - .center-orientation .vertical-timeline-block:first-child { - margin-top: 0; - } - .center-orientation .vertical-timeline-block:last-child { - margin-bottom: 0; - } -} -.vertical-timeline-icon { - position: absolute; - top: 0; - left: 0; - width: 40px; - height: 40px; - border-radius: 50%; - font-size: 16px; - border: 3px solid #f1f1f1; - text-align: center; -} -.vertical-timeline-icon i { - display: block; - width: 24px; - height: 24px; - position: relative; - left: 50%; - top: 50%; - margin-left: -12px; - margin-top: -9px; -} -@media only screen and (min-width: 1170px) { - .center-orientation .vertical-timeline-icon { - width: 50px; - height: 50px; - left: 50%; - margin-left: -25px; - -webkit-transform: translateZ(0); - -webkit-backface-visibility: hidden; - font-size: 19px; - } - .center-orientation .vertical-timeline-icon i { - margin-left: -12px; - margin-top: -10px; - } - .center-orientation .cssanimations .vertical-timeline-icon.is-hidden { - visibility: hidden; - } -} -.vertical-timeline-content { - position: relative; - margin-left: 60px; - background: white; - border-radius: 0.25em; - padding: 1em; -} -.vertical-timeline-content:after { - content: ""; - display: table; - clear: both; -} -.vertical-timeline-content h2 { - font-weight: 400; - margin-top: 4px; -} -.vertical-timeline-content p { - margin: 1em 0; - line-height: 1.6; -} -.vertical-timeline-content .vertical-date { - float: left; - font-weight: 500; -} -.vertical-date small { - color: #1ab394; - font-weight: 400; -} -.vertical-timeline-content::before { - content: ''; - position: absolute; - top: 16px; - right: 100%; - height: 0; - width: 0; - border: 7px solid transparent; - border-right: 7px solid white; -} -@media only screen and (min-width: 768px) { - .vertical-timeline-content h2 { - font-size: 18px; - } - .vertical-timeline-content p { - font-size: 13px; - } -} -@media only screen and (min-width: 1170px) { - .center-orientation .vertical-timeline-content { - margin-left: 0; - padding: 1.6em; - width: 45%; - } - .center-orientation .vertical-timeline-content::before { - top: 24px; - left: 100%; - border-color: transparent; - border-left-color: white; - } - .center-orientation .vertical-timeline-content .btn { - float: left; - } - .center-orientation .vertical-timeline-content .vertical-date { - position: absolute; - width: 100%; - left: 122%; - top: 2px; - font-size: 14px; - } - .center-orientation .vertical-timeline-block:nth-child(even) .vertical-timeline-content { - float: right; - } - .center-orientation .vertical-timeline-block:nth-child(even) .vertical-timeline-content::before { - top: 24px; - left: auto; - right: 100%; - border-color: transparent; - border-right-color: white; - } - .center-orientation .vertical-timeline-block:nth-child(even) .vertical-timeline-content .btn { - float: right; - } - .center-orientation .vertical-timeline-block:nth-child(even) .vertical-timeline-content .vertical-date { - left: auto; - right: 122%; - text-align: right; - } - .center-orientation .cssanimations .vertical-timeline-content.is-hidden { - visibility: hidden; - } -} -/* Tabs */ -.tabs-container .panel-body { - background: #fff; - border: 1px solid #e7eaec; - border-radius: 2px; - padding: 20px; - position: relative; -} -.tabs-container .nav-tabs > li.active > a, -.tabs-container .nav-tabs > li.active > a:hover, -.tabs-container .nav-tabs > li.active > a:focus { - border: 1px solid #e7eaec; - border-bottom-color: transparent; - background-color: #fff; -} -.tabs-container .nav-tabs > li { - float: left; - margin-bottom: -1px; -} -.tabs-container .tab-pane .panel-body { - border-top: none; -} -.tabs-container .nav-tabs > li.active > a, -.tabs-container .nav-tabs > li.active > a:hover, -.tabs-container .nav-tabs > li.active > a:focus { - border: 1px solid #e7eaec; - border-bottom-color: transparent; -} -.tabs-container .nav-tabs { - border-bottom: 1px solid #e7eaec; -} -.tabs-container .tab-pane .panel-body { - border-top: none; -} -.tabs-container .tabs-left .tab-pane .panel-body, -.tabs-container .tabs-right .tab-pane .panel-body { - border-top: 1px solid #e7eaec; -} -.tabs-container .tabs-below > .nav-tabs, -.tabs-container .tabs-right > .nav-tabs, -.tabs-container .tabs-left > .nav-tabs { - border-bottom: 0; -} -.tabs-container .tabs-left .panel-body { - position: static; -} -.tabs-container .tabs-left > .nav-tabs, -.tabs-container .tabs-right > .nav-tabs { - width: 20%; -} -.tabs-container .tabs-left .panel-body { - width: 80%; - margin-left: 20%; -} -.tabs-container .tabs-right .panel-body { - width: 80%; - margin-right: 20%; -} -.tabs-container .tab-content > .tab-pane, -.tabs-container .pill-content > .pill-pane { - display: none; -} -.tabs-container .tab-content > .active, -.tabs-container .pill-content > .active { - display: block; -} -.tabs-container .tabs-below > .nav-tabs { - border-top: 1px solid #e7eaec; -} -.tabs-container .tabs-below > .nav-tabs > li { - margin-top: -1px; - margin-bottom: 0; -} -.tabs-container .tabs-below > .nav-tabs > li > a { - -webkit-border-radius: 0 0 4px 4px; - -moz-border-radius: 0 0 4px 4px; - border-radius: 0 0 4px 4px; -} -.tabs-container .tabs-below > .nav-tabs > li > a:hover, -.tabs-container .tabs-below > .nav-tabs > li > a:focus { - border-top-color: #e7eaec; - border-bottom-color: transparent; -} -.tabs-container .tabs-left > .nav-tabs > li, -.tabs-container .tabs-right > .nav-tabs > li { - float: none; - word-break: break-word; - width: 100%; -} -.tabs-container .tabs-left > .nav-tabs > li > a, -.tabs-container .tabs-right > .nav-tabs > li > a { - margin-right: 0; - margin-bottom: 3px; -} -.tabs-container .tabs-left > .nav-tabs { - float: left; - margin-right: 19px; -} -.tabs-container .tabs-left > .nav-tabs > li > a { - margin-right: -1px; - -webkit-border-radius: 4px 0 0 4px; - -moz-border-radius: 4px 0 0 4px; - border-radius: 4px 0 0 4px; -} -.tabs-container .tabs-left > .nav-tabs a.active, -.tabs-container .tabs-left > .nav-tabs a.active:hover, -.tabs-container .tabs-left > .nav-tabs a.active:focus { - border-color: #e7eaec transparent #e7eaec #e7eaec; -} -.tabs-container .tabs-right > .nav-tabs { - float: right; - margin-left: 19px; -} -.tabs-container .tabs-right > .nav-tabs > li > a { - margin-left: -1px; - -webkit-border-radius: 0 4px 4px 0; - -moz-border-radius: 0 4px 4px 0; - border-radius: 0 4px 4px 0; -} -.tabs-container .tabs-right > .nav-tabs a.active, -.tabs-container .tabs-right > .nav-tabs a.active:hover, -.tabs-container .tabs-right > .nav-tabs a.active:focus { - border-color: #e7eaec #e7eaec #e7eaec transparent; - z-index: 1; -} -.tabs-container .tabs-right > .nav-tabs li { - z-index: 1; -} -.nav-tabs .nav-link:not(.active):focus, -.nav-tabs .nav-link:not(.active):hover { - border-color: transparent; -} -@media (max-width: 767px) { - .tabs-container .nav-tabs > li { - float: none !important; - } - .tabs-container .nav-tabs > li.active > a { - border-bottom: 1px solid #e7eaec !important; - margin: 0; - } -} -/* jsvectormap */ -.jvectormap-container { - width: 100%; - height: 100%; - position: relative; - overflow: hidden; -} -.jvectormap-tip { - position: absolute; - display: none; - border: solid 1px #CDCDCD; - border-radius: 3px; - background: #292929; - color: white; - font-family: sans-serif, Verdana; - font-size: smaller; - padding: 5px; -} -.jvectormap-zoomin, -.jvectormap-zoomout, -.jvectormap-goback { - position: absolute; - left: 10px; - border-radius: 3px; - background: #1ab394; - padding: 3px; - color: white; - cursor: pointer; - line-height: 10px; - text-align: center; - box-sizing: content-box; -} -.jvectormap-zoomin, -.jvectormap-zoomout { - width: 10px; - height: 10px; -} -.jvectormap-zoomin { - top: 10px; -} -.jvectormap-zoomout { - top: 30px; -} -.jvectormap-goback { - bottom: 10px; - z-index: 1000; - padding: 6px; -} -.jvectormap-spinner { - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; - background: center no-repeat url(data:image/gif;base64,R0lGODlhIAAgAPMAAP///wAAAMbGxoSEhLa2tpqamjY2NlZWVtjY2OTk5Ly8vB4eHgQEBAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAIAAgAAAE5xDISWlhperN52JLhSSdRgwVo1ICQZRUsiwHpTJT4iowNS8vyW2icCF6k8HMMBkCEDskxTBDAZwuAkkqIfxIQyhBQBFvAQSDITM5VDW6XNE4KagNh6Bgwe60smQUB3d4Rz1ZBApnFASDd0hihh12BkE9kjAJVlycXIg7CQIFA6SlnJ87paqbSKiKoqusnbMdmDC2tXQlkUhziYtyWTxIfy6BE8WJt5YJvpJivxNaGmLHT0VnOgSYf0dZXS7APdpB309RnHOG5gDqXGLDaC457D1zZ/V/nmOM82XiHRLYKhKP1oZmADdEAAAh+QQJCgAAACwAAAAAIAAgAAAE6hDISWlZpOrNp1lGNRSdRpDUolIGw5RUYhhHukqFu8DsrEyqnWThGvAmhVlteBvojpTDDBUEIFwMFBRAmBkSgOrBFZogCASwBDEY/CZSg7GSE0gSCjQBMVG023xWBhklAnoEdhQEfyNqMIcKjhRsjEdnezB+A4k8gTwJhFuiW4dokXiloUepBAp5qaKpp6+Ho7aWW54wl7obvEe0kRuoplCGepwSx2jJvqHEmGt6whJpGpfJCHmOoNHKaHx61WiSR92E4lbFoq+B6QDtuetcaBPnW6+O7wDHpIiK9SaVK5GgV543tzjgGcghAgAh+QQJCgAAACwAAAAAIAAgAAAE7hDISSkxpOrN5zFHNWRdhSiVoVLHspRUMoyUakyEe8PTPCATW9A14E0UvuAKMNAZKYUZCiBMuBakSQKG8G2FzUWox2AUtAQFcBKlVQoLgQReZhQlCIJesQXI5B0CBnUMOxMCenoCfTCEWBsJColTMANldx15BGs8B5wlCZ9Po6OJkwmRpnqkqnuSrayqfKmqpLajoiW5HJq7FL1Gr2mMMcKUMIiJgIemy7xZtJsTmsM4xHiKv5KMCXqfyUCJEonXPN2rAOIAmsfB3uPoAK++G+w48edZPK+M6hLJpQg484enXIdQFSS1u6UhksENEQAAIfkECQoAAAAsAAAAACAAIAAABOcQyEmpGKLqzWcZRVUQnZYg1aBSh2GUVEIQ2aQOE+G+cD4ntpWkZQj1JIiZIogDFFyHI0UxQwFugMSOFIPJftfVAEoZLBbcLEFhlQiqGp1Vd140AUklUN3eCA51C1EWMzMCezCBBmkxVIVHBWd3HHl9JQOIJSdSnJ0TDKChCwUJjoWMPaGqDKannasMo6WnM562R5YluZRwur0wpgqZE7NKUm+FNRPIhjBJxKZteWuIBMN4zRMIVIhffcgojwCF117i4nlLnY5ztRLsnOk+aV+oJY7V7m76PdkS4trKcdg0Zc0tTcKkRAAAIfkECQoAAAAsAAAAACAAIAAABO4QyEkpKqjqzScpRaVkXZWQEximw1BSCUEIlDohrft6cpKCk5xid5MNJTaAIkekKGQkWyKHkvhKsR7ARmitkAYDYRIbUQRQjWBwJRzChi9CRlBcY1UN4g0/VNB0AlcvcAYHRyZPdEQFYV8ccwR5HWxEJ02YmRMLnJ1xCYp0Y5idpQuhopmmC2KgojKasUQDk5BNAwwMOh2RtRq5uQuPZKGIJQIGwAwGf6I0JXMpC8C7kXWDBINFMxS4DKMAWVWAGYsAdNqW5uaRxkSKJOZKaU3tPOBZ4DuK2LATgJhkPJMgTwKCdFjyPHEnKxFCDhEAACH5BAkKAAAALAAAAAAgACAAAATzEMhJaVKp6s2nIkolIJ2WkBShpkVRWqqQrhLSEu9MZJKK9y1ZrqYK9WiClmvoUaF8gIQSNeF1Er4MNFn4SRSDARWroAIETg1iVwuHjYB1kYc1mwruwXKC9gmsJXliGxc+XiUCby9ydh1sOSdMkpMTBpaXBzsfhoc5l58Gm5yToAaZhaOUqjkDgCWNHAULCwOLaTmzswadEqggQwgHuQsHIoZCHQMMQgQGubVEcxOPFAcMDAYUA85eWARmfSRQCdcMe0zeP1AAygwLlJtPNAAL19DARdPzBOWSm1brJBi45soRAWQAAkrQIykShQ9wVhHCwCQCACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq+E71SRQeyqUToLA7VxF0JDyIQh/MVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiRMDjI0Fd30/iI2UA5GSS5UDj2l6NoqgOgN4gksEBgYFf0FDqKgHnyZ9OX8HrgYHdHpcHQULXAS2qKpENRg7eAMLC7kTBaixUYFkKAzWAAnLC7FLVxLWDBLKCwaKTULgEwbLA4hJtOkSBNqITT3xEgfLpBtzE/jiuL04RGEBgwWhShRgQExHBAAh+QQJCgAAACwAAAAAIAAgAAAE7xDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfZiCqGk5dTESJeaOAlClzsJsqwiJwiqnFrb2nS9kmIcgEsjQydLiIlHehhpejaIjzh9eomSjZR+ipslWIRLAgMDOR2DOqKogTB9pCUJBagDBXR6XB0EBkIIsaRsGGMMAxoDBgYHTKJiUYEGDAzHC9EACcUGkIgFzgwZ0QsSBcXHiQvOwgDdEwfFs0sDzt4S6BK4xYjkDOzn0unFeBzOBijIm1Dgmg5YFQwsCMjp1oJ8LyIAACH5BAkKAAAALAAAAAAgACAAAATwEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq+E71SRQeyqUToLA7VxF0JDyIQh/MVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GGl6NoiPOH16iZKNlH6KmyWFOggHhEEvAwwMA0N9GBsEC6amhnVcEwavDAazGwIDaH1ipaYLBUTCGgQDA8NdHz0FpqgTBwsLqAbWAAnIA4FWKdMLGdYGEgraigbT0OITBcg5QwPT4xLrROZL6AuQAPUS7bxLpoWidY0JtxLHKhwwMJBTHgPKdEQAACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq+E71SRQeyqUToLA7VxF0JDyIQh/MVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GAULDJCRiXo1CpGXDJOUjY+Yip9DhToJA4RBLwMLCwVDfRgbBAaqqoZ1XBMHswsHtxtFaH1iqaoGNgAIxRpbFAgfPQSqpbgGBqUD1wBXeCYp1AYZ19JJOYgH1KwA4UBvQwXUBxPqVD9L3sbp2BNk2xvvFPJd+MFCN6HAAIKgNggY0KtEBAAh+QQJCgAAACwAAAAAIAAgAAAE6BDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfYIDMaAFdTESJeaEDAIMxYFqrOUaNW4E4ObYcCXaiBVEgULe0NJaxxtYksjh2NLkZISgDgJhHthkpU4mW6blRiYmZOlh4JWkDqILwUGBnE6TYEbCgevr0N1gH4At7gHiRpFaLNrrq8HNgAJA70AWxQIH1+vsYMDAzZQPC9VCNkDWUhGkuE5PxJNwiUK4UfLzOlD4WvzAHaoG9nxPi5d+jYUqfAhhykOFwJWiAAAIfkECQoAAAAsAAAAACAAIAAABPAQyElpUqnqzaciSoVkXVUMFaFSwlpOCcMYlErAavhOMnNLNo8KsZsMZItJEIDIFSkLGQoQTNhIsFehRww2CQLKF0tYGKYSg+ygsZIuNqJksKgbfgIGepNo2cIUB3V1B3IvNiBYNQaDSTtfhhx0CwVPI0UJe0+bm4g5VgcGoqOcnjmjqDSdnhgEoamcsZuXO1aWQy8KAwOAuTYYGwi7w5h+Kr0SJ8MFihpNbx+4Erq7BYBuzsdiH1jCAzoSfl0rVirNbRXlBBlLX+BP0XJLAPGzTkAuAOqb0WT5AH7OcdCm5B8TgRwSRKIHQtaLCwg1RAAAOwAAAAAAAAAAAA==); -} -.jvectormap-legend-title { - font-weight: bold; - font-size: 14px; - text-align: center; -} -.jvectormap-legend-cnt { - position: absolute; -} -.jvectormap-legend-cnt-h { - bottom: 0; - right: 0; -} -.jvectormap-legend-cnt-v { - top: 0; - right: 0; -} -.jvectormap-legend { - background: black; - color: white; - border-radius: 3px; -} -.jvectormap-legend-cnt-h .jvectormap-legend { - float: left; - margin: 0 10px 10px 0; - padding: 3px 3px 1px 3px; -} -.jvectormap-legend-cnt-h .jvectormap-legend .jvectormap-legend-tick { - float: left; -} -.jvectormap-legend-cnt-v .jvectormap-legend { - margin: 10px 10px 0 0; - padding: 3px; -} -.jvectormap-legend-cnt-h .jvectormap-legend-tick { - width: 40px; -} -.jvectormap-legend-cnt-h .jvectormap-legend-tick-sample { - height: 15px; -} -.jvectormap-legend-cnt-v .jvectormap-legend-tick-sample { - height: 20px; - width: 20px; - display: inline-block; - vertical-align: middle; -} -.jvectormap-legend-tick-text { - font-size: 12px; -} -.jvectormap-legend-cnt-h .jvectormap-legend-tick-text { - text-align: center; -} -.jvectormap-legend-cnt-v .jvectormap-legend-tick-text { - display: inline-block; - vertical-align: middle; - line-height: 20px; - padding-left: 3px; -} -/*Slick Carousel */ -.slick-prev:before, -.slick-next:before { - color: #1ab394 !important; -} -/* Payments */ -.payment-card { - background: #ffffff; - padding: 20px; - margin-bottom: 25px; - border: 1px solid #e7eaec; -} -.payment-icon-big { - font-size: 60px; - color: #d1dade; -} -.payments-method.panel-group .panel + .panel { - margin-top: -1px; -} -.payments-method .panel-heading { - padding: 15px; - background-color: #f3f3f4; -} -.payments-method .panel-default { - border: 1px solid #e7eaec; -} -.payments-method .panel { - border-radius: 0; -} -.payments-method .panel-heading h5 { - margin-bottom: 5px; -} -.payments-method .panel-heading i { - font-size: 26px; -} -/* Select2 custom styles */ -.select2-container--bootstrap4 .select2-results__option--highlighted, -.select2-container--bootstrap4 .select2-results__option--highlighted.select2-results__option[aria-selected=true] { - background-color: #1ab394; -} -.select2-container--bootstrap4 .select2-selection, -.select2-container--bootstrap4 .select2-dropdown.select2-dropdown--above, -.select2-container--bootstrap4 .select2-dropdown { - border-color: #e7eaec; -} -.select2-container :focus { - outline: none; -} -.select2-container--bootstrap4.select2-container--focus .select2-selection { - box-shadow: none; - border-color: #1ab394; -} -.select2-container--bootstrap4 .select2-selection__clear { - margin-top: 0.9em; -} -/* Tour */ -.tour-tour .btn.btn-default { - background-color: #ffffff; - border: 1px solid #d2d2d2; - color: inherit; -} -.tour-step-backdrop { - z-index: 2101; -} -.tour-backdrop { - z-index: 2100; - opacity: .7; -} -.popover[class*=tour-] { - z-index: 2100; -} -.popover-header { - margin-top: 0; -} -body.tour-open .animated { - animation-fill-mode: initial; -} -.tour-tour .btn.btn-secondary { - background-color: #ffffff; - border: 1px solid #d2d2d2; - color: inherit; -} -/* Resizable */ -.resizable-panels .ibox { - clear: none; - margin: 10px; - float: left; - overflow: hidden; - min-height: 150px; - min-width: 150px; -} -.resizable-panels .ibox .ibox-content { - height: calc(100% - 49px); -} -.ui-resizable-helper { - background: rgba(211, 211, 211, 0.4); -} -/* Wizard step fix */ -.wizard > .content > .body { - position: relative; -} -/* PDF js style */ -.pdf-toolbar { - max-width: 600px; - margin: 0 auto; -} -.pdf-toolbar .input-group { - width: 100px; -} -/* Dropzone */ -.dropzone { - min-height: 140px; - border: 1px dashed #1ab394; - background: white; - padding: 20px 20px; -} -.dropzone .dz-message { - font-size: 16px; -} -/* Activity stream */ -.stream { - position: relative; - padding: 10px 0; -} -.stream:first-child .stream-badge:before { - top: 10px; -} -.stream:last-child .stream-badge:before { - height: 30px; -} -.stream .stream-badge { - width: 50px; -} -.stream .stream-badge i { - border: 1px solid #e7eaec; - border-radius: 50%; - padding: 6px; - color: #808486; - position: absolute; - background-color: #ffffff; - left: 8px; -} -.stream .stream-badge i.fa-circle { - color: #ced0d1; -} -.stream .stream-badge i.bg-success { - color: #ffffff; - background-color: #1c84c6; - border-color: #1c84c6; -} -.stream .stream-badge i.bg-primary { - color: #ffffff; - background-color: #1ab394; - border-color: #1ab394; -} -.stream .stream-badge i.bg-warning { - color: #ffffff; - background-color: #f8ac59; - border-color: #f8ac59; -} -.stream .stream-badge i.bg-info { - color: #ffffff; - background-color: #23c6c8; - border-color: #23c6c8; -} -.stream .stream-badge i.bg-danger { - color: #ffffff; - background-color: #ed5565; - border-color: #ed5565; -} -.stream .stream-badge:before { - content: ''; - width: 1px; - background-color: #e7eaec; - position: absolute; - top: 0; - bottom: 0; - left: 20px; -} -.stream .stream-info { - font-size: 12px; - margin-bottom: 5px; -} -.stream .stream-info img { - border-radius: 50%; - width: 18px; - height: 18px; - margin-right: 2px; - margin-top: -4px; -} -.stream .stream-info .date { - color: #9a9d9f; - font-size: 80%; -} -.stream .stream-panel { - margin-left: 55px; -} -.stream-small { - margin: 10px 0; -} -.stream-small .label { - padding: 2px 6px; - margin-right: 2px; -} -/* Touch Spin */ -.bootstrap-touchspin-postfix.input-group-addon { - padding: inherit; -} -.bootstrap-touchspin-postfix .input-group-text { - background-color: inherit; - line-height: 1; - border: none; -} -/* Code */ -pre { - display: block; - padding: 9.5px; - margin: 0 0 10px; - font-size: 13px; - line-height: 1.42857143; - color: #333; - word-break: break-all; - word-wrap: break-word; - background-color: #eff2f3; - border: 1px solid #d1dade; - border-radius: 2px; -} -code, -kbd, -pre, -samp { - font-family: Menlo, Monaco, Consolas, "Courier New", monospace; -} -.sidebar-panel { - width: 220px; - background: #ebebed; - padding: 10px 20px; - position: absolute; - right: 0; - height: calc(100% - 62px); -} -.sidebar-panel .feed-element img.rounded-circle { - width: 32px; - height: 32px; -} -.sidebar-panel .feed-element, -.media-body, -.sidebar-panel p { - font-size: 12px; -} -.sidebar-panel .feed-element { - margin-top: 20px; - padding-bottom: 0; -} -.sidebar-panel .list-group { - margin-bottom: 10px; -} -.sidebar-panel .list-group .list-group-item { - padding: 5px 0; - font-size: 12px; - border: 0; -} -.sidebar-content .wrapper, -.wrapper.sidebar-content { - padding-right: 230px !important; -} -.body-small .sidebar-content .wrapper, -.body-small .wrapper.sidebar-content { - padding-right: 20px !important; -} -#right-sidebar { - background-color: #fff; - border-left: 1px solid #e7eaec; - border-top: 1px solid #e7eaec; - overflow: hidden; - position: fixed; - top: 60px; - width: 260px !important; - z-index: 1009; - bottom: 0; - right: -260px; -} -#right-sidebar.sidebar-open { - right: 0; -} -#right-sidebar.sidebar-open.sidebar-top { - top: 0; - border-top: none; -} -.sidebar-container ul.nav-tabs { - border: none; -} -.sidebar-container ul.nav-tabs.navs-4 li { - width: 25%; -} -.sidebar-container ul.nav-tabs.navs-3 li { - width: 33.3333%; -} -.sidebar-container ul.nav-tabs.navs-2 li { - width: 50%; -} -.sidebar-container ul.nav-tabs li { - border: none; -} -.sidebar-container ul.nav-tabs li a { - border: none; - padding: 12px 10px; - margin: 0; - border-radius: 0; - background: #2f4050; - color: #fff; - text-align: center; - border-right: 1px solid #334556; -} -.sidebar-container ul.nav-tabs li.active a { - border: none; - background: #f9f9f9; - color: #676a6c; - font-weight: bold; -} -.sidebar-container .nav-tabs > li.active > a:hover, -.sidebar-container .nav-tabs > li.active > a:focus { - border: none; -} -.sidebar-container ul.sidebar-list { - margin: 0; - padding: 0; -} -.sidebar-container ul.sidebar-list li { - border-bottom: 1px solid #e7eaec; - padding: 15px 20px; - list-style: none; - font-size: 12px; -} -.sidebar-container .sidebar-message:nth-child(2n+2) { - background: #f9f9f9; -} -.sidebar-container ul.sidebar-list li a { - text-decoration: none; - color: inherit; -} -.sidebar-container .sidebar-content { - padding: 15px 20px; - font-size: 12px; -} -.sidebar-container .sidebar-title { - background: #f9f9f9; - padding: 20px; - border-bottom: 1px solid #e7eaec; -} -.sidebar-container .sidebar-title h3 { - margin-bottom: 3px; - padding-left: 2px; -} -.sidebar-container .tab-content h4 { - margin-bottom: 5px; -} -.sidebar-container .sidebar-message > a > .float-left { - margin-right: 10px; -} -.sidebar-container .sidebar-message > a { - text-decoration: none; - color: inherit; -} -.sidebar-container .sidebar-message { - padding: 15px 20px; -} -.sidebar-container .sidebar-message .media-body { - display: block; - width: auto; -} -.sidebar-container .sidebar-message .message-avatar { - height: 38px; - width: 38px; - border-radius: 50%; -} -.sidebar-container .setings-item { - padding: 15px 20px; - border-bottom: 1px solid #e7eaec; -} -body { - font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif; - background-color: #2f4050; - font-size: 13px; - color: #676a6c; - overflow-x: hidden; -} -html, -body { - height: 100%; -} -body.full-height-layout #wrapper, -body.full-height-layout #page-wrapper { - height: 100%; -} -#page-wrapper { - min-height: 100vh; -} -body.boxed-layout { - background: url('patterns/shattered.png'); -} -body.boxed-layout #wrapper { - background-color: #2f4050; - max-width: 1200px; - margin: 0 auto; - -webkit-box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.75); - -moz-box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.75); - box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.75); -} -.top-navigation.boxed-layout #wrapper, -.boxed-layout #wrapper.top-navigation { - max-width: 1300px !important; -} -.block { - display: block; -} -.clear { - display: block; - overflow: hidden; -} -a { - cursor: pointer; -} -a:hover, -a:focus { - text-decoration: none; -} -.border-bottom { - border-bottom: 1px solid #e7eaec !important; -} -.font-bold { - font-weight: 600; -} -.font-normal { - font-weight: 400; -} -.text-uppercase { - text-transform: uppercase; -} -.font-italic { - font-style: italic; -} -.b-r { - border-right: 1px solid #e7eaec; -} -.hr-line-dashed { - border-top: 1px dashed #e7eaec; - color: #ffffff; - background-color: #ffffff; - height: 1px; - margin: 20px 0; -} -.hr-line-solid { - border-bottom: 1px solid #e7eaec; - background-color: rgba(0, 0, 0, 0); - border-style: solid !important; - margin-top: 15px; - margin-bottom: 15px; -} -video { - width: 100% !important; - height: auto !important; -} -/* GALLERY */ -.gallery > .row > div { - margin-bottom: 15px; -} -.fancybox img { - margin-bottom: 5px; - /* Only for demo */ - width: 24%; -} -/* Summernote text editor */ -.note-editor { - height: auto !important; -} -.note-editor.fullscreen { - z-index: 2050; -} -.note-editor.note-frame.fullscreen { - z-index: 2020; -} -.note-editor.note-frame .note-editing-area .note-editable { - color: #676a6c; - padding: 15px; -} -.note-editor.note-frame { - border: none; -} -.note-editor.panel { - margin-bottom: 0; -} -/* MODAL */ -.modal-content { - background-clip: padding-box; - background-color: #FFFFFF; - border: 1px solid rgba(0, 0, 0, 0); - border-radius: 4px; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); - outline: 0 none; - position: relative; -} -.modal-dialog { - z-index: 2200; -} -.modal-body { - padding: 20px 30px 30px 30px; -} -.inmodal .modal-body { - background: #f8fafb; -} -.inmodal .modal-header { - padding: 30px 15px; - text-align: center; - display: block; -} -.animated.modal.fade .modal-dialog { - -webkit-transform: none; - -ms-transform: none; - -o-transform: none; - transform: none; -} -.inmodal .modal-title { - font-size: 26px; -} -.inmodal .modal-icon { - font-size: 84px; - color: #e2e3e3; -} -.modal-footer { - margin-top: 0; -} -/* WRAPPERS */ -#wrapper { - width: 100%; - overflow-x: hidden; - display: -ms-flex; - display: -webkit-flex; - display: flex; -} -.wrapper { - padding: 0 20px; -} -.wrapper-content { - padding: 20px 10px 40px; -} -#page-wrapper { - padding: 0 15px; - position: relative !important; - flex-shrink: 1; - width: calc(100% - 220px); -} -@media (min-width: 768px) { - #page-wrapper { - position: inherit; - } -} -.title-action { - text-align: right; - padding-top: 30px; -} -.ibox-content h1, -.ibox-content h2, -.ibox-content h3, -.ibox-content h4, -.ibox-content h5, -.ibox-title h1, -.ibox-title h2, -.ibox-title h3, -.ibox-title h4, -.ibox-title h5 { - margin-top: 5px; -} -ul.unstyled, -ol.unstyled { - list-style: none outside none; - margin-left: 0; -} -.big-icon { - font-size: 160px !important; - color: #e5e6e7; -} -/* FOOTER */ -.footer { - background: none repeat scroll 0 0 white; - border-top: 1px solid #e7eaec; - bottom: 0; - left: 0; - padding: 10px 20px; - position: absolute; - right: 0; -} -.footer.fixed_full { - position: fixed; - bottom: 0; - left: 0; - right: 0; - z-index: 1000; - padding: 10px 20px; - background: white; - border-top: 1px solid #e7eaec; -} -.footer.fixed { - position: fixed; - bottom: 0; - left: 0; - right: 0; - z-index: 1000; - padding: 10px 20px; - background: white; - border-top: 1px solid #e7eaec; - margin-left: 220px; -} -body.mini-navbar .footer.fixed, -body.body-small.mini-navbar .footer.fixed { - margin: 0 0 0 70px; -} -body.mini-navbar.fixed-sidebar .footer.fixed { - margin: 0; -} -body.mini-navbar.canvas-menu .footer.fixed, -body.canvas-menu .footer.fixed { - margin: 0 !important; -} -body.fixed-sidebar.body-small.mini-navbar .footer.fixed { - margin: 0 0 0 220px; -} -body.body-small .footer.fixed { - margin-left: 0; -} -/* PANELS */ -.panel-title > .small, -.panel-title > .small > a, -.panel-title > a, -.panel-title > small, -.panel-title > small > a { - color: inherit; -} -.page-heading { - border-top: 0; - padding: 0 10px 20px 10px; -} -.panel-heading h1, -.panel-heading h2 { - margin-bottom: 5px; -} -.panel-body { - padding: 15px; -} -/* Bootstrap 3.3.x panels */ -.panel { - margin-bottom: 20px; - background-color: #fff; - border: 1px solid transparent; - border-radius: 4px; -} -.panel-heading { - color: white; - padding: 10px 15px; - border-bottom: 1px solid transparent; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel-footer { - padding: 10px 15px; - border-top: 1px solid #e7eaec; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel-default > .panel-heading { - color: #333; - background-color: #f5f5f5; - border-color: #e7eaec; -} -.panel-default { - border-color: #e7eaec; -} -.panel-group .panel + .panel { - margin-top: 5px; -} -.panel-group .panel { - margin-bottom: 0; - border-radius: 4px; -} -/* TABLES */ -.table > caption + thead > tr:first-child > td, -.table > caption + thead > tr:first-child > th, -.table > colgroup + thead > tr:first-child > td, -.table > colgroup + thead > tr:first-child > th, -.table > thead:first-child > tr:first-child > td, -.table > thead:first-child > tr:first-child > th { - border-top: 0; -} -.table-bordered { - border: 1px solid #EBEBEB; -} -.table-bordered > thead > tr > th, -.table-bordered > thead > tr > td { - background-color: #F5F5F6; - border-bottom-width: 1px; -} -.table-bordered > thead > tr > th, -.table-bordered > tbody > tr > th, -.table-bordered > tfoot > tr > th, -.table-bordered > thead > tr > td, -.table-bordered > tbody > tr > td, -.table-bordered > tfoot > tr > td { - border: 1px solid #e7e7e7; -} -.table > thead > tr > th { - border-bottom: 1px solid #DDDDDD; - vertical-align: bottom; -} -.table > thead > tr > th, -.table > tbody > tr > th, -.table > tfoot > tr > th, -.table > thead > tr > td, -.table > tbody > tr > td, -.table > tfoot > tr > td { - border-top: 1px solid #e7eaec; - line-height: 1.42857; - padding: 8px; - vertical-align: top; -} -/* PANELS */ -.panel.blank-panel { - background: none; - margin: 0; -} -.blank-panel .panel-heading { - padding-bottom: 0; -} -.nav-tabs > li > a { - color: #A7B1C2; - font-weight: 600; - padding: 10px 20px 10px 25px; -} -.nav-tabs > li > a:hover, -.nav-tabs > li > a:focus { - color: #676a6c; -} -.ui-tab .tab-content { - padding: 20px 0; -} -/* GLOBAL */ -.no-padding { - padding: 0 !important; -} -.no-borders { - border: none !important; -} -.no-margins { - margin: 0 !important; -} -.no-top-border { - border-top: 0 !important; -} -.ibox-content.text-box { - padding-bottom: 0; - padding-top: 15px; -} -.border-left-right { - border-left: 1px solid #e7eaec; - border-right: 1px solid #e7eaec; -} -.border-top-bottom { - border-top: 1px solid #e7eaec; - border-bottom: 1px solid #e7eaec; -} -.border-left { - border-left: 1px solid #e7eaec; -} -.border-right { - border-right: 1px solid #e7eaec; -} -.border-top { - border-top: 1px solid #e7eaec; -} -.border-bottom { - border-bottom: 1px solid #e7eaec; -} -.border-size-sm { - border-width: 3px; -} -.border-size-md { - border-width: 6px; -} -.border-size-lg { - border-width: 9px; -} -.border-size-xl { - border-width: 12px; -} -.full-width { - width: 100% !important; -} -.link-block { - font-size: 12px; - padding: 10px; -} -.nav.navbar-top-links .link-block a { - font-size: 12px; -} -.navbar-top-links { - text-align: right; -} -.link-block a { - font-size: 10px; - color: inherit; -} -body.mini-navbar .branding { - display: none; -} -img.circle-border { - border: 6px solid #FFFFFF; - border-radius: 50%; -} -.branding { - float: left; - color: #FFFFFF; - font-size: 18px; - font-weight: 600; - padding: 17px 20px; - text-align: center; - background-color: #1ab394; -} -.login-panel { - margin-top: 25%; -} -.icons-box h3 { - margin-top: 10px; - margin-bottom: 10px; -} -.icons-box .infont a i { - font-size: 25px; - display: block; - color: #676a6c; -} -.icons-box .infont a { - color: #a6a8a9; -} -.icons-box .infont a { - padding: 10px; - margin: 1px; - display: block; -} -.ui-draggable .ibox-title { - cursor: move; -} -.breadcrumb { - background-color: #ffffff; - padding: 0; - margin-bottom: 0; -} -.breadcrumb > li a { - color: inherit; -} -.breadcrumb > .active { - color: inherit; -} -code { - background-color: #F9F2F4; - border-radius: 4px; - color: #ca4440; - font-size: 90%; - padding: 2px 4px; - white-space: nowrap; -} -.ibox { - clear: both; - margin-bottom: 25px; - margin-top: 0; - padding: 0; -} -.ibox.collapsed .ibox-content { - display: none; -} -.ibox.collapsed .fa.fa-chevron-up:before { - content: "\f078"; -} -.ibox.collapsed .fa.fa-chevron-down:before { - content: "\f077"; -} -.ibox:after, -.ibox:before { - display: table; -} -.ibox-title { - background-color: #ffffff; - border-color: #e7eaec; - border-image: none; - border-style: solid solid none; - border-width: 1px; - color: inherit; - margin-bottom: 0; - padding: 15px 90px 8px 15px; - min-height: 48px; - position: relative; - clear: both; - -webkit-border-radius: 3px 3px 0 0; - -moz-border-radius: 3px 3px 0 0; - border-radius: 2px 2px 0 0; -} -.ibox-content { - background-color: #ffffff; - color: inherit; - padding: 15px 20px 20px 20px; - border-color: #e7eaec; - border-image: none; - border-style: solid; - border-width: 1px; -} -.ibox-footer { - color: inherit; - border-top: 1px solid #e7eaec; - font-size: 90%; - background: #ffffff; - padding: 10px 15px; -} -table.table-mail tr td { - padding: 12px; -} -.table-mail .check-mail { - padding-left: 20px; -} -.table-mail .mail-date { - padding-right: 20px; -} -.star-mail, -.check-mail { - width: 40px; -} -.unread td a, -.unread td { - font-weight: 600; - color: inherit; -} -.read td a, -.read td { - font-weight: normal; - color: inherit; -} -.unread td { - background-color: #f9f8f8; -} -.ibox-content { - clear: both; -} -.ibox-heading { - background-color: #f3f6fb; - border-bottom: none; -} -.ibox-heading h3 { - font-weight: 200; - font-size: 24px; -} -.ibox-title h5 { - display: inline-block; - font-size: 14px; - margin: 0 0 7px; - padding: 0; - text-overflow: ellipsis; - float: none; -} -.ibox-title .label { - margin-left: 4px; -} -.ibox-title .pull-right { - position: absolute; - right: 15px; - top: 15px; -} -.ibox-tools { - display: block; - float: none; - margin-top: 0; - position: absolute; - top: 15px; - right: 15px; - padding: 0; - text-align: right; -} -.ibox-tools a { - cursor: pointer; - margin-left: 5px; - color: #c4c4c4 !important; -} -.ibox-tools a.btn-primary { - color: #fff !important; -} -.ibox-tools .dropdown-menu > li > a { - padding: 4px 10px; - font-size: 12px; - color: #676a6c !important; -} -.ibox .ibox-tools.open > .dropdown-menu { - left: auto; - right: 0; -} -.ibox-tools .dropdown-toggle::after { - display: none; -} -.dropdown-item { - width: auto; -} -.dropdown-item.active, -.dropdown-item:active { - background-color: inherit; - color: inherit; -} -/* BACKGROUNDS */ -.gray-bg, -.bg-muted { - background-color: #f3f3f4; -} -.white-bg { - background-color: #ffffff; -} -.blue-bg, -.bg-success { - background-color: #1c84c6 !important; - color: #ffffff; -} -.navy-bg, -.bg-primary { - background-color: #1ab394 !important; - color: #ffffff; -} -.lazur-bg, -.bg-info { - background-color: #23c6c8 !important; - color: #ffffff; -} -.yellow-bg, -.bg-warning { - background-color: #f8ac59 !important; - color: #ffffff; -} -.red-bg, -.bg-danger { - background-color: #ed5565 !important; - color: #ffffff; -} -.black-bg { - background-color: #262626; -} -.panel-primary { - border-color: #1ab394; -} -.panel-primary > .panel-heading { - background-color: #1ab394; - border-color: #1ab394; -} -.panel-success { - border-color: #1c84c6; -} -.panel-success > .panel-heading { - background-color: #1c84c6; - border-color: #1c84c6; - color: #ffffff; -} -.panel-info { - border-color: #23c6c8; -} -.panel-info > .panel-heading { - background-color: #23c6c8; - border-color: #23c6c8; - color: #ffffff; -} -.panel-warning { - border-color: #f8ac59; -} -.panel-warning > .panel-heading { - background-color: #f8ac59; - border-color: #f8ac59; - color: #ffffff; -} -.panel-danger { - border-color: #ed5565; -} -.panel-danger > .panel-heading { - background-color: #ed5565; - border-color: #ed5565; - color: #ffffff; -} -.progress-bar { - background-color: #1ab394; -} -.progress-small, -.progress-small .progress-bar { - height: 10px; -} -.progress-small, -.progress-mini { - margin-top: 5px; -} -.progress-mini, -.progress-mini .progress-bar { - height: 5px; - margin-bottom: 0; -} -.progress-bar-navy-light { - background-color: #3dc7ab; -} -.progress-bar-success { - background-color: #1c84c6; -} -.progress-bar-info { - background-color: #23c6c8; -} -.progress-bar-warning { - background-color: #f8ac59; -} -.progress-bar-danger { - background-color: #ed5565; -} -.panel-title { - font-size: inherit; -} -.jumbotron { - border-radius: 6px; - padding: 40px; -} -.jumbotron h1 { - margin-top: 0; -} -/* COLORS */ -.text-navy { - color: #1ab394 !important; -} -.text-primary { - color: inherit !important; -} -.text-success { - color: #1c84c6 !important; -} -.text-info { - color: #23c6c8 !important; -} -.text-warning { - color: #f8ac59 !important; -} -.text-danger { - color: #ed5565 !important; -} -.text-muted { - color: #888888 !important; -} -.text-white { - color: #ffffff; -} -.simple_tag { - background-color: #f3f3f4; - border: 1px solid #e7eaec; - border-radius: 2px; - color: inherit; - font-size: 10px; - margin-right: 5px; - margin-top: 5px; - padding: 5px 12px; - display: inline-block; -} -.img-shadow { - -webkit-box-shadow: 0 0 3px 0 #919191; - -moz-box-shadow: 0 0 3px 0 #919191; - box-shadow: 0 0 3px 0 #919191; -} -/* For handle diferent bg color in AngularJS version */ -.dashboards\.dashboard_2 nav.navbar, -.dashboards\.dashboard_3 nav.navbar, -.mailbox\.inbox nav.navbar, -.mailbox\.email_view nav.navbar, -.mailbox\.email_compose nav.navbar, -.dashboards\.dashboard_4_1 nav.navbar, -.metrics nav.navbar, -.metrics\.index nav.navbar, -.dashboards\.dashboard_5 nav.navbar { - background: #fff; -} -/* For handle diferent bg color in MVC version */ -.Dashboard_2 .navbar.navbar-static-top, -.Dashboard_3 .navbar.navbar-static-top, -.Dashboard_4_1 .navbar.navbar-static-top, -.ComposeEmail .navbar.navbar-static-top, -.EmailView .navbar.navbar-static-top, -.Inbox .navbar.navbar-static-top, -.Metrics .navbar.navbar-static-top, -.Dashboard_5 .navbar.navbar-static-top { - background: #fff; -} -a.close-canvas-menu { - position: absolute; - top: 10px; - right: 15px; - z-index: 1011; - color: #a7b1c2; -} -a.close-canvas-menu:hover { - color: #fff; -} -.close-canvas-menu { - display: none; -} -.canvas-menu .close-canvas-menu { - display: block; -} -.light-navbar .navbar.navbar-static-top { - background-color: #ffffff; -} -/* FULL HEIGHT */ -.full-height { - height: 100%; -} -.fh-breadcrumb { - height: calc(100% - 196px); - margin: 0 -15px; - position: relative; -} -.fh-no-breadcrumb { - height: calc(100% - 99px); - margin: 0 -15px; - position: relative; -} -.fh-column { - background: #fff; - height: 100%; - width: 240px; - float: left; -} -.modal-backdrop { - z-index: 2040 !important; -} -.modal { - z-index: 2050 !important; -} -.spiner-example { - height: 200px; - padding-top: 70px; -} -legend { - font-size: 1rem; -} -/* MARGINS & PADDINGS */ -.p-xxs { - padding: 5px; -} -.p-xs { - padding: 10px; -} -.p-sm { - padding: 15px; -} -.p-m { - padding: 20px; -} -.p-md { - padding: 25px; -} -.p-lg { - padding: 30px; -} -.p-xl { - padding: 40px; -} -.p-w-xs { - padding: 0 10px; -} -.p-w-sm { - padding: 0 15px; -} -.p-w-m { - padding: 0 20px; -} -.p-w-md { - padding: 0 25px; -} -.p-w-lg { - padding: 0 30px; -} -.p-w-xl { - padding: 0 40px; -} -.p-h-xs { - padding: 10px 0; -} -.p-h-sm { - padding: 15px 0; -} -.p-h-m { - padding: 20px 0; -} -.p-h-md { - padding: 25px 0; -} -.p-h-lg { - padding: 30px 0; -} -.p-h-xl { - padding: 40px 0; -} -.m-xxs { - margin: 2px 4px; -} -.m { - margin: 15px; -} -.m-xs { - margin: 5px; -} -.m-sm { - margin: 10px; -} -.m-md { - margin: 20px; -} -.m-lg { - margin: 30px; -} -.m-xl { - margin: 50px; -} -.m-n { - margin: 0 !important; -} -.m-l-none { - margin-left: 0; -} -.m-l-xs { - margin-left: 5px; -} -.m-l-sm { - margin-left: 10px; -} -.m-l { - margin-left: 15px; -} -.m-l-md { - margin-left: 20px; -} -.m-l-lg { - margin-left: 30px; -} -.m-l-xl { - margin-left: 40px; -} -.m-l-n-xxs { - margin-left: -1px; -} -.m-l-n-xs { - margin-left: -5px; -} -.m-l-n-sm { - margin-left: -10px; -} -.m-l-n { - margin-left: -15px; -} -.m-l-n-md { - margin-left: -20px; -} -.m-l-n-lg { - margin-left: -30px; -} -.m-l-n-xl { - margin-left: -40px; -} -.m-t-none { - margin-top: 0; -} -.m-t-xxs { - margin-top: 1px; -} -.m-t-xs { - margin-top: 5px; -} -.m-t-sm { - margin-top: 10px; -} -.m-t { - margin-top: 15px; -} -.m-t-md { - margin-top: 20px; -} -.m-t-lg { - margin-top: 30px; -} -.m-t-xl { - margin-top: 40px; -} -.m-t-n-xxs { - margin-top: -1px; -} -.m-t-n-xs { - margin-top: -5px; -} -.m-t-n-sm { - margin-top: -10px; -} -.m-t-n { - margin-top: -15px; -} -.m-t-n-md { - margin-top: -20px; -} -.m-t-n-lg { - margin-top: -30px; -} -.m-t-n-xl { - margin-top: -40px; -} -.m-r-none { - margin-right: 0; -} -.m-r-xxs { - margin-right: 1px; -} -.m-r-xs { - margin-right: 5px; -} -.m-r-sm { - margin-right: 10px; -} -.m-r { - margin-right: 15px; -} -.m-r-md { - margin-right: 20px; -} -.m-r-lg { - margin-right: 30px; -} -.m-r-xl { - margin-right: 40px; -} -.m-r-n-xxs { - margin-right: -1px; -} -.m-r-n-xs { - margin-right: -5px; -} -.m-r-n-sm { - margin-right: -10px; -} -.m-r-n { - margin-right: -15px; -} -.m-r-n-md { - margin-right: -20px; -} -.m-r-n-lg { - margin-right: -30px; -} -.m-r-n-xl { - margin-right: -40px; -} -.m-b-none { - margin-bottom: 0; -} -.m-b-xxs { - margin-bottom: 1px; -} -.m-b-xs { - margin-bottom: 5px; -} -.m-b-sm { - margin-bottom: 10px; -} -.m-b { - margin-bottom: 15px; -} -.m-b-md { - margin-bottom: 20px; -} -.m-b-lg { - margin-bottom: 30px; -} -.m-b-xl { - margin-bottom: 40px; -} -.m-b-n-xxs { - margin-bottom: -1px; -} -.m-b-n-xs { - margin-bottom: -5px; -} -.m-b-n-sm { - margin-bottom: -10px; -} -.m-b-n { - margin-bottom: -15px; -} -.m-b-n-md { - margin-bottom: -20px; -} -.m-b-n-lg { - margin-bottom: -30px; -} -.m-b-n-xl { - margin-bottom: -40px; -} -.space-15 { - margin: 15px 0; -} -.space-20 { - margin: 20px 0; -} -.space-25 { - margin: 25px 0; -} -.space-30 { - margin: 30px 0; -} -.img-sm { - width: 32px; - height: 32px; -} -.img-md { - width: 64px; - height: 64px; -} -.img-lg { - width: 96px; - height: 96px; -} -.b-r-xs { - -webkit-border-radius: 1px; - -moz-border-radius: 1px; - border-radius: 1px; -} -.b-r-sm { - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} -.b-r-md { - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} -.b-r-lg { - -webkit-border-radius: 12px; - -moz-border-radius: 12px; - border-radius: 12px; -} -.b-r-xl { - -webkit-border-radius: 24px; - -moz-border-radius: 24px; - border-radius: 24px; -} -.fullscreen-ibox-mode .animated { - animation: none; -} -body.fullscreen-ibox-mode { - overflow-y: hidden; -} -.ibox.fullscreen { - z-index: 2030; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - overflow: auto; - margin-bottom: 0; -} -.ibox.fullscreen .collapse-link { - display: none; -} -.ibox.fullscreen .ibox-content { - min-height: calc(100% - 48px); -} -body.modal-open { - padding-right: inherit !important; -} -_::-webkit-full-page-media, -_:future, -:root body.modal-open .wrapper-content.animated { - -webkit-animation: none; - -ms-animation-nam: none; - animation: none; -} -body.modal-open .animated { - animation-fill-mode: initial; - z-index: inherit; -} -/* Show profile dropdown on fixed sidebar */ -body.mini-navbar.fixed-sidebar .profile-element, -.block { - display: block !important; -} -body.mini-navbar.fixed-sidebar .nav-header { - padding: 33px 25px; -} -body.mini-navbar.fixed-sidebar .logo-element { - display: none; -} -.fullscreen-video .animated { - animation: none; -} -.list-inline > li { - display: inline-block; -} -.custom-file-label { - padding: .5rem .75rem; -} -.custom-file-label::after { - padding: .5rem .75rem; -} -/* SEARCH PAGE */ -.search-form { - margin-top: 10px; -} -.search-result h3 { - margin-bottom: 0; - color: #1E0FBE; -} -.search-result .search-link { - color: #006621; -} -.search-result p { - font-size: 12px; - margin-top: 5px; -} -/* CONTACTS */ -.contact-box { - background-color: #ffffff; - border: 1px solid #e7eaec; - padding: 20px; - margin-bottom: 20px; -} -.contact-box > a { - color: inherit; -} -.contact-box.center-version { - border: 1px solid #e7eaec; - padding: 0; -} -.contact-box.center-version > a { - display: block; - background-color: #ffffff; - padding: 20px; - text-align: center; -} -.contact-box.center-version > a img { - width: 80px; - height: 80px; - margin-top: 10px; - margin-bottom: 10px; -} -.contact-box.center-version address { - margin-bottom: 0; -} -.contact-box .contact-box-footer { - text-align: center; - background-color: #ffffff; - border-top: 1px solid #e7eaec; - padding: 15px 20px; -} -/* INVOICE */ -.invoice-table tbody > tr > td:last-child, -.invoice-table tbody > tr > td:nth-child(4), -.invoice-table tbody > tr > td:nth-child(3), -.invoice-table tbody > tr > td:nth-child(2) { - text-align: right; -} -.invoice-table thead > tr > th:last-child, -.invoice-table thead > tr > th:nth-child(4), -.invoice-table thead > tr > th:nth-child(3), -.invoice-table thead > tr > th:nth-child(2) { - text-align: right; -} -.invoice-total > tbody > tr > td:first-child { - text-align: right; -} -.invoice-total > tbody > tr > td { - border: 0 none; -} -.invoice-total > tbody > tr > td:last-child { - border-bottom: 1px solid #DDDDDD; - text-align: right; - width: 15%; -} -/* ERROR & LOGIN & LOCKSCREEN*/ -.middle-box { - max-width: 400px; - z-index: 100; - margin: 0 auto; - padding-top: 40px; -} -.lockscreen.middle-box { - width: 200px; - padding-top: 110px; -} -.loginscreen.middle-box { - width: 300px; -} -.loginColumns { - max-width: 800px; - margin: 0 auto; -} -.passwordBox { - max-width: 460px; - margin: 0 auto; - padding: 100px 20px 20px 20px; -} -.logo-name { - color: #e6e6e6; - font-size: 180px; - font-weight: 800; - letter-spacing: -10px; - margin-bottom: 0; -} -.middle-box h1 { - font-size: 170px; -} -.wrapper .middle-box { - margin-top: 140px; -} -.lock-word { - z-index: 10; - position: absolute; - top: 110px; - left: 50%; - margin-left: -470px; -} -.lock-word span { - font-size: 100px; - font-weight: 600; - color: #e9e9e9; - display: inline-block; -} -.lock-word .first-word { - margin-right: 160px; -} -/* DASBOARD */ -.dashboard-header { - border-top: 0; - padding: 20px 20px 20px 20px; -} -.dashboard-header h2 { - margin-top: 10px; - font-size: 26px; -} -.fist-item { - border-top: none !important; -} -.statistic-box { - margin-top: 40px; -} -.dashboard-header .list-group-item span.label { - margin-right: 10px; -} -.list-group.clear-list .list-group-item { - border-top: 1px solid #e7eaec; - border-bottom: 0; - border-right: 0; - border-left: 0; - padding: 10px 0; -} -ul.clear-list:first-child { - border-top: none !important; -} -/* Intimeline */ -.timeline-item .date i { - position: absolute; - top: 0; - right: 0; - padding: 5px; - width: 30px; - text-align: center; - border-top: 1px solid #e7eaec; - border-bottom: 1px solid #e7eaec; - border-left: 1px solid #e7eaec; - background: #f8f8f8; -} -.timeline-item .date { - text-align: right; - width: 110px; - position: relative; - padding-top: 30px; -} -.timeline-item .content { - border-left: 1px solid #e7eaec; - border-top: 1px solid #e7eaec; - padding-top: 10px; - min-height: 100px; -} -.timeline-item .content:hover { - background: #f6f6f6; -} -/* PIN BOARD */ -ul.notes li, -ul.tag-list li { - list-style: none; -} -ul.notes li h4 { - margin-top: 20px; - font-size: 16px; -} -ul.notes li div { - text-decoration: none; - color: #000; - background: #ffc; - display: block; - height: 140px; - width: 140px; - padding: 1em; - position: relative; -} -ul.notes li div small { - position: absolute; - top: 5px; - right: 5px; - font-size: 10px; -} -ul.notes li div a { - position: absolute; - right: 10px; - bottom: 10px; - color: inherit; -} -ul.notes li { - margin: 10px 40px 50px 0; - float: left; -} -ul.notes li div p { - font-size: 12px; -} -ul.notes li div { - text-decoration: none; - color: #000; - background: #ffc; - display: block; - height: 140px; - width: 140px; - padding: 1em; - /* Firefox */ - -moz-box-shadow: 5px 5px 2px #212121; - /* Safari+Chrome */ - -webkit-box-shadow: 5px 5px 2px rgba(33, 33, 33, 0.7); - /* Opera */ - box-shadow: 5px 5px 2px rgba(33, 33, 33, 0.7); -} -ul.notes li div { - -webkit-transform: rotate(-6deg); - -o-transform: rotate(-6deg); - -moz-transform: rotate(-6deg); - -ms-transform: rotate(-6deg); -} -ul.notes li:nth-child(even) div { - -o-transform: rotate(4deg); - -webkit-transform: rotate(4deg); - -moz-transform: rotate(4deg); - -ms-transform: rotate(4deg); - position: relative; - top: 5px; -} -ul.notes li:nth-child(3n) div { - -o-transform: rotate(-3deg); - -webkit-transform: rotate(-3deg); - -moz-transform: rotate(-3deg); - -ms-transform: rotate(-3deg); - position: relative; - top: -5px; -} -ul.notes li:nth-child(5n) div { - -o-transform: rotate(5deg); - -webkit-transform: rotate(5deg); - -moz-transform: rotate(5deg); - -ms-transform: rotate(5deg); - position: relative; - top: -10px; -} -ul.notes li div:hover, -ul.notes li div:focus { - -webkit-transform: scale(1.1); - -moz-transform: scale(1.1); - -o-transform: scale(1.1); - -ms-transform: scale(1.1); - position: relative; - z-index: 5; -} -ul.notes li div { - text-decoration: none; - color: #000; - background: #ffc; - display: block; - height: 210px; - width: 210px; - padding: 1em; - -moz-box-shadow: 5px 5px 7px #212121; - -webkit-box-shadow: 5px 5px 7px rgba(33, 33, 33, 0.7); - box-shadow: 5px 5px 7px rgba(33, 33, 33, 0.7); - -moz-transition: -moz-transform 0.15s linear; - -o-transition: -o-transform 0.15s linear; - -webkit-transition: -webkit-transform 0.15s linear; -} -/* FILE MANAGER */ -.file-box { - float: left; - width: 220px; -} -.file-manager h5 { - text-transform: uppercase; -} -.file-manager { - list-style: none outside none; - margin: 0; - padding: 0; -} -.folder-list li a { - color: #666666; - display: block; - padding: 5px 0; -} -.folder-list li { - border-bottom: 1px solid #e7eaec; - display: block; -} -.folder-list li i { - margin-right: 8px; - color: #3d4d5d; -} -.category-list li a { - color: #666666; - display: block; - padding: 5px 0; -} -.category-list li { - display: block; -} -.category-list li i { - margin-right: 8px; - color: #3d4d5d; -} -.category-list li a .text-navy { - color: #1ab394; -} -.category-list li a .text-primary { - color: #1c84c6; -} -.category-list li a .text-info { - color: #23c6c8; -} -.category-list li a .text-danger { - color: #EF5352; -} -.category-list li a .text-warning { - color: #F8AC59; -} -.file-manager h5.tag-title { - margin-top: 20px; -} -.tag-list li { - float: left; -} -.tag-list li a { - font-size: 10px; - background-color: #f3f3f4; - padding: 5px 12px; - color: inherit; - border-radius: 2px; - border: 1px solid #e7eaec; - margin-right: 5px; - margin-top: 5px; - display: block; -} -.file { - border: 1px solid #e7eaec; - padding: 0; - background-color: #ffffff; - position: relative; - margin-bottom: 20px; - margin-right: 20px; -} -.file-manager .hr-line-dashed { - margin: 15px 0; -} -.file .icon, -.file .image { - height: 100px; - overflow: hidden; -} -.file .icon { - padding: 15px 10px; - text-align: center; -} -.file-control { - color: inherit; - font-size: 11px; - margin-right: 10px; -} -.file-control.active { - text-decoration: underline; -} -.file .icon i { - font-size: 70px; - color: #dadada; -} -.file .file-name { - padding: 10px; - background-color: #f8f8f8; - border-top: 1px solid #e7eaec; -} -.file-name small { - color: #676a6c; -} -.corner { - position: absolute; - display: inline-block; - width: 0; - height: 0; - line-height: 0; - border: 0.6em solid transparent; - border-right: 0.6em solid #f1f1f1; - border-bottom: 0.6em solid #f1f1f1; - right: 0em; - bottom: 0em; -} -a.compose-mail { - padding: 8px 10px; -} -.mail-search { - max-width: 300px; -} -/* PROFILE */ -.profile-content { - border-top: none !important; -} -.profile-stats { - margin-right: 10px; -} -.profile-image { - width: 120px; - float: left; -} -.profile-image img { - width: 96px; - height: 96px; -} -.profile-info { - margin-left: 120px; -} -.feed-activity-list .feed-element { - border-bottom: 1px solid #e7eaec; -} -.feed-element:first-child { - margin-top: 0; -} -.feed-element { - padding-bottom: 15px; -} -.feed-element, -.feed-element .media { - margin-top: 15px; -} -.feed-element, -.media-body { - overflow: hidden; -} -.feed-element > a img { - margin-right: 10px; -} -.feed-element img.rounded-circle, -.dropdown-messages-box img.rounded-circle { - width: 38px; - height: 38px; -} -.feed-element .well { - border: 1px solid #e7eaec; - box-shadow: none; - margin-top: 10px; - margin-bottom: 5px; - padding: 10px 20px; - font-size: 11px; - line-height: 16px; -} -.feed-element .actions { - margin-top: 10px; -} -.feed-element .photos { - margin: 10px 0; -} -.dropdown-messages-box .dropdown-item:focus, -.dropdown-messages-box .dropdown-item:hover { - background-color: inherit; -} -.feed-photo { - max-height: 180px; - border-radius: 4px; - overflow: hidden; - margin-right: 10px; - margin-bottom: 10px; -} -.file-list li { - padding: 5px 10px; - font-size: 11px; - border-radius: 2px; - border: 1px solid #e7eaec; - margin-bottom: 5px; -} -.file-list li a { - color: inherit; -} -.file-list li a:hover { - color: #1ab394; -} -.user-friends img { - width: 42px; - height: 42px; - margin-bottom: 5px; - margin-right: 5px; -} -/* MAILBOX */ -.mail-box { - background-color: #ffffff; - border: 1px solid #e7eaec; - border-top: 0; - padding: 0; - margin-bottom: 20px; -} -.mail-box-header { - background-color: #ffffff; - border: 1px solid #e7eaec; - border-bottom: 0; - padding: 30px 20px 20px 20px; -} -.mail-box-header h2 { - margin-top: 0; -} -.mailbox-content .tag-list li a { - background: #ffffff; -} -.mail-body { - border-top: 1px solid #e7eaec; - padding: 20px; -} -.mail-text { - border-top: 1px solid #e7eaec; -} -.mail-text .note-toolbar { - padding: 10px 15px; -} -.mail-body .form-group { - margin-bottom: 5px; -} -.mail-text .note-editor .note-toolbar { - background-color: #F9F8F8; -} -.mail-attachment { - border-top: 1px solid #e7eaec; - padding: 20px; - font-size: 12px; -} -.mailbox-content { - background: none; - border: none; - padding: 10px; -} -.mail-ontact { - width: 23%; -} -/* PROJECTS */ -.project-people, -.project-actions { - text-align: right; - vertical-align: middle; -} -dd.project-people { - text-align: left; - margin-top: 5px; -} -.project-people img { - width: 32px; - height: 32px; -} -.project-title a { - font-size: 14px; - color: #676a6c; - font-weight: 600; -} -.project-list table tr td { - border-top: none; - border-bottom: 1px solid #e7eaec; - padding: 15px 10px; - vertical-align: middle; -} -.project-manager .tag-list li a { - font-size: 10px; - background-color: white; - padding: 5px 12px; - color: inherit; - border-radius: 2px; - border: 1px solid #e7eaec; - margin-right: 5px; - margin-top: 5px; - display: block; -} -.project-files li a { - font-size: 11px; - color: #676a6c; - margin-left: 10px; - line-height: 22px; -} -/* FAQ */ -.faq-item { - padding: 20px; - margin-bottom: 2px; - background: #fff; -} -.faq-question { - font-size: 18px; - font-weight: 600; - color: #1ab394; - display: block; -} -.faq-question:hover { - color: #179d82; -} -.faq-answer { - margin-top: 10px; - background: #f3f3f4; - border: 1px solid #e7eaec; - border-radius: 3px; - padding: 15px; -} -.faq-item .tag-item { - background: #f3f3f4; - padding: 2px 6px; - font-size: 10px; - text-transform: uppercase; -} -/* Chat view */ -.message-input { - height: 90px !important; -} -.chat-avatar { - width: 36px; - height: 36px; - float: left; - margin-right: 10px; -} -.chat-user-name { - padding: 10px; -} -.chat-user { - padding: 8px 10px; - border-bottom: 1px solid #e7eaec; -} -.chat-user a { - color: inherit; -} -.chat-view { - z-index: 20012; -} -.chat-users, -.chat-statistic { - margin-left: -30px; -} -@media (max-width: 992px) { - .chat-users, - .chat-statistic { - margin-left: 0; - } -} -.chat-view .ibox-content { - padding: 0; -} -.chat-message { - padding: 10px 20px; -} -.message-avatar { - height: 48px; - width: 48px; - border: 1px solid #e7eaec; - border-radius: 4px; - margin-top: 1px; -} -.chat-discussion .chat-message.left .message-avatar { - float: left; - margin-right: 10px; -} -.chat-discussion .chat-message.right .message-avatar { - float: right; - margin-left: 10px; -} -.message { - background-color: #fff; - border: 1px solid #e7eaec; - text-align: left; - display: block; - padding: 10px 20px; - position: relative; - border-radius: 4px; -} -.chat-discussion .chat-message.left .message-date { - float: right; -} -.chat-discussion .chat-message.right .message-date { - float: left; -} -.chat-discussion .chat-message.left .message { - text-align: left; - margin-left: 55px; -} -.chat-discussion .chat-message.right .message { - text-align: right; - margin-right: 55px; -} -.message-date { - font-size: 10px; - color: #888888; -} -.message-content { - display: block; -} -.chat-discussion { - background: #eee; - padding: 15px; - height: 400px; - overflow-y: auto; -} -.chat-users { - overflow-y: auto; - height: 400px; -} -.chat-message-form .form-group { - margin-bottom: 0; -} -/* jsTree */ -.jstree-open > .jstree-anchor > .fa-folder:before { - content: "\f07c"; -} -.jstree-default .jstree-icon.none { - width: 0; -} -/* CLIENTS */ -.clients-list { - margin-top: 20px; -} -.clients-list .tab-pane { - position: relative; - height: 600px; -} -.client-detail { - position: relative; - height: 620px; -} -.clients-list table tr td { - height: 46px; - vertical-align: middle; - border: none; -} -.client-link { - font-weight: 600; - color: inherit; -} -.client-link:hover { - color: inherit; -} -.client-avatar { - width: 42px; -} -.client-avatar img { - width: 28px; - height: 28px; - border-radius: 50%; -} -.contact-type { - width: 20px; - color: #c1c3c4; -} -.client-status { - text-align: left; -} -.client-detail .vertical-timeline-content p { - margin: 0; -} -.client-detail .vertical-timeline-icon.gray-bg { - color: #a7aaab; -} -.clients-list .nav-tabs > li.active > a, -.clients-list .nav-tabs > li.active > a:hover, -.clients-list .nav-tabs > li.active > a:focus { - border-bottom: 1px solid #fff; -} -/* BLOG ARTICLE */ -.blog h2 { - font-weight: 700; -} -.blog h5 { - margin: 0 0 5px 0; -} -.blog .btn { - margin: 0 0 5px 0; -} -.article h1 { - font-size: 48px; - font-weight: 700; - color: #2f4050; -} -.article p { - font-size: 15px; - line-height: 26px; -} -.article-title { - text-align: center; - margin: 40px 0 100px 0; -} -.article .ibox-content { - padding: 40px; -} -/* ISSUE TRACKER */ -.issue-tracker .btn-link { - color: #1ab394; -} -table.issue-tracker tbody tr td { - vertical-align: middle; - height: 50px; -} -.issue-info { - width: 50%; -} -.issue-info a { - font-weight: 600; - color: #676a6c; -} -.issue-info small { - display: block; -} -/* TEAMS */ -.team-members { - margin: 10px 0; -} -.team-members img.rounded-circle { - width: 42px; - height: 42px; - margin-bottom: 5px; -} -/* AGILE BOARD */ -.sortable-list { - padding: 10px 0; -} -.agile-list { - list-style: none; - margin: 0; -} -.agile-list li { - background: #FAFAFB; - border: 1px solid #e7eaec; - margin: 0 0 10px 0; - padding: 10px; - border-radius: 2px; -} -.agile-list li:hover { - cursor: pointer; - background: #fff; -} -.agile-list li.warning-element { - border-left: 3px solid #f8ac59; -} -.agile-list li.danger-element { - border-left: 3px solid #ed5565; -} -.agile-list li.info-element { - border-left: 3px solid #1c84c6; -} -.agile-list li.success-element { - border-left: 3px solid #1ab394; -} -.agile-detail { - margin-top: 5px; - font-size: 12px; -} -/* DIFF */ -ins { - background-color: #c6ffc6; - text-decoration: none; -} -del { - background-color: #ffc6c6; -} -/* E-commerce */ -.product-box { - padding: 0; - border: 1px solid #e7eaec; -} -.product-box:hover, -.product-box.active { - border: 1px solid transparent; - -webkit-box-shadow: 0 3px 7px 0 #a8a8a8; - -moz-box-shadow: 0 3px 7px 0 #a8a8a8; - box-shadow: 0 3px 7px 0 #a8a8a8; -} -.product-imitation { - text-align: center; - padding: 90px 0; - background-color: #f8f8f9; - color: #bebec3; - font-weight: 600; -} -.cart-product-imitation { - text-align: center; - padding-top: 30px; - height: 80px; - width: 80px; - background-color: #f8f8f9; -} -.product-imitation.xl { - padding: 120px 0; -} -.product-desc { - padding: 20px; - position: relative; -} -.ecommerce .tag-list { - padding: 0; -} -.ecommerce .fa-star { - color: #d1dade; -} -.ecommerce .fa-star.active { - color: #f8ac59; -} -.ecommerce .note-editor { - border: 1px solid #e7eaec; -} -table.shoping-cart-table { - margin-bottom: 0; -} -table.shoping-cart-table tr td { - border: none; - text-align: right; -} -table.shoping-cart-table tr td.desc, -table.shoping-cart-table tr td:first-child { - text-align: left; -} -table.shoping-cart-table tr td:last-child { - width: 80px; -} -.product-name { - font-size: 16px; - font-weight: 600; - color: #676a6c; - display: block; - margin: 2px 0 5px 0; -} -.product-name:hover, -.product-name:focus { - color: #1ab394; -} -.product-price { - font-size: 14px; - font-weight: 600; - color: #ffffff; - background-color: #1ab394; - padding: 6px 12px; - position: absolute; - top: -32px; - right: 0; -} -.product-detail .ibox-content { - padding: 30px 30px 50px 30px; -} -.image-imitation { - background-color: #f8f8f9; - text-align: center; - padding: 200px 0; -} -.product-main-price small { - font-size: 10px; -} -.product-images { - margin: 0 20px; -} -/* Social feed */ -.social-feed-separated .social-feed-box { - margin-left: 62px; -} -.social-feed-separated .social-avatar { - float: left; - padding: 0; -} -.social-feed-separated .social-avatar img { - width: 52px; - height: 52px; - border: 1px solid #e7eaec; -} -.social-feed-separated .social-feed-box .social-avatar { - padding: 15px 15px 0 15px; - float: none; -} -.social-feed-box { - /*padding: 15px;*/ - border: 1px solid #e7eaec; - background: #fff; - margin-bottom: 15px; -} -.article .social-feed-box { - margin-bottom: 0; - border-bottom: none; -} -.article .social-feed-box:last-child { - margin-bottom: 0; - border-bottom: 1px solid #e7eaec; -} -.article .social-feed-box p { - font-size: 13px; - line-height: 18px; -} -.social-action { - margin: 15px; -} -.social-action .dropdown-toggle::after { - margin-left: auto; -} -.social-avatar { - padding: 15px 15px 0 15px; -} -.social-comment .social-comment { - margin-left: 45px; -} -.social-avatar img { - height: 40px; - width: 40px; - margin-right: 10px; -} -.social-avatar .media-body a { - font-size: 14px; - display: block; -} -.social-body { - padding: 15px; -} -.social-body img { - margin-bottom: 10px; -} -.social-footer { - border-top: 1px solid #e7eaec; - padding: 10px 15px; - background: #f9f9f9; -} -.social-footer .social-comment img { - width: 32px; - margin-right: 10px; -} -.social-comment:first-child { - margin-top: 0; -} -.social-comment { - margin-top: 15px; -} -.social-comment textarea { - font-size: 12px; -} -/* Vote list */ -.vote-item { - padding: 20px 25px; - background: #ffffff; - border-top: 1px solid #e7eaec; -} -.vote-item:last-child { - border-bottom: 1px solid #e7eaec; -} -.vote-item:hover { - background: #fbfbfb; -} -.vote-actions { - float: left; - width: 30px; - margin-right: 15px; - text-align: center; -} -.vote-actions a { - color: #1ab394; - font-weight: 600; -} -.vote-actions { - font-weight: 600; -} -.vote-title { - display: block; - color: inherit; - font-size: 18px; - font-weight: 600; - margin-top: 5px; - margin-bottom: 2px; -} -.vote-title:hover, -.vote-title:focus { - color: inherit; -} -.vote-info, -.vote-title { - margin-left: 45px; -} -.vote-info, -.vote-info a { - color: #b4b6b8; - font-size: 12px; -} -.vote-info a { - margin-right: 10px; -} -.vote-info a:hover { - color: #1ab394; -} -.vote-icon { - text-align: right; - font-size: 38px; - display: block; - color: #e8e9ea; -} -.vote-icon.active { - color: #1ab394; -} -body.body-small .vote-icon { - display: none; -} -.lightBoxGallery { - text-align: center; -} -.lightBoxGallery img { - margin: 5px; -} -#small-chat { - position: fixed; - bottom: 20px; - right: 20px; - z-index: 1000; -} -#small-chat .badge { - position: absolute; - top: -3px; - right: -4px; -} -.open-small-chat { - height: 38px; - width: 38px; - display: block; - background: #1ab394; - padding: 9px 8px; - text-align: center; - color: #fff; - border-radius: 50%; -} -.open-small-chat:hover { - color: white; - background: #1ab394; -} -.small-chat-box { - display: none; - position: fixed; - bottom: 20px; - right: 75px; - background: #fff; - border: 1px solid #e7eaec; - width: 230px; - height: 320px; - border-radius: 4px; -} -.small-chat-box.ng-small-chat { - display: block; -} -.body-small .small-chat-box { - bottom: 70px; - right: 20px; -} -.small-chat-box.active { - display: block; -} -.small-chat-box { - z-index: 1001; -} -.small-chat-box .heading { - background: #2f4050; - padding: 8px 15px; - font-weight: bold; - color: #fff; -} -.small-chat-box .chat-date { - opacity: 0.6; - font-size: 10px; - font-weight: normal; -} -.small-chat-box .content { - padding: 15px 15px; -} -.small-chat-box .content .author-name { - font-weight: bold; - margin-bottom: 3px; - font-size: 11px; -} -.small-chat-box .content > div { - padding-bottom: 20px; -} -.small-chat-box .content .chat-message { - padding: 5px 10px; - border-radius: 6px; - font-size: 11px; - line-height: 14px; - max-width: 80%; - background: #f3f3f4; - margin-bottom: 10px; -} -.small-chat-box .content .chat-message.active { - background: #1ab394; - color: #fff; -} -.small-chat-box .content .left { - text-align: left; - clear: both; -} -.small-chat-box .content .left .chat-message { - float: left; -} -.small-chat-box .content .right { - text-align: right; - clear: both; -} -.small-chat-box .content .right .chat-message { - float: right; -} -.small-chat-box .form-chat { - padding: 10px 10px; -} -/* - * metismenu - v2.0.2 - * A jQuery menu plugin - * https://github.com/onokumus/metisMenu - * - * Made by Osman Nuri Okumus - * Under MIT License - */ -.metismenu .plus-minus, -.metismenu .plus-times { - float: right; -} -.metismenu .arrow { - float: right; - line-height: 1.42857; -} -.metismenu .glyphicon.arrow:before { - content: "\e079"; -} -.metismenu .active > a > .glyphicon.arrow:before { - content: "\e114"; -} -.metismenu .fa.arrow:before { - content: "\f104"; -} -.metismenu .active > a > .fa.arrow:before { - content: "\f107"; -} -.metismenu .ion.arrow:before { - content: "\f3d2"; -} -.metismenu .active > a > .ion.arrow:before { - content: "\f3d0"; -} -.metismenu .fa.plus-minus:before, -.metismenu .fa.plus-times:before { - content: "\f067"; -} -.metismenu .active > a > .fa.plus-times { - -webkit-transform: rotate(45deg); - -ms-transform: rotate(45deg); - transform: rotate(45deg); -} -.metismenu .active > a > .fa.plus-minus:before { - content: "\f068"; -} -.metismenu .collapse { - display: none; -} -.metismenu .collapse.in { - display: block; -} -.metismenu .collapsing { - position: relative; - height: 0; - overflow: hidden; - -webkit-transition-timing-function: ease; - transition-timing-function: ease; - -webkit-transition-duration: .35s; - transition-duration: .35s; - -webkit-transition-property: height, visibility; - transition-property: height, visibility; -} -.mini-navbar .metismenu .collapse { - opacity: 0; -} -.mini-navbar .metismenu .collapse.in { - opacity: 1; -} -.mini-navbar .metismenu .collapse a { - display: none; -} -.mini-navbar .metismenu .collapse.in a { - display: block; -} -/* - * Usage: - * - *
- * - */ -.sk-spinner-rotating-plane.sk-spinner { - width: 30px; - height: 30px; - background-color: #1ab394; - margin: 0 auto; - -webkit-animation: sk-rotatePlane 1.2s infinite ease-in-out; - animation: sk-rotatePlane 1.2s infinite ease-in-out; -} -@-webkit-keyframes sk-rotatePlane { - 0% { - -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg); - transform: perspective(120px) rotateX(0deg) rotateY(0deg); - } - 50% { - -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg); - transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg); - } - 100% { - -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); - transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); - } -} -@keyframes sk-rotatePlane { - 0% { - -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg); - transform: perspective(120px) rotateX(0deg) rotateY(0deg); - } - 50% { - -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg); - transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg); - } - 100% { - -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); - transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); - } -} -/* - * Usage: - * - *
- *
- *
- *
- * - */ -.sk-spinner-double-bounce.sk-spinner { - width: 40px; - height: 40px; - position: relative; - margin: 0 auto; -} -.sk-spinner-double-bounce .sk-double-bounce1, -.sk-spinner-double-bounce .sk-double-bounce2 { - width: 100%; - height: 100%; - border-radius: 50%; - background-color: #1ab394; - opacity: 0.6; - position: absolute; - top: 0; - left: 0; - -webkit-animation: sk-doubleBounce 2s infinite ease-in-out; - animation: sk-doubleBounce 2s infinite ease-in-out; -} -.sk-spinner-double-bounce .sk-double-bounce2 { - -webkit-animation-delay: -1s; - animation-delay: -1s; -} -@-webkit-keyframes sk-doubleBounce { - 0%, - 100% { - -webkit-transform: scale(0); - transform: scale(0); - } - 50% { - -webkit-transform: scale(1); - transform: scale(1); - } -} -@keyframes sk-doubleBounce { - 0%, - 100% { - -webkit-transform: scale(0); - transform: scale(0); - } - 50% { - -webkit-transform: scale(1); - transform: scale(1); - } -} -/* - * Usage: - * - *
- *
- *
- *
- *
- *
- *
- * - */ -.sk-spinner-wave.sk-spinner { - margin: 0 auto; - width: 50px; - height: 30px; - text-align: center; - font-size: 10px; -} -.sk-spinner-wave div { - background-color: #1ab394; - height: 100%; - width: 6px; - display: inline-block; - -webkit-animation: sk-waveStretchDelay 1.2s infinite ease-in-out; - animation: sk-waveStretchDelay 1.2s infinite ease-in-out; -} -.sk-spinner-wave .sk-rect2 { - -webkit-animation-delay: -1.1s; - animation-delay: -1.1s; -} -.sk-spinner-wave .sk-rect3 { - -webkit-animation-delay: -1s; - animation-delay: -1s; -} -.sk-spinner-wave .sk-rect4 { - -webkit-animation-delay: -0.9s; - animation-delay: -0.9s; -} -.sk-spinner-wave .sk-rect5 { - -webkit-animation-delay: -0.8s; - animation-delay: -0.8s; -} -@-webkit-keyframes sk-waveStretchDelay { - 0%, - 40%, - 100% { - -webkit-transform: scaleY(0.4); - transform: scaleY(0.4); - } - 20% { - -webkit-transform: scaleY(1); - transform: scaleY(1); - } -} -@keyframes sk-waveStretchDelay { - 0%, - 40%, - 100% { - -webkit-transform: scaleY(0.4); - transform: scaleY(0.4); - } - 20% { - -webkit-transform: scaleY(1); - transform: scaleY(1); - } -} -/* - * Usage: - * - *
- *
- *
- *
- * - */ -.sk-spinner-wandering-cubes.sk-spinner { - margin: 0 auto; - width: 32px; - height: 32px; - position: relative; -} -.sk-spinner-wandering-cubes .sk-cube1, -.sk-spinner-wandering-cubes .sk-cube2 { - background-color: #1ab394; - width: 10px; - height: 10px; - position: absolute; - top: 0; - left: 0; - -webkit-animation: sk-wanderingCubeMove 1.8s infinite ease-in-out; - animation: sk-wanderingCubeMove 1.8s infinite ease-in-out; -} -.sk-spinner-wandering-cubes .sk-cube2 { - -webkit-animation-delay: -0.9s; - animation-delay: -0.9s; -} -@-webkit-keyframes sk-wanderingCubeMove { - 25% { - -webkit-transform: translateX(42px) rotate(-90deg) scale(0.5); - transform: translateX(42px) rotate(-90deg) scale(0.5); - } - 50% { - /* Hack to make FF rotate in the right direction */ - -webkit-transform: translateX(42px) translateY(42px) rotate(-179deg); - transform: translateX(42px) translateY(42px) rotate(-179deg); - } - 50.1% { - -webkit-transform: translateX(42px) translateY(42px) rotate(-180deg); - transform: translateX(42px) translateY(42px) rotate(-180deg); - } - 75% { - -webkit-transform: translateX(0px) translateY(42px) rotate(-270deg) scale(0.5); - transform: translateX(0px) translateY(42px) rotate(-270deg) scale(0.5); - } - 100% { - -webkit-transform: rotate(-360deg); - transform: rotate(-360deg); - } -} -@keyframes sk-wanderingCubeMove { - 25% { - -webkit-transform: translateX(42px) rotate(-90deg) scale(0.5); - transform: translateX(42px) rotate(-90deg) scale(0.5); - } - 50% { - /* Hack to make FF rotate in the right direction */ - -webkit-transform: translateX(42px) translateY(42px) rotate(-179deg); - transform: translateX(42px) translateY(42px) rotate(-179deg); - } - 50.1% { - -webkit-transform: translateX(42px) translateY(42px) rotate(-180deg); - transform: translateX(42px) translateY(42px) rotate(-180deg); - } - 75% { - -webkit-transform: translateX(0px) translateY(42px) rotate(-270deg) scale(0.5); - transform: translateX(0px) translateY(42px) rotate(-270deg) scale(0.5); - } - 100% { - -webkit-transform: rotate(-360deg); - transform: rotate(-360deg); - } -} -/* - * Usage: - * - *
- * - */ -.sk-spinner-pulse.sk-spinner { - width: 40px; - height: 40px; - margin: 0 auto; - background-color: #1ab394; - border-radius: 100%; - -webkit-animation: sk-pulseScaleOut 1s infinite ease-in-out; - animation: sk-pulseScaleOut 1s infinite ease-in-out; -} -@-webkit-keyframes sk-pulseScaleOut { - 0% { - -webkit-transform: scale(0); - transform: scale(0); - } - 100% { - -webkit-transform: scale(1); - transform: scale(1); - opacity: 0; - } -} -@keyframes sk-pulseScaleOut { - 0% { - -webkit-transform: scale(0); - transform: scale(0); - } - 100% { - -webkit-transform: scale(1); - transform: scale(1); - opacity: 0; - } -} -/* - * Usage: - * - *
- *
- *
- *
- * - */ -.sk-spinner-chasing-dots.sk-spinner { - margin: 0 auto; - width: 40px; - height: 40px; - position: relative; - text-align: center; - -webkit-animation: sk-chasingDotsRotate 2s infinite linear; - animation: sk-chasingDotsRotate 2s infinite linear; -} -.sk-spinner-chasing-dots .sk-dot1, -.sk-spinner-chasing-dots .sk-dot2 { - width: 60%; - height: 60%; - display: inline-block; - position: absolute; - top: 0; - background-color: #1ab394; - border-radius: 100%; - -webkit-animation: sk-chasingDotsBounce 2s infinite ease-in-out; - animation: sk-chasingDotsBounce 2s infinite ease-in-out; -} -.sk-spinner-chasing-dots .sk-dot2 { - top: auto; - bottom: 0; - -webkit-animation-delay: -1s; - animation-delay: -1s; -} -@-webkit-keyframes sk-chasingDotsRotate { - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} -@keyframes sk-chasingDotsRotate { - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} -@-webkit-keyframes sk-chasingDotsBounce { - 0%, - 100% { - -webkit-transform: scale(0); - transform: scale(0); - } - 50% { - -webkit-transform: scale(1); - transform: scale(1); - } -} -@keyframes sk-chasingDotsBounce { - 0%, - 100% { - -webkit-transform: scale(0); - transform: scale(0); - } - 50% { - -webkit-transform: scale(1); - transform: scale(1); - } -} -/* - * Usage: - * - *
- *
- *
- *
- *
- * - */ -.sk-spinner-three-bounce.sk-spinner { - margin: 0 auto; - width: 70px; - text-align: center; -} -.sk-spinner-three-bounce div { - width: 18px; - height: 18px; - background-color: #1ab394; - border-radius: 100%; - display: inline-block; - -webkit-animation: sk-threeBounceDelay 1.4s infinite ease-in-out; - animation: sk-threeBounceDelay 1.4s infinite ease-in-out; - /* Prevent first frame from flickering when animation starts */ - -webkit-animation-fill-mode: both; - animation-fill-mode: both; -} -.sk-spinner-three-bounce .sk-bounce1 { - -webkit-animation-delay: -0.32s; - animation-delay: -0.32s; -} -.sk-spinner-three-bounce .sk-bounce2 { - -webkit-animation-delay: -0.16s; - animation-delay: -0.16s; -} -@-webkit-keyframes sk-threeBounceDelay { - 0%, - 80%, - 100% { - -webkit-transform: scale(0); - transform: scale(0); - } - 40% { - -webkit-transform: scale(1); - transform: scale(1); - } -} -@keyframes sk-threeBounceDelay { - 0%, - 80%, - 100% { - -webkit-transform: scale(0); - transform: scale(0); - } - 40% { - -webkit-transform: scale(1); - transform: scale(1); - } -} -/* - * Usage: - * - *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- * - */ -.sk-spinner-circle.sk-spinner { - margin: 0 auto; - width: 22px; - height: 22px; - position: relative; -} -.sk-spinner-circle .sk-circle { - width: 100%; - height: 100%; - position: absolute; - left: 0; - top: 0; -} -.sk-spinner-circle .sk-circle:before { - content: ''; - display: block; - margin: 0 auto; - width: 20%; - height: 20%; - background-color: #1ab394; - border-radius: 100%; - -webkit-animation: sk-circleBounceDelay 1.2s infinite ease-in-out; - animation: sk-circleBounceDelay 1.2s infinite ease-in-out; - /* Prevent first frame from flickering when animation starts */ - -webkit-animation-fill-mode: both; - animation-fill-mode: both; -} -.sk-spinner-circle .sk-circle2 { - -webkit-transform: rotate(30deg); - -ms-transform: rotate(30deg); - transform: rotate(30deg); -} -.sk-spinner-circle .sk-circle3 { - -webkit-transform: rotate(60deg); - -ms-transform: rotate(60deg); - transform: rotate(60deg); -} -.sk-spinner-circle .sk-circle4 { - -webkit-transform: rotate(90deg); - -ms-transform: rotate(90deg); - transform: rotate(90deg); -} -.sk-spinner-circle .sk-circle5 { - -webkit-transform: rotate(120deg); - -ms-transform: rotate(120deg); - transform: rotate(120deg); -} -.sk-spinner-circle .sk-circle6 { - -webkit-transform: rotate(150deg); - -ms-transform: rotate(150deg); - transform: rotate(150deg); -} -.sk-spinner-circle .sk-circle7 { - -webkit-transform: rotate(180deg); - -ms-transform: rotate(180deg); - transform: rotate(180deg); -} -.sk-spinner-circle .sk-circle8 { - -webkit-transform: rotate(210deg); - -ms-transform: rotate(210deg); - transform: rotate(210deg); -} -.sk-spinner-circle .sk-circle9 { - -webkit-transform: rotate(240deg); - -ms-transform: rotate(240deg); - transform: rotate(240deg); -} -.sk-spinner-circle .sk-circle10 { - -webkit-transform: rotate(270deg); - -ms-transform: rotate(270deg); - transform: rotate(270deg); -} -.sk-spinner-circle .sk-circle11 { - -webkit-transform: rotate(300deg); - -ms-transform: rotate(300deg); - transform: rotate(300deg); -} -.sk-spinner-circle .sk-circle12 { - -webkit-transform: rotate(330deg); - -ms-transform: rotate(330deg); - transform: rotate(330deg); -} -.sk-spinner-circle .sk-circle2:before { - -webkit-animation-delay: -1.1s; - animation-delay: -1.1s; -} -.sk-spinner-circle .sk-circle3:before { - -webkit-animation-delay: -1s; - animation-delay: -1s; -} -.sk-spinner-circle .sk-circle4:before { - -webkit-animation-delay: -0.9s; - animation-delay: -0.9s; -} -.sk-spinner-circle .sk-circle5:before { - -webkit-animation-delay: -0.8s; - animation-delay: -0.8s; -} -.sk-spinner-circle .sk-circle6:before { - -webkit-animation-delay: -0.7s; - animation-delay: -0.7s; -} -.sk-spinner-circle .sk-circle7:before { - -webkit-animation-delay: -0.6s; - animation-delay: -0.6s; -} -.sk-spinner-circle .sk-circle8:before { - -webkit-animation-delay: -0.5s; - animation-delay: -0.5s; -} -.sk-spinner-circle .sk-circle9:before { - -webkit-animation-delay: -0.4s; - animation-delay: -0.4s; -} -.sk-spinner-circle .sk-circle10:before { - -webkit-animation-delay: -0.3s; - animation-delay: -0.3s; -} -.sk-spinner-circle .sk-circle11:before { - -webkit-animation-delay: -0.2s; - animation-delay: -0.2s; -} -.sk-spinner-circle .sk-circle12:before { - -webkit-animation-delay: -0.1s; - animation-delay: -0.1s; -} -@-webkit-keyframes sk-circleBounceDelay { - 0%, - 80%, - 100% { - -webkit-transform: scale(0); - transform: scale(0); - } - 40% { - -webkit-transform: scale(1); - transform: scale(1); - } -} -@keyframes sk-circleBounceDelay { - 0%, - 80%, - 100% { - -webkit-transform: scale(0); - transform: scale(0); - } - 40% { - -webkit-transform: scale(1); - transform: scale(1); - } -} -/* - * Usage: - * - *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- * - */ -.sk-spinner-cube-grid { - /* - * Spinner positions - * 1 2 3 - * 4 5 6 - * 7 8 9 - */ -} -.sk-spinner-cube-grid.sk-spinner { - width: 30px; - height: 30px; - margin: 0 auto; -} -.sk-spinner-cube-grid .sk-cube { - width: 33%; - height: 33%; - background-color: #1ab394; - float: left; - -webkit-animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out; - animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out; -} -.sk-spinner-cube-grid .sk-cube:nth-child(1) { - -webkit-animation-delay: 0.2s; - animation-delay: 0.2s; -} -.sk-spinner-cube-grid .sk-cube:nth-child(2) { - -webkit-animation-delay: 0.3s; - animation-delay: 0.3s; -} -.sk-spinner-cube-grid .sk-cube:nth-child(3) { - -webkit-animation-delay: 0.4s; - animation-delay: 0.4s; -} -.sk-spinner-cube-grid .sk-cube:nth-child(4) { - -webkit-animation-delay: 0.1s; - animation-delay: 0.1s; -} -.sk-spinner-cube-grid .sk-cube:nth-child(5) { - -webkit-animation-delay: 0.2s; - animation-delay: 0.2s; -} -.sk-spinner-cube-grid .sk-cube:nth-child(6) { - -webkit-animation-delay: 0.3s; - animation-delay: 0.3s; -} -.sk-spinner-cube-grid .sk-cube:nth-child(7) { - -webkit-animation-delay: 0s; - animation-delay: 0s; -} -.sk-spinner-cube-grid .sk-cube:nth-child(8) { - -webkit-animation-delay: 0.1s; - animation-delay: 0.1s; -} -.sk-spinner-cube-grid .sk-cube:nth-child(9) { - -webkit-animation-delay: 0.2s; - animation-delay: 0.2s; -} -@-webkit-keyframes sk-cubeGridScaleDelay { - 0%, - 70%, - 100% { - -webkit-transform: scale3D(1, 1, 1); - transform: scale3D(1, 1, 1); - } - 35% { - -webkit-transform: scale3D(0, 0, 1); - transform: scale3D(0, 0, 1); - } -} -@keyframes sk-cubeGridScaleDelay { - 0%, - 70%, - 100% { - -webkit-transform: scale3D(1, 1, 1); - transform: scale3D(1, 1, 1); - } - 35% { - -webkit-transform: scale3D(0, 0, 1); - transform: scale3D(0, 0, 1); - } -} -/* - * Usage: - * - *
- * - *
- * - */ -.sk-spinner-wordpress.sk-spinner { - background-color: #1ab394; - width: 30px; - height: 30px; - border-radius: 30px; - position: relative; - margin: 0 auto; - -webkit-animation: sk-innerCircle 1s linear infinite; - animation: sk-innerCircle 1s linear infinite; -} -.sk-spinner-wordpress .sk-inner-circle { - display: block; - background-color: #fff; - width: 8px; - height: 8px; - position: absolute; - border-radius: 8px; - top: 5px; - left: 5px; -} -@-webkit-keyframes sk-innerCircle { - 0% { - -webkit-transform: rotate(0); - transform: rotate(0); - } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} -@keyframes sk-innerCircle { - 0% { - -webkit-transform: rotate(0); - transform: rotate(0); - } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} -/* - * Usage: - * - *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- * - */ -.sk-spinner-fading-circle.sk-spinner { - margin: 0 auto; - width: 22px; - height: 22px; - position: relative; -} -.sk-spinner-fading-circle .sk-circle { - width: 100%; - height: 100%; - position: absolute; - left: 0; - top: 0; -} -.sk-spinner-fading-circle .sk-circle:before { - content: ''; - display: block; - margin: 0 auto; - width: 18%; - height: 18%; - background-color: #1ab394; - border-radius: 100%; - -webkit-animation: sk-circleFadeDelay 1.2s infinite ease-in-out; - animation: sk-circleFadeDelay 1.2s infinite ease-in-out; - /* Prevent first frame from flickering when animation starts */ - -webkit-animation-fill-mode: both; - animation-fill-mode: both; -} -.sk-spinner-fading-circle .sk-circle2 { - -webkit-transform: rotate(30deg); - -ms-transform: rotate(30deg); - transform: rotate(30deg); -} -.sk-spinner-fading-circle .sk-circle3 { - -webkit-transform: rotate(60deg); - -ms-transform: rotate(60deg); - transform: rotate(60deg); -} -.sk-spinner-fading-circle .sk-circle4 { - -webkit-transform: rotate(90deg); - -ms-transform: rotate(90deg); - transform: rotate(90deg); -} -.sk-spinner-fading-circle .sk-circle5 { - -webkit-transform: rotate(120deg); - -ms-transform: rotate(120deg); - transform: rotate(120deg); -} -.sk-spinner-fading-circle .sk-circle6 { - -webkit-transform: rotate(150deg); - -ms-transform: rotate(150deg); - transform: rotate(150deg); -} -.sk-spinner-fading-circle .sk-circle7 { - -webkit-transform: rotate(180deg); - -ms-transform: rotate(180deg); - transform: rotate(180deg); -} -.sk-spinner-fading-circle .sk-circle8 { - -webkit-transform: rotate(210deg); - -ms-transform: rotate(210deg); - transform: rotate(210deg); -} -.sk-spinner-fading-circle .sk-circle9 { - -webkit-transform: rotate(240deg); - -ms-transform: rotate(240deg); - transform: rotate(240deg); -} -.sk-spinner-fading-circle .sk-circle10 { - -webkit-transform: rotate(270deg); - -ms-transform: rotate(270deg); - transform: rotate(270deg); -} -.sk-spinner-fading-circle .sk-circle11 { - -webkit-transform: rotate(300deg); - -ms-transform: rotate(300deg); - transform: rotate(300deg); -} -.sk-spinner-fading-circle .sk-circle12 { - -webkit-transform: rotate(330deg); - -ms-transform: rotate(330deg); - transform: rotate(330deg); -} -.sk-spinner-fading-circle .sk-circle2:before { - -webkit-animation-delay: -1.1s; - animation-delay: -1.1s; -} -.sk-spinner-fading-circle .sk-circle3:before { - -webkit-animation-delay: -1s; - animation-delay: -1s; -} -.sk-spinner-fading-circle .sk-circle4:before { - -webkit-animation-delay: -0.9s; - animation-delay: -0.9s; -} -.sk-spinner-fading-circle .sk-circle5:before { - -webkit-animation-delay: -0.8s; - animation-delay: -0.8s; -} -.sk-spinner-fading-circle .sk-circle6:before { - -webkit-animation-delay: -0.7s; - animation-delay: -0.7s; -} -.sk-spinner-fading-circle .sk-circle7:before { - -webkit-animation-delay: -0.6s; - animation-delay: -0.6s; -} -.sk-spinner-fading-circle .sk-circle8:before { - -webkit-animation-delay: -0.5s; - animation-delay: -0.5s; -} -.sk-spinner-fading-circle .sk-circle9:before { - -webkit-animation-delay: -0.4s; - animation-delay: -0.4s; -} -.sk-spinner-fading-circle .sk-circle10:before { - -webkit-animation-delay: -0.3s; - animation-delay: -0.3s; -} -.sk-spinner-fading-circle .sk-circle11:before { - -webkit-animation-delay: -0.2s; - animation-delay: -0.2s; -} -.sk-spinner-fading-circle .sk-circle12:before { - -webkit-animation-delay: -0.1s; - animation-delay: -0.1s; -} -@-webkit-keyframes sk-circleFadeDelay { - 0%, - 39%, - 100% { - opacity: 0; - } - 40% { - opacity: 1; - } -} -@keyframes sk-circleFadeDelay { - 0%, - 39%, - 100% { - opacity: 0; - } - 40% { - opacity: 1; - } -} -.ibox-content > .sk-spinner { - display: none; -} -.ibox-content.sk-loading { - position: relative; -} -.ibox-content.sk-loading:after { - content: ''; - background-color: rgba(255, 255, 255, 0.7); - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; -} -.ibox-content.sk-loading > .sk-spinner { - display: block; - position: absolute; - top: 40%; - left: 0; - right: 0; - z-index: 2000; -} -/* PACE PLUGIN --------------------------------------------------- */ -.landing-page.pace .pace-progress { - background: #fff; - position: fixed; - z-index: 2000; - top: 0; - left: 0; - height: 2px; - -webkit-transition: width 1s; - -moz-transition: width 1s; - -o-transition: width 1s; - transition: width 1s; -} -.pace-inactive { - display: none; -} -body.landing-page { - color: #676a6c; - font-family: 'Open Sans', helvetica, arial, sans-serif; - background-color: #fff; -} -.landing-page { - /* CUSTOMIZE THE NAVBAR - -------------------------------------------------- */ - /* Flip around the padding for proper display in narrow viewports */ - /* BACKGROUNDS SLIDER - -------------------------------------------------- */ - /* CUSTOMIZE THE CAROUSEL - -------------------------------------------------- */ - /* Carousel base class */ - /* Since positioning the image, we need to help out the caption */ - /* Declare heights because of positioning of img element */ - /* Sections - ------------------------- */ - /* Buttons - only primary custom button - ------------------------- */ - /* RESPONSIVE CSS - -------------------------------------------------- */ -} -.landing-page button:focus { - outline: 0; -} -.landing-page .container { - overflow: hidden; -} -.landing-page span.navy { - color: #1ab394; -} -.landing-page p.text-color { - color: #676a6c; -} -.landing-page a.navy-link { - color: #1ab394; - text-decoration: none; -} -.landing-page a.navy-link:hover { - color: #179d82; -} -.landing-page section p { - color: #aeaeae; - font-size: 13px; -} -.landing-page address { - font-size: 13px; -} -.landing-page h1 { - margin-top: 10px; - font-size: 30px; - font-weight: 200; -} -.landing-page .navy-line { - width: 60px; - height: 1px; - margin: 60px auto 0; - border-bottom: 2px solid #1ab394; -} -.landing-page .navbar { - padding: 0 1rem; -} -.landing-page .navbar-wrapper { - position: fixed; - top: 0; - right: 0; - left: 0; - z-index: 200; -} -.landing-page .navbar-wrapper > .container { - padding-right: 0; - padding-left: 0; -} -.landing-page .navbar-wrapper .navbar { - padding-right: 15px; - padding-left: 15px; -} -.landing-page .navbar-default.navbar-scroll { - background-color: #fff; - border-color: #fff; - padding: 15px 0; -} -.landing-page .navbar-default { - background-color: transparent; - border-color: transparent; - transition: all 0.3s ease-in-out 0s; -} -.landing-page .navbar-default .nav li a { - color: #fff; - font-family: 'Open Sans', helvetica, arial, sans-serif; - font-weight: 700; - letter-spacing: 1px; - text-transform: uppercase; - font-size: 14px; -} -.landing-page .navbar-nav > li > a { - padding-top: 25px; - border-top: 6px solid transparent; -} -.landing-page .navbar-default .navbar-nav > .active > a, -.landing-page .navbar-default .navbar-nav > .active > a:hover { - background: transparent; - color: #fff; - border-top: 6px solid #1ab394; -} -.landing-page .navbar-default .navbar-nav > li > a:hover, -.landing-page .navbar-default .navbar-nav > li > a:focus { - color: #1ab394; - background: inherit; -} -.landing-page .navbar-default .navbar-nav > .active > a:focus { - background: transparent; - color: #fff; -} -.landing-page .navbar-default .navbar-nav > .active > a:focus { - background: transparent; - color: #ffffff; -} -.landing-page .navbar-default.navbar-scroll .navbar-nav > .active > a:focus { - background: transparent; - color: inherit; -} -.landing-page .navbar-default .navbar-brand:hover, -.landing-page .navbar-default .navbar-brand:focus { - background: #179d82; - color: #fff; -} -.landing-page .navbar-default .navbar-brand { - color: #fff; - height: auto; - display: block; - font-size: 14px; - background: #1ab394; - padding: 15px 20px 15px 20px; - border-radius: 0 0 5px 5px; - font-weight: 700; - transition: all 0.3s ease-in-out 0s; - margin-top: -16px; -} -.landing-page .navbar-scroll.navbar-default .nav li a { - color: #676a6c; -} -.landing-page .navbar-scroll.navbar-default .nav li a:hover { - color: #1ab394; -} -.landing-page .navbar-wrapper .navbar.navbar-scroll { - padding-top: 0; - padding-bottom: 5px; - border-bottom: 1px solid #e7eaec; - border-radius: 0; -} -.landing-page .nav.navbar-right { - flex-direction: row; -} -.landing-page .nav > li.active { - border: none; - background: inherit; -} -.landing-page .nav > li > a { - padding: 25px 10px 15px 10px; -} -.landing-page .navbar-scroll .navbar-nav > li > a { - padding: 20px 10px; -} -.landing-page .navbar-default .navbar-nav > li .nav-link.active, -.landing-page .navbar-default .navbar-nav > li .nav-link.active:hover { - border-top: 6px solid #1ab394; -} -.landing-page .navbar-fixed-top { - border: none !important; -} -.landing-page .navbar-fixed-top.navbar-scroll { - border-bottom: 1px solid #e7eaec !important; -} -.landing-page .navbar.navbar-scroll .navbar-brand { - margin-top: 5px; - border-radius: 5px; - font-size: 12px; - padding: 10px; - height: auto; -} -.landing-page .header-back { - height: 470px; - width: 100%; -} -.landing-page .header-back.one { - background: url('../img/landing/header_one.jpg') 50% 0 no-repeat; -} -.landing-page .header-back.two { - background: url('../img/landing/header_two.jpg') 50% 0 no-repeat; -} -.landing-page .carousel { - height: 470px; - overflow: hidden; -} -.landing-page .carousel-caption { - z-index: 10; -} -.landing-page .carousel .item { - height: 470px; - background-color: #777; -} -.landing-page .carousel-inner > .item > img { - position: absolute; - top: 0; - left: 0; - min-width: 100%; - height: 470px; -} -.landing-page .carousel-fade .carousel-inner .item { - opacity: 0; - -webkit-transition-property: opacity; - transition-property: opacity; -} -.landing-page .carousel-fade .carousel-inner .active { - opacity: 1; -} -.landing-page .carousel-fade .carousel-inner .active.left, -.landing-page .carousel-fade .carousel-inner .active.right { - left: 0; - opacity: 0; - z-index: 1; -} -.landing-page .carousel-fade .carousel-inner .next.left, -.landing-page .carousel-fade .carousel-inner .prev.right { - opacity: 1; -} -.landing-page .carousel-fade .carousel-control { - z-index: 2; -} -.landing-page .carousel-control.left, -.landing-page .carousel-control.right { - background: none; -} -.landing-page .carousel-control { - width: 6%; -} -.landing-page .carousel-inner .container { - position: relative; - overflow: visible; -} -.landing-page .carousel-inner { - overflow: visible; -} -.landing-page .carousel-caption { - position: absolute; - top: 100px; - left: 0; - bottom: auto; - right: auto; - text-align: left; -} -.landing-page .carousel-caption { - position: absolute; - top: 100px; - left: 0; - bottom: auto; - right: auto; - text-align: left; -} -.landing-page .carousel-caption.blank { - top: 140px; -} -.landing-page .carousel-image { - position: absolute; - right: 10px; - top: 150px; -} -.landing-page .carousel-indicators { - padding-right: 60px; -} -.landing-page .carousel-caption h1 { - font-weight: 700; - font-size: 38px; - text-transform: uppercase; - text-shadow: none; - letter-spacing: -1.5px; -} -.landing-page .carousel-caption p { - font-weight: 700; - text-transform: uppercase; - text-shadow: none; -} -.landing-page .caption-link { - color: #fff; - margin-left: 10px; - text-transform: capitalize; - font-weight: 400; -} -.landing-page .caption-link:hover { - text-decoration: none; - color: inherit; -} -.landing-page .services { - padding-top: 60px; -} -.landing-page .services h2 { - font-size: 20px; - letter-spacing: -1px; - font-weight: 600; - text-transform: uppercase; -} -.landing-page .features-block { - margin-top: 40px; -} -.landing-page .features-text { - margin-top: 40px; -} -.landing-page .features small { - color: #1ab394; -} -.landing-page .features h2 { - font-size: 18px; - margin-top: 5px; -} -.landing-page .features-text-alone { - margin: 40px 0; -} -.landing-page .features-text-alone h1 { - font-weight: 200; -} -.landing-page .features-icon { - color: #1ab394; - font-size: 40px; -} -.landing-page .navy-section { - margin-top: 60px; - background: #1ab394; - color: #fff; - padding: 20px 0; -} -.landing-page .gray-section { - background: #f4f4f4; - margin-top: 60px; -} -.landing-page .team-member { - text-align: center; -} -.landing-page .team-member img { - margin: auto; -} -.landing-page .social-icon a { - background: #1ab394; - color: #fff; - padding: 4px 8px; - height: 28px; - width: 28px; - display: block; - border-radius: 50px; -} -.landing-page .social-icon a:hover { - background: #179d82; -} -.landing-page .img-small { - height: 88px; - width: 88px; -} -.landing-page .pricing-plan { - margin: 20px 30px 0 30px; - border-radius: 4px; -} -.landing-page .pricing-plan.selected { - transform: scale(1.1); - background: #f4f4f4; -} -.landing-page .pricing-plan li { - padding: 10px 16px; - border-top: 1px solid #e7eaec; - text-align: center; - color: #aeaeae; -} -.landing-page .pricing-plan .pricing-price span { - font-weight: 700; - color: #1ab394; -} -.landing-page li.pricing-desc { - font-size: 13px; - border-top: none; - padding: 20px 16px; -} -.landing-page li.pricing-title { - background: #1ab394; - color: #fff; - padding: 10px; - border-radius: 4px 4px 0 0; - font-size: 22px; - font-weight: 600; -} -.landing-page .testimonials { - padding-top: 80px; - padding-bottom: 90px; - background-color: #1ab394; - background-image: url('../img/landing/avatar_all.png'); -} -.landing-page .big-icon { - font-size: 56px !important; -} -.landing-page .features .big-icon { - color: #1ab394 !important; -} -.landing-page .contact { - background-image: url('../img/landing/word_map.png'); - background-position: 50% 50%; - background-repeat: no-repeat; - margin-top: 60px; -} -.landing-page section.timeline { - padding-bottom: 30px; -} -.landing-page section.comments { - padding-bottom: 80px; -} -.landing-page .comments-avatar { - margin-top: 25px; - margin-left: 22px; - margin-bottom: 25px; -} -.landing-page .comments-avatar .commens-name { - font-weight: 600; - font-size: 14px; -} -.landing-page .comments-avatar img { - width: 42px; - height: 42px; - border-radius: 50%; - margin-right: 10px; -} -.landing-page .bubble { - position: relative; - height: 120px; - padding: 20px; - background: #FFFFFF; - -webkit-border-radius: 10px; - -moz-border-radius: 10px; - border-radius: 10px; - font-style: italic; - font-size: 14px; -} -.landing-page .bubble:after { - content: ''; - position: absolute; - border-style: solid; - border-width: 15px 14px 0; - border-color: #FFFFFF transparent; - display: block; - width: 0; - z-index: 1; - bottom: -15px; - left: 30px; -} -.landing-page .btn-primary.btn-outline:hover, -.landing-page .btn-success.btn-outline:hover, -.landing-page .btn-info.btn-outline:hover, -.landing-page .btn-warning.btn-outline:hover, -.landing-page .btn-danger.btn-outline:hover { - color: #fff; -} -.landing-page .btn-primary { - background-color: #1ab394; - border-color: #1ab394; - color: #FFFFFF; - font-size: 14px; - padding: 10px 20px; - font-weight: 600; -} -.landing-page .btn-primary:hover, -.landing-page .btn-primary:focus, -.landing-page .btn-primary:active, -.landing-page .btn-primary.active, -.landing-page .open .dropdown-toggle.btn-primary { - background-color: #179d82; - border-color: #179d82; - color: #FFFFFF; -} -.landing-page .btn-primary:active, -.landing-page .btn-primary.active, -.landing-page .open .dropdown-toggle.btn-primary { - background-image: none; -} -.landing-page .btn-primary.disabled, -.landing-page .btn-primary.disabled:hover, -.landing-page .btn-primary.disabled:focus, -.landing-page .btn-primary.disabled:active, -.landing-page .btn-primary.disabled.active, -.landing-page .btn-primary[disabled], -.landing-page .btn-primary[disabled]:hover, -.landing-page .btn-primary[disabled]:focus, -.landing-page .btn-primary[disabled]:active, -.landing-page .btn-primary.active[disabled], -.landing-page fieldset[disabled] .btn-primary, -.landing-page fieldset[disabled] .btn-primary:hover, -.landing-page fieldset[disabled] .btn-primary:focus, -.landing-page fieldset[disabled] .btn-primary:active, -.landing-page fieldset[disabled] .btn-primary.active { - background-color: #1dc5a3; - border-color: #1dc5a3; -} -@media (min-width: 768px) { - .landing-page { - /* Navbar positioning foo */ - /* The navbar becomes detached from the top, so we round the corners */ - /* Bump up size of carousel content */ - } - .landing-page .navbar-wrapper .container { - padding-right: 15px; - padding-left: 15px; - } - .landing-page .navbar-wrapper .navbar { - padding-right: 0; - padding-left: 0; - } - .landing-page .navbar-wrapper .navbar { - border-radius: 4px; - } - .landing-page .carousel-caption p { - margin-bottom: 20px; - font-size: 14px; - line-height: 1.4; - } - .landing-page .featurette-heading { - font-size: 50px; - } -} -@media (max-width: 992px) { - .landing-page .carousel-image { - display: none; - } -} -@media (max-width: 768px) { - .landing-page .carousel-caption, - .landing-page .carousel-caption.blank { - left: 5%; - top: 80px; - } - .landing-page .carousel-caption h1 { - font-size: 28px; - } - .landing-page .navbar.navbar-scroll .navbar-brand { - margin-top: 6px; - } - .landing-page .navbar-default { - background-color: #fff; - border-color: #fff; - padding: 15px 0; - } - .landing-page .navbar-default .navbar-nav > .active > a:focus { - background: transparent; - color: inherit; - } - .landing-page .navbar-default .nav li a { - color: #676a6c; - } - .landing-page .navbar-default .nav li a:hover { - color: #1ab394; - } - .landing-page .navbar-wrapper .navbar { - padding-top: 0; - padding-bottom: 5px; - border-bottom: 1px solid #e7eaec; - border-radius: 0; - } - .landing-page .nav > li > a { - padding: 10px 10px 15px 10px; - } - .landing-page .navbar-nav > li > a { - padding: 20px 10px; - } - .landing-page .navbar .navbar-brand { - margin-top: 5px; - border-radius: 5px; - font-size: 12px; - padding: 10px; - height: auto; - } - .landing-page .navbar-wrapper .navbar { - padding-left: 15px; - padding-right: 5px; - } - .landing-page .navbar-default .navbar-nav > .active > a, - .landing-page .navbar-default .navbar-nav > .active > a:hover { - color: inherit; - } - .landing-page .carousel-control { - display: none; - } -} -@media (min-width: 992px) { - .landing-page .featurette-heading { - margin-top: 120px; - } -} -@media (max-width: 768px) { - .landing-page .navbar .navbar-header { - display: block; - float: none; - } - .landing-page .navbar .navbar-header .navbar-toggle { - background-color: #ffffff; - padding: 9px 10px; - border: none; - } - .landing-page .nav.navbar-right { - flex-direction: column; - } -} -.landing-page .navbar-toggle { - color: #ddd; - float: right; -} -.landing-page .navbar-toggle i { - font-size: 24px; -} -body.rtls { - text-align: right !important; - /* Theme config */ -} -body.rtls .nav-second-level li a { - padding: 7px 35px 7px 10px; -} -body.rtls .ibox-title h5 { - float: right; -} -body.rtls .float-right { - float: left !important; -} -body.rtls .float-left { - float: right !important; -} -body.rtls .ibox-title { - padding: 15px 15px 8px 15px; -} -body.rtls .ibox-title .label { - float: left; -} -body.rtls #small-chat { - right: auto; - left: 20px; -} -body.rtls .small-chat-box { - right: auto; - left: 75px; -} -body.rtls .ibox-tools { - float: left; - position: static; -} -body.rtls .stat-percent { - float: left; -} -body.rtls .navbar-right { - float: left !important; -} -body.rtls .navbar-top-links li:last-child { - margin-left: 40px; - margin-right: 0; -} -body.rtls .minimalize-styl-2 { - float: right; - margin: 14px 20px 5px 5px; -} -body.rtls .feed-element > .float-left { - margin-left: 10px; - margin-right: 0; -} -body.rtls .timeline-item .date { - text-align: left; -} -body.rtls .timeline-item .date i { - left: 0; - right: auto; -} -body.rtls .timeline-item .content { - border-right: 1px solid #e7eaec; - border-left: none; -} -body.rtls .theme-config { - left: 0; - right: auto; -} -body.rtls .spin-icon { - border-radius: 0 20px 20px 0; -} -body.rtls .toast-close-button { - float: left; -} -body.rtls #toast-container > .toast:before { - margin: auto -1.5em auto 0.5em; -} -body.rtls #toast-container > div { - padding: 15px 50px 15px 15px; -} -body.rtls #toast-container > div { - background-position: 95% center; -} -body.rtls .center-orientation .vertical-timeline-icon i { - margin-left: 0; - margin-right: -12px; -} -body.rtls .vertical-timeline-icon i { - right: 50%; - left: auto; - margin-left: auto; - margin-right: -12px; -} -body.rtls .file-box { - float: right; -} -body.rtls ul.notes li { - float: right; -} -body.rtls .chat-users, -body.rtls .chat-statistic { - margin-right: -30px; - margin-left: auto; -} -body.rtls .dropdown-menu > li > a { - text-align: right; -} -body.rtls .b-r { - border-left: 1px solid #e7eaec; - border-right: none; -} -body.rtls .dd-list .dd-list { - padding-right: 30px; - padding-left: 0; -} -body.rtls .dd-item > button { - float: right; -} -body.rtls .theme-config-box { - margin-left: -220px; - margin-right: 0; -} -body.rtls .theme-config-box.show { - margin-left: 0; - margin-right: 0; -} -body.rtls .spin-icon { - right: 0; - left: auto; -} -body.rtls .skin-settings { - margin-right: 40px; - margin-left: 0; -} -body.rtls .skin-settings { - direction: ltr; -} -body.rtls .footer.fixed { - margin-right: 220px; - margin-left: 0; -} -body.rtls .navbar-static-top .dropdown-menu { - left: 0; - right: auto; -} -body.rtls .social-footer .social-comment img, -body.rtls .social-avatar img { - margin-left: 10px; - margin-right: 0; -} -body.rtls .sidebar-container .sidebar-message > a > .float-left { - margin-left: 10px; - margin-right: 0; -} -body.rtls .setings-item .switch { - margin-left: 5px; -} -body.rtls .nav > li > a i { - margin-left: 6px; -} -@media (max-width: 992px) { - body.rtls .chat-users, - body.rtls .chat-statistic { - margin-right: 0; - } -} -body.rtls.mini-navbar .footer.fixed, -body.body-small.mini-navbar .footer.fixed { - margin: 0 70px 0 0; -} -body.rtls.mini-navbar.fixed-sidebar .footer.fixed, -body.body-small.mini-navbar .footer.fixed { - margin: 0 0 0 0; -} -body.rtls.top-navigation .navbar-toggle { - float: right; - margin-left: 15px; - margin-right: 15px; -} -.body-small.rtls.top-navigation .navbar-header { - float: none; -} -body.rtls.top-navigation #page-wrapper { - margin: 0; -} -body.rtls.mini-navbar.fixed-sidebar #page-wrapper { - margin: 0 0 0 0; -} -body.rtls.body-small.fixed-sidebar.mini-navbar #page-wrapper { - margin: 0 220px 0 0; -} -body.rtls.body-small.fixed-sidebar.mini-navbar .navbar-static-side { - width: 220px; -} -.body-small.rtls .navbar-fixed-top { - margin-right: 0; -} -.body-small.rtls .navbar-header { - float: right; -} -body.rtls .navbar-top-links li:last-child { - margin-left: 20px; -} -body.rtls .top-navigation #page-wrapper, -body.rtls.mini-navbar .top-navigation #page-wrapper, -body.rtls.mini-navbar.top-navigation #page-wrapper { - margin: 0; -} -body.rtls .top-navigation .footer.fixed, -body.rtls.top-navigation .footer.fixed { - margin: 0; -} -@media (max-width: 768px) { - body.rtls .navbar-top-links li:last-child { - margin-left: 10px; - } - .navbar-top-links li a { - padding: 20px 5px; - } - .body-small.rtls #page-wrapper { - position: inherit; - margin: 0 0 0 0; - min-height: 1000px; - } - .rtls.fixed-sidebar.body-small .navbar-static-side { - display: none; - z-index: 2001; - position: fixed; - width: 220px; - } - .rtls.fixed-sidebar.body-small.mini-navbar .navbar-static-side { - display: block; - } -} -.rtls .ltr-support { - direction: ltr; -} -.rtls.mini-navbar .nav-second-level, -.rtls.mini-navbar li.active .nav-second-level { - left: auto; - right: 70px; -} -.rtls #right-sidebar { - left: -260px; - right: auto; -} -.rtls #right-sidebar.sidebar-open { - left: 0; -} -/* - * - * This is style for skin config - * Use only in demo theme - * -*/ -.theme-config { - position: absolute; - top: 90px; - right: 0; - overflow: hidden; -} -.theme-config-box { - margin-right: -220px; - position: relative; - z-index: 2100; - transition-duration: 0.8s; -} -.theme-config-box.show { - margin-right: 0; -} -.spin-icon { - background: #1ab394; - position: absolute; - padding: 7px 10px 7px 13px; - border-radius: 20px 0 0 20px; - font-size: 16px; - top: 0; - left: 0; - width: 40px; - color: #fff; - cursor: pointer; -} -.skin-settings { - width: 220px; - margin-left: 40px; - background: #f3f3f4; -} -.skin-settings .title { - background: #efefef; - text-align: center; - text-transform: uppercase; - font-weight: 600; - display: block; - padding: 10px 15px; - font-size: 12px; -} -.setings-item { - padding: 10px 30px; -} -.setings-item.skin { - text-align: center; -} -.setings-item .switch { - float: right; -} -.skin-name a { - text-transform: uppercase; -} -.setings-item a { - color: #fff; -} -.default-skin, -.blue-skin, -.ultra-skin, -.yellow-skin { - text-align: center; -} -.default-skin { - font-weight: 600; - background: #283A49; -} -.default-skin:hover { - background: #1e2e3d; -} -.blue-skin { - font-weight: 600; - background: url("patterns/header-profile-skin-1.png") repeat scroll 0 0; -} -.blue-skin:hover { - background: #0d8ddb; -} -.yellow-skin { - font-weight: 600; - background: url("patterns/header-profile-skin-3.png") repeat scroll 0 100%; -} -.yellow-skin:hover { - background: #ce8735; -} -.ultra-skin { - padding: 20px 10px; - font-weight: 600; - background: url("patterns/3.png") repeat scroll 0 0; -} -.ultra-skin:hover { - background: url("patterns/4.png") repeat scroll 0 0; -} -/* - * - * SKIN 1 - INSPINIA - Responsive Admin Theme - * NAME - Blue light - * -*/ -.skin-1 .minimalize-styl-2 { - margin: 14px 5px 5px 30px; -} -.skin-1 .navbar-top-links li:last-child { - margin-right: 30px; -} -.skin-1.fixed-nav .minimalize-styl-2 { - margin: 14px 5px 5px 15px; -} -.skin-1 .spin-icon { - background: #0e9aef !important; -} -.skin-1 .nav-header { - background-color: #0e9aef; - background-image: url('patterns/header-profile-skin-1.png'); -} -.skin-1.mini-navbar .nav-second-level { - background: #3e495f; -} -.skin-1 .breadcrumb { - background: transparent; -} -.skin-1 .page-heading { - border: none; -} -.skin-1 .nav > li.active { - background: #3a4459; -} -.skin-1 .nav > li > a { - color: #9ea6b9; -} -.skin-1 ul.nav-second-level { - background-color: inherit; -} -.skin-1 .nav > li.active > a { - color: #fff; -} -.skin-1 .navbar-minimalize { - background: #0e9aef; - border-color: #0e9aef; -} -body.skin-1 { - background: #3e495f; -} -.skin-1 .navbar-static-top { - background: #ffffff; -} -.skin-1 .dashboard-header { - background: transparent; - border-bottom: none !important; - border-top: none; - padding: 20px 30px 10px 30px; -} -.fixed-nav.skin-1 .navbar-fixed-top { - background: #fff; -} -.skin-1 .wrapper-content { - padding: 30px 15px; -} -.skin-1 #page-wrapper { - background: #f4f6fa; -} -.skin-1 .ibox-title, -.skin-1 .ibox-content { - border-width: 1px; -} -.skin-1 .ibox-content:last-child { - border-style: solid solid solid solid; -} -.skin-1 .nav > li.active { - border: none; -} -.skin-1 .nav-header { - padding: 35px 25px 25px 25px; -} -.skin-1 .nav-header a.dropdown-toggle { - color: #fff; - margin-top: 10px; -} -.skin-1 .nav-header a.dropdown-toggle .text-muted { - color: #fff; - opacity: 0.8; -} -.skin-1 .profile-element { - text-align: center; -} -.skin-1 .rounded-circle { - border-radius: 5px; -} -.skin-1 .navbar-default .nav > li > a:hover, -.skin-1 .navbar-default .nav > li > a:focus { - background: #3a4459; - color: #fff; -} -.skin-1 .nav.nav-tabs > li.active > a { - color: #555; -} -.skin-1 .nav.nav-tabs > li.active { - background: transparent; -} -/* - * - * SKIN 2 - INSPINIA - Responsive Admin Theme - * NAME - Inspinia Ultra - * -*/ -body.skin-2 { - color: #565758 !important; -} -.skin-2 .minimalize-styl-2 { - margin: 14px 5px 5px 25px; -} -.skin-2 .navbar-top-links li:last-child { - margin-right: 30px; -} -.skin-2 .spin-icon { - background: #23c6c8 !important; -} -.skin-2 .nav-header { - background-color: #23c6c8; - background-image: url('patterns/header-profile-skin-2.png'); -} -.skin-2.mini-navbar .nav-second-level { - background: #ededed; -} -.skin-2 .breadcrumb { - background: transparent; -} -.skin-2.fixed-nav .minimalize-styl-2 { - margin: 14px 5px 5px 15px; -} -.skin-2 .page-heading { - border: none; - background: rgba(255, 255, 255, 0.7); -} -.skin-2 ul.nav-second-level { - background-color: inherit; -} -.skin-2 .nav > li.active { - background: #e0e0e0; -} -.skin-2 .logo-element { - padding: 17px 0; -} -.skin-2 .nav > li > a, -.skin-2 .welcome-message { - color: #edf6ff; -} -.skin-2 #top-search::-moz-placeholder { - color: #edf6ff; - opacity: 0.5; -} -.skin-2 #side-menu > li > a, -.skin-2 .nav.nav-second-level > li > a { - color: #586b7d; -} -.skin-2 .nav > li.active > a { - color: #213a53; -} -.skin-2.mini-navbar .nav-header { - background: #213a53; -} -.skin-2 .navbar-minimalize { - background: #23c6c8; - border-color: #23c6c8; -} -.skin-2 .border-bottom { - border-bottom: none !important; -} -.skin-2 #top-search { - color: #fff; -} -body.skin-2 #wrapper { - background-color: #ededed; -} -.skin-2 .navbar-static-top { - background: #213a53; -} -.fixed-nav.skin-2 .navbar-fixed-top { - background: #213a53; - border-bottom: none !important; -} -.skin-2 .nav-header { - padding: 30px 25px 30px 25px; -} -.skin-2 .dashboard-header { - background: rgba(255, 255, 255, 0.4); - border-bottom: none !important; - border-top: none; - padding: 20px 30px 20px 30px; -} -.skin-2 .wrapper-content { - padding: 30px 15px; -} -.skin-2 .dashoard-1 .wrapper-content { - padding: 0 30px 25px 30px; -} -.skin-2 .ibox-title { - background: rgba(255, 255, 255, 0.7); - border: none; - margin-bottom: 1px; -} -.skin-2 .ibox-content { - background: rgba(255, 255, 255, 0.4); - border: none !important; -} -.skin-2 #page-wrapper { - background: #f6f6f6; - background: -webkit-radial-gradient(center, ellipse cover, #f6f6f6 20%, #d5d5d5 100%); - background: -o-radial-gradient(center, ellipse cover, #f6f6f6 20%, #d5d5d5 100%); - background: -ms-radial-gradient(center, ellipse cover, #f6f6f6 20%, #d5d5d5 100%); - background: radial-gradient(ellipse at center, #f6f6f6 20%, #d5d5d5 100%); - -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#f6f6f6, endColorstr=#d5d5d5)"; -} -.skin-2 .ibox-title, -.skin-2 .ibox-content { - border-width: 1px; -} -.skin-2 .ibox-content:last-child { - border-style: solid solid solid solid; -} -.skin-2 .nav > li.active { - border: none; -} -.skin-2 .nav-header a.dropdown-toggle { - color: #edf6ff; - margin-top: 10px; -} -.skin-2 .nav-header a.dropdown-toggle .text-muted { - color: #edf6ff; - opacity: 0.8; -} -.skin-2 .rounded-circle { - border-radius: 10px; -} -.skin-2 .nav.navbar-top-links > li > a:hover, -.skin-2 .nav.navbar-top-links > li > a:focus { - background: #1a2d41; -} -.skin-2 .navbar-default .nav > li > a:hover, -.skin-2 .navbar-default .nav > li > a:focus { - background: #e0e0e0; - color: #213a53; -} -.skin-2 .nav.nav-tabs > li.active > a { - color: #555; -} -.skin-2 .nav.nav-tabs > li.active { - background: transparent; -} -/* - * - * SKIN 3 - INSPINIA - Responsive Admin Theme - * NAME - Yellow/purple - * -*/ -.skin-3 .minimalize-styl-2 { - margin: 14px 5px 5px 30px; -} -.skin-3 .navbar-top-links li:last-child { - margin-right: 30px; -} -.skin-3.fixed-nav .minimalize-styl-2 { - margin: 14px 5px 5px 15px; -} -.skin-3 .spin-icon { - background: #ecba52 !important; -} -body.boxed-layout.skin-3 #wrapper { - background: #3e2c42; -} -.skin-3 .nav-header { - background-color: #ecba52; - background-image: url('patterns/header-profile-skin-3.png'); -} -.skin-3.mini-navbar .nav-second-level { - background: #3e2c42; -} -.skin-3 .breadcrumb { - background: transparent; -} -.skin-3 .page-heading { - border: none; -} -.skin-3 ul.nav-second-level { - background-color: inherit; -} -.skin-3 .nav > li.active { - background: #38283c; -} -.fixed-nav.skin-3 .navbar-fixed-top { - background: #fff; -} -.skin-3 .nav > li > a { - color: #948b96; -} -.skin-3 .nav > li.active > a { - color: #fff; -} -.skin-3 .navbar-minimalize { - background: #ecba52; - border-color: #ecba52; -} -body.skin-3 { - background: #3e2c42; -} -.skin-3 .navbar-static-top { - background: #ffffff; -} -.skin-3 .dashboard-header { - background: transparent; - border-bottom: none !important; - border-top: none; - padding: 20px 30px 10px 30px; -} -.skin-3 .wrapper-content { - padding: 30px 15px; -} -.skin-3 #page-wrapper { - background: #f4f6fa; -} -.skin-3 .ibox-title, -.skin-3 .ibox-content { - border-width: 1px; -} -.skin-3 .ibox-content:last-child { - border-style: solid solid solid solid; -} -.skin-3 .nav > li.active { - border: none; -} -.skin-3 .nav-header { - padding: 35px 25px 25px 25px; -} -.skin-3 .nav-header a.dropdown-toggle { - color: #fff; - margin-top: 10px; -} -.skin-3 .nav-header a.dropdown-toggle .text-muted { - color: #fff; - opacity: 0.8; -} -.skin-3 .profile-element { - text-align: center; -} -.skin-3 .rounded-circle { - border-radius: 5px; -} -.skin-3 .navbar-default .nav > li > a:hover, -.skin-3 .navbar-default .nav > li > a:focus { - background: #38283c; - color: #fff; -} -.skin-3 .nav.nav-tabs > li.active > a { - color: #555; -} -.skin-3 .nav.nav-tabs > li.active { - background: transparent; -} -/* - * - * SKIN 4 - INSPINIA - Responsive Admin Theme - * NAME - Light-Skin - * -*/ -body.light-skin { - background-color: #f9f9f9; - color: #3e5476; -} -.light-skin { - /* For handle diferent bg color in MVC version */ -} -.light-skin .select2-container--default .select2-selection--single, -.light-skin .select2-container--default .select2-selection--multiple, -.light-skin .select2-container--default .select2-selection--single .select2-selection__arrow { - height: 2.05rem; -} -.light-skin .select2-container--default .select2-selection--single .select2-selection__rendered { - line-height: 32px; -} -.light-skin .navbar-fixed-top, -.light-skin .navbar-static-top { - background-color: transparent; -} -.light-skin .Dashboard_2 .navbar.navbar-static-top, -.light-skin .Dashboard_3 .navbar.navbar-static-top, -.light-skin .Dashboard_4_1 .navbar.navbar-static-top, -.light-skin .ComposeEmail .navbar.navbar-static-top, -.light-skin .EmailView .navbar.navbar-static-top, -.light-skin .Inbox .navbar.navbar-static-top, -.light-skin .Metrics .navbar.navbar-static-top, -.light-skin .Dashboard_5 .navbar.navbar-static-top { - background: transparent; -} -.light-skin.fixed-nav .navbar-fixed-top { - background-color: #FFFFFF; -} -.light-skin.mini-navbar .nav .nav-second-level { - background-color: #f9f9f9; - box-shadow: 0 1px 8px rgba(0, 0, 0, 0.05), 0 1px 2px rgba(0, 0, 0, 0.1); -} -.light-skin.fixed-sidebar.mini-navbar .nav .nav-second-level, -.light-skin.canvas-menu.mini-navbar .nav .nav-second-level { - box-shadow: none; -} -.light-skin.canvas-menu nav.navbar-static-side { - background-color: #f9f9f9; -} -.light-skin.canvas-menu.mini-navbar nav.navbar-static-side { - box-shadow: 0 1px 8px rgba(0, 0, 0, 0.05), 0 1px 2px rgba(0, 0, 0, 0.1); -} -.light-skin.mini-navbar .nav-header { - background-color: #f9f9f9; -} -.light-skin #page-wrapper.gray-bg, -.light-skin #page-wrapper.bg-muted { - background-color: #f9f9f9; -} -.light-skin .logo-element { - color: #7c899a; -} -.light-skin nav > .sidebar-collapse > ul > li a { - color: #7c899a; - padding-top: 9px; - padding-bottom: 9px; -} -.light-skin nav > .sidebar-collapse .nav-second-level li a { - padding-top: 7px; - padding-bottom: 7px; -} -.light-skin .nav > li.active > a { - color: #384d6c; - font-weight: 700; -} -.light-skin .nav-header a { - color: #384d6c; -} -.light-skin .navbar-default .nav > li > a:hover, -.light-skin .navbar-default .nav > li > a:focus { - background-color: inherit; - color: #384d6c; - font-weight: 700; -} -.light-skin .nav-header .font-bold { - font-size: 12px; - font-weight: 700; - color: #384d6c; -} -.light-skin .nav-header .text-muted { - color: #8291a3 !important; - font-size: 12px; -} -.light-skin .nav-header { - background-color: #f9f9f9; - background-image: none; - padding: 20px 25px 20px 25px; -} -.light-skin .profile-element img { - border-radius: 6px !important; -} -.light-skin .nav > li.active { - background: #f9f9f9; -} -.light-skin ul.nav-second-level { - background: #f9f9f9; -} -.light-skin .dashboard-header { - border-bottom: none !important; - border-top: 0; - border-radius: 4px; - padding: 20px 20px 20px 20px; - margin: 10px 10px 0 10px; - box-shadow: 0 1px 8px rgba(0, 0, 0, 0.05), 0 1px 2px rgba(0, 0, 0, 0.1); -} -.light-skin .page-heading { - padding-bottom: 10px; -} -.light-skin .ibox-title { - background-color: transparent; - border: none; - padding-left: 5px; -} -.light-skin .ibox-title h5 { - font-size: 12px; -} -.light-skin .ibox-tools { - right: 5px; -} -.light-skin .ibox-tools a { - color: #7c899a !important; -} -.light-skin .ibox-heading { - background-color: #fff; - margin-bottom: 20px; -} -.light-skin .ibox-content { - box-shadow: 0 1px 8px rgba(0, 0, 0, 0.05), 0 1px 2px rgba(0, 0, 0, 0.1); - border-radius: 4px; - border: none; - background-color: #FFFFFF; -} -.light-skin .breadcrumb { - background-color: transparent; -} -.light-skin .minimalize-styl-2 { - margin: 14px 5px 5px 30px; -} -.light-skin .footer { - border: none; - background: none; -} -.light-skin .sidebar-panel { - background-color: #fff; -} -.light-skin #page-wrapper > .border-bottom, -.light-skin .ibox.border-bottom { - border-bottom: transparent !important; -} -.light-skin .fh-breadcrumb { - height: calc(100% - 176px); -} -.light-skin.top-navigation #page-wrapper > .border-bottom { - border-bottom: 1px solid #e7eaec !important; -} -.light-skin .wrapper.white-bg { - background-color: transparent; -} -.light-skin .ibox-tools a.btn-primary { - color: #FFFFFF !important; -} -.light-skin .chat-discussion { - background-color: #FFFFFF; -} -.light-skin .ibox-footer { - margin-top: 4px; - border: none; - box-shadow: 0 1px 8px rgba(0, 0, 0, 0.05), 0 1px 2px rgba(0, 0, 0, 0.1); - border-radius: 4px; -} -.light-skin .contact-box, -.light-skin .social-feed-box, -.light-skin .vertical-timeline-content { - box-shadow: 0 1px 8px rgba(0, 0, 0, 0.05), 0 1px 2px rgba(0, 0, 0, 0.1); - border: none; -} -.light-skin .navbar-default .landing_link a, -.light-skin .navbar-default .special_link a { - color: #FFFFFF; -} -body.md-skin { - font-family: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif; - background-color: #ffffff; -} -.md-skin .nav-header { - background: url("patterns/4.png") no-repeat; -} -.md-skin .label, -.md-skin .badge { - font-family: 'Roboto'; -} -.md-skin ul.nav-second-level { - background-color: inherit; -} -.md-skin .font-bold { - font-weight: 500; -} -.md-skin .wrapper-content { - padding: 30px 20px 40px; -} -@media (max-width: 768px) { - .md-skin .wrapper-content { - padding: 30px 0 40px; - } -} -.md-skin .page-heading { - border-bottom: none !important; - border-top: 0; - padding: 0 10px 20px 10px; - box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.34), 0 0 6px 0 rgba(0, 0, 0, 0.14); -} -.md-skin .full-height-layout .page-heading { - border-bottom: 1px solid #e7eaec !important; -} -.md-skin .ibox { - clear: both; - margin-bottom: 25px; - margin-top: 0; - padding: 0; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); -} -.md-skin .ibox.border-bottom { - border-bottom: none !important; -} -.md-skin .ibox-title, -.md-skin .ibox-content { - border-style: none; -} -.md-skin .ibox-title h5 { - font-size: 16px; - font-weight: 400; -} -.md-skin a.close-canvas-menu { - color: #ffffff; -} -.md-skin .welcome-message { - color: #ffffff !important; - font-weight: 300; -} -.md-skin #top-search::-moz-placeholder { - color: #ffffff; -} -.md-skin #top-search::-webkit-input-placeholder { - color: #ffffff; -} -.md-skin #nestable-output, -.md-skin #nestable2-output { - font-family: 'Roboto', lucida grande, lucida sans unicode, helvetica, arial, sans-serif; -} -.md-skin .landing-page { - font-family: 'Roboto', helvetica, arial, sans-serif; -} -.md-skin .landing-page.navbar-default.navbar-scroll { - background-color: #fff !important; -} -.md-skin .landing-page.navbar-default { - background-color: transparent !important; - box-shadow: none; -} -.md-skin .landing-page.navbar-default .nav li a { - font-family: 'Roboto', helvetica, arial, sans-serif; -} -.md-skin .nav > li > a { - color: #676a6c; - padding: 14px 20px 14px 25px; -} -.md-skin .nav.navbar-right > li > a { - color: #ffffff; -} -.md-skin .nav > li.active > a { - color: #5b5d5f; - font-weight: 700; -} -.md-skin .navbar-default .nav > li > a:hover, -.md-skin .navbar-default .nav > li > a:focus { - font-weight: 700; - color: #5b5d5f; -} -.md-skin .nav .open > a, -.md-skin .nav .open > a:hover, -.md-skin .nav .open > a:focus { - background: #1ab394; -} -.md-skin .navbar-top-links li { - display: inline-table; -} -.md-skin .navbar-top-links .dropdown-menu li { - display: block; -} -.md-skin .pace-done .nav-header { - transition: all 0.4s; -} -.md-skin .nav > li.active { - background: #f8f8f9; -} -.md-skin .nav-second-level li a { - padding: 7px 10px 7px 52px; -} -.md-skin .nav-third-level li a { - padding-left: 62px; -} -.md-skin .navbar-top-links li a { - padding: 20px 10px; - min-height: 50px; -} -.md-skin .nav > li > a { - font-weight: 400; -} -.md-skin .navbar-static-side .nav > li > a:focus, -.md-skin .navbar-static-side .nav > li > a:hover { - background-color: inherit; -} -.md-skin .navbar-top-links .dropdown-menu li a { - padding: 3px 20px; - min-height: inherit; -} -.md-skin .nav-header .navbar-fixed-top a { - color: #ffffff; -} -.md-skin .nav-header .text-muted { - color: #ffffff !important; -} -.md-skin .navbar-form-custom .form-control { - font-weight: 300; -} -.md-skin .mini-navbar .nav-second-level { - background-color: inherit; -} -.md-skin .mini-navbar li.active .nav-second-level { - left: 65px; -} -.md-skin .canvas-menu.mini-navbar .nav-second-level { - background: inherit; -} -.md-skin .pace-done .navbar-static-side, -.md-skin .pace-done .nav-header, -.md-skin .pace-done li.active, -.md-skin .pace-done #page-wrapper, -.md-skin .pace-done .footer { - -webkit-transition: all 0.4s; - -moz-transition: all 0.4s; - -o-transition: all 0.4s; - transition: all 0.4s; -} -.md-skin .navbar-fixed-top { - background: #fff; - transition-duration: 0.4s; - z-index: 2030; - border-bottom: none !important; -} -.md-skin .navbar-fixed-top, -.md-skin .navbar-static-top { - background-color: #1ab394 !important; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); -} -.md-skin .navbar-static-side { - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); -} -.md-skin #right-sidebar { - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); - border: none; - z-index: 900; -} -.md-skin .white-bg .navbar-fixed-top, -.md-skin .white-bg .navbar-static-top { - background: #fff !important; -} -.md-skin .contact-box { - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); - border: none; -} -.md-skin .dashboard-header { - border-bottom: none !important; - border-top: 0; - padding: 20px 20px 20px 20px; - margin: 30px 20px 0 20px; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); -} -@media (max-width: 768px) { - .md-skin .dashboard-header { - margin: 20px 0 0 0; - } -} -.md-skin ul.notes li div { - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); -} -.md-skin .file { - border: none; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); -} -.md-skin .mail-box { - background-color: #ffffff; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); - padding: 0; - margin-bottom: 20px; - border: none; -} -.md-skin .mail-box-header { - border: none; - background-color: #ffffff; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); - padding: 30px 20px 20px 20px; -} -.md-skin .mailbox-content { - border: none; - padding: 20px; - background: #ffffff; -} -.md-skin .social-feed-box { - border: none; - background: #fff; - margin-bottom: 15px; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); -} -.md-skin.landing-page .navbar-default { - background-color: transparent !important; - border-color: transparent; - transition: all 0.3s ease-in-out 0s; - box-shadow: none; -} -.md-skin.landing-page .navbar-default.navbar-scroll, -.md-skin.landing-page.body-small .navbar-default { - background-color: #ffffff !important; -} -.md-skin.landing-page .nav > li.active { - background: inherit; -} -.md-skin.landing-page .navbar-scroll .navbar-nav > li > a { - padding: 20px 10px; -} -.md-skin.landing-page .navbar-default .nav li a { - font-family: 'Roboto', helvetica, arial, sans-serif; -} -.md-skin.landing-page .nav > li > a { - padding: 25px 10px 15px 10px; -} -.md-skin.landing-page .navbar-default .navbar-nav > li > a:hover, -.md-skin.landing-page .navbar-default .navbar-nav > li > a:focus { - background: inherit; - color: #1ab394; -} -.md-skin.landing-page.body-small .nav.navbar-right > li > a { - color: #676a6c; -} -.md-skin .landing_link a, -.md-skin .special_link a { - color: #ffffff !important; -} -.md-skin.canvas-menu.mini-navbar .nav-second-level { - background: #f8f8f9; -} -.md-skin.mini-navbar .nav-second-level { - background-color: #ffffff; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); -} -.md-skin.mini-navbar .nav-second-level li a { - padding-left: 0; -} -.md-skin.mini-navbar.fixed-sidebar .nav-second-level li a { - padding-left: 52px; -} -.md-skin.top-navigation .nav.navbar-right > li > a { - padding: 15px 20px; - color: #676a6c; -} -.md-skin.top-navigation .nav > li a:hover, -.md-skin .top-navigation .nav > li a:focus, -.md-skin.top-navigation .nav .open > a, -.md-skin.top-navigation .nav .open > a:hover, -.md-skin.top-navigation .nav .open > a:focus { - color: #1ab394; - background: #ffffff; -} -.md-skin.top-navigation .nav > li.active a { - color: #1ab394; - background: #ffffff; -} -.md-skin.fixed-nav #side-menu { - background-color: #fff; -} -.md-skin.fixed-nav #wrapper.top-navigation #page-wrapper { - margin-top: 0; -} -.md-skin.fixed-sidebar.mini-navbar #page-wrapper { - margin: 0 0 0 0; -} -.md-skin.body-small.fixed-sidebar.mini-navbar .navbar-static-side { - width: 220px; - background-color: #ffffff; -} -.md-skin.boxed-layout #wrapper { - background-color: #ffffff; -} -.md-skin.canvas-menu nav.navbar-static-side { - z-index: 2001; - background: #ffffff; - height: 100%; - position: fixed; - display: none; -} -@media (min-width: 768px) { - #page-wrapper { - position: inherit; - min-height: 100vh; - } - .navbar-static-side { - z-index: 2001; - width: 220px; - } - .navbar-top-links .dropdown-messages, - .navbar-top-links .dropdown-tasks, - .navbar-top-links .dropdown-alerts { - margin-left: auto; - } -} -@media (max-width: 768px) { - #page-wrapper { - position: inherit; - margin: 0 0 0 0; - min-height: 100vh; - width: 100%; - } - .body-small .navbar-static-side { - display: block; - z-index: 2001; - width: 0; - overflow: hidden; - } - .body-small.mini-navbar .navbar-static-side { - display: block; - overflow: visible; - } - .lock-word { - display: none; - } - .navbar-form-custom { - display: none; - } - .navbar-header { - display: inline; - float: left; - } - .sidebar-panel { - z-index: 2; - position: relative; - width: auto; - min-height: 100% !important; - } - .sidebar-content .wrapper { - padding-right: 0; - z-index: 1; - } - .fixed-sidebar.body-small .navbar-static-side { - display: none; - z-index: 2001; - position: fixed; - width: 220px; - } - .fixed-sidebar.body-small.mini-navbar .navbar-static-side { - display: block; - } - .ibox-tools { - float: none; - text-align: right; - display: block; - } - .navbar-static-side { - display: none; - } - body:not(.mini-navbar) { - -webkit-transition: background-color 500ms linear; - -moz-transition: background-color 500ms linear; - -o-transition: background-color 500ms linear; - -ms-transition: background-color 500ms linear; - transition: background-color 500ms linear; - background-color: #f3f3f4; - } -} -@media (max-width: 350px) { - .timeline-item .date { - text-align: left; - width: 110px; - position: relative; - padding-top: 30px; - } - .timeline-item .date i { - position: absolute; - top: 0; - left: 15px; - padding: 5px; - width: 30px; - text-align: center; - border: 1px solid #e7eaec; - background: #f8f8f8; - } - .timeline-item .content { - border-left: none; - border-top: 1px solid #e7eaec; - padding-top: 10px; - min-height: 100px; - } - .nav.navbar-top-links li.dropdown { - display: none; - } - .ibox-tools { - float: none; - text-align: left; - display: inline-block; - } -} -/* Only demo */ -@media (max-width: 1000px) { - .welcome-message { - display: none; - } -} -@media print { - nav.navbar-static-side { - display: none; - } - body { - overflow: visible !important; - } - #page-wrapper { - margin: 0; - } -} diff --git a/web/static/img/flag/en-US.png b/web/static/img/flag/en-US.png deleted file mode 100644 index 5706b57..0000000 Binary files a/web/static/img/flag/en-US.png and /dev/null differ diff --git a/web/static/img/flag/zh-CN.png b/web/static/img/flag/zh-CN.png deleted file mode 100644 index 8c35f25..0000000 Binary files a/web/static/img/flag/zh-CN.png and /dev/null differ diff --git a/web/static/js/bootstrap-table-locale-all.min.js b/web/static/js/bootstrap-table-locale-all.min.js deleted file mode 100644 index 2d11efc..0000000 --- a/web/static/js/bootstrap-table-locale-all.min.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * bootstrap-table - An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation) - * - * @version v1.16.0 - * @homepage https://bootstrap-table.com - * @author wenzhixin (http://wenzhixin.net.cn/) - * @license MIT - */ - -!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(require("jquery")):"function"==typeof define&&define.amd?define(["jquery"],n):n((t=t||self).jQuery)}(this,(function(t){"use strict";t=t&&t.hasOwnProperty("default")?t.default:t;var n="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function o(t,n){return t(n={exports:{}},n.exports),n.exports}var r=function(t){return t&&t.Math==Math&&t},a=r("object"==typeof globalThis&&globalThis)||r("object"==typeof window&&window)||r("object"==typeof self&&self)||r("object"==typeof n&&n)||Function("return this")(),e=function(t){try{return!!t()}catch(t){return!0}},c=!e((function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})),i={}.propertyIsEnumerable,u=Object.getOwnPropertyDescriptor,f={f:u&&!i.call({1:2},1)?function(t){var n=u(this,t);return!!n&&n.enumerable}:i},l=function(t,n){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:n}},s={}.toString,m=function(t){return s.call(t).slice(8,-1)},g="".split,d=e((function(){return!Object("z").propertyIsEnumerable(0)}))?function(t){return"String"==m(t)?g.call(t,""):Object(t)}:Object,h=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t},p=function(t){return d(h(t))},S=function(t){return"object"==typeof t?null!==t:"function"==typeof t},w=function(t,n){if(!S(t))return t;var o,r;if(n&&"function"==typeof(o=t.toString)&&!S(r=o.call(t)))return r;if("function"==typeof(o=t.valueOf)&&!S(r=o.call(t)))return r;if(!n&&"function"==typeof(o=t.toString)&&!S(r=o.call(t)))return r;throw TypeError("Can't convert object to primitive value")},T={}.hasOwnProperty,P=function(t,n){return T.call(t,n)},R=a.document,v=S(R)&&S(R.createElement),A=!c&&!e((function(){return 7!=Object.defineProperty((t="div",v?R.createElement(t):{}),"a",{get:function(){return 7}}).a;var t})),b=Object.getOwnPropertyDescriptor,x={f:c?b:function(t,n){if(t=p(t),n=w(n,!0),A)try{return b(t,n)}catch(t){}if(P(t,n))return l(!f.f.call(t,n),t[n])}},C=function(t){if(!S(t))throw TypeError(String(t)+" is not an object");return t},O=Object.defineProperty,k={f:c?O:function(t,n,o){if(C(t),n=w(n,!0),C(o),A)try{return O(t,n,o)}catch(t){}if("get"in o||"set"in o)throw TypeError("Accessors not supported");return"value"in o&&(t[n]=o.value),t}},M=c?function(t,n,o){return k.f(t,n,l(1,o))}:function(t,n,o){return t[n]=o,t},E=function(t,n){try{M(a,t,n)}catch(o){a[t]=n}return n},N=a["__core-js_shared__"]||E("__core-js_shared__",{}),y=Function.toString;"function"!=typeof N.inspectSource&&(N.inspectSource=function(t){return y.call(t)});var D,H,F,L,B=N.inspectSource,z=a.WeakMap,j="function"==typeof z&&/native code/.test(B(z)),U=o((function(t){(t.exports=function(t,n){return N[t]||(N[t]=void 0!==n?n:{})})("versions",[]).push({version:"3.6.0",mode:"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})})),G=0,J=Math.random(),V=function(t){return"Symbol("+String(void 0===t?"":t)+")_"+(++G+J).toString(36)},Z=U("keys"),I={},K=a.WeakMap;if(j){var q=new K,_=q.get,W=q.has,Y=q.set;D=function(t,n){return Y.call(q,t,n),n},H=function(t){return _.call(q,t)||{}},F=function(t){return W.call(q,t)}}else{var Q=Z[L="state"]||(Z[L]=V(L));I[Q]=!0,D=function(t,n){return M(t,Q,n),n},H=function(t){return P(t,Q)?t[Q]:{}},F=function(t){return P(t,Q)}}var X,$,tt={set:D,get:H,has:F,enforce:function(t){return F(t)?H(t):D(t,{})},getterFor:function(t){return function(n){var o;if(!S(n)||(o=H(n)).type!==t)throw TypeError("Incompatible receiver, "+t+" required");return o}}},nt=o((function(t){var n=tt.get,o=tt.enforce,r=String(String).split("String");(t.exports=function(t,n,e,c){var i=!!c&&!!c.unsafe,u=!!c&&!!c.enumerable,f=!!c&&!!c.noTargetGet;"function"==typeof e&&("string"!=typeof n||P(e,"name")||M(e,"name",n),o(e).source=r.join("string"==typeof n?n:"")),t!==a?(i?!f&&t[n]&&(u=!0):delete t[n],u?t[n]=e:M(t,n,e)):u?t[n]=e:E(n,e)})(Function.prototype,"toString",(function(){return"function"==typeof this&&n(this).source||B(this)}))})),ot=a,rt=function(t){return"function"==typeof t?t:void 0},at=function(t,n){return arguments.length<2?rt(ot[t])||rt(a[t]):ot[t]&&ot[t][n]||a[t]&&a[t][n]},et=Math.ceil,ct=Math.floor,it=function(t){return isNaN(t=+t)?0:(t>0?ct:et)(t)},ut=Math.min,ft=function(t){return t>0?ut(it(t),9007199254740991):0},lt=Math.max,st=Math.min,mt=function(t){return function(n,o,r){var a,e=p(n),c=ft(e.length),i=function(t,n){var o=it(t);return o<0?lt(o+n,0):st(o,n)}(r,c);if(t&&o!=o){for(;c>i;)if((a=e[i++])!=a)return!0}else for(;c>i;i++)if((t||i in e)&&e[i]===o)return t||i||0;return!t&&-1}},gt={includes:mt(!0),indexOf:mt(!1)}.indexOf,dt=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"].concat("length","prototype"),ht={f:Object.getOwnPropertyNames||function(t){return function(t,n){var o,r=p(t),a=0,e=[];for(o in r)!P(I,o)&&P(r,o)&&e.push(o);for(;n.length>a;)P(r,o=n[a++])&&(~gt(e,o)||e.push(o));return e}(t,dt)}},pt={f:Object.getOwnPropertySymbols},St=at("Reflect","ownKeys")||function(t){var n=ht.f(C(t)),o=pt.f;return o?n.concat(o(t)):n},wt=function(t,n){for(var o=St(n),r=k.f,a=x.f,e=0;e=74)&&(X=zt.match(/Chrome\/(\d+)/))&&($=X[1]);var Jt,Vt=$&&+$,Zt=Ft("species"),It=Ft("isConcatSpreadable"),Kt=Vt>=51||!e((function(){var t=[];return t[It]=!1,t.concat()[0]!==t})),qt=(Jt="concat",Vt>=51||!e((function(){var t=[];return(t.constructor={})[Zt]=function(){return{foo:1}},1!==t[Jt](Boolean).foo}))),_t=function(t){if(!S(t))return!1;var n=t[It];return void 0!==n?!!n:Ot(t)};!function(t,n){var o,r,e,c,i,u=t.target,f=t.global,l=t.stat;if(o=f?a:l?a[u]||E(u,{}):(a[u]||{}).prototype)for(r in n){if(c=n[r],e=t.noTargetGet?(i=Ct(o,r))&&i.value:o[r],!xt(f?r:u+(l?".":"#")+r,t.forced)&&void 0!==e){if(typeof c==typeof e)continue;wt(c,e)}(t.sham||e&&e.sham)&&M(c,"sham",!0),nt(o,r,c,t)}}({target:"Array",proto:!0,forced:!Kt||!qt},{concat:function(t){var n,o,r,a,e,c=kt(this),i=Bt(c,0),u=0;for(n=-1,r=arguments.length;n9007199254740991)throw TypeError("Maximum allowed index exceeded");for(o=0;o=9007199254740991)throw TypeError("Maximum allowed index exceeded");Mt(i,u++,e)}return i.length=u,i}}),t.fn.bootstrapTable.locales["af-ZA"]={formatLoadingMessage:function(){return"Besig om te laai, wag asseblief"},formatRecordsPerPage:function(t){return"".concat(t," rekords per bladsy")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Resultate ".concat(t," tot ").concat(n," van ").concat(o," rye (filtered from ").concat(r," total rows)"):"Resultate ".concat(t," tot ").concat(n," van ").concat(o," rye")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Soek"},formatNoMatches:function(){return"Geen rekords gevind nie"},formatPaginationSwitch:function(){return"Wys/verberg bladsy nummering"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Herlaai"},formatToggle:function(){return"Wissel"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Kolomme"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["af-ZA"]),t.fn.bootstrapTable.locales["ar-SA"]={formatLoadingMessage:function(){return"جاري التحميل, يرجى الإنتظار"},formatRecordsPerPage:function(t){return"".concat(t," سجل لكل صفحة")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"الظاهر ".concat(t," إلى ").concat(n," من ").concat(o," سجل ").concat(r," total rows)"):"الظاهر ".concat(t," إلى ").concat(n," من ").concat(o," سجل")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"بحث"},formatNoMatches:function(){return"لا توجد نتائج مطابقة للبحث"},formatPaginationSwitch:function(){return"إخفاءإظهار ترقيم الصفحات"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"تحديث"},formatToggle:function(){return"تغيير"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"أعمدة"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["ar-SA"]),t.fn.bootstrapTable.locales["ca-ES"]={formatLoadingMessage:function(){return"Espereu, si us plau"},formatRecordsPerPage:function(t){return"".concat(t," resultats per pàgina")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Mostrant de ".concat(t," fins ").concat(n," - total ").concat(o," resultats (filtered from ").concat(r," total rows)"):"Mostrant de ".concat(t," fins ").concat(n," - total ").concat(o," resultats")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Cerca"},formatNoMatches:function(){return"No s'han trobat resultats"},formatPaginationSwitch:function(){return"Amaga/Mostra paginació"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Refresca"},formatToggle:function(){return"Alterna formatació"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Columnes"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Tots"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["ca-ES"]),t.fn.bootstrapTable.locales["cs-CZ"]={formatLoadingMessage:function(){return"Čekejte, prosím"},formatRecordsPerPage:function(t){return"".concat(t," položek na stránku")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Zobrazena ".concat(t,". - ").concat(n," . položka z celkových ").concat(o," (filtered from ").concat(r," total rows)"):"Zobrazena ".concat(t,". - ").concat(n," . položka z celkových ").concat(o)},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Vyhledávání"},formatNoMatches:function(){return"Nenalezena žádná vyhovující položka"},formatPaginationSwitch:function(){return"Skrýt/Zobrazit stránkování"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Aktualizovat"},formatToggle:function(){return"Přepni"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Sloupce"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Vše"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["cs-CZ"]),t.fn.bootstrapTable.locales["da-DK"]={formatLoadingMessage:function(){return"Indlæser, vent venligst"},formatRecordsPerPage:function(t){return"".concat(t," poster pr side")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Viser ".concat(t," til ").concat(n," af ").concat(o," række").concat(o>1?"r":""," (filtered from ").concat(r," total rows)"):"Viser ".concat(t," til ").concat(n," af ").concat(o," række").concat(o>1?"r":"")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Viser ".concat(t," række").concat(t>1?"r":"")},formatClearSearch:function(){return"Ryd filtre"},formatSearch:function(){return"Søg"},formatNoMatches:function(){return"Ingen poster fundet"},formatPaginationSwitch:function(){return"Skjul/vis nummerering"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Opdater"},formatToggle:function(){return"Skift"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Kolonner"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Alle"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Eksporter"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["da-DK"]),t.fn.bootstrapTable.locales["de-DE"]={formatLoadingMessage:function(){return"Lade, bitte warten"},formatRecordsPerPage:function(t){return"".concat(t," Zeilen pro Seite.")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Zeige Zeile ".concat(t," bis ").concat(n," von ").concat(o," Zeile").concat(o>1?"n":""," (Gefiltert von ").concat(r," Zeile").concat(r>1?"n":"",")"):"Zeige Zeile ".concat(t," bis ").concat(n," von ").concat(o," Zeile").concat(o>1?"n":"",".")},formatSRPaginationPreText:function(){return"Vorherige Seite"},formatSRPaginationPageText:function(t){return"Zu Seite ".concat(t)},formatSRPaginationNextText:function(){return"Nächste Seite"},formatDetailPagination:function(t){return"Zeige ".concat(t," Zeile").concat(t>1?"n":"",".")},formatClearSearch:function(){return"Lösche Filter"},formatSearch:function(){return"Suchen"},formatNoMatches:function(){return"Keine passenden Ergebnisse gefunden"},formatPaginationSwitch:function(){return"Verstecke/Zeige Nummerierung"},formatPaginationSwitchDown:function(){return"Zeige Nummerierung"},formatPaginationSwitchUp:function(){return"Verstecke Nummerierung"},formatRefresh:function(){return"Neu laden"},formatToggle:function(){return"Umschalten"},formatToggleOn:function(){return"Normale Ansicht"},formatToggleOff:function(){return"Kartenansicht"},formatColumns:function(){return"Spalten"},formatColumnsToggleAll:function(){return"Alle umschalten"},formatFullscreen:function(){return"Vollbild"},formatAllRows:function(){return"Alle"},formatAutoRefresh:function(){return"Automatisches Neuladen"},formatExport:function(){return"Datenexport"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Erweiterte Suche"},formatAdvancedCloseButton:function(){return"Schließen"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["de-DE"]),t.fn.bootstrapTable.locales["el-GR"]={formatLoadingMessage:function(){return"Φορτώνει, παρακαλώ περιμένετε"},formatRecordsPerPage:function(t){return"".concat(t," αποτελέσματα ανά σελίδα")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Εμφανίζονται από την ".concat(t," ως την ").concat(n," από σύνολο ").concat(o," σειρών (filtered from ").concat(r," total rows)"):"Εμφανίζονται από την ".concat(t," ως την ").concat(n," από σύνολο ").concat(o," σειρών")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Αναζητήστε"},formatNoMatches:function(){return"Δεν βρέθηκαν αποτελέσματα"},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Refresh"},formatToggle:function(){return"Toggle"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Columns"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["el-GR"]),t.fn.bootstrapTable.locales["en-US"]={formatLoadingMessage:function(){return"Loading, please wait"},formatRecordsPerPage:function(t){return"".concat(t," rows per page")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Showing ".concat(t," to ").concat(n," of ").concat(o," rows (filtered from ").concat(r," total rows)"):"Showing ".concat(t," to ").concat(n," of ").concat(o," rows")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Search"},formatNoMatches:function(){return"No matching records found"},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Refresh"},formatToggle:function(){return"Toggle"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Columns"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["en-US"]),t.fn.bootstrapTable.locales["es-AR"]={formatLoadingMessage:function(){return"Cargando, espere por favor"},formatRecordsPerPage:function(t){return"".concat(t," registros por página")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Mostrando desde ".concat(t," a ").concat(n," de ").concat(o," filas (filtrado de ").concat(r," columnas totales)"):"Mostrando desde ".concat(t," a ").concat(n," de ").concat(o," filas")},formatSRPaginationPreText:function(){return"página anterior"},formatSRPaginationPageText:function(t){return"a la página ".concat(t)},formatSRPaginationNextText:function(){return"siguiente página"},formatDetailPagination:function(t){return"Mostrando ".concat(t," columnas")},formatClearSearch:function(){return"Limpiar búsqueda"},formatSearch:function(){return"Buscar"},formatNoMatches:function(){return"No se encontraron registros"},formatPaginationSwitch:function(){return"Ocultar/Mostrar paginación"},formatPaginationSwitchDown:function(){return"Mostrar paginación"},formatPaginationSwitchUp:function(){return"Ocultar paginación"},formatRefresh:function(){return"Recargar"},formatToggle:function(){return"Cambiar"},formatToggleOn:function(){return"Mostrar vista de carta"},formatToggleOff:function(){return"Ocultar vista de carta"},formatColumns:function(){return"Columnas"},formatColumnsToggleAll:function(){return"Cambiar todo"},formatFullscreen:function(){return"Pantalla completa"},formatAllRows:function(){return"Todo"},formatAutoRefresh:function(){return"Auto Recargar"},formatExport:function(){return"Exportar datos"},formatJumpTo:function(){return"Ir"},formatAdvancedSearch:function(){return"Búsqueda avanzada"},formatAdvancedCloseButton:function(){return"Cerrar"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["es-AR"]),t.fn.bootstrapTable.locales["es-CL"]={formatLoadingMessage:function(){return"Cargando, espere por favor"},formatRecordsPerPage:function(t){return"".concat(t," filas por página")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Mostrando ".concat(t," a ").concat(n," de ").concat(o," filas (filtered from ").concat(r," total rows)"):"Mostrando ".concat(t," a ").concat(n," de ").concat(o," filas")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Limpiar búsqueda"},formatSearch:function(){return"Buscar"},formatNoMatches:function(){return"No se encontraron registros"},formatPaginationSwitch:function(){return"Ocultar/Mostrar paginación"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Refrescar"},formatToggle:function(){return"Cambiar"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Columnas"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Todo"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["es-CL"]),t.fn.bootstrapTable.locales["es-CR"]={formatLoadingMessage:function(){return"Cargando, por favor espere"},formatRecordsPerPage:function(t){return"".concat(t," registros por página")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Mostrando de ".concat(t," a ").concat(n," registros de ").concat(o," registros en total (filtered from ").concat(r," total rows)"):"Mostrando de ".concat(t," a ").concat(n," registros de ").concat(o," registros en total")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Limpiar búsqueda"},formatSearch:function(){return"Buscar"},formatNoMatches:function(){return"No se encontraron registros"},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Refrescar"},formatToggle:function(){return"Alternar"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Columnas"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Todo"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["es-CR"]),t.fn.bootstrapTable.locales["es-ES"]={formatLoadingMessage:function(){return"Por favor espere"},formatRecordsPerPage:function(t){return"".concat(t," resultados por página")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Mostrando desde ".concat(t," hasta ").concat(n," - En total ").concat(o," resultados (filtered from ").concat(r," total rows)"):"Mostrando desde ".concat(t," hasta ").concat(n," - En total ").concat(o," resultados")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Limpiar búsqueda"},formatSearch:function(){return"Buscar"},formatNoMatches:function(){return"No se encontraron resultados"},formatPaginationSwitch:function(){return"Ocultar/Mostrar paginación"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Refrescar"},formatToggle:function(){return"Ocultar/Mostrar"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Columnas"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Todos"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Exportar los datos"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Búsqueda avanzada"},formatAdvancedCloseButton:function(){return"Cerrar"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["es-ES"]),t.fn.bootstrapTable.locales["es-MX"]={formatLoadingMessage:function(){return"Cargando, espere por favor"},formatRecordsPerPage:function(t){return"".concat(t," registros por página")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Mostrando ".concat(t," a ").concat(n," de ").concat(o," filas (filtered from ").concat(r," total rows)"):"Mostrando ".concat(t," a ").concat(n," de ").concat(o," filas")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Mostrando ".concat(t," filas")},formatClearSearch:function(){return"Limpiar búsqueda"},formatSearch:function(){return"Buscar"},formatNoMatches:function(){return"No se encontraron registros que coincidan"},formatPaginationSwitch:function(){return"Mostrar/ocultar paginación"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Actualizar"},formatToggle:function(){return"Cambiar vista"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Columnas"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Pantalla completa"},formatAllRows:function(){return"Todo"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["es-MX"]),t.fn.bootstrapTable.locales["es-NI"]={formatLoadingMessage:function(){return"Cargando, por favor espere"},formatRecordsPerPage:function(t){return"".concat(t," registros por página")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Mostrando de ".concat(t," a ").concat(n," registros de ").concat(o," registros en total (filtered from ").concat(r," total rows)"):"Mostrando de ".concat(t," a ").concat(n," registros de ").concat(o," registros en total")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Limpiar búsqueda"},formatSearch:function(){return"Buscar"},formatNoMatches:function(){return"No se encontraron registros"},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Refrescar"},formatToggle:function(){return"Alternar"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Columnas"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Todo"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["es-NI"]),t.fn.bootstrapTable.locales["es-SP"]={formatLoadingMessage:function(){return"Cargando, por favor espera"},formatRecordsPerPage:function(t){return"".concat(t," registros por página.")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"".concat(t," - ").concat(n," de ").concat(o," registros (filtered from ").concat(r," total rows)"):"".concat(t," - ").concat(n," de ").concat(o," registros.")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Limpiar búsqueda"},formatSearch:function(){return"Buscar"},formatNoMatches:function(){return"No se han encontrado registros."},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Actualizar"},formatToggle:function(){return"Alternar"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Columnas"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Todo"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["es-SP"]),t.fn.bootstrapTable.locales["et-EE"]={formatLoadingMessage:function(){return"Päring käib, palun oota"},formatRecordsPerPage:function(t){return"".concat(t," rida lehe kohta")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Näitan tulemusi ".concat(t," kuni ").concat(n," - kokku ").concat(o," tulemust (filtered from ").concat(r," total rows)"):"Näitan tulemusi ".concat(t," kuni ").concat(n," - kokku ").concat(o," tulemust")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Otsi"},formatNoMatches:function(){return"Päringu tingimustele ei vastanud ühtegi tulemust"},formatPaginationSwitch:function(){return"Näita/Peida lehtedeks jagamine"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Värskenda"},formatToggle:function(){return"Lülita"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Veerud"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Kõik"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["et-EE"]),t.fn.bootstrapTable.locales["eu-EU"]={formatLoadingMessage:function(){return"Itxaron mesedez"},formatRecordsPerPage:function(t){return"".concat(t," emaitza orriko.")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"".concat(o," erregistroetatik ").concat(t,"etik ").concat(n,"erakoak erakusten (filtered from ").concat(r," total rows)"):"".concat(o," erregistroetatik ").concat(t,"etik ").concat(n,"erakoak erakusten.")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Bilatu"},formatNoMatches:function(){return"Ez da emaitzarik aurkitu"},formatPaginationSwitch:function(){return"Ezkutatu/Erakutsi orrikatzea"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Eguneratu"},formatToggle:function(){return"Ezkutatu/Erakutsi"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Zutabeak"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Guztiak"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["eu-EU"]),t.fn.bootstrapTable.locales["fa-IR"]={formatLoadingMessage:function(){return"در حال بارگذاری, لطفا صبر کنید"},formatRecordsPerPage:function(t){return"".concat(t," رکورد در صفحه")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"نمایش ".concat(t," تا ").concat(n," از ").concat(o," ردیف (filtered from ").concat(r," total rows)"):"نمایش ".concat(t," تا ").concat(n," از ").concat(o," ردیف")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"جستجو"},formatNoMatches:function(){return"رکوردی یافت نشد."},formatPaginationSwitch:function(){return"نمایش/مخفی صفحه بندی"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"به روز رسانی"},formatToggle:function(){return"تغییر نمایش"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"سطر ها"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"همه"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["fa-IR"]),t.fn.bootstrapTable.locales["fi-FI"]={formatLoadingMessage:function(){return"Ladataan, ole hyvä ja odota"},formatRecordsPerPage:function(t){return"".concat(t," riviä sivulla")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Näytetään rivit ".concat(t," - ").concat(n," / ").concat(o," (filtered from ").concat(r," total rows)"):"Näytetään rivit ".concat(t," - ").concat(n," / ").concat(o)},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Poista suodattimet"},formatSearch:function(){return"Hae"},formatNoMatches:function(){return"Hakuehtoja vastaavia tuloksia ei löytynyt"},formatPaginationSwitch:function(){return"Näytä/Piilota sivutus"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Päivitä"},formatToggle:function(){return"Valitse"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Sarakkeet"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Kaikki"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Vie tiedot"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["fi-FI"]),t.fn.bootstrapTable.locales["fr-BE"]={formatLoadingMessage:function(){return"Chargement en cours"},formatRecordsPerPage:function(t){return"".concat(t," lignes par page")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Affiche de ".concat(t," à ").concat(n," sur ").concat(o," lignes (filtrés à partir de ").concat(r," lignes)"):"Affiche de ".concat(t," à ").concat(n," sur ").concat(o," lignes")},formatSRPaginationPreText:function(){return"page précédente"},formatSRPaginationPageText:function(t){return"vers la page ".concat(t)},formatSRPaginationNextText:function(){return"page suivante"},formatDetailPagination:function(t){return"Affiche ".concat(t," lignes")},formatClearSearch:function(){return"Effacer la recherche"},formatSearch:function(){return"Recherche"},formatNoMatches:function(){return"Pas de lignes trouvés"},formatPaginationSwitch:function(){return"Cacher/Afficher pagination"},formatPaginationSwitchDown:function(){return"Afficher pagination"},formatPaginationSwitchUp:function(){return"Cacher pagination"},formatRefresh:function(){return"Rafraichir"},formatToggle:function(){return"Basculer"},formatToggleOn:function(){return"Afficher vue carte"},formatToggleOff:function(){return"Cacher vue carte"},formatColumns:function(){return"Colonnes"},formatColumnsToggleAll:function(){return"Tout basculer"},formatFullscreen:function(){return"Plein écran"},formatAllRows:function(){return"Tout"},formatAutoRefresh:function(){return"Rafraîchissement automatique"},formatExport:function(){return"Exporter les données"},formatJumpTo:function(){return"Aller à"},formatAdvancedSearch:function(){return"Recherche avancée"},formatAdvancedCloseButton:function(){return"Fermer"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["fr-BE"]),t.fn.bootstrapTable.locales["fr-CH"]={formatLoadingMessage:function(){return"Chargement en cours"},formatRecordsPerPage:function(t){return"".concat(t," lignes par page")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Affiche de ".concat(t," à ").concat(n," sur ").concat(o," lignes (filtrés à partir de ").concat(r," lignes)"):"Affiche de ".concat(t," à ").concat(n," sur ").concat(o," lignes")},formatSRPaginationPreText:function(){return"page précédente"},formatSRPaginationPageText:function(t){return"vers la page ".concat(t)},formatSRPaginationNextText:function(){return"page suivante"},formatDetailPagination:function(t){return"Affiche ".concat(t," lignes")},formatClearSearch:function(){return"Effacer la recherche"},formatSearch:function(){return"Recherche"},formatNoMatches:function(){return"Pas de lignes trouvés"},formatPaginationSwitch:function(){return"Cacher/Afficher pagination"},formatPaginationSwitchDown:function(){return"Afficher pagination"},formatPaginationSwitchUp:function(){return"Cacher pagination"},formatRefresh:function(){return"Rafraichir"},formatToggle:function(){return"Basculer"},formatToggleOn:function(){return"Afficher vue carte"},formatToggleOff:function(){return"Cacher vue carte"},formatColumns:function(){return"Colonnes"},formatColumnsToggleAll:function(){return"Tout basculer"},formatFullscreen:function(){return"Plein écran"},formatAllRows:function(){return"Tout"},formatAutoRefresh:function(){return"Rafraîchissement automatique"},formatExport:function(){return"Exporter les données"},formatJumpTo:function(){return"Aller à"},formatAdvancedSearch:function(){return"Recherche avancée"},formatAdvancedCloseButton:function(){return"Fermer"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["fr-CH"]),t.fn.bootstrapTable.locales["fr-FR"]={formatLoadingMessage:function(){return"Chargement en cours"},formatRecordsPerPage:function(t){return"".concat(t," lignes par page")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Affiche de ".concat(t," à ").concat(n," sur ").concat(o," lignes (filtrés à partir de ").concat(r," lignes)"):"Affiche de ".concat(t," à ").concat(n," sur ").concat(o," lignes")},formatSRPaginationPreText:function(){return"page précédente"},formatSRPaginationPageText:function(t){return"vers la page ".concat(t)},formatSRPaginationNextText:function(){return"page suivante"},formatDetailPagination:function(t){return"Affiche ".concat(t," lignes")},formatClearSearch:function(){return"Effacer la recherche"},formatSearch:function(){return"Recherche"},formatNoMatches:function(){return"Aucun résultat"},formatPaginationSwitch:function(){return"Cacher/Afficher pagination"},formatPaginationSwitchDown:function(){return"Afficher pagination"},formatPaginationSwitchUp:function(){return"Cacher pagination"},formatRefresh:function(){return"Rafraichir"},formatToggle:function(){return"Basculer"},formatToggleOn:function(){return"Afficher vue carte"},formatToggleOff:function(){return"Cacher vue carte"},formatColumns:function(){return"Colonnes"},formatColumnsToggleAll:function(){return"Tout basculer"},formatFullscreen:function(){return"Plein écran"},formatAllRows:function(){return"Tout"},formatAutoRefresh:function(){return"Rafraîchissement automatique"},formatExport:function(){return"Exporter les données"},formatJumpTo:function(){return"Aller à"},formatAdvancedSearch:function(){return"Recherche avancée"},formatAdvancedCloseButton:function(){return"Fermer"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["fr-FR"]),t.fn.bootstrapTable.locales["fr-LU"]={formatLoadingMessage:function(){return"Chargement en cours"},formatRecordsPerPage:function(t){return"".concat(t," lignes par page")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Affiche de ".concat(t," à ").concat(n," sur ").concat(o," lignes (filtrés à partir de ").concat(r," lignes)"):"Affiche de ".concat(t," à ").concat(n," sur ").concat(o," lignes")},formatSRPaginationPreText:function(){return"page précédente"},formatSRPaginationPageText:function(t){return"vers la page ".concat(t)},formatSRPaginationNextText:function(){return"page suivante"},formatDetailPagination:function(t){return"Affiche ".concat(t," lignes")},formatClearSearch:function(){return"Effacer la recherche"},formatSearch:function(){return"Recherche"},formatNoMatches:function(){return"Pas de lignes trouvés"},formatPaginationSwitch:function(){return"Cacher/Afficher pagination"},formatPaginationSwitchDown:function(){return"Afficher pagination"},formatPaginationSwitchUp:function(){return"Cacher pagination"},formatRefresh:function(){return"Rafraichir"},formatToggle:function(){return"Basculer"},formatToggleOn:function(){return"Afficher vue carte"},formatToggleOff:function(){return"Cacher vue carte"},formatColumns:function(){return"Colonnes"},formatColumnsToggleAll:function(){return"Tout basculer"},formatFullscreen:function(){return"Plein écran"},formatAllRows:function(){return"Tout"},formatAutoRefresh:function(){return"Rafraîchissement automatique"},formatExport:function(){return"Exporter les données"},formatJumpTo:function(){return"Aller à"},formatAdvancedSearch:function(){return"Recherche avancée"},formatAdvancedCloseButton:function(){return"Fermer"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["fr-LU"]),t.fn.bootstrapTable.locales["he-IL"]={formatLoadingMessage:function(){return"טוען, נא להמתין"},formatRecordsPerPage:function(t){return"".concat(t," שורות בעמוד")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"מציג ".concat(t," עד ").concat(n," מ-").concat(o,"שורות").concat(r," total rows)"):"מציג ".concat(t," עד ").concat(n," מ-").concat(o," שורות")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"חיפוש"},formatNoMatches:function(){return"לא נמצאו רשומות תואמות"},formatPaginationSwitch:function(){return"הסתר/הצג מספור דפים"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"רענן"},formatToggle:function(){return"החלף תצוגה"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"עמודות"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"הכל"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["he-IL"]),t.fn.bootstrapTable.locales["hr-HR"]={formatLoadingMessage:function(){return"Molimo pričekajte"},formatRecordsPerPage:function(t){return"".concat(t," broj zapisa po stranici")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Prikazujem ".concat(t,". - ").concat(n,". od ukupnog broja zapisa ").concat(o," (filtered from ").concat(r," total rows)"):"Prikazujem ".concat(t,". - ").concat(n,". od ukupnog broja zapisa ").concat(o)},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Pretraži"},formatNoMatches:function(){return"Nije pronađen niti jedan zapis"},formatPaginationSwitch:function(){return"Prikaži/sakrij stranice"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Osvježi"},formatToggle:function(){return"Promijeni prikaz"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Kolone"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Sve"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["hr-HR"]),t.fn.bootstrapTable.locales["hu-HU"]={formatLoadingMessage:function(){return"Betöltés, kérem várjon"},formatRecordsPerPage:function(t){return"".concat(t," rekord per oldal")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Megjelenítve ".concat(t," - ").concat(n," / ").concat(o," összesen (filtered from ").concat(r," total rows)"):"Megjelenítve ".concat(t," - ").concat(n," / ").concat(o," összesen")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Keresés"},formatNoMatches:function(){return"Nincs találat"},formatPaginationSwitch:function(){return"Lapozó elrejtése/megjelenítése"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Frissítés"},formatToggle:function(){return"Összecsuk/Kinyit"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Oszlopok"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Összes"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["hu-HU"]),t.fn.bootstrapTable.locales["id-ID"]={formatLoadingMessage:function(){return"Memuat, mohon tunggu"},formatRecordsPerPage:function(t){return"".concat(t," baris per halaman")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Menampilkan ".concat(t," sampai ").concat(n," dari ").concat(o," baris (filtered from ").concat(r," total rows)"):"Menampilkan ".concat(t," sampai ").concat(n," dari ").concat(o," baris")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Bersihkan filter"},formatSearch:function(){return"Pencarian"},formatNoMatches:function(){return"Tidak ditemukan data yang cocok"},formatPaginationSwitch:function(){return"Sembunyikan/Tampilkan halaman"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Muat ulang"},formatToggle:function(){return"Beralih"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"kolom"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Semua"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Ekspor data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["id-ID"]),t.fn.bootstrapTable.locales["it-IT"]={formatLoadingMessage:function(){return"Caricamento in corso"},formatRecordsPerPage:function(t){return"".concat(t," elementi per pagina")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Visualizzazione da ".concat(t," a ").concat(n," di ").concat(o," elementi (filtered from ").concat(r," total rows)"):"Visualizzazione da ".concat(t," a ").concat(n," di ").concat(o," elementi")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Pulisci filtri"},formatSearch:function(){return"Cerca"},formatNoMatches:function(){return"Nessun elemento trovato"},formatPaginationSwitch:function(){return"Nascondi/Mostra paginazione"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Aggiorna"},formatToggle:function(){return"Attiva/Disattiva"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Colonne"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Tutto"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Esporta dati"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["it-IT"]),t.fn.bootstrapTable.locales["ja-JP"]={formatLoadingMessage:function(){return"読み込み中です。少々お待ちください。"},formatRecordsPerPage:function(t){return"ページ当たり最大".concat(t,"件")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"全".concat(o,"件から、").concat(t,"から").concat(n,"件目まで表示しています (filtered from ").concat(r," total rows)"):"全".concat(o,"件から、").concat(t,"から").concat(n,"件目まで表示しています")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"検索"},formatNoMatches:function(){return"該当するレコードが見つかりません"},formatPaginationSwitch:function(){return"ページ数を表示・非表示"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"更新"},formatToggle:function(){return"トグル"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"列"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"すべて"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["ja-JP"]),t.fn.bootstrapTable.locales["ka-GE"]={formatLoadingMessage:function(){return"იტვირთება, გთხოვთ მოიცადოთ"},formatRecordsPerPage:function(t){return"".concat(t," ჩანაწერი თითო გვერდზე")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"ნაჩვენებია ".concat(t,"-დან ").concat(n,"-მდე ჩანაწერი ჯამური ").concat(o,"-დან (filtered from ").concat(r," total rows)"):"ნაჩვენებია ".concat(t,"-დან ").concat(n,"-მდე ჩანაწერი ჯამური ").concat(o,"-დან")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"ძებნა"},formatNoMatches:function(){return"მონაცემები არ არის"},formatPaginationSwitch:function(){return"გვერდების გადამრთველის დამალვა/გამოჩენა"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"განახლება"},formatToggle:function(){return"ჩართვა/გამორთვა"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"სვეტები"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["ka-GE"]),t.fn.bootstrapTable.locales["ko-KR"]={formatLoadingMessage:function(){return"데이터를 불러오는 중입니다"},formatRecordsPerPage:function(t){return"페이지 당 ".concat(t,"개 데이터 출력")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"전체 ".concat(o,"개 중 ").concat(t,"~").concat(n,"번째 데이터 출력, (filtered from ").concat(r," total rows)"):"전체 ".concat(o,"개 중 ").concat(t,"~").concat(n,"번째 데이터 출력,")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"검색"},formatNoMatches:function(){return"조회된 데이터가 없습니다."},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"새로 고침"},formatToggle:function(){return"전환"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"컬럼 필터링"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["ko-KR"]),t.fn.bootstrapTable.locales["ms-MY"]={formatLoadingMessage:function(){return"Permintaan sedang dimuatkan. Sila tunggu sebentar"},formatRecordsPerPage:function(t){return"".concat(t," rekod setiap muka surat")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Sedang memaparkan rekod ".concat(t," hingga ").concat(n," daripada jumlah ").concat(o," rekod (filtered from ").concat(r," total rows)"):"Sedang memaparkan rekod ".concat(t," hingga ").concat(n," daripada jumlah ").concat(o," rekod")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Cari"},formatNoMatches:function(){return"Tiada rekod yang menyamai permintaan"},formatPaginationSwitch:function(){return"Tunjuk/sembunyi muka surat"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Muatsemula"},formatToggle:function(){return"Tukar"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Lajur"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Semua"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["ms-MY"]),t.fn.bootstrapTable.locales["nb-NO"]={formatLoadingMessage:function(){return"Oppdaterer, vennligst vent"},formatRecordsPerPage:function(t){return"".concat(t," poster pr side")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Viser ".concat(t," til ").concat(n," av ").concat(o," rekker (filtered from ").concat(r," total rows)"):"Viser ".concat(t," til ").concat(n," av ").concat(o," rekker")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Søk"},formatNoMatches:function(){return"Ingen poster funnet"},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Oppdater"},formatToggle:function(){return"Endre"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Kolonner"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["nb-NO"]),t.fn.bootstrapTable.locales["nl-BE"]={formatLoadingMessage:function(){return"Laden, even geduld"},formatRecordsPerPage:function(t){return"".concat(t," records per pagina")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Toon ".concat(t," tot ").concat(n," van ").concat(o," record").concat(o>1?"s":""," (gefilterd van ").concat(r," records in totaal)"):"Toon ".concat(t," tot ").concat(n," van ").concat(o," record").concat(o>1?"s":"")},formatSRPaginationPreText:function(){return"vorige pagina"},formatSRPaginationPageText:function(t){return"tot pagina ".concat(t)},formatSRPaginationNextText:function(){return"volgende pagina"},formatDetailPagination:function(t){return"Toon ".concat(t," record").concat(t>1?"s":"")},formatClearSearch:function(){return"Verwijder filters"},formatSearch:function(){return"Zoeken"},formatNoMatches:function(){return"Geen resultaten gevonden"},formatPaginationSwitch:function(){return"Verberg/Toon paginering"},formatPaginationSwitchDown:function(){return"Toon paginering"},formatPaginationSwitchUp:function(){return"Verberg paginering"},formatRefresh:function(){return"Vernieuwen"},formatToggle:function(){return"Omschakelen"},formatToggleOn:function(){return"Toon kaartweergave"},formatToggleOff:function(){return"Verberg kaartweergave"},formatColumns:function(){return"Kolommen"},formatColumnsToggleAll:function(){return"Allen omschakelen"},formatFullscreen:function(){return"Volledig scherm"},formatAllRows:function(){return"Alle"},formatAutoRefresh:function(){return"Automatisch vernieuwen"},formatExport:function(){return"Exporteer gegevens"},formatJumpTo:function(){return"GA"},formatAdvancedSearch:function(){return"Geavanceerd zoeken"},formatAdvancedCloseButton:function(){return"Sluiten"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["nl-BE"]),t.fn.bootstrapTable.locales["nl-NL"]={formatLoadingMessage:function(){return"Laden, even geduld"},formatRecordsPerPage:function(t){return"".concat(t," records per pagina")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Toon ".concat(t," tot ").concat(n," van ").concat(o," record").concat(o>1?"s":""," (gefilterd van ").concat(r," records in totaal)"):"Toon ".concat(t," tot ").concat(n," van ").concat(o," record").concat(o>1?"s":"")},formatSRPaginationPreText:function(){return"vorige pagina"},formatSRPaginationPageText:function(t){return"tot pagina ".concat(t)},formatSRPaginationNextText:function(){return"volgende pagina"},formatDetailPagination:function(t){return"Toon ".concat(t," record").concat(t>1?"s":"")},formatClearSearch:function(){return"Verwijder filters"},formatSearch:function(){return"Zoeken"},formatNoMatches:function(){return"Geen resultaten gevonden"},formatPaginationSwitch:function(){return"Verberg/Toon paginering"},formatPaginationSwitchDown:function(){return"Toon paginering"},formatPaginationSwitchUp:function(){return"Verberg paginering"},formatRefresh:function(){return"Vernieuwen"},formatToggle:function(){return"Omschakelen"},formatToggleOn:function(){return"Toon kaartweergave"},formatToggleOff:function(){return"Verberg kaartweergave"},formatColumns:function(){return"Kolommen"},formatColumnsToggleAll:function(){return"Allen omschakelen"},formatFullscreen:function(){return"Volledig scherm"},formatAllRows:function(){return"Alle"},formatAutoRefresh:function(){return"Automatisch vernieuwen"},formatExport:function(){return"Exporteer gegevens"},formatJumpTo:function(){return"GA"},formatAdvancedSearch:function(){return"Geavanceerd zoeken"},formatAdvancedCloseButton:function(){return"Sluiten"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["nl-NL"]),t.fn.bootstrapTable.locales["pl-PL"]={formatLoadingMessage:function(){return"Ładowanie, proszę czekać"},formatRecordsPerPage:function(t){return"".concat(t," rekordów na stronę")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Wyświetlanie rekordów od ".concat(t," do ").concat(n," z ").concat(o," (filtered from ").concat(r," total rows)"):"Wyświetlanie rekordów od ".concat(t," do ").concat(n," z ").concat(o)},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Szukaj"},formatNoMatches:function(){return"Niestety, nic nie znaleziono"},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Odśwież"},formatToggle:function(){return"Przełącz"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Kolumny"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["pl-PL"]),t.fn.bootstrapTable.locales["pt-BR"]={formatLoadingMessage:function(){return"Carregando, aguarde"},formatRecordsPerPage:function(t){return"".concat(t," registros por página")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Exibindo ".concat(t," até ").concat(n," de ").concat(o," linhas (filtered from ").concat(r," total rows)"):"Exibindo ".concat(t," até ").concat(n," de ").concat(o," linhas")},formatSRPaginationPreText:function(){return"página anterior"},formatSRPaginationPageText:function(t){return"Para a página ".concat(t)},formatSRPaginationNextText:function(){return"próxima página"},formatDetailPagination:function(t){return"Mostrando ".concat(t," linhas")},formatClearSearch:function(){return"Limpar Pesquisa"},formatSearch:function(){return"Pesquisar"},formatNoMatches:function(){return"Nenhum registro encontrado"},formatPaginationSwitch:function(){return"Ocultar/Exibir paginação"},formatPaginationSwitchDown:function(){return"Mostrar Paginação"},formatPaginationSwitchUp:function(){return"Esconder Paginação"},formatRefresh:function(){return"Recarregar"},formatToggle:function(){return"Alternar"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Colunas"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Tela cheia"},formatAllRows:function(){return"Tudo"},formatAutoRefresh:function(){return"Atualização Automática"},formatExport:function(){return"Exportar dados"},formatJumpTo:function(){return"IR"},formatAdvancedSearch:function(){return"Pesquisa Avançada"},formatAdvancedCloseButton:function(){return"Fechar"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["pt-BR"]),t.fn.bootstrapTable.locales["pt-PT"]={formatLoadingMessage:function(){return"A carregar, por favor aguarde"},formatRecordsPerPage:function(t){return"".concat(t," registos por página")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"A mostrar ".concat(t," até ").concat(n," de ").concat(o," linhas (filtered from ").concat(r," total rows)"):"A mostrar ".concat(t," até ").concat(n," de ").concat(o," linhas")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Pesquisa"},formatNoMatches:function(){return"Nenhum registo encontrado"},formatPaginationSwitch:function(){return"Esconder/Mostrar paginação"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Atualizar"},formatToggle:function(){return"Alternar"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Colunas"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Tudo"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["pt-PT"]),t.fn.bootstrapTable.locales["ro-RO"]={formatLoadingMessage:function(){return"Se incarca, va rugam asteptati"},formatRecordsPerPage:function(t){return"".concat(t," inregistrari pe pagina")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Arata de la ".concat(t," pana la ").concat(n," din ").concat(o," randuri (filtered from ").concat(r," total rows)"):"Arata de la ".concat(t," pana la ").concat(n," din ").concat(o," randuri")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Cauta"},formatNoMatches:function(){return"Nu au fost gasite inregistrari"},formatPaginationSwitch:function(){return"Ascunde/Arata paginatia"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Reincarca"},formatToggle:function(){return"Comuta"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Coloane"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Toate"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["ro-RO"]),t.fn.bootstrapTable.locales["ru-RU"]={formatLoadingMessage:function(){return"Пожалуйста, подождите, идёт загрузка"},formatRecordsPerPage:function(t){return"".concat(t," записей на страницу")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Записи с ".concat(t," по ").concat(n," из ").concat(o," (filtered from ").concat(r," total rows)"):"Записи с ".concat(t," по ").concat(n," из ").concat(o)},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Очистить фильтры"},formatSearch:function(){return"Поиск"},formatNoMatches:function(){return"Ничего не найдено"},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Обновить"},formatToggle:function(){return"Переключить"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Колонки"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["ru-RU"]),t.fn.bootstrapTable.locales["sk-SK"]={formatLoadingMessage:function(){return"Prosím čakajte"},formatRecordsPerPage:function(t){return"".concat(t," záznamov na stranu")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Zobrazená ".concat(t,". - ").concat(n,". položka z celkových ").concat(o," (filtered from ").concat(r," total rows)"):"Zobrazená ".concat(t,". - ").concat(n,". položka z celkových ").concat(o)},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Odstráň filtre"},formatSearch:function(){return"Vyhľadávanie"},formatNoMatches:function(){return"Nenájdená žiadna vyhovujúca položka"},formatPaginationSwitch:function(){return"Skry/Zobraz stránkovanie"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Obnoviť"},formatToggle:function(){return"Prepni"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Stĺpce"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Všetky"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Exportuj dáta"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["sk-SK"]),t.fn.bootstrapTable.locales["sr-Cyrl-RS"]={formatLoadingMessage:function(){return"Молим сачекај"},formatRecordsPerPage:function(t){return"".concat(t," редова по страни")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Приказано ".concat(t,". - ").concat(n,". од укупног броја редова ").concat(o," (филтрирано од ").concat(r,")"):"Приказано ".concat(t,". - ").concat(n,". од укупног броја редова ").concat(o)},formatSRPaginationPreText:function(){return"претходна страна"},formatSRPaginationPageText:function(t){return"на страну ".concat(t)},formatSRPaginationNextText:function(){return"следећа страна"},formatDetailPagination:function(t){return"Приказано ".concat(t," редова")},formatClearSearch:function(){return"Обриши претрагу"},formatSearch:function(){return"Пронађи"},formatNoMatches:function(){return"Није пронађен ни један податак"},formatPaginationSwitch:function(){return"Прикажи/сакриј пагинацију"},formatPaginationSwitchDown:function(){return"Прикажи пагинацију"},formatPaginationSwitchUp:function(){return"Сакриј пагинацију"},formatRefresh:function(){return"Освежи"},formatToggle:function(){return"Промени приказ"},formatToggleOn:function(){return"Прикажи картице"},formatToggleOff:function(){return"Сакриј картице"},formatColumns:function(){return"Колоне"},formatColumnsToggleAll:function(){return"Прикажи/сакриј све"},formatFullscreen:function(){return"Цео екран"},formatAllRows:function(){return"Све"},formatAutoRefresh:function(){return"Аутоматско освежавање"},formatExport:function(){return"Извези податке"},formatJumpTo:function(){return"Иди"},formatAdvancedSearch:function(){return"Напредна претрага"},formatAdvancedCloseButton:function(){return"Затвори"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["sr-Cyrl-RS"]),t.fn.bootstrapTable.locales["sr-Latn-RS"]={formatLoadingMessage:function(){return"Molim sačekaj"},formatRecordsPerPage:function(t){return"".concat(t," redova po strani")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Prikazano ".concat(t,". - ").concat(n,". od ukupnog broja redova ").concat(o," (filtrirano od ").concat(r,")"):"Prikazano ".concat(t,". - ").concat(n,". od ukupnog broja redova ").concat(o)},formatSRPaginationPreText:function(){return"prethodna strana"},formatSRPaginationPageText:function(t){return"na stranu ".concat(t)},formatSRPaginationNextText:function(){return"sledeća strana"},formatDetailPagination:function(t){return"Prikazano ".concat(t," redova")},formatClearSearch:function(){return"Obriši pretragu"},formatSearch:function(){return"Pronađi"},formatNoMatches:function(){return"Nije pronađen ni jedan podatak"},formatPaginationSwitch:function(){return"Prikaži/sakrij paginaciju"},formatPaginationSwitchDown:function(){return"Prikaži paginaciju"},formatPaginationSwitchUp:function(){return"Sakrij paginaciju"},formatRefresh:function(){return"Osveži"},formatToggle:function(){return"Promeni prikaz"},formatToggleOn:function(){return"Prikaži kartice"},formatToggleOff:function(){return"Sakrij kartice"},formatColumns:function(){return"Kolone"},formatColumnsToggleAll:function(){return"Prikaži/sakrij sve"},formatFullscreen:function(){return"Ceo ekran"},formatAllRows:function(){return"Sve"},formatAutoRefresh:function(){return"Automatsko osvežavanje"},formatExport:function(){return"Izvezi podatke"},formatJumpTo:function(){return"Idi"},formatAdvancedSearch:function(){return"Napredna pretraga"},formatAdvancedCloseButton:function(){return"Zatvori"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["sr-Latn-RS"]),t.fn.bootstrapTable.locales["sv-SE"]={formatLoadingMessage:function(){return"Laddar, vänligen vänta"},formatRecordsPerPage:function(t){return"".concat(t," rader per sida")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Visa ".concat(t," till ").concat(n," av ").concat(o," rader (filtered from ").concat(r," total rows)"):"Visa ".concat(t," till ").concat(n," av ").concat(o," rader")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Sök"},formatNoMatches:function(){return"Inga matchande resultat funna."},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Uppdatera"},formatToggle:function(){return"Skifta"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"kolumn"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["sv-SE"]),t.fn.bootstrapTable.locales["th-TH"]={formatLoadingMessage:function(){return"กำลังโหลดข้อมูล, กรุณารอสักครู่"},formatRecordsPerPage:function(t){return"".concat(t," รายการต่อหน้า")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"รายการที่ ".concat(t," ถึง ").concat(n," จากทั้งหมด ").concat(o," รายการ (filtered from ").concat(r," total rows)"):"รายการที่ ".concat(t," ถึง ").concat(n," จากทั้งหมด ").concat(o," รายการ")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"ค้นหา"},formatNoMatches:function(){return"ไม่พบรายการที่ค้นหา !"},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"รีเฟรส"},formatToggle:function(){return"สลับมุมมอง"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"คอลัมน์"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["th-TH"]),t.fn.bootstrapTable.locales["tr-TR"]={formatLoadingMessage:function(){return"Yükleniyor, lütfen bekleyin"},formatRecordsPerPage:function(t){return"Sayfa başına ".concat(t," kayıt.")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"".concat(o," kayıttan ").concat(t,"-").concat(n," arası gösteriliyor (filtered from ").concat(r," total rows)."):"".concat(o," kayıttan ").concat(t,"-").concat(n," arası gösteriliyor.")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Ara"},formatNoMatches:function(){return"Eşleşen kayıt bulunamadı."},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Yenile"},formatToggle:function(){return"Değiştir"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Sütunlar"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Tüm Satırlar"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["tr-TR"]),t.fn.bootstrapTable.locales["uk-UA"]={formatLoadingMessage:function(){return"Завантаження, будь ласка, зачекайте"},formatRecordsPerPage:function(t){return"".concat(t," записів на сторінку")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Показано з ".concat(t," по ").concat(n,". Всього: ").concat(o," (filtered from ").concat(r," total rows)"):"Показано з ".concat(t," по ").concat(n,". Всього: ").concat(o)},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Очистити фільтри"},formatSearch:function(){return"Пошук"},formatNoMatches:function(){return"Не знайдено жодного запису"},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Оновити"},formatToggle:function(){return"Змінити"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Стовпці"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["uk-UA"]),t.fn.bootstrapTable.locales["ur-PK"]={formatLoadingMessage:function(){return"براۓ مہربانی انتظار کیجئے"},formatRecordsPerPage:function(t){return"".concat(t," ریکارڈز فی صفہ ")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"دیکھیں ".concat(t," سے ").concat(n," کے ").concat(o,"ریکارڈز (filtered from ").concat(r," total rows)"):"دیکھیں ".concat(t," سے ").concat(n," کے ").concat(o,"ریکارڈز")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"تلاش"},formatNoMatches:function(){return"کوئی ریکارڈ نہیں ملا"},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"تازہ کریں"},formatToggle:function(){return"تبدیل کریں"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"کالم"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["ur-PK"]),t.fn.bootstrapTable.locales["uz-Latn-UZ"]={formatLoadingMessage:function(){return"Yuklanyapti, iltimos kuting"},formatRecordsPerPage:function(t){return"".concat(t," qator har sahifada")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Ko'rsatypati ".concat(t," dan ").concat(n," gacha ").concat(o," qatorlarni (filtered from ").concat(r," total rows)"):"Ko'rsatypati ".concat(t," dan ").concat(n," gacha ").concat(o," qatorlarni")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Filtrlarni tozalash"},formatSearch:function(){return"Qidirish"},formatNoMatches:function(){return"Hech narsa topilmadi"},formatPaginationSwitch:function(){return"Sahifalashni yashirish/ko'rsatish"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Yangilash"},formatToggle:function(){return"Ko'rinish"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Ustunlar"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Hammasi"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Eksport"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["uz-Latn-UZ"]),t.fn.bootstrapTable.locales["vi-VN"]={formatLoadingMessage:function(){return"Đang tải"},formatRecordsPerPage:function(t){return"".concat(t," bản ghi mỗi trang")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"Hiển thị từ trang ".concat(t," đến ").concat(n," của ").concat(o," bảng ghi (filtered from ").concat(r," total rows)"):"Hiển thị từ trang ".concat(t," đến ").concat(n," của ").concat(o," bảng ghi")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Tìm kiếm"},formatNoMatches:function(){return"Không có dữ liệu"},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Refresh"},formatToggle:function(){return"Toggle"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Columns"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["vi-VN"]),t.fn.bootstrapTable.locales["zh-CN"]={formatLoadingMessage:function(){return"正在努力地加载数据中,请稍候"},formatRecordsPerPage:function(t){return"每页显示 ".concat(t," 条记录")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"显示第 ".concat(t," 到第 ").concat(n," 条记录,总共 ").concat(o," 条记录(从 ").concat(r," 总记录中过滤)"):"显示第 ".concat(t," 到第 ").concat(n," 条记录,总共 ").concat(o," 条记录")},formatSRPaginationPreText:function(){return"上一页"},formatSRPaginationPageText:function(t){return"第".concat(t,"页")},formatSRPaginationNextText:function(){return"下一页"},formatDetailPagination:function(t){return"总共 ".concat(t," 条记录")},formatClearSearch:function(){return"清空过滤"},formatSearch:function(){return"搜索"},formatNoMatches:function(){return"没有找到匹配的记录"},formatPaginationSwitch:function(){return"隐藏/显示分页"},formatPaginationSwitchDown:function(){return"显示分页"},formatPaginationSwitchUp:function(){return"隐藏分页"},formatRefresh:function(){return"刷新"},formatToggle:function(){return"切换"},formatToggleOn:function(){return"显示卡片视图"},formatToggleOff:function(){return"隐藏卡片视图"},formatColumns:function(){return"列"},formatColumnsToggleAll:function(){return"切换所有"},formatFullscreen:function(){return"全屏"},formatAllRows:function(){return"所有"},formatAutoRefresh:function(){return"自动刷新"},formatExport:function(){return"导出数据"},formatJumpTo:function(){return"跳转"},formatAdvancedSearch:function(){return"高级搜索"},formatAdvancedCloseButton:function(){return"关闭"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["zh-CN"]),t.fn.bootstrapTable.locales["zh-TW"]={formatLoadingMessage:function(){return"正在努力地載入資料,請稍候"},formatRecordsPerPage:function(t){return"每頁顯示 ".concat(t," 項記錄")},formatShowingRows:function(t,n,o,r){return void 0!==r&&r>0&&r>o?"顯示第 ".concat(t," 到第 ").concat(n," 項記錄,總共 ").concat(o," 項記錄(從 ").concat(r," 總記錄中過濾)"):"顯示第 ".concat(t," 到第 ").concat(n," 項記錄,總共 ").concat(o," 項記錄")},formatSRPaginationPreText:function(){return"上一頁"},formatSRPaginationPageText:function(t){return"第".concat(t,"頁")},formatSRPaginationNextText:function(){return"下一頁"},formatDetailPagination:function(t){return"總共 ".concat(t," 項記錄")},formatClearSearch:function(){return"清空過濾"},formatSearch:function(){return"搜尋"},formatNoMatches:function(){return"沒有找到符合的結果"},formatPaginationSwitch:function(){return"隱藏/顯示分頁"},formatPaginationSwitchDown:function(){return"顯示分頁"},formatPaginationSwitchUp:function(){return"隱藏分頁"},formatRefresh:function(){return"重新整理"},formatToggle:function(){return"切換"},formatToggleOn:function(){return"顯示卡片視圖"},formatToggleOff:function(){return"隱藏卡片視圖"},formatColumns:function(){return"列"},formatColumnsToggleAll:function(){return"切換所有"},formatFullscreen:function(){return"全屏"},formatAllRows:function(){return"所有"},formatAutoRefresh:function(){return"自動刷新"},formatExport:function(){return"導出數據"},formatJumpTo:function(){return"跳轉"},formatAdvancedSearch:function(){return"高級搜尋"},formatAdvancedCloseButton:function(){return"關閉"}},t.extend(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["zh-TW"])})); diff --git a/web/static/js/bootstrap-table.min.js b/web/static/js/bootstrap-table.min.js deleted file mode 100644 index e37e1a7..0000000 --- a/web/static/js/bootstrap-table.min.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * bootstrap-table - An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation) - * - * @version v1.16.0 - * @homepage https://bootstrap-table.com - * @author wenzhixin (http://wenzhixin.net.cn/) - * @license MIT - */ - -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("jquery")):"function"==typeof define&&define.amd?define(["jquery"],e):(t=t||self).BootstrapTable=e(t.jQuery)}(this,(function(t){"use strict";t=t&&t.hasOwnProperty("default")?t.default:t;var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function i(t,e){return t(e={exports:{}},e.exports),e.exports}var n=function(t){return t&&t.Math==Math&&t},o=n("object"==typeof globalThis&&globalThis)||n("object"==typeof window&&window)||n("object"==typeof self&&self)||n("object"==typeof e&&e)||Function("return this")(),r=function(t){try{return!!t()}catch(t){return!0}},a=!r((function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})),s={}.propertyIsEnumerable,l=Object.getOwnPropertyDescriptor,c={f:l&&!s.call({1:2},1)?function(t){var e=l(this,t);return!!e&&e.enumerable}:s},h=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}},u={}.toString,d=function(t){return u.call(t).slice(8,-1)},f="".split,p=r((function(){return!Object("z").propertyIsEnumerable(0)}))?function(t){return"String"==d(t)?f.call(t,""):Object(t)}:Object,g=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t},v=function(t){return p(g(t))},b=function(t){return"object"==typeof t?null!==t:"function"==typeof t},m=function(t,e){if(!b(t))return t;var i,n;if(e&&"function"==typeof(i=t.toString)&&!b(n=i.call(t)))return n;if("function"==typeof(i=t.valueOf)&&!b(n=i.call(t)))return n;if(!e&&"function"==typeof(i=t.toString)&&!b(n=i.call(t)))return n;throw TypeError("Can't convert object to primitive value")},y={}.hasOwnProperty,w=function(t,e){return y.call(t,e)},S=o.document,x=b(S)&&b(S.createElement),k=function(t){return x?S.createElement(t):{}},O=!a&&!r((function(){return 7!=Object.defineProperty(k("div"),"a",{get:function(){return 7}}).a})),C=Object.getOwnPropertyDescriptor,T={f:a?C:function(t,e){if(t=v(t),e=m(e,!0),O)try{return C(t,e)}catch(t){}if(w(t,e))return h(!c.f.call(t,e),t[e])}},P=function(t){if(!b(t))throw TypeError(String(t)+" is not an object");return t},$=Object.defineProperty,I={f:a?$:function(t,e,i){if(P(t),e=m(e,!0),P(i),O)try{return $(t,e,i)}catch(t){}if("get"in i||"set"in i)throw TypeError("Accessors not supported");return"value"in i&&(t[e]=i.value),t}},A=a?function(t,e,i){return I.f(t,e,h(1,i))}:function(t,e,i){return t[e]=i,t},E=function(t,e){try{A(o,t,e)}catch(i){o[t]=e}return e},R=o["__core-js_shared__"]||E("__core-js_shared__",{}),j=Function.toString;"function"!=typeof R.inspectSource&&(R.inspectSource=function(t){return j.call(t)});var N,F,_,B=R.inspectSource,V=o.WeakMap,L="function"==typeof V&&/native code/.test(B(V)),D=i((function(t){(t.exports=function(t,e){return R[t]||(R[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.6.0",mode:"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})})),H=0,M=Math.random(),U=function(t){return"Symbol("+String(void 0===t?"":t)+")_"+(++H+M).toString(36)},z=D("keys"),q=function(t){return z[t]||(z[t]=U(t))},W={},G=o.WeakMap;if(L){var K=new G,J=K.get,Y=K.has,X=K.set;N=function(t,e){return X.call(K,t,e),e},F=function(t){return J.call(K,t)||{}},_=function(t){return Y.call(K,t)}}else{var Q=q("state");W[Q]=!0,N=function(t,e){return A(t,Q,e),e},F=function(t){return w(t,Q)?t[Q]:{}},_=function(t){return w(t,Q)}}var Z,tt={set:N,get:F,has:_,enforce:function(t){return _(t)?F(t):N(t,{})},getterFor:function(t){return function(e){var i;if(!b(e)||(i=F(e)).type!==t)throw TypeError("Incompatible receiver, "+t+" required");return i}}},et=i((function(t){var e=tt.get,i=tt.enforce,n=String(String).split("String");(t.exports=function(t,e,r,a){var s=!!a&&!!a.unsafe,l=!!a&&!!a.enumerable,c=!!a&&!!a.noTargetGet;"function"==typeof r&&("string"!=typeof e||w(r,"name")||A(r,"name",e),i(r).source=n.join("string"==typeof e?e:"")),t!==o?(s?!c&&t[e]&&(l=!0):delete t[e],l?t[e]=r:A(t,e,r)):l?t[e]=r:E(e,r)})(Function.prototype,"toString",(function(){return"function"==typeof this&&e(this).source||B(this)}))})),it=o,nt=function(t){return"function"==typeof t?t:void 0},ot=function(t,e){return arguments.length<2?nt(it[t])||nt(o[t]):it[t]&&it[t][e]||o[t]&&o[t][e]},rt=Math.ceil,at=Math.floor,st=function(t){return isNaN(t=+t)?0:(t>0?at:rt)(t)},lt=Math.min,ct=function(t){return t>0?lt(st(t),9007199254740991):0},ht=Math.max,ut=Math.min,dt=function(t,e){var i=st(t);return i<0?ht(i+e,0):ut(i,e)},ft=function(t){return function(e,i,n){var o,r=v(e),a=ct(r.length),s=dt(n,a);if(t&&i!=i){for(;a>s;)if((o=r[s++])!=o)return!0}else for(;a>s;s++)if((t||s in r)&&r[s]===i)return t||s||0;return!t&&-1}},pt={includes:ft(!0),indexOf:ft(!1)},gt=pt.indexOf,vt=function(t,e){var i,n=v(t),o=0,r=[];for(i in n)!w(W,i)&&w(n,i)&&r.push(i);for(;e.length>o;)w(n,i=e[o++])&&(~gt(r,i)||r.push(i));return r},bt=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],mt=bt.concat("length","prototype"),yt={f:Object.getOwnPropertyNames||function(t){return vt(t,mt)}},wt={f:Object.getOwnPropertySymbols},St=ot("Reflect","ownKeys")||function(t){var e=yt.f(P(t)),i=wt.f;return i?e.concat(i(t)):e},xt=function(t,e){for(var i=St(e),n=I.f,o=T.f,r=0;rr;)I.f(t,i=n[r++],e[i]);return t},Vt=ot("document","documentElement"),Lt=q("IE_PROTO"),Dt=function(){},Ht=function(t){return" diff --git a/web/views/index/add.html b/web/views/index/add.html deleted file mode 100644 index c50602b..0000000 --- a/web/views/index/add.html +++ /dev/null @@ -1,155 +0,0 @@ -
-
-
-

-
-
-
- -
- : - - - - - - - - - - -
-
- -
- -
- -
-
- -
- -
- -
-
- {{if eq true .allow_multi_ip}} -
- -
- -
-
- {{end}} -
- -
- -
-
- - {{if eq true .allow_local_proxy}} -
- -
- -
-
- {{end}} - -
- -
- - -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- - -
-
-
-
-
- -
-
- -
-
-
-
-
- diff --git a/web/views/index/edit.html b/web/views/index/edit.html deleted file mode 100644 index 7f8dba0..0000000 --- a/web/views/index/edit.html +++ /dev/null @@ -1,146 +0,0 @@ -
-
-
-

-
-
- -
- -
- : - - - - - - - - - - -
-
- -
- -
- -
-
- -
- -
- -
-
- {{if eq true .allow_multi_ip}} -
- -
- -
-
- {{end}} -
- -
- -
-
- {{if eq true .allow_local_proxy}} -
- -
- -
-
- {{end}} -
- -
- - -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- - -
-
-
-
-
- -
-
- -
-
-
-
-
- diff --git a/web/views/index/hadd.html b/web/views/index/hadd.html deleted file mode 100644 index 6c3087b..0000000 --- a/web/views/index/hadd.html +++ /dev/null @@ -1,113 +0,0 @@ -
-
-
-

-
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
- {{if eq false .https_just_proxy}} -
- -
- -
-
-
- -
- -
-
- {{end}} -
- -
- -
-
- {{if eq true .allow_local_proxy}} -
- -
- -
-
- {{end}} -
- -
- - - -
-
- -
- -
- -
-
-
-
-
- -
-
-
-
-
-
-
- \ No newline at end of file diff --git a/web/views/index/hedit.html b/web/views/index/hedit.html deleted file mode 100644 index a78623c..0000000 --- a/web/views/index/hedit.html +++ /dev/null @@ -1,117 +0,0 @@ -
-
-
-

-
-
- -
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
- {{if eq false .https_just_proxy}} -
- -
- -
-
-
- -
- -
-
- {{end}} -
- -
- -
-
- {{if eq true .allow_local_proxy}} -
- -
- -
-
- {{end}} -
- -
- - - -
-
- -
- -
- -
-
-
-
-
- -
-
-
-
-
-
-
- - \ No newline at end of file diff --git a/web/views/index/help.html b/web/views/index/help.html deleted file mode 100644 index 758b57b..0000000 --- a/web/views/index/help.html +++ /dev/null @@ -1,138 +0,0 @@ -
-
-
- - - -
-
-
-
- -
-
-

域名代理模式

-

- 适用范围: 小程序开发、微信公众号开发、产品演示 -

-

- 假设场景: -

  • 有一个域名proxy.com,有一台公网机器ip为{{.ip}}
  • -
  • 两个内网开发站点127.0.0.1:81,127.0.0.1:82
  • -
  • 想通过a.proxy.com访问127.0.0.1:81,通过b.proxy.com访问127.0.0.1:82
  • -

    -

    使用步骤:

    -
      -
    • 将*.proxy.com解析到公网服务器{{.ip}}
    • -
    • 在客户端管理中创建一个客户端,记录下验证密钥
    • -
    • 点击该客户端的域名管理,添加两条规则规则:1、域名:a.proxy.com,内网目标:127.0.0.1:81,2、域名:b.proxy.com,内网目标:127.0.0.1:82
    • -
    • 内网客户端运行 -
      ./npc -server={{.ip}}:{{.p}} -vkey=客户端的密钥
      -
    • -
    • 现在访问a.proxy.com,b.proxy.com即可成功
    • -
    -

    注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,如需使用https请在配置文件中将https端口设置为443,和将对应的证书文件路径添加到配置文件中 -

    -
    -
    -
    -
    -
    -
    -

    tcp隧道模式

    -

    - 适用范围: ssh、远程桌面等tcp连接场景 -

    -

    - 假设场景: 想通过访问公网服务器{{.ip}}的8001端口,连接内网机器10.1.50.101的22端口,实现ssh连接 -

    -

    使用步骤:

    -
      -
    • 在客户端管理中创建一个客户端,记录下验证密钥
    • -
    • 内网客户端运行 -
      ./npc -server={{.ip}}:{{.p}} -vkey=客户端的密钥
      -
      -
    • -
    • 在该客户端隧道管理中添加一条tcp隧道,填写监听的端口(8001)、内网目标ip和目标端口(10.1.50.101:22),选择压缩方式,保存。
    • -
    • 访问公网服务器ip({{.ip}}),填写的监听端口(8001),相当于访问内网ip(10.1.50.101):目标端口(22),例如:ssh -p 8001 root@{{.ip}}
    • -
    -

    注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认内网客户端已经启动

    -
    -
    -
    -
    -

    udp隧道模式

    -

    - 适用范围: 内网dns解析等udp连接场景 -

    -

    - 假设场景: 内网有一台dns(10.1.50.102:53),在非内网环境下想使用该dns,公网服务器为{{.ip}} -

    -

    使用步骤:

    -
      -
    • 在客户端管理中创建一个客户端,记录下验证密钥
    • -
    • 内网客户端运行 -
      ./npc -server={{.ip}}:{{.p}} -vkey=客户端的密钥
      -
      -
    • -
    • 在该客户端的隧道管理中添加一条udp隧道,填写监听的端口(53)、内网目标ip和目标端口(10.1.50.102:53),选择压缩方式,保存。
    • -
    • 修改本机dns为{{.ip}},则相当于使用10.1.50.202作为dns服务器
    • -
    -

    注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认内网客户端已经启动

    -
    -
    -
    -
    -
    -
    -

    socks5代理模式

    -

    - 适用范围: 在外网环境下如同使用vpn一样访问内网设备或者资源 -

    -

    - 假设场景: 想将公网服务器{{.ip}}的8003端口作为socks5代理,达到访问内网任意设备或者资源的效果 -

    -

    使用步骤:

    -
      -
    • 在客户端管理中创建一个客户端,记录下验证密钥
    • -
    • 内网客户端运行 -
      ./npc -server={{.ip}}:{{.p}} -vkey=客户端的密钥
      -
      -
    • -
    • 在该客户端隧道管理中添加一条socks5代理,填写监听的端口(8003),验证用户名和密码自行选择(建议先不填,部分客户端不支持,proxifer支持),选择压缩方式,保存。
    • -
    • 在外网环境的本机配置socks5代理,ip为公网服务器ip({{.ip}}),端口为填写的监听端口(8003),即可畅享内网了
    • -
    -

    注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认内网客户端已经启动

    -
    -
    -
    -
    -

    http代理模式

    -

    - 适用范围: 在外网环境下访问内网站点 -

    -

    - 假设场景: 想将公网服务器{{.ip}}的8004端口作为http代理,访问内网网站 -

    -

    使用步骤:

    -
      -
    • 在客户端管理中创建一个客户端,记录下验证密钥
    • -
    • 内网客户端运行 -
      ./npc -server={{.ip}}:{{.p}} -vkey=客户端的密钥
      -
      -
    • -
    • 在该客户端隧道管理中添加一条http代理,填写监听的端口(8004),选择压缩方式,保存。
    • -
    • 在外网环境的本机配置http代理,ip为公网服务器ip({{.ip}}),端口为填写的监听端口(8004),即可访问了
    • -
    -

    注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认内网客户端已经启动

    -
    -
    -
    -
    -

    单个客户端可以添加多条隧道或者域名解析

    -
    -
    -
    diff --git a/web/views/index/hlist.html b/web/views/index/hlist.html deleted file mode 100644 index e3613c4..0000000 --- a/web/views/index/hlist.html +++ /dev/null @@ -1,162 +0,0 @@ -
    - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    -
    - - diff --git a/web/views/index/index.html b/web/views/index/index.html deleted file mode 100644 index d3b0773..0000000 --- a/web/views/index/index.html +++ /dev/null @@ -1,740 +0,0 @@ -
    -
    -
    -
    -
    -
    -
    -
    -

    {{.p}}

    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    {{.data.clientCount}}

    -
    -
    -
    -
    -
    -
    - {{/*今日*/}} -
    -
    -
    -

    {{.data.clientOnlineCount}}

    - {{/*
    44%
    */}} - {{/*新访客*/}} -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    {{.data.tcpCount}}

    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    - - - - - - - -
    -
    -
    -
      -
    • -
      -
      - -
      -
      - {{.data.bridgeType}} -
      -
      -
    • -
    • -
      -
      - -
      -
      - {{.data.httpProxyPort}} -
      -
      -
    • -
    • -
      -
      - -
      -
      - {{.data.httpsProxyPort}} -
      -
      -
    • -
    • -
      -
      - -
      -
      - -
      -
      -
    • -
    • -
      -
      - -
      -
      - {{.data.flowStoreInterval}} -
      -
      -
    • -
    • -
      -
      - -
      -
      - {{.data.logLevel}} -
      -
      -
    • -
    • -
      -
      - -
      -
      - {{.data.p2pPort}} -
      -
      -
    • -
    • -
      -
      - -
      -
      - {{.data.serverIp}} -
      -
      -
    • -
    • -
      -
      - -
      -
      - {{.data.version}} -
      -
      -
    • -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
      -
    • -
      -
      - -
      -
      - -
      -
      -
      -
      -
      -
    • -
    • -
      -
      - -
      -
      - -
      -
      -
      -
      -
      -
    • -
    • -
      -
      - -
      -
      - -
      -
      -
    • -
    • -
      -
      - -
      -
      - -
      -
      -
    • -
    • -
      -
      - -
      -
      - -
      -
      -
    • -
    • -
      -
      - -
      -
      - -
      -
      -
    • -
    • -
      -
      - -
      -
      - -
      -
      -
    • -
    -
    -
    -
    -
    - -{{if eq true .system_info_display}} -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -{{end}} - -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    - - -
    -
    - - diff --git a/web/views/index/list.html b/web/views/index/list.html deleted file mode 100644 index 9918ffc..0000000 --- a/web/views/index/list.html +++ /dev/null @@ -1,212 +0,0 @@ -
    - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    -
    - - diff --git a/web/views/login/index.html b/web/views/login/index.html deleted file mode 100644 index b2816ed..0000000 --- a/web/views/login/index.html +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - -
    -
    -
    - -
    -

    -
      -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    -
    - -
    -
    -
    -
    - -
    -
    - -
    - - {{if eq true .register_allow}} -

    - - {{end}} -
    -
    -
    -
    -
    - -
    - - - - - - - - diff --git a/web/views/login/register.html b/web/views/login/register.html deleted file mode 100644 index 8e7f0e3..0000000 --- a/web/views/login/register.html +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - -
    -
    -
    -

    -
    -

    -

    -
    -
    - -
    -
    - -
    - -

    - -
    -
    -
    - - - - - diff --git a/web/views/public/error.html b/web/views/public/error.html deleted file mode 100644 index cfed194..0000000 --- a/web/views/public/error.html +++ /dev/null @@ -1,5 +0,0 @@ -
    -

    Error 404: Page not found

    -

    The page you have requested is not found.

    -

    Go Back

    -
    \ No newline at end of file diff --git a/web/views/public/layout.html b/web/views/public/layout.html deleted file mode 100644 index 0859093..0000000 --- a/web/views/public/layout.html +++ /dev/null @@ -1,153 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - -
    -
    - -
    - - {{.LayoutContent}} - - -
    -
    - - - - - -{{/**/}} -