├── leaf-core ├── src │ ├── test │ │ ├── resources │ │ │ ├── leaf.properties │ │ │ ├── log4j2.xml │ │ │ └── applicationContext.xml │ │ └── java │ │ │ └── com │ │ │ └── sankuai │ │ │ └── inf │ │ │ └── leaf │ │ │ ├── snowflake │ │ │ └── SnowflakeIDGenImplTest.java │ │ │ └── segment │ │ │ ├── SpringIDGenServiceTest.java │ │ │ └── IDGenServiceTest.java │ └── main │ │ └── java │ │ └── com │ │ └── sankuai │ │ └── inf │ │ └── leaf │ │ ├── common │ │ ├── Status.java │ │ ├── ZeroIDGen.java │ │ ├── CheckVO.java │ │ ├── PropertyFactory.java │ │ ├── Result.java │ │ └── Utils.java │ │ ├── IDGen.java │ │ ├── snowflake │ │ ├── exception │ │ │ ├── CheckLastTimeException.java │ │ │ ├── ClockGoBackException.java │ │ │ └── CheckOtherNodeException.java │ │ ├── SnowflakeIDGenImpl.java │ │ └── SnowflakeZookeeperHolder.java │ │ └── segment │ │ ├── dao │ │ ├── IDAllocDao.java │ │ ├── IDAllocMapper.java │ │ └── impl │ │ │ └── IDAllocDaoImpl.java │ │ ├── model │ │ ├── LeafAlloc.java │ │ ├── Segment.java │ │ └── SegmentBuffer.java │ │ └── SegmentIDGenImpl.java └── pom.xml ├── .gitignore ├── leaf-server ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── sankuai │ │ │ │ └── inf │ │ │ │ └── leaf │ │ │ │ └── server │ │ │ │ ├── exception │ │ │ │ ├── InitException.java │ │ │ │ ├── NoKeyException.java │ │ │ │ └── LeafServerException.java │ │ │ │ ├── LeafServerApplication.java │ │ │ │ ├── Constants.java │ │ │ │ ├── service │ │ │ │ ├── SnowflakeService.java │ │ │ │ └── SegmentService.java │ │ │ │ ├── controller │ │ │ │ ├── LeafController.java │ │ │ │ └── LeafMonitorController.java │ │ │ │ └── model │ │ │ │ └── SegmentBufferView.java │ │ └── resources │ │ │ ├── leaf.properties │ │ │ ├── application.properties │ │ │ ├── templates │ │ │ ├── db.ftl │ │ │ └── segment.ftl │ │ │ └── static │ │ │ └── css │ │ │ └── bootstrap.min.css │ └── test │ │ └── java │ │ └── com │ │ └── sankuai │ │ └── inf │ │ └── leaf │ │ └── server │ │ └── LeafServerApplicationTests.java ├── deploy │ └── run.sh └── pom.xml ├── scripts └── leaf_alloc.sql ├── README_CN.md ├── README.md ├── pom.xml └── LICENSE /leaf-core/src/test/resources/leaf.properties: -------------------------------------------------------------------------------- 1 | #leaf.name=default 2 | #leaf.zk.list= 3 | 4 | #jdbc.url= 5 | #jdbc.username= 6 | #jdbc.password= 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Package Files # 4 | *.jar 5 | *.war 6 | *.ear 7 | *.versionsBackup 8 | 9 | 10 | *.iml 11 | .idea/ 12 | target/ 13 | logs/ 14 | log/ 15 | -------------------------------------------------------------------------------- /leaf-core/src/main/java/com/sankuai/inf/leaf/common/Status.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.common; 2 | 3 | public enum Status { 4 | SUCCESS, 5 | EXCEPTION 6 | } 7 | -------------------------------------------------------------------------------- /leaf-core/src/main/java/com/sankuai/inf/leaf/IDGen.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf; 2 | 3 | import com.sankuai.inf.leaf.common.Result; 4 | 5 | public interface IDGen { 6 | Result get(String key); 7 | boolean init(); 8 | } 9 | -------------------------------------------------------------------------------- /leaf-server/src/main/java/com/sankuai/inf/leaf/server/exception/InitException.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.server.exception; 2 | 3 | public class InitException extends Exception{ 4 | public InitException(String msg) { 5 | super(msg); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /leaf-server/src/main/resources/leaf.properties: -------------------------------------------------------------------------------- 1 | leaf.name=com.sankuai.leaf.opensource.test 2 | leaf.segment.enable=false 3 | #leaf.jdbc.url= 4 | #leaf.jdbc.username= 5 | #leaf.jdbc.password= 6 | 7 | leaf.snowflake.enable=false 8 | #leaf.snowflake.zk.address= 9 | #leaf.snowflake.port= -------------------------------------------------------------------------------- /leaf-core/src/main/java/com/sankuai/inf/leaf/snowflake/exception/CheckLastTimeException.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.snowflake.exception; 2 | 3 | public class CheckLastTimeException extends RuntimeException { 4 | public CheckLastTimeException(String msg){ 5 | super(msg); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /leaf-core/src/main/java/com/sankuai/inf/leaf/snowflake/exception/ClockGoBackException.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.snowflake.exception; 2 | 3 | public class ClockGoBackException extends RuntimeException { 4 | public ClockGoBackException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /leaf-core/src/main/java/com/sankuai/inf/leaf/snowflake/exception/CheckOtherNodeException.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.snowflake.exception; 2 | 3 | public class CheckOtherNodeException extends RuntimeException { 4 | public CheckOtherNodeException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /leaf-server/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.freemarker.cache=false 2 | spring.freemarker.charset=UTF-8 3 | spring.freemarker.check-template-location=true 4 | spring.freemarker.content-type=text/html 5 | spring.freemarker.expose-request-attributes=true 6 | spring.freemarker.expose-session-attributes=true 7 | spring.freemarker.request-context-attribute=request 8 | -------------------------------------------------------------------------------- /leaf-server/src/main/java/com/sankuai/inf/leaf/server/exception/NoKeyException.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.server.exception; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | @ResponseStatus(code=HttpStatus.INTERNAL_SERVER_ERROR,reason="Key is none") 7 | public class NoKeyException extends RuntimeException { 8 | } 9 | -------------------------------------------------------------------------------- /scripts/leaf_alloc.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS `leaf_alloc`; 2 | 3 | CREATE TABLE `leaf_alloc` ( 4 | `biz_tag` varchar(128) NOT NULL DEFAULT '', 5 | `max_id` bigint(20) NOT NULL DEFAULT '1', 6 | `step` int(11) NOT NULL, 7 | `description` varchar(256) DEFAULT NULL, 8 | `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 9 | PRIMARY KEY (`biz_tag`) 10 | ) ENGINE=InnoDB; -------------------------------------------------------------------------------- /leaf-core/src/main/java/com/sankuai/inf/leaf/common/ZeroIDGen.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.common; 2 | 3 | import com.sankuai.inf.leaf.IDGen; 4 | 5 | public class ZeroIDGen implements IDGen { 6 | @Override 7 | public Result get(String key) { 8 | return new Result(0, Status.SUCCESS); 9 | } 10 | 11 | @Override 12 | public boolean init() { 13 | return true; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /leaf-server/deploy/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | 5 | # 程序执行的根路径 6 | PROJ_DIR=./target 7 | 8 | # 程序目标 9 | PROJ_TARGET_JAR=${PROJ_DIR}/leaf.jar 10 | JAVA_CMD=java 11 | JVM_ARGS="-jar" 12 | 13 | CMD="${JAVA_CMD} ${JVM_ARGS} ${PROJ_TARGET_JAR}" 14 | echo "Leaf Start--------------" 15 | echo "JVM ARGS ${JVM_ARGS}" 16 | echo "->" 17 | echo "PROJ_TARGET_JAR ${PROJ_TARGET_JAR}" 18 | echo "------------------------" 19 | $CMD 20 | -------------------------------------------------------------------------------- /leaf-server/src/main/java/com/sankuai/inf/leaf/server/LeafServerApplication.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.server; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class LeafServerApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(LeafServerApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /leaf-server/src/main/java/com/sankuai/inf/leaf/server/exception/LeafServerException.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.server.exception; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | @ResponseStatus(code=HttpStatus.INTERNAL_SERVER_ERROR) 7 | public class LeafServerException extends RuntimeException { 8 | public LeafServerException(String msg) { 9 | super(msg); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /leaf-core/src/main/java/com/sankuai/inf/leaf/segment/dao/IDAllocDao.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.segment.dao; 2 | 3 | import com.sankuai.inf.leaf.segment.model.LeafAlloc; 4 | 5 | import java.util.List; 6 | 7 | public interface IDAllocDao { 8 | List getAllLeafAllocs(); 9 | LeafAlloc updateMaxIdAndGetLeafAlloc(String tag); 10 | LeafAlloc updateMaxIdByCustomStepAndGetLeafAlloc(LeafAlloc leafAlloc); 11 | List getAllTags(); 12 | } 13 | -------------------------------------------------------------------------------- /leaf-core/src/test/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /leaf-server/src/test/java/com/sankuai/inf/leaf/server/LeafServerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.server; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class LeafServerApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /leaf-server/src/main/java/com/sankuai/inf/leaf/server/Constants.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.server; 2 | 3 | public class Constants { 4 | public static final String LEAF_SEGMENT_ENABLE = "leaf.segment.enable"; 5 | public static final String LEAF_JDBC_URL = "leaf.jdbc.url"; 6 | public static final String LEAF_JDBC_USERNAME = "leaf.jdbc.username"; 7 | public static final String LEAF_JDBC_PASSWORD = "leaf.jdbc.password"; 8 | 9 | public static final String LEAF_SNOWFLAKE_ENABLE = "leaf.snowflake.enable"; 10 | public static final String LEAF_SNOWFLAKE_PORT = "leaf.snowflake.port"; 11 | public static final String LEAF_SNOWFLAKE_ZK_ADDRESS = "leaf.snowflake.zk.address"; 12 | } 13 | -------------------------------------------------------------------------------- /leaf-core/src/main/java/com/sankuai/inf/leaf/common/CheckVO.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.common; 2 | 3 | public class CheckVO { 4 | private long timestamp; 5 | private int workID; 6 | 7 | public CheckVO(long timestamp, int workID) { 8 | this.timestamp = timestamp; 9 | this.workID = workID; 10 | } 11 | 12 | public long getTimestamp() { 13 | return timestamp; 14 | } 15 | 16 | public void setTimestamp(long timestamp) { 17 | this.timestamp = timestamp; 18 | } 19 | 20 | public int getWorkID() { 21 | return workID; 22 | } 23 | 24 | public void setWorkID(int workID) { 25 | this.workID = workID; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /leaf-core/src/test/java/com/sankuai/inf/leaf/snowflake/SnowflakeIDGenImplTest.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.snowflake; 2 | 3 | import com.sankuai.inf.leaf.IDGen; 4 | import com.sankuai.inf.leaf.common.PropertyFactory; 5 | import com.sankuai.inf.leaf.common.Result; 6 | import org.junit.Test; 7 | 8 | import java.util.Properties; 9 | 10 | public class SnowflakeIDGenImplTest { 11 | @Test 12 | public void testGetId() { 13 | Properties properties = PropertyFactory.getProperties(); 14 | 15 | IDGen idGen = new SnowflakeIDGenImpl(properties.getProperty("leaf.zk.list"), 8080); 16 | for (int i = 1; i < 1000; ++i) { 17 | Result r = idGen.get("a"); 18 | System.out.println(r); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /leaf-core/src/main/java/com/sankuai/inf/leaf/common/PropertyFactory.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.common; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.io.IOException; 7 | import java.util.Properties; 8 | 9 | public class PropertyFactory { 10 | private static final Logger logger = LoggerFactory.getLogger(PropertyFactory.class); 11 | private static final Properties prop = new Properties(); 12 | static { 13 | try { 14 | prop.load(PropertyFactory.class.getClassLoader().getResourceAsStream("leaf.properties")); 15 | } catch (IOException e) { 16 | logger.warn("Load Properties Ex", e); 17 | } 18 | } 19 | public static Properties getProperties() { 20 | return prop; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /leaf-server/src/main/resources/templates/db.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Leaf 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | <#if items?exists> 20 | <#list items as item> 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
tagmaxstepupdate
${item.key}${item.maxId}${item.step}${item.updateTime}
34 | 35 | -------------------------------------------------------------------------------- /leaf-core/src/test/java/com/sankuai/inf/leaf/segment/SpringIDGenServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.segment; 2 | 3 | import com.sankuai.inf.leaf.IDGen; 4 | import com.sankuai.inf.leaf.common.Result; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.test.context.ContextConfiguration; 9 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 10 | 11 | @RunWith(SpringJUnit4ClassRunner.class) 12 | @ContextConfiguration(locations={"classpath:applicationContext.xml"}) //加载配置文件 13 | public class SpringIDGenServiceTest { 14 | @Autowired 15 | IDGen idGen; 16 | 17 | @Test 18 | public void testGetId() { 19 | for (int i = 0; i < 100; i++) { 20 | Result r = idGen.get("leaf-segment-test"); 21 | System.out.println(r); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /leaf-core/src/main/java/com/sankuai/inf/leaf/segment/model/LeafAlloc.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.segment.model; 2 | 3 | public class LeafAlloc { 4 | private String key; 5 | private long maxId; 6 | private int step; 7 | private String updateTime; 8 | 9 | public String getKey() { 10 | return key; 11 | } 12 | 13 | public void setKey(String key) { 14 | this.key = key; 15 | } 16 | 17 | public long getMaxId() { 18 | return maxId; 19 | } 20 | 21 | public void setMaxId(long maxId) { 22 | this.maxId = maxId; 23 | } 24 | 25 | public int getStep() { 26 | return step; 27 | } 28 | 29 | public void setStep(int step) { 30 | this.step = step; 31 | } 32 | 33 | public String getUpdateTime() { 34 | return updateTime; 35 | } 36 | 37 | public void setUpdateTime(String updateTime) { 38 | this.updateTime = updateTime; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /leaf-core/src/main/java/com/sankuai/inf/leaf/common/Result.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.common; 2 | 3 | public class Result { 4 | private long id; 5 | private Status status; 6 | 7 | public Result() { 8 | 9 | } 10 | public Result(long id, Status status) { 11 | this.id = id; 12 | this.status = status; 13 | } 14 | 15 | public long getId() { 16 | return id; 17 | } 18 | 19 | public void setId(long id) { 20 | this.id = id; 21 | } 22 | 23 | public Status getStatus() { 24 | return status; 25 | } 26 | 27 | public void setStatus(Status status) { 28 | this.status = status; 29 | } 30 | 31 | @Override 32 | public String toString() { 33 | final StringBuilder sb = new StringBuilder("Result{"); 34 | sb.append("id=").append(id); 35 | sb.append(", status=").append(status); 36 | sb.append('}'); 37 | return sb.toString(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /leaf-server/src/main/resources/templates/segment.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Leaf 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | <#if data?exists> 27 | <#list data?keys as key> 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
nameinitnextposvalue0max0step0value1max1step1
${key}${data[key].initOk?string('true','false')}${data[key].nextReady?string('true','false')}${data[key].pos}${data[key].value0}${data[key].max0}${data[key].step0}${data[key].value1}${data[key].max1}${data[key].step1}
47 | 48 | -------------------------------------------------------------------------------- /leaf-core/src/main/java/com/sankuai/inf/leaf/segment/dao/IDAllocMapper.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.segment.dao; 2 | 3 | import com.sankuai.inf.leaf.segment.model.LeafAlloc; 4 | import org.apache.ibatis.annotations.*; 5 | 6 | import java.util.List; 7 | 8 | public interface IDAllocMapper { 9 | 10 | @Select("SELECT biz_tag, max_id, step, update_time FROM leaf_alloc") 11 | @Results(value = { 12 | @Result(column = "biz_tag", property = "key"), 13 | @Result(column = "max_id", property = "maxId"), 14 | @Result(column = "step", property = "step"), 15 | @Result(column = "update_time", property = "updateTime") 16 | }) 17 | List getAllLeafAllocs(); 18 | 19 | @Select("SELECT biz_tag, max_id, step FROM leaf_alloc WHERE biz_tag = #{tag}") 20 | @Results(value = { 21 | @Result(column = "biz_tag", property = "key"), 22 | @Result(column = "max_id", property = "maxId"), 23 | @Result(column = "step", property = "step") 24 | }) 25 | LeafAlloc getLeafAlloc(@Param("tag") String tag); 26 | 27 | @Update("UPDATE leaf_alloc SET max_id = max_id + step WHERE biz_tag = #{tag}") 28 | void updateMaxId(@Param("tag") String tag); 29 | 30 | @Update("UPDATE leaf_alloc SET max_id = max_id + #{step} WHERE biz_tag = #{key}") 31 | void updateMaxIdByCustomStep(@Param("leafAlloc") LeafAlloc leafAlloc); 32 | 33 | @Select("SELECT biz_tag FROM leaf_alloc") 34 | List getAllTags(); 35 | } 36 | -------------------------------------------------------------------------------- /leaf-core/src/main/java/com/sankuai/inf/leaf/segment/model/Segment.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.segment.model; 2 | 3 | import java.util.concurrent.atomic.AtomicLong; 4 | 5 | public class Segment { 6 | private AtomicLong value = new AtomicLong(0); 7 | private volatile long max; 8 | private volatile int step; 9 | private SegmentBuffer buffer; 10 | 11 | public Segment(SegmentBuffer buffer) { 12 | this.buffer = buffer; 13 | } 14 | 15 | public AtomicLong getValue() { 16 | return value; 17 | } 18 | 19 | public void setValue(AtomicLong value) { 20 | this.value = value; 21 | } 22 | 23 | public long getMax() { 24 | return max; 25 | } 26 | 27 | public void setMax(long max) { 28 | this.max = max; 29 | } 30 | 31 | public int getStep() { 32 | return step; 33 | } 34 | 35 | public void setStep(int step) { 36 | this.step = step; 37 | } 38 | 39 | public SegmentBuffer getBuffer() { 40 | return buffer; 41 | } 42 | 43 | public long getIdle() { 44 | return this.getMax() - getValue().get(); 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | StringBuilder sb = new StringBuilder("Segment("); 50 | sb.append("value:"); 51 | sb.append(value); 52 | sb.append(",max:"); 53 | sb.append(max); 54 | sb.append(",step:"); 55 | sb.append(step); 56 | sb.append(")"); 57 | return sb.toString(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /leaf-core/src/test/java/com/sankuai/inf/leaf/segment/IDGenServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.segment; 2 | 3 | import com.alibaba.druid.pool.DruidDataSource; 4 | import com.sankuai.inf.leaf.IDGen; 5 | import com.sankuai.inf.leaf.common.PropertyFactory; 6 | import com.sankuai.inf.leaf.common.Result; 7 | import com.sankuai.inf.leaf.segment.dao.IDAllocDao; 8 | import com.sankuai.inf.leaf.segment.dao.impl.IDAllocDaoImpl; 9 | import org.junit.After; 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | 13 | import java.io.IOException; 14 | import java.sql.SQLException; 15 | import java.util.Properties; 16 | 17 | public class IDGenServiceTest { 18 | IDGen idGen; 19 | DruidDataSource dataSource; 20 | @Before 21 | public void before() throws IOException, SQLException { 22 | // Load Db Config 23 | Properties properties = PropertyFactory.getProperties(); 24 | 25 | // Config dataSource 26 | dataSource = new DruidDataSource(); 27 | dataSource.setUrl(properties.getProperty("jdbc.url")); 28 | dataSource.setUsername(properties.getProperty("jdbc.username")); 29 | dataSource.setPassword(properties.getProperty("jdbc.password")); 30 | dataSource.init(); 31 | 32 | // Config Dao 33 | IDAllocDao dao = new IDAllocDaoImpl(dataSource); 34 | 35 | // Config ID Gen 36 | idGen = new SegmentIDGenImpl(); 37 | ((SegmentIDGenImpl) idGen).setDao(dao); 38 | idGen.init(); 39 | } 40 | @Test 41 | public void testGetId() { 42 | for (int i = 0; i < 100; ++i) { 43 | Result r = idGen.get("leaf-segment-test"); 44 | System.out.println(r); 45 | } 46 | } 47 | @After 48 | public void after() { 49 | dataSource.close(); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /leaf-server/src/main/java/com/sankuai/inf/leaf/server/service/SnowflakeService.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.server.service; 2 | 3 | import com.sankuai.inf.leaf.IDGen; 4 | import com.sankuai.inf.leaf.common.PropertyFactory; 5 | import com.sankuai.inf.leaf.common.Result; 6 | import com.sankuai.inf.leaf.common.ZeroIDGen; 7 | import com.sankuai.inf.leaf.server.Constants; 8 | import com.sankuai.inf.leaf.server.exception.InitException; 9 | import com.sankuai.inf.leaf.snowflake.SnowflakeIDGenImpl; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.stereotype.Service; 13 | 14 | import java.util.Properties; 15 | 16 | @Service("SnowflakeService") 17 | public class SnowflakeService { 18 | private Logger logger = LoggerFactory.getLogger(SnowflakeService.class); 19 | 20 | private IDGen idGen; 21 | 22 | public SnowflakeService() throws InitException { 23 | Properties properties = PropertyFactory.getProperties(); 24 | boolean flag = Boolean.parseBoolean(properties.getProperty(Constants.LEAF_SNOWFLAKE_ENABLE, "true")); 25 | if (flag) { 26 | String zkAddress = properties.getProperty(Constants.LEAF_SNOWFLAKE_ZK_ADDRESS); 27 | int port = Integer.parseInt(properties.getProperty(Constants.LEAF_SNOWFLAKE_PORT)); 28 | idGen = new SnowflakeIDGenImpl(zkAddress, port); 29 | if(idGen.init()) { 30 | logger.info("Snowflake Service Init Successfully"); 31 | } else { 32 | throw new InitException("Snowflake Service Init Fail"); 33 | } 34 | } else { 35 | idGen = new ZeroIDGen(); 36 | logger.info("Zero ID Gen Service Init Successfully"); 37 | } 38 | } 39 | 40 | public Result getId(String key) { 41 | return idGen.get(key); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /leaf-core/src/test/resources/applicationContext.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /leaf-server/src/main/java/com/sankuai/inf/leaf/server/controller/LeafController.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.server.controller; 2 | 3 | import com.sankuai.inf.leaf.common.Result; 4 | import com.sankuai.inf.leaf.common.Status; 5 | import com.sankuai.inf.leaf.server.exception.LeafServerException; 6 | import com.sankuai.inf.leaf.server.exception.NoKeyException; 7 | import com.sankuai.inf.leaf.server.service.SegmentService; 8 | import com.sankuai.inf.leaf.server.service.SnowflakeService; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.web.bind.annotation.PathVariable; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.RestController; 15 | 16 | @RestController 17 | public class LeafController { 18 | private Logger logger = LoggerFactory.getLogger(LeafController.class); 19 | 20 | @Autowired 21 | private SegmentService segmentService; 22 | @Autowired 23 | private SnowflakeService snowflakeService; 24 | 25 | @RequestMapping(value = "/api/segment/get/{key}") 26 | public String getSegmentId(@PathVariable("key") String key) { 27 | return get(key, segmentService.getId(key)); 28 | } 29 | 30 | @RequestMapping(value = "/api/snowflake/get/{key}") 31 | public String getSnowflakeId(@PathVariable("key") String key) { 32 | return get(key, snowflakeService.getId(key)); 33 | } 34 | 35 | private String get(@PathVariable("key") String key, Result id) { 36 | Result result; 37 | if (key == null || key.isEmpty()) { 38 | throw new NoKeyException(); 39 | } 40 | result = id; 41 | if (result.getStatus().equals(Status.EXCEPTION)) { 42 | throw new LeafServerException(result.toString()); 43 | } 44 | return String.valueOf(result.getId()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /leaf-server/src/main/java/com/sankuai/inf/leaf/server/model/SegmentBufferView.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.server.model; 2 | 3 | public class SegmentBufferView { 4 | private String key; 5 | private long value0; 6 | private int step0; 7 | private long max0; 8 | 9 | private long value1; 10 | private int step1; 11 | private long max1; 12 | private int pos; 13 | private boolean nextReady; 14 | private boolean initOk; 15 | 16 | public String getKey() { 17 | return key; 18 | } 19 | 20 | public void setKey(String key) { 21 | this.key = key; 22 | } 23 | 24 | public long getValue1() { 25 | return value1; 26 | } 27 | 28 | public void setValue1(long value1) { 29 | this.value1 = value1; 30 | } 31 | 32 | public int getStep1() { 33 | return step1; 34 | } 35 | 36 | public void setStep1(int step1) { 37 | this.step1 = step1; 38 | } 39 | 40 | public long getMax1() { 41 | return max1; 42 | } 43 | 44 | public void setMax1(long max1) { 45 | this.max1 = max1; 46 | } 47 | 48 | public long getValue0() { 49 | return value0; 50 | } 51 | 52 | public void setValue0(long value0) { 53 | this.value0 = value0; 54 | } 55 | 56 | public int getStep0() { 57 | return step0; 58 | } 59 | 60 | public void setStep0(int step0) { 61 | this.step0 = step0; 62 | } 63 | 64 | public long getMax0() { 65 | return max0; 66 | } 67 | 68 | public void setMax0(long max0) { 69 | this.max0 = max0; 70 | } 71 | 72 | public int getPos() { 73 | return pos; 74 | } 75 | 76 | public void setPos(int pos) { 77 | this.pos = pos; 78 | } 79 | 80 | public boolean isNextReady() { 81 | return nextReady; 82 | } 83 | 84 | public void setNextReady(boolean nextReady) { 85 | this.nextReady = nextReady; 86 | } 87 | 88 | public boolean isInitOk() { 89 | return initOk; 90 | } 91 | 92 | public void setInitOk(boolean initOk) { 93 | this.initOk = initOk; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /leaf-server/src/main/java/com/sankuai/inf/leaf/server/service/SegmentService.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.server.service; 2 | 3 | import com.alibaba.druid.pool.DruidDataSource; 4 | import com.sankuai.inf.leaf.IDGen; 5 | import com.sankuai.inf.leaf.common.PropertyFactory; 6 | import com.sankuai.inf.leaf.common.Result; 7 | import com.sankuai.inf.leaf.common.ZeroIDGen; 8 | import com.sankuai.inf.leaf.segment.SegmentIDGenImpl; 9 | import com.sankuai.inf.leaf.segment.dao.IDAllocDao; 10 | import com.sankuai.inf.leaf.segment.dao.impl.IDAllocDaoImpl; 11 | import com.sankuai.inf.leaf.server.Constants; 12 | import com.sankuai.inf.leaf.server.exception.InitException; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.stereotype.Service; 16 | 17 | import java.sql.SQLException; 18 | import java.util.Properties; 19 | 20 | @Service("SegmentService") 21 | public class SegmentService { 22 | private Logger logger = LoggerFactory.getLogger(SegmentService.class); 23 | 24 | private IDGen idGen; 25 | private DruidDataSource dataSource; 26 | 27 | public SegmentService() throws SQLException, InitException { 28 | Properties properties = PropertyFactory.getProperties(); 29 | boolean flag = Boolean.parseBoolean(properties.getProperty(Constants.LEAF_SEGMENT_ENABLE, "true")); 30 | if (flag) { 31 | // Config dataSource 32 | dataSource = new DruidDataSource(); 33 | dataSource.setUrl(properties.getProperty(Constants.LEAF_JDBC_URL)); 34 | dataSource.setUsername(properties.getProperty(Constants.LEAF_JDBC_USERNAME)); 35 | dataSource.setPassword(properties.getProperty(Constants.LEAF_JDBC_PASSWORD)); 36 | dataSource.init(); 37 | 38 | // Config Dao 39 | IDAllocDao dao = new IDAllocDaoImpl(dataSource); 40 | 41 | // Config ID Gen 42 | idGen = new SegmentIDGenImpl(); 43 | ((SegmentIDGenImpl) idGen).setDao(dao); 44 | if (idGen.init()) { 45 | logger.info("Segment Service Init Successfully"); 46 | } else { 47 | throw new InitException("Segment Service Init Fail"); 48 | } 49 | } else { 50 | idGen = new ZeroIDGen(); 51 | logger.info("Zero ID Gen Service Init Successfully"); 52 | } 53 | } 54 | 55 | public Result getId(String key) { 56 | return idGen.get(key); 57 | } 58 | 59 | public SegmentIDGenImpl getIdGen() { 60 | if (idGen instanceof SegmentIDGenImpl) { 61 | return (SegmentIDGenImpl) idGen; 62 | } 63 | return null; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /leaf-core/src/main/java/com/sankuai/inf/leaf/common/Utils.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.common; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.net.Inet6Address; 7 | import java.net.InetAddress; 8 | import java.net.NetworkInterface; 9 | import java.net.SocketException; 10 | import java.util.ArrayList; 11 | import java.util.Enumeration; 12 | import java.util.List; 13 | 14 | public class Utils { 15 | private static final Logger logger = LoggerFactory.getLogger(Utils.class); 16 | 17 | public static String getIp() { 18 | String ip; 19 | try { 20 | List ipList = getHostAddress(null); 21 | // default the first 22 | ip = (!ipList.isEmpty()) ? ipList.get(0) : ""; 23 | } catch (Exception ex) { 24 | ip = ""; 25 | logger.warn("Utils get IP warn", ex); 26 | } 27 | return ip; 28 | } 29 | 30 | public static String getIp(String interfaceName) { 31 | String ip; 32 | interfaceName = interfaceName.trim(); 33 | try { 34 | List ipList = getHostAddress(interfaceName); 35 | ip = (!ipList.isEmpty()) ? ipList.get(0) : ""; 36 | } catch (Exception ex) { 37 | ip = ""; 38 | logger.warn("Utils get IP warn", ex); 39 | } 40 | return ip; 41 | } 42 | 43 | /** 44 | * 获取已激活网卡的IP地址 45 | * 46 | * @param interfaceName 可指定网卡名称,null则获取全部 47 | * @return List 48 | */ 49 | private static List getHostAddress(String interfaceName) throws SocketException { 50 | List ipList = new ArrayList(5); 51 | Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); 52 | while (interfaces.hasMoreElements()) { 53 | NetworkInterface ni = interfaces.nextElement(); 54 | Enumeration allAddress = ni.getInetAddresses(); 55 | while (allAddress.hasMoreElements()) { 56 | InetAddress address = allAddress.nextElement(); 57 | if (address.isLoopbackAddress()) { 58 | // skip the loopback addr 59 | continue; 60 | } 61 | if (address instanceof Inet6Address) { 62 | // skip the IPv6 addr 63 | continue; 64 | } 65 | String hostAddress = address.getHostAddress(); 66 | if (null == interfaceName) { 67 | ipList.add(hostAddress); 68 | } else if (interfaceName.equals(ni.getDisplayName())) { 69 | ipList.add(hostAddress); 70 | } 71 | } 72 | } 73 | return ipList; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /leaf-core/src/main/java/com/sankuai/inf/leaf/segment/dao/impl/IDAllocDaoImpl.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.segment.dao.impl; 2 | 3 | import com.sankuai.inf.leaf.segment.dao.IDAllocDao; 4 | import com.sankuai.inf.leaf.segment.dao.IDAllocMapper; 5 | import com.sankuai.inf.leaf.segment.model.LeafAlloc; 6 | import org.apache.ibatis.mapping.Environment; 7 | import org.apache.ibatis.session.Configuration; 8 | import org.apache.ibatis.session.SqlSession; 9 | import org.apache.ibatis.session.SqlSessionFactory; 10 | import org.apache.ibatis.session.SqlSessionFactoryBuilder; 11 | import org.apache.ibatis.transaction.TransactionFactory; 12 | import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; 13 | 14 | import javax.sql.DataSource; 15 | import java.util.List; 16 | 17 | public class IDAllocDaoImpl implements IDAllocDao { 18 | 19 | SqlSessionFactory sqlSessionFactory; 20 | 21 | public IDAllocDaoImpl(DataSource dataSource) { 22 | TransactionFactory transactionFactory = new JdbcTransactionFactory(); 23 | Environment environment = new Environment("development", transactionFactory, dataSource); 24 | Configuration configuration = new Configuration(environment); 25 | configuration.addMapper(IDAllocMapper.class); 26 | sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration); 27 | } 28 | 29 | @Override 30 | public List getAllLeafAllocs() { 31 | SqlSession sqlSession = sqlSessionFactory.openSession(false); 32 | try { 33 | return sqlSession.selectList("com.sankuai.inf.leaf.segment.dao.IDAllocMapper.getAllLeafAllocs"); 34 | } finally { 35 | sqlSession.close(); 36 | } 37 | } 38 | 39 | @Override 40 | public LeafAlloc updateMaxIdAndGetLeafAlloc(String tag) { 41 | SqlSession sqlSession = sqlSessionFactory.openSession(); 42 | try { 43 | sqlSession.update("com.sankuai.inf.leaf.segment.dao.IDAllocMapper.updateMaxId", tag); 44 | LeafAlloc result = sqlSession.selectOne("com.sankuai.inf.leaf.segment.dao.IDAllocMapper.getLeafAlloc", tag); 45 | sqlSession.commit(); 46 | return result; 47 | } finally { 48 | sqlSession.close(); 49 | } 50 | } 51 | 52 | @Override 53 | public LeafAlloc updateMaxIdByCustomStepAndGetLeafAlloc(LeafAlloc leafAlloc) { 54 | SqlSession sqlSession = sqlSessionFactory.openSession(); 55 | try { 56 | sqlSession.update("com.sankuai.inf.leaf.segment.dao.IDAllocMapper.updateMaxIdByCustomStep", leafAlloc); 57 | LeafAlloc result = sqlSession.selectOne("com.sankuai.inf.leaf.segment.dao.IDAllocMapper.getLeafAlloc", leafAlloc.getKey()); 58 | sqlSession.commit(); 59 | return result; 60 | } finally { 61 | sqlSession.close(); 62 | } 63 | } 64 | 65 | @Override 66 | public List getAllTags() { 67 | SqlSession sqlSession = sqlSessionFactory.openSession(false); 68 | try { 69 | return sqlSession.selectList("com.sankuai.inf.leaf.segment.dao.IDAllocMapper.getAllTags"); 70 | } finally { 71 | sqlSession.close(); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # Leaf 2 | 3 | > There are no two identical leaves in the world. 4 | > 5 | > 世界上没有两片完全相同的树叶。 6 | > 7 | > ​ — 莱布尼茨 8 | 9 | [中文文档](./README_CN.md) | [English Document](./README.md) 10 | 11 | ## Introduction 12 | 13 | Leaf 最早期需求是各个业务线的订单ID生成需求。在美团早期,有的业务直接通过DB自增的方式生成ID,有的业务通过redis缓存来生成ID,也有的业务直接用UUID这种方式来生成ID。以上的方式各自有各自的问题,因此我们决定实现一套分布式ID生成服务来满足需求。具体Leaf 设计文档见:[ leaf 美团分布式ID生成服务 ](https://tech.meituan.com/MT_Leaf.html ) 14 | 15 | 目前Leaf覆盖了美团点评公司内部金融、餐饮、外卖、酒店旅游、猫眼电影等众多业务线。在4C8G VM基础上,通过公司RPC方式调用,QPS压测结果近5w/s,TP999 1ms。 16 | 17 | ## Quick Start 18 | 19 | ### 使用starter注解启动leaf 20 | https://github.com/Meituan-Dianping/Leaf/blob/feature/spring-boot-starter/README_CN.md 21 | 22 | ### Leaf Server 23 | 24 | 我们提供了一个基于spring boot的HTTP服务来获取ID 25 | 26 | 27 | #### 配置介绍 28 | 29 | Leaf 提供两种生成的ID的方式(号段模式和snowflake模式),你可以同时开启两种方式,也可以指定开启某种方式(默认两种方式为关闭状态)。 30 | 31 | Leaf Server的配置都在leaf-server/src/main/resources/leaf.properties中 32 | 33 | | 配置项 | 含义 | 默认值 | 34 | | ------------------------- | ----------------------------- | ------ | 35 | | leaf.name | leaf 服务名 | | 36 | | leaf.segment.enable | 是否开启号段模式 | false | 37 | | leaf.jdbc.url | mysql 库地址 | | 38 | | leaf.jdbc.username | mysql 用户名 | | 39 | | leaf.jdbc.password | mysql 密码 | | 40 | | leaf.snowflake.enable | 是否开启snowflake模式 | false | 41 | | leaf.snowflake.zk.address | snowflake模式下的zk地址 | | 42 | | leaf.snowflake.port | snowflake模式下的服务注册端口 | | 43 | 44 | #### 号段模式 45 | 46 | 如果使用号段模式,需要建立DB表,并配置leaf.jdbc.url, leaf.jdbc.username, leaf.jdbc.password 47 | 48 | 如果不想使用该模式配置leaf.segment.enable=false即可。 49 | 50 | ##### 创建数据表 51 | 52 | ```sql 53 | CREATE DATABASE leaf 54 | CREATE TABLE `leaf_alloc` ( 55 | `biz_tag` varchar(128) NOT NULL DEFAULT '', 56 | `max_id` bigint(20) NOT NULL DEFAULT '1', 57 | `step` int(11) NOT NULL, 58 | `description` varchar(256) DEFAULT NULL, 59 | `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 60 | PRIMARY KEY (`biz_tag`) 61 | ) ENGINE=InnoDB; 62 | 63 | insert into leaf_alloc(biz_tag, max_id, step, description) values('leaf-segment-test', 1, 2000, 'Test leaf Segment Mode Get Id') 64 | ``` 65 | 66 | ##### 配置相关数据项 67 | 68 | 在leaf.properties中配置leaf.jdbc.url, leaf.jdbc.username, leaf.jdbc.password参数 69 | 70 | #### Snowflake模式 71 | 72 | 算法取自twitter开源的snowflake算法。 73 | 74 | 如果不想使用该模式配置leaf.snowflake.enable=false即可。 75 | 76 | ##### 配置zookeeper地址 77 | 78 | 在leaf.properties中配置leaf.snowflake.zk.address,配置leaf 服务监听的端口leaf.snowflake.port。 79 | #### 运行Leaf Server 80 | 81 | ##### 打包服务 82 | 83 | ```shell 84 | git clone git@github.com:Meituan-Dianping/Leaf.git 85 | //按照上面的号段模式在工程里面配置好 86 | cd leaf 87 | mvn clean install -DskipTests 88 | cd leaf-server 89 | ``` 90 | 91 | ##### 运行服务 92 | 93 | *注意:首先得先配置好数据库表或者zk地址* 94 | ###### mvn方式 95 | 96 | ```shell 97 | mvn spring-boot:run 98 | ``` 99 | 100 | ###### 脚本方式 101 | 102 | ```shell 103 | sh deploy/run.sh 104 | ``` 105 | ##### 测试 106 | 107 | ```shell 108 | #segment 109 | curl http://localhost:8080/api/segment/get/leaf-segment-test 110 | #snowflake 111 | curl http://localhost:8080/api/snowflake/get/test 112 | ``` 113 | 114 | ##### 监控页面 115 | 116 | 号段模式:http://localhost:8080/cache 117 | 118 | ### Leaf Core 119 | 120 | 当然,为了追求更高的性能,需要通过RPC Server来部署Leaf 服务,那仅需要引入leaf-core的包,把生成ID的API封装到指定的RPC框架中即可。 121 | 122 | ### 注意事项 123 | 注意现在leaf使用snowflake模式的情况下 其获取ip的逻辑直接取首个网卡ip【特别对于会更换ip的服务要注意】避免浪费workId 124 | -------------------------------------------------------------------------------- /leaf-core/src/main/java/com/sankuai/inf/leaf/segment/model/SegmentBuffer.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.segment.model; 2 | 3 | import java.util.Arrays; 4 | import java.util.concurrent.atomic.AtomicBoolean; 5 | import java.util.concurrent.locks.Lock; 6 | import java.util.concurrent.locks.ReadWriteLock; 7 | import java.util.concurrent.locks.ReentrantReadWriteLock; 8 | 9 | /** 10 | * 双buffer 11 | */ 12 | public class SegmentBuffer { 13 | private String key; 14 | private Segment[] segments; //双buffer 15 | private volatile int currentPos; //当前的使用的segment的index 16 | private volatile boolean nextReady; //下一个segment是否处于可切换状态 17 | private volatile boolean initOk; //是否初始化完成 18 | private final AtomicBoolean threadRunning; //线程是否在运行中 19 | private final ReadWriteLock lock; 20 | 21 | private volatile int step; 22 | private volatile int minStep; 23 | private volatile long updateTimestamp; 24 | 25 | public SegmentBuffer() { 26 | segments = new Segment[]{new Segment(this), new Segment(this)}; 27 | currentPos = 0; 28 | nextReady = false; 29 | initOk = false; 30 | threadRunning = new AtomicBoolean(false); 31 | lock = new ReentrantReadWriteLock(); 32 | } 33 | 34 | public String getKey() { 35 | return key; 36 | } 37 | 38 | public void setKey(String key) { 39 | this.key = key; 40 | } 41 | 42 | public Segment[] getSegments() { 43 | return segments; 44 | } 45 | 46 | public Segment getCurrent() { 47 | return segments[currentPos]; 48 | } 49 | 50 | public int getCurrentPos() { 51 | return currentPos; 52 | } 53 | 54 | public int nextPos() { 55 | return (currentPos + 1) % 2; 56 | } 57 | 58 | public void switchPos() { 59 | currentPos = nextPos(); 60 | } 61 | 62 | public boolean isInitOk() { 63 | return initOk; 64 | } 65 | 66 | public void setInitOk(boolean initOk) { 67 | this.initOk = initOk; 68 | } 69 | 70 | public boolean isNextReady() { 71 | return nextReady; 72 | } 73 | 74 | public void setNextReady(boolean nextReady) { 75 | this.nextReady = nextReady; 76 | } 77 | 78 | public AtomicBoolean getThreadRunning() { 79 | return threadRunning; 80 | } 81 | 82 | public Lock rLock() { 83 | return lock.readLock(); 84 | } 85 | 86 | public Lock wLock() { 87 | return lock.writeLock(); 88 | } 89 | 90 | public int getStep() { 91 | return step; 92 | } 93 | 94 | public void setStep(int step) { 95 | this.step = step; 96 | } 97 | 98 | public int getMinStep() { 99 | return minStep; 100 | } 101 | 102 | public void setMinStep(int minStep) { 103 | this.minStep = minStep; 104 | } 105 | 106 | public long getUpdateTimestamp() { 107 | return updateTimestamp; 108 | } 109 | 110 | public void setUpdateTimestamp(long updateTimestamp) { 111 | this.updateTimestamp = updateTimestamp; 112 | } 113 | 114 | @Override 115 | public String toString() { 116 | final StringBuilder sb = new StringBuilder("SegmentBuffer{"); 117 | sb.append("key='").append(key).append('\''); 118 | sb.append(", segments=").append(Arrays.toString(segments)); 119 | sb.append(", currentPos=").append(currentPos); 120 | sb.append(", nextReady=").append(nextReady); 121 | sb.append(", initOk=").append(initOk); 122 | sb.append(", threadRunning=").append(threadRunning); 123 | sb.append(", step=").append(step); 124 | sb.append(", minStep=").append(minStep); 125 | sb.append(", updateTimestamp=").append(updateTimestamp); 126 | sb.append('}'); 127 | return sb.toString(); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /leaf-core/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.sankuai.inf.leaf 6 | leaf-parent 7 | 1.0.1 8 | 9 | com.sankuai.inf.leaf 10 | leaf-core 11 | jar 12 | 1.0.1 13 | leaf-core 14 | 15 | 5.1.38 16 | 2.4 17 | 2.7 18 | 1.2.5 19 | 20 | 21 | 22 | org.mybatis 23 | mybatis 24 | 25 | 26 | 27 | org.perf4j 28 | perf4j 29 | 30 | 31 | org.slf4j 32 | slf4j-api 33 | 34 | 35 | commons-io 36 | commons-io 37 | 38 | 39 | 40 | org.apache.curator 41 | curator-recipes 42 | provided 43 | 44 | 45 | com.fasterxml.jackson.core 46 | jackson-databind 47 | ${jackson-databind.version} 48 | provided 49 | 50 | 51 | 52 | org.springframework 53 | spring-core 54 | test 55 | 56 | 57 | org.springframework 58 | spring-beans 59 | test 60 | 61 | 62 | org.springframework 63 | spring-jdbc 64 | test 65 | 66 | 67 | org.springframework 68 | spring-context 69 | test 70 | 71 | 72 | org.springframework 73 | spring-test 74 | test 75 | 76 | 77 | 78 | org.apache.logging.log4j 79 | log4j-slf4j-impl 80 | test 81 | 82 | 83 | org.apache.logging.log4j 84 | log4j-api 85 | test 86 | 87 | 88 | org.apache.logging.log4j 89 | log4j-core 90 | test 91 | 92 | 93 | 94 | com.alibaba 95 | druid 96 | test 97 | 98 | 99 | mysql 100 | mysql-connector-java 101 | 102 | 103 | org.mybatis 104 | mybatis-spring 105 | test 106 | 107 | 108 | 109 | junit 110 | junit 111 | test 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /leaf-core/src/main/java/com/sankuai/inf/leaf/snowflake/SnowflakeIDGenImpl.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.snowflake; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.sankuai.inf.leaf.IDGen; 5 | import com.sankuai.inf.leaf.common.Result; 6 | import com.sankuai.inf.leaf.common.Status; 7 | import com.sankuai.inf.leaf.common.Utils; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.util.Random; 12 | 13 | public class SnowflakeIDGenImpl implements IDGen { 14 | 15 | @Override 16 | public boolean init() { 17 | return true; 18 | } 19 | 20 | private static final Logger LOGGER = LoggerFactory.getLogger(SnowflakeIDGenImpl.class); 21 | 22 | private final long twepoch; 23 | private final long workerIdBits = 10L; 24 | private final long maxWorkerId = ~(-1L << workerIdBits);//最大能够分配的workerid =1023 25 | private final long sequenceBits = 12L; 26 | private final long workerIdShift = sequenceBits; 27 | private final long timestampLeftShift = sequenceBits + workerIdBits; 28 | private final long sequenceMask = ~(-1L << sequenceBits); 29 | private long workerId; 30 | private long sequence = 0L; 31 | private long lastTimestamp = -1L; 32 | private static final Random RANDOM = new Random(); 33 | 34 | public SnowflakeIDGenImpl(String zkAddress, int port) { 35 | //Thu Nov 04 2010 09:42:54 GMT+0800 (中国标准时间) 36 | this(zkAddress, port, 1288834974657L); 37 | } 38 | 39 | /** 40 | * @param zkAddress zk地址 41 | * @param port snowflake监听端口 42 | * @param twepoch 起始的时间戳 43 | */ 44 | public SnowflakeIDGenImpl(String zkAddress, int port, long twepoch) { 45 | this.twepoch = twepoch; 46 | Preconditions.checkArgument(timeGen() > twepoch, "Snowflake not support twepoch gt currentTime"); 47 | final String ip = Utils.getIp(); 48 | SnowflakeZookeeperHolder holder = new SnowflakeZookeeperHolder(ip, String.valueOf(port), zkAddress); 49 | LOGGER.info("twepoch:{} ,ip:{} ,zkAddress:{} port:{}", twepoch, ip, zkAddress, port); 50 | boolean initFlag = holder.init(); 51 | if (initFlag) { 52 | workerId = holder.getWorkerID(); 53 | LOGGER.info("START SUCCESS USE ZK WORKERID-{}", workerId); 54 | } else { 55 | Preconditions.checkArgument(initFlag, "Snowflake Id Gen is not init ok"); 56 | } 57 | Preconditions.checkArgument(workerId >= 0 && workerId <= maxWorkerId, "workerID must gte 0 and lte 1023"); 58 | } 59 | 60 | @Override 61 | public synchronized Result get(String key) { 62 | long timestamp = timeGen(); 63 | if (timestamp < lastTimestamp) { 64 | long offset = lastTimestamp - timestamp; 65 | if (offset <= 5) { 66 | try { 67 | wait(offset << 1); 68 | timestamp = timeGen(); 69 | if (timestamp < lastTimestamp) { 70 | return new Result(-1, Status.EXCEPTION); 71 | } 72 | } catch (InterruptedException e) { 73 | LOGGER.error("wait interrupted"); 74 | return new Result(-2, Status.EXCEPTION); 75 | } 76 | } else { 77 | return new Result(-3, Status.EXCEPTION); 78 | } 79 | } 80 | if (lastTimestamp == timestamp) { 81 | sequence = (sequence + 1) & sequenceMask; 82 | if (sequence == 0) { 83 | //seq 为0的时候表示是下一毫秒时间开始对seq做随机 84 | sequence = RANDOM.nextInt(100); 85 | timestamp = tilNextMillis(lastTimestamp); 86 | } 87 | } else { 88 | //如果是新的ms开始 89 | sequence = RANDOM.nextInt(100); 90 | } 91 | lastTimestamp = timestamp; 92 | long id = ((timestamp - twepoch) << timestampLeftShift) | (workerId << workerIdShift) | sequence; 93 | return new Result(id, Status.SUCCESS); 94 | 95 | } 96 | 97 | protected long tilNextMillis(long lastTimestamp) { 98 | long timestamp = timeGen(); 99 | while (timestamp <= lastTimestamp) { 100 | timestamp = timeGen(); 101 | } 102 | return timestamp; 103 | } 104 | 105 | protected long timeGen() { 106 | return System.currentTimeMillis(); 107 | } 108 | 109 | public long getWorkerId() { 110 | return workerId; 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /leaf-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.sankuai.inf.leaf 7 | leaf-parent 8 | 1.0.1 9 | 10 | com.sankuai.inf.leaf 11 | leaf-server 12 | 1.0.1 13 | jar 14 | leaf-server 15 | Leaf HTTP Server 16 | 17 | 1.5.18.RELEASE 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-dependencies 24 | ${spring-boot-dependencies.version} 25 | pom 26 | import 27 | 28 | 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-web 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-freemarker 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-test 44 | test 45 | 46 | 47 | com.sankuai.inf.leaf 48 | leaf-core 49 | 50 | 51 | com.alibaba 52 | druid 53 | 54 | 55 | 56 | org.apache.curator 57 | curator-recipes 58 | 59 | 60 | log4j 61 | log4j 62 | 63 | 64 | 65 | 66 | junit 67 | junit 68 | test 69 | 70 | 71 | 72 | 73 | 74 | org.springframework.boot 75 | spring-boot-maven-plugin 76 | ${spring-boot-dependencies.version} 77 | 78 | com.sankuai.inf.leaf.server.LeafServerApplication 79 | 80 | 81 | 82 | 83 | repackage 84 | 85 | 86 | 87 | 88 | 89 | maven-dependency-plugin 90 | 91 | 92 | copy-dependencies 93 | compile 94 | 95 | copy-dependencies 96 | 97 | 98 | ${project.build.directory}/lib 99 | false 100 | 101 | false 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /leaf-server/src/main/java/com/sankuai/inf/leaf/server/controller/LeafMonitorController.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.server.controller; 2 | 3 | import com.sankuai.inf.leaf.segment.SegmentIDGenImpl; 4 | import com.sankuai.inf.leaf.server.model.SegmentBufferView; 5 | import com.sankuai.inf.leaf.segment.model.LeafAlloc; 6 | import com.sankuai.inf.leaf.segment.model.SegmentBuffer; 7 | import com.sankuai.inf.leaf.server.service.SegmentService; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Controller; 12 | import org.springframework.ui.Model; 13 | import org.springframework.web.bind.annotation.RequestBody; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | import org.springframework.web.bind.annotation.RequestParam; 16 | import org.springframework.web.bind.annotation.ResponseBody; 17 | 18 | import java.text.SimpleDateFormat; 19 | import java.util.Date; 20 | import java.util.HashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | 24 | @Controller 25 | public class LeafMonitorController { 26 | private Logger logger = LoggerFactory.getLogger(LeafMonitorController.class); 27 | 28 | @Autowired 29 | private SegmentService segmentService; 30 | 31 | @RequestMapping(value = "cache") 32 | public String getCache(Model model) { 33 | Map data = new HashMap<>(); 34 | SegmentIDGenImpl segmentIDGen = segmentService.getIdGen(); 35 | if (segmentIDGen == null) { 36 | throw new IllegalArgumentException("You should config leaf.segment.enable=true first"); 37 | } 38 | Map cache = segmentIDGen.getCache(); 39 | for (Map.Entry entry : cache.entrySet()) { 40 | SegmentBufferView sv = new SegmentBufferView(); 41 | SegmentBuffer buffer = entry.getValue(); 42 | sv.setInitOk(buffer.isInitOk()); 43 | sv.setKey(buffer.getKey()); 44 | sv.setPos(buffer.getCurrentPos()); 45 | sv.setNextReady(buffer.isNextReady()); 46 | sv.setMax0(buffer.getSegments()[0].getMax()); 47 | sv.setValue0(buffer.getSegments()[0].getValue().get()); 48 | sv.setStep0(buffer.getSegments()[0].getStep()); 49 | 50 | sv.setMax1(buffer.getSegments()[1].getMax()); 51 | sv.setValue1(buffer.getSegments()[1].getValue().get()); 52 | sv.setStep1(buffer.getSegments()[1].getStep()); 53 | 54 | data.put(entry.getKey(), sv); 55 | 56 | } 57 | logger.info("Cache info {}", data); 58 | model.addAttribute("data", data); 59 | return "segment"; 60 | } 61 | 62 | @RequestMapping(value = "db") 63 | public String getDb(Model model) { 64 | SegmentIDGenImpl segmentIDGen = segmentService.getIdGen(); 65 | if (segmentIDGen == null) { 66 | throw new IllegalArgumentException("You should config leaf.segment.enable=true first"); 67 | } 68 | List items = segmentIDGen.getAllLeafAllocs(); 69 | logger.info("DB info {}", items); 70 | model.addAttribute("items", items); 71 | return "db"; 72 | } 73 | 74 | /** 75 | * the output is like this: 76 | * { 77 | * "timestamp": "1567733700834(2019-09-06 09:35:00.834)", 78 | * "sequenceId": "3448", 79 | * "workerId": "39" 80 | * } 81 | */ 82 | @RequestMapping(value = "decodeSnowflakeId") 83 | @ResponseBody 84 | public Map decodeSnowflakeId(@RequestParam("snowflakeId") String snowflakeIdStr) { 85 | Map map = new HashMap<>(); 86 | try { 87 | long snowflakeId = Long.parseLong(snowflakeIdStr); 88 | 89 | long originTimestamp = (snowflakeId >> 22) + 1288834974657L; 90 | Date date = new Date(originTimestamp); 91 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); 92 | map.put("timestamp", String.valueOf(originTimestamp) + "(" + sdf.format(date) + ")"); 93 | 94 | long workerId = (snowflakeId >> 12) ^ (snowflakeId >> 22 << 10); 95 | map.put("workerId", String.valueOf(workerId)); 96 | 97 | long sequence = snowflakeId ^ (snowflakeId >> 12 << 12); 98 | map.put("sequenceId", String.valueOf(sequence)); 99 | } catch (NumberFormatException e) { 100 | map.put("errorMsg", "snowflake Id反解析发生异常!"); 101 | } 102 | return map; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Leaf 2 | 3 | > There are no two identical leaves in the world. 4 | > 5 | > ​ — Leibnitz 6 | 7 | [中文文档](./README_CN.md) | [English Document](./README.md) 8 | 9 | ## Introduction 10 | 11 | Leaf refers to some common ID generation schemes in the industry, including redis, UUID, snowflake, etc. 12 | Each of the above approaches has its own problems, so we decided to implement a set of distributed ID generation services to meet the requirements. 13 | At present, Leaf covers Meituan review company's internal finance, catering, takeaway, hotel travel, cat's eye movie and many other business lines. On the basis of 4C8G VM, through the company RPC method, QPS pressure test results are nearly 5w/s, TP999 1ms. 14 | 15 | You can use it to encapsulate a distributed unique id distribution center in a service-oriented SOA architecture as the id distribution provider for all applications 16 | 17 | ## Quick Start 18 | 19 | ### Leaf Server 20 | 21 | Leaf provides an HTTP service based on spring boot to get the id 22 | 23 | #### run Leaf Server 24 | 25 | ##### build 26 | 27 | ```shell 28 | git clone git@github.com:Meituan-Dianping/Leaf.git 29 | cd leaf 30 | mvn clean install -DskipTests 31 | cd leaf-server 32 | ``` 33 | 34 | ##### run 35 | ###### maven 36 | 37 | ```shell 38 | mvn spring-boot:run 39 | ``` 40 | 41 | or 42 | ###### shell command 43 | 44 | ```shell 45 | sh deploy/run.sh 46 | ``` 47 | 48 | ##### test 49 | 50 | ```shell 51 | #segment 52 | curl http://localhost:8080/api/segment/get/leaf-segment-test 53 | #snowflake 54 | curl http://localhost:8080/api/snowflake/get/test 55 | ``` 56 | 57 | #### Configuration 58 | 59 | Leaf provides two ways to generate ids (segment mode and snowflake mode), which you can turn on at the same time or specify one way to turn on (both are off by default). 60 | 61 | Leaf Server configuration is in the leaf-server/src/main/resources/leaf.properties 62 | 63 | | configuration | meaning | default | 64 | | ------------------------- | ----------------------------- | ------ | 65 | | leaf.name | leaf service name | | 66 | | leaf.segment.enable | whether segment mode is enabled | false | 67 | | leaf.jdbc.url | mysql url | | 68 | | leaf.jdbc.username | mysql username | | 69 | | leaf.jdbc.password | mysql password | | 70 | | leaf.snowflake.enable | whether snowflake mode is enabled | false | 71 | | leaf.snowflake.zk.address | zk address under snowflake mode | | 72 | | leaf.snowflake.port | service registration port under snowflake mode | | 73 | 74 | ### Segment mode 75 | 76 | In order to use segment mode, you need to create DB table first, and configure leaf.jdbc.url, leaf.jdbc.username, leaf.jdbc.password 77 | 78 | If you do not want use it, just configure leaf.segment.enable=false to disable it. 79 | 80 | ```sql 81 | CREATE DATABASE leaf 82 | CREATE TABLE `leaf_alloc` ( 83 | `biz_tag` varchar(128) NOT NULL DEFAULT '', -- your biz unique name 84 | `max_id` bigint(20) NOT NULL DEFAULT '1', 85 | `step` int(11) NOT NULL, 86 | `description` varchar(256) DEFAULT NULL, 87 | `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 88 | PRIMARY KEY (`biz_tag`) 89 | ) ENGINE=InnoDB; 90 | 91 | insert into leaf_alloc(biz_tag, max_id, step, description) values('leaf-segment-test', 1, 2000, 'Test leaf Segment Mode Get Id') 92 | ``` 93 | ### Snowflake mode 94 | 95 | The algorithm is taken from twitter's open-source snowflake algorithm. 96 | 97 | If you do not want to use it, just configure leaf.snowflake.enable=false to disable it. 98 | 99 | Configure the zookeeper address 100 | 101 | ``` 102 | leaf.snowflake.zk.address=${address} 103 | leaf.snowflake.enable=true 104 | leaf.snowflake.port=${port} 105 | ``` 106 | 107 | configure leaf.snowflake.zk.address in the leaf.properties, and configure the leaf service listen port leaf.snowflake.port. 108 | 109 | ### monitor page 110 | 111 | segment mode: http://localhost:8080/cache 112 | 113 | ### Leaf Core 114 | 115 | Of course, in order to pursue higher performance, you need to deploy the Leaf service through RPC Server, which only needs to introduce the leaf-core package and encapsulate the API that generates the ID into the specified RPC framework. 116 | 117 | #### Attention 118 | Note that leaf's current IP acquisition logic in the case of snowflake mode takes the first network card IP directly (especially for services that change IP) to avoid wasting the workId 119 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.sankuai.inf.leaf 6 | leaf-parent 7 | pom 8 | 1.0.1 9 | Leaf 10 | 11 | leaf-core 12 | leaf-server 13 | 14 | Distributed ID Generate Service 15 | 16 | 17 | zhangzhitong 18 | zhangzhitong 19 | zhitongzhang@outlook.com 20 | 21 | 22 | zhanghan 23 | zhanghan 24 | han122655904@163.com 25 | 26 | 27 | xiezhaodong 28 | xiezhaodong 29 | pursuer_xie@foxmail.com 30 | 31 | 32 | 33 | Meituan-Dianping Group 34 | https://github.com/Meituan-Dianping 35 | 36 | 37 | UTF-8 38 | UTF-8 39 | 4.3.18.RELEASE 40 | 4.12 41 | 3.5.1 42 | 3.3.0 43 | 0.9.16 44 | 2.6.0 45 | 1.7.2 46 | 1.0.18 47 | 2.9.6 48 | 5.1.38 49 | 2.4 50 | 2.7 51 | 1.2.5 52 | 53 | 54 | 55 | 56 | com.sankuai.inf.leaf 57 | leaf-core 58 | 1.0.1 59 | 60 | 61 | com.alibaba 62 | druid 63 | ${druid.version} 64 | 65 | 66 | 67 | org.apache.curator 68 | curator-recipes 69 | ${curator.version} 70 | 71 | 72 | log4j 73 | log4j 74 | 75 | 76 | 77 | 78 | commons-io 79 | commons-io 80 | ${commons-io.version} 81 | 82 | 83 | org.mybatis 84 | mybatis 85 | ${mybatis.version} 86 | 87 | 88 | 89 | org.perf4j 90 | perf4j 91 | ${perf4j.version} 92 | 93 | 94 | org.slf4j 95 | slf4j-api 96 | ${slf4j.version} 97 | 98 | 99 | 100 | org.springframework 101 | spring-core 102 | ${spring.version} 103 | 104 | 105 | org.springframework 106 | spring-beans 107 | ${spring.version} 108 | 109 | 110 | org.springframework 111 | spring-jdbc 112 | ${spring.version} 113 | 114 | 115 | org.springframework 116 | spring-context 117 | ${spring.version} 118 | 119 | 120 | org.springframework 121 | spring-test 122 | ${spring.version} 123 | 124 | 125 | 126 | org.apache.logging.log4j 127 | log4j-slf4j-impl 128 | ${log4j.version} 129 | 130 | 131 | org.apache.logging.log4j 132 | log4j-api 133 | ${log4j.version} 134 | 135 | 136 | org.apache.logging.log4j 137 | log4j-core 138 | ${log4j.version} 139 | 140 | 141 | mysql 142 | mysql-connector-java 143 | ${mysql-connector-java.version} 144 | 145 | 146 | org.mybatis 147 | mybatis-spring 148 | ${mybatis-spring.version} 149 | 150 | 151 | junit 152 | junit 153 | ${junit.version} 154 | 155 | 156 | 157 | 158 | leaf 159 | 160 | 161 | org.apache.maven.plugins 162 | maven-compiler-plugin 163 | ${maven.compiler.version} 164 | 165 | 1.7 166 | 1.7 167 | 168 | 169 | 170 | org.jacoco 171 | jacoco-maven-plugin 172 | 0.7.5.201505241946 173 | 174 | 175 | pre-unit-test 176 | 177 | prepare-agent 178 | 179 | 180 | 181 | ${project.build.directory}/${project.artifactId}-jacoco.exec 182 | 183 | 184 | 185 | 186 | post-unit-test 187 | test 188 | 189 | report 190 | 191 | 192 | 193 | ${project.build.directory}/${project.artifactId}-jacoco.exec 194 | 195 | ${project.build.directory}/jacoco 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [2018] Leaf 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /leaf-core/src/main/java/com/sankuai/inf/leaf/snowflake/SnowflakeZookeeperHolder.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.snowflake; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.sankuai.inf.leaf.snowflake.exception.CheckLastTimeException; 6 | import org.apache.commons.io.FileUtils; 7 | import org.apache.curator.framework.CuratorFramework; 8 | import org.apache.curator.retry.RetryUntilElapsed; 9 | import org.apache.zookeeper.data.Stat; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import com.google.common.collect.Maps; 14 | import com.sankuai.inf.leaf.common.*; 15 | import org.apache.curator.RetryPolicy; 16 | import org.apache.curator.framework.CuratorFrameworkFactory; 17 | import org.apache.zookeeper.CreateMode; 18 | 19 | import java.io.File; 20 | import java.io.FileInputStream; 21 | import java.io.IOException; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.Properties; 25 | import java.util.concurrent.Executors; 26 | import java.util.concurrent.ThreadFactory; 27 | import java.util.concurrent.TimeUnit; 28 | 29 | public class SnowflakeZookeeperHolder { 30 | private static final Logger LOGGER = LoggerFactory.getLogger(SnowflakeZookeeperHolder.class); 31 | private String zk_AddressNode = null;//保存自身的key ip:port-000000001 32 | private String listenAddress = null;//保存自身的key ip:port 33 | private int workerID; 34 | private static final String PREFIX_ZK_PATH = "/snowflake/" + PropertyFactory.getProperties().getProperty("leaf.name"); 35 | private static final String PROP_PATH = System.getProperty("java.io.tmpdir") + File.separator + PropertyFactory.getProperties().getProperty("leaf.name") + "/leafconf/{port}/workerID.properties"; 36 | private static final String PATH_FOREVER = PREFIX_ZK_PATH + "/forever";//保存所有数据持久的节点 37 | private String ip; 38 | private String port; 39 | private String connectionString; 40 | private long lastUpdateTime; 41 | 42 | public SnowflakeZookeeperHolder(String ip, String port, String connectionString) { 43 | this.ip = ip; 44 | this.port = port; 45 | this.listenAddress = ip + ":" + port; 46 | this.connectionString = connectionString; 47 | } 48 | 49 | public boolean init() { 50 | try { 51 | CuratorFramework curator = createWithOptions(connectionString, new RetryUntilElapsed(1000, 4), 10000, 6000); 52 | curator.start(); 53 | Stat stat = curator.checkExists().forPath(PATH_FOREVER); 54 | if (stat == null) { 55 | //不存在根节点,机器第一次启动,创建/snowflake/ip:port-000000000,并上传数据 56 | zk_AddressNode = createNode(curator); 57 | //worker id 默认是0 58 | updateLocalWorkerID(workerID); 59 | //定时上报本机时间给forever节点 60 | ScheduledUploadData(curator, zk_AddressNode); 61 | return true; 62 | } else { 63 | Map nodeMap = Maps.newHashMap();//ip:port->00001 64 | Map realNode = Maps.newHashMap();//ip:port->(ipport-000001) 65 | //存在根节点,先检查是否有属于自己的根节点 66 | List keys = curator.getChildren().forPath(PATH_FOREVER); 67 | for (String key : keys) { 68 | String[] nodeKey = key.split("-"); 69 | realNode.put(nodeKey[0], key); 70 | nodeMap.put(nodeKey[0], Integer.parseInt(nodeKey[1])); 71 | } 72 | Integer workerid = nodeMap.get(listenAddress); 73 | if (workerid != null) { 74 | //有自己的节点,zk_AddressNode=ip:port 75 | zk_AddressNode = PATH_FOREVER + "/" + realNode.get(listenAddress); 76 | workerID = workerid;//启动worder时使用会使用 77 | if (!checkInitTimeStamp(curator, zk_AddressNode)) { 78 | throw new CheckLastTimeException("init timestamp check error,forever node timestamp gt this node time"); 79 | } 80 | //准备创建临时节点 81 | doService(curator); 82 | updateLocalWorkerID(workerID); 83 | LOGGER.info("[Old NODE]find forever node have this endpoint ip-{} port-{} workid-{} childnode and start SUCCESS", ip, port, workerID); 84 | } else { 85 | //表示新启动的节点,创建持久节点 ,不用check时间 86 | String newNode = createNode(curator); 87 | zk_AddressNode = newNode; 88 | String[] nodeKey = newNode.split("-"); 89 | workerID = Integer.parseInt(nodeKey[1]); 90 | doService(curator); 91 | updateLocalWorkerID(workerID); 92 | LOGGER.info("[New NODE]can not find node on forever node that endpoint ip-{} port-{} workid-{},create own node on forever node and start SUCCESS ", ip, port, workerID); 93 | } 94 | } 95 | } catch (Exception e) { 96 | LOGGER.error("Start node ERROR {}", e); 97 | try { 98 | Properties properties = new Properties(); 99 | properties.load(new FileInputStream(new File(PROP_PATH.replace("{port}", port + "")))); 100 | workerID = Integer.valueOf(properties.getProperty("workerID")); 101 | LOGGER.warn("START FAILED ,use local node file properties workerID-{}", workerID); 102 | } catch (Exception e1) { 103 | LOGGER.error("Read file error ", e1); 104 | return false; 105 | } 106 | } 107 | return true; 108 | } 109 | 110 | private void doService(CuratorFramework curator) { 111 | ScheduledUploadData(curator, zk_AddressNode);// /snowflake_forever/ip:port-000000001 112 | } 113 | 114 | private void ScheduledUploadData(final CuratorFramework curator, final String zk_AddressNode) { 115 | Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { 116 | @Override 117 | public Thread newThread(Runnable r) { 118 | Thread thread = new Thread(r, "schedule-upload-time"); 119 | thread.setDaemon(true); 120 | return thread; 121 | } 122 | }).scheduleWithFixedDelay(new Runnable() { 123 | @Override 124 | public void run() { 125 | updateNewData(curator, zk_AddressNode); 126 | } 127 | }, 1L, 3L, TimeUnit.SECONDS);//每3s上报数据 128 | 129 | } 130 | 131 | private boolean checkInitTimeStamp(CuratorFramework curator, String zk_AddressNode) throws Exception { 132 | byte[] bytes = curator.getData().forPath(zk_AddressNode); 133 | Endpoint endPoint = deBuildData(new String(bytes)); 134 | //该节点的时间不能小于最后一次上报的时间 135 | return !(endPoint.getTimestamp() > System.currentTimeMillis()); 136 | } 137 | 138 | /** 139 | * 创建持久顺序节点 ,并把节点数据放入 value 140 | * 141 | * @param curator 142 | * @return 143 | * @throws Exception 144 | */ 145 | private String createNode(CuratorFramework curator) throws Exception { 146 | try { 147 | return curator.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath(PATH_FOREVER + "/" + listenAddress + "-", buildData().getBytes()); 148 | } catch (Exception e) { 149 | LOGGER.error("create node error msg {} ", e.getMessage()); 150 | throw e; 151 | } 152 | } 153 | 154 | private void updateNewData(CuratorFramework curator, String path) { 155 | try { 156 | if (System.currentTimeMillis() < lastUpdateTime) { 157 | return; 158 | } 159 | curator.setData().forPath(path, buildData().getBytes()); 160 | lastUpdateTime = System.currentTimeMillis(); 161 | } catch (Exception e) { 162 | LOGGER.info("update init data error path is {} error is {}", path, e); 163 | } 164 | } 165 | 166 | /** 167 | * 构建需要上传的数据 168 | * 169 | * @return 170 | */ 171 | private String buildData() throws JsonProcessingException { 172 | Endpoint endpoint = new Endpoint(ip, port, System.currentTimeMillis()); 173 | ObjectMapper mapper = new ObjectMapper(); 174 | String json = mapper.writeValueAsString(endpoint); 175 | return json; 176 | } 177 | 178 | private Endpoint deBuildData(String json) throws IOException { 179 | ObjectMapper mapper = new ObjectMapper(); 180 | Endpoint endpoint = mapper.readValue(json, Endpoint.class); 181 | return endpoint; 182 | } 183 | 184 | /** 185 | * 在节点文件系统上缓存一个workid值,zk失效,机器重启时保证能够正常启动 186 | * 187 | * @param workerID 188 | */ 189 | private void updateLocalWorkerID(int workerID) { 190 | File leafConfFile = new File(PROP_PATH.replace("{port}", port)); 191 | boolean exists = leafConfFile.exists(); 192 | LOGGER.info("file exists status is {}", exists); 193 | if (exists) { 194 | try { 195 | FileUtils.writeStringToFile(leafConfFile, "workerID=" + workerID, false); 196 | LOGGER.info("update file cache workerID is {}", workerID); 197 | } catch (IOException e) { 198 | LOGGER.error("update file cache error ", e); 199 | } 200 | } else { 201 | //不存在文件,父目录页肯定不存在 202 | try { 203 | boolean mkdirs = leafConfFile.getParentFile().mkdirs(); 204 | LOGGER.info("init local file cache create parent dis status is {}, worker id is {}", mkdirs, workerID); 205 | if (mkdirs) { 206 | if (leafConfFile.createNewFile()) { 207 | FileUtils.writeStringToFile(leafConfFile, "workerID=" + workerID, false); 208 | LOGGER.info("local file cache workerID is {}", workerID); 209 | } 210 | } else { 211 | LOGGER.warn("create parent dir error==="); 212 | } 213 | } catch (IOException e) { 214 | LOGGER.warn("craete workerID conf file error", e); 215 | } 216 | } 217 | } 218 | 219 | private CuratorFramework createWithOptions(String connectionString, RetryPolicy retryPolicy, int connectionTimeoutMs, int sessionTimeoutMs) { 220 | return CuratorFrameworkFactory.builder().connectString(connectionString) 221 | .retryPolicy(retryPolicy) 222 | .connectionTimeoutMs(connectionTimeoutMs) 223 | .sessionTimeoutMs(sessionTimeoutMs) 224 | .build(); 225 | } 226 | 227 | /** 228 | * 上报数据结构 229 | */ 230 | static class Endpoint { 231 | private String ip; 232 | private String port; 233 | private long timestamp; 234 | 235 | public Endpoint() { 236 | } 237 | 238 | public Endpoint(String ip, String port, long timestamp) { 239 | this.ip = ip; 240 | this.port = port; 241 | this.timestamp = timestamp; 242 | } 243 | 244 | public String getIp() { 245 | return ip; 246 | } 247 | 248 | public void setIp(String ip) { 249 | this.ip = ip; 250 | } 251 | 252 | public String getPort() { 253 | return port; 254 | } 255 | 256 | public void setPort(String port) { 257 | this.port = port; 258 | } 259 | 260 | public long getTimestamp() { 261 | return timestamp; 262 | } 263 | 264 | public void setTimestamp(long timestamp) { 265 | this.timestamp = timestamp; 266 | } 267 | } 268 | 269 | public String getZk_AddressNode() { 270 | return zk_AddressNode; 271 | } 272 | 273 | public void setZk_AddressNode(String zk_AddressNode) { 274 | this.zk_AddressNode = zk_AddressNode; 275 | } 276 | 277 | public String getListenAddress() { 278 | return listenAddress; 279 | } 280 | 281 | public void setListenAddress(String listenAddress) { 282 | this.listenAddress = listenAddress; 283 | } 284 | 285 | public int getWorkerID() { 286 | return workerID; 287 | } 288 | 289 | public void setWorkerID(int workerID) { 290 | this.workerID = workerID; 291 | } 292 | 293 | } 294 | -------------------------------------------------------------------------------- /leaf-core/src/main/java/com/sankuai/inf/leaf/segment/SegmentIDGenImpl.java: -------------------------------------------------------------------------------- 1 | package com.sankuai.inf.leaf.segment; 2 | 3 | import com.sankuai.inf.leaf.IDGen; 4 | import com.sankuai.inf.leaf.common.Result; 5 | import com.sankuai.inf.leaf.common.Status; 6 | import com.sankuai.inf.leaf.segment.dao.IDAllocDao; 7 | import com.sankuai.inf.leaf.segment.model.*; 8 | import org.perf4j.StopWatch; 9 | import org.perf4j.slf4j.Slf4JStopWatch; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import java.util.*; 14 | import java.util.concurrent.*; 15 | import java.util.concurrent.atomic.AtomicLong; 16 | 17 | public class SegmentIDGenImpl implements IDGen { 18 | private static final Logger logger = LoggerFactory.getLogger(SegmentIDGenImpl.class); 19 | 20 | /** 21 | * IDCache未初始化成功时的异常码 22 | */ 23 | private static final long EXCEPTION_ID_IDCACHE_INIT_FALSE = -1; 24 | /** 25 | * key不存在时的异常码 26 | */ 27 | private static final long EXCEPTION_ID_KEY_NOT_EXISTS = -2; 28 | /** 29 | * SegmentBuffer中的两个Segment均未从DB中装载时的异常码 30 | */ 31 | private static final long EXCEPTION_ID_TWO_SEGMENTS_ARE_NULL = -3; 32 | /** 33 | * 最大步长不超过100,0000 34 | */ 35 | private static final int MAX_STEP = 1000000; 36 | /** 37 | * 一个Segment维持时间为15分钟 38 | */ 39 | private static final long SEGMENT_DURATION = 15 * 60 * 1000L; 40 | private ExecutorService service = new ThreadPoolExecutor(5, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue(), new UpdateThreadFactory()); 41 | private volatile boolean initOK = false; 42 | private Map cache = new ConcurrentHashMap(); 43 | private IDAllocDao dao; 44 | 45 | public static class UpdateThreadFactory implements ThreadFactory { 46 | 47 | private static int threadInitNumber = 0; 48 | 49 | private static synchronized int nextThreadNum() { 50 | return threadInitNumber++; 51 | } 52 | 53 | @Override 54 | public Thread newThread(Runnable r) { 55 | return new Thread(r, "Thread-Segment-Update-" + nextThreadNum()); 56 | } 57 | } 58 | 59 | @Override 60 | public boolean init() { 61 | logger.info("Init ..."); 62 | // 确保加载到kv后才初始化成功 63 | updateCacheFromDb(); 64 | initOK = true; 65 | updateCacheFromDbAtEveryMinute(); 66 | return initOK; 67 | } 68 | 69 | private void updateCacheFromDbAtEveryMinute() { 70 | ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { 71 | @Override 72 | public Thread newThread(Runnable r) { 73 | Thread t = new Thread(r); 74 | t.setName("check-idCache-thread"); 75 | t.setDaemon(true); 76 | return t; 77 | } 78 | }); 79 | service.scheduleWithFixedDelay(new Runnable() { 80 | @Override 81 | public void run() { 82 | updateCacheFromDb(); 83 | } 84 | }, 60, 60, TimeUnit.SECONDS); 85 | } 86 | 87 | private void updateCacheFromDb() { 88 | logger.info("update cache from db"); 89 | StopWatch sw = new Slf4JStopWatch(); 90 | try { 91 | List dbTags = dao.getAllTags(); 92 | if (dbTags == null || dbTags.isEmpty()) { 93 | return; 94 | } 95 | List cacheTags = new ArrayList(cache.keySet()); 96 | Set insertTagsSet = new HashSet<>(dbTags); 97 | Set removeTagsSet = new HashSet<>(cacheTags); 98 | //db中新加的tags灌进cache 99 | for(int i = 0; i < cacheTags.size(); i++){ 100 | String tmp = cacheTags.get(i); 101 | if(insertTagsSet.contains(tmp)){ 102 | insertTagsSet.remove(tmp); 103 | } 104 | } 105 | for (String tag : insertTagsSet) { 106 | SegmentBuffer buffer = new SegmentBuffer(); 107 | buffer.setKey(tag); 108 | Segment segment = buffer.getCurrent(); 109 | segment.setValue(new AtomicLong(0)); 110 | segment.setMax(0); 111 | segment.setStep(0); 112 | cache.put(tag, buffer); 113 | logger.info("Add tag {} from db to IdCache, SegmentBuffer {}", tag, buffer); 114 | } 115 | //cache中已失效的tags从cache删除 116 | for(int i = 0; i < dbTags.size(); i++){ 117 | String tmp = dbTags.get(i); 118 | if(removeTagsSet.contains(tmp)){ 119 | removeTagsSet.remove(tmp); 120 | } 121 | } 122 | for (String tag : removeTagsSet) { 123 | cache.remove(tag); 124 | logger.info("Remove tag {} from IdCache", tag); 125 | } 126 | } catch (Exception e) { 127 | logger.warn("update cache from db exception", e); 128 | } finally { 129 | sw.stop("updateCacheFromDb"); 130 | } 131 | } 132 | 133 | @Override 134 | public Result get(final String key) { 135 | if (!initOK) { 136 | return new Result(EXCEPTION_ID_IDCACHE_INIT_FALSE, Status.EXCEPTION); 137 | } 138 | if (cache.containsKey(key)) { 139 | SegmentBuffer buffer = cache.get(key); 140 | if (!buffer.isInitOk()) { 141 | synchronized (buffer) { 142 | if (!buffer.isInitOk()) { 143 | try { 144 | updateSegmentFromDb(key, buffer.getCurrent()); 145 | logger.info("Init buffer. Update leafkey {} {} from db", key, buffer.getCurrent()); 146 | buffer.setInitOk(true); 147 | } catch (Exception e) { 148 | logger.warn("Init buffer {} exception", buffer.getCurrent(), e); 149 | } 150 | } 151 | } 152 | } 153 | return getIdFromSegmentBuffer(cache.get(key)); 154 | } 155 | return new Result(EXCEPTION_ID_KEY_NOT_EXISTS, Status.EXCEPTION); 156 | } 157 | 158 | public void updateSegmentFromDb(String key, Segment segment) { 159 | StopWatch sw = new Slf4JStopWatch(); 160 | SegmentBuffer buffer = segment.getBuffer(); 161 | LeafAlloc leafAlloc; 162 | if (!buffer.isInitOk()) { 163 | leafAlloc = dao.updateMaxIdAndGetLeafAlloc(key); 164 | buffer.setStep(leafAlloc.getStep()); 165 | buffer.setMinStep(leafAlloc.getStep());//leafAlloc中的step为DB中的step 166 | } else if (buffer.getUpdateTimestamp() == 0) { 167 | leafAlloc = dao.updateMaxIdAndGetLeafAlloc(key); 168 | buffer.setUpdateTimestamp(System.currentTimeMillis()); 169 | buffer.setStep(leafAlloc.getStep()); 170 | buffer.setMinStep(leafAlloc.getStep());//leafAlloc中的step为DB中的step 171 | } else { 172 | long duration = System.currentTimeMillis() - buffer.getUpdateTimestamp(); 173 | int nextStep = buffer.getStep(); 174 | if (duration < SEGMENT_DURATION) { 175 | if (nextStep * 2 > MAX_STEP) { 176 | //do nothing 177 | } else { 178 | nextStep = nextStep * 2; 179 | } 180 | } else if (duration < SEGMENT_DURATION * 2) { 181 | //do nothing with nextStep 182 | } else { 183 | nextStep = nextStep / 2 >= buffer.getMinStep() ? nextStep / 2 : nextStep; 184 | } 185 | logger.info("leafKey[{}], step[{}], duration[{}mins], nextStep[{}]", key, buffer.getStep(), String.format("%.2f",((double)duration / (1000 * 60))), nextStep); 186 | LeafAlloc temp = new LeafAlloc(); 187 | temp.setKey(key); 188 | temp.setStep(nextStep); 189 | leafAlloc = dao.updateMaxIdByCustomStepAndGetLeafAlloc(temp); 190 | buffer.setUpdateTimestamp(System.currentTimeMillis()); 191 | buffer.setStep(nextStep); 192 | buffer.setMinStep(leafAlloc.getStep());//leafAlloc的step为DB中的step 193 | } 194 | // must set value before set max 195 | long value = leafAlloc.getMaxId() - buffer.getStep(); 196 | segment.getValue().set(value); 197 | segment.setMax(leafAlloc.getMaxId()); 198 | segment.setStep(buffer.getStep()); 199 | sw.stop("updateSegmentFromDb", key + " " + segment); 200 | } 201 | 202 | public Result getIdFromSegmentBuffer(final SegmentBuffer buffer) { 203 | while (true) { 204 | buffer.rLock().lock(); 205 | try { 206 | final Segment segment = buffer.getCurrent(); 207 | if (!buffer.isNextReady() && (segment.getIdle() < 0.9 * segment.getStep()) && buffer.getThreadRunning().compareAndSet(false, true)) { 208 | service.execute(new Runnable() { 209 | @Override 210 | public void run() { 211 | Segment next = buffer.getSegments()[buffer.nextPos()]; 212 | boolean updateOk = false; 213 | try { 214 | updateSegmentFromDb(buffer.getKey(), next); 215 | updateOk = true; 216 | logger.info("update segment {} from db {}", buffer.getKey(), next); 217 | } catch (Exception e) { 218 | logger.warn(buffer.getKey() + " updateSegmentFromDb exception", e); 219 | } finally { 220 | if (updateOk) { 221 | buffer.wLock().lock(); 222 | buffer.setNextReady(true); 223 | buffer.getThreadRunning().set(false); 224 | buffer.wLock().unlock(); 225 | } else { 226 | buffer.getThreadRunning().set(false); 227 | } 228 | } 229 | } 230 | }); 231 | } 232 | long value = segment.getValue().getAndIncrement(); 233 | if (value < segment.getMax()) { 234 | return new Result(value, Status.SUCCESS); 235 | } 236 | } finally { 237 | buffer.rLock().unlock(); 238 | } 239 | waitAndSleep(buffer); 240 | buffer.wLock().lock(); 241 | try { 242 | final Segment segment = buffer.getCurrent(); 243 | long value = segment.getValue().getAndIncrement(); 244 | if (value < segment.getMax()) { 245 | return new Result(value, Status.SUCCESS); 246 | } 247 | if (buffer.isNextReady()) { 248 | buffer.switchPos(); 249 | buffer.setNextReady(false); 250 | } else { 251 | logger.error("Both two segments in {} are not ready!", buffer); 252 | return new Result(EXCEPTION_ID_TWO_SEGMENTS_ARE_NULL, Status.EXCEPTION); 253 | } 254 | } finally { 255 | buffer.wLock().unlock(); 256 | } 257 | } 258 | } 259 | 260 | private void waitAndSleep(SegmentBuffer buffer) { 261 | int roll = 0; 262 | while (buffer.getThreadRunning().get()) { 263 | roll += 1; 264 | if(roll > 10000) { 265 | try { 266 | TimeUnit.MILLISECONDS.sleep(10); 267 | break; 268 | } catch (InterruptedException e) { 269 | logger.warn("Thread {} Interrupted",Thread.currentThread().getName()); 270 | break; 271 | } 272 | } 273 | } 274 | } 275 | 276 | public List getAllLeafAllocs() { 277 | return dao.getAllLeafAllocs(); 278 | } 279 | 280 | public Map getCache() { 281 | return cache; 282 | } 283 | 284 | public IDAllocDao getDao() { 285 | return dao; 286 | } 287 | 288 | public void setDao(IDAllocDao dao) { 289 | this.dao = dao; 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /leaf-server/src/main/resources/static/css/bootstrap.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.7 (http://getbootstrap.com) 3 | * Copyright 2011-2016 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{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 #ddd!important}}@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#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro: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"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}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:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.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-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-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-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.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{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.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{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.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{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.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>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.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:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-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-image:-o-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-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);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-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-image:-o-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-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)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-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-image:-o-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-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)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-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-image:-o-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-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)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-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-image:-o-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-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)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.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-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} 6 | /*# sourceMappingURL=bootstrap.min.css.map */ --------------------------------------------------------------------------------